| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | // Copyright 2020 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 04:35:24 -07:00
										 |  |  | //go:build !nonetworkroute
 | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | // +build !nonetworkroute
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package collector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	"net" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-20 10:23:06 -07:00
										 |  |  | 	"github.com/jsimonetti/rtnetlink/v2" | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2024-09-20 10:23:06 -07:00
										 |  |  | 	"golang.org/x/sys/unix" | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type networkRouteCollector struct { | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 	routeInfoDesc *prometheus.Desc | 
					
						
							|  |  |  | 	routesDesc    *prometheus.Desc | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	logger        *slog.Logger | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	registerCollector("network_route", defaultDisabled, NewNetworkRouteCollector) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 18:55:52 -07:00
										 |  |  | // NewNetworkRouteCollector returns a new Collector exposing systemd statistics.
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | func NewNetworkRouteCollector(logger *slog.Logger) (Collector, error) { | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	const subsystem = "network" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 	routeInfoDesc := prometheus.NewDesc( | 
					
						
							|  |  |  | 		prometheus.BuildFQName(namespace, subsystem, "route_info"), | 
					
						
							|  |  |  | 		"network routing table information", []string{"device", "src", "dest", "gw", "priority", "proto", "weight"}, nil, | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 	routesDesc := prometheus.NewDesc( | 
					
						
							|  |  |  | 		prometheus.BuildFQName(namespace, subsystem, "routes"), | 
					
						
							|  |  |  | 		"network routes by interface", []string{"device"}, nil, | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &networkRouteCollector{ | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 		routeInfoDesc: routeInfoDesc, | 
					
						
							|  |  |  | 		routesDesc:    routesDesc, | 
					
						
							|  |  |  | 		logger:        logger, | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (n networkRouteCollector) Update(ch chan<- prometheus.Metric) error { | 
					
						
							|  |  |  | 	deviceRoutes := make(map[string]int) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 	conn, err := rtnetlink.Dial(nil) | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 		return fmt.Errorf("couldn't connect rtnetlink: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer conn.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	links, err := conn.Link.List() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("couldn't get links: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	routes, err := conn.Route.List() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("couldn't get routes: %w", err) | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, route := range routes { | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 		if route.Type != unix.RTA_DST { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(route.Attributes.Multipath) != 0 { | 
					
						
							|  |  |  | 			for _, nextHop := range route.Attributes.Multipath { | 
					
						
							|  |  |  | 				ifName := "" | 
					
						
							|  |  |  | 				for _, link := range links { | 
					
						
							|  |  |  | 					if link.Index == nextHop.Hop.IfIndex { | 
					
						
							|  |  |  | 						ifName = link.Attributes.Name | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 				labels := []string{ | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 					ifName, // if
 | 
					
						
							|  |  |  | 					networkRouteIPToString(route.Attributes.Src),                            // src
 | 
					
						
							|  |  |  | 					networkRouteIPWithPrefixToString(route.Attributes.Dst, route.DstLength), // dest
 | 
					
						
							|  |  |  | 					networkRouteIPToString(nextHop.Gateway),                                 // gw
 | 
					
						
							|  |  |  | 					strconv.FormatUint(uint64(route.Attributes.Priority), 10),               // priority(metrics)
 | 
					
						
							|  |  |  | 					networkRouteProtocolToString(route.Protocol),                            // proto
 | 
					
						
							|  |  |  | 					strconv.Itoa(int(nextHop.Hop.Hops) + 1),                                 // weight
 | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 				ch <- prometheus.MustNewConstMetric(n.routeInfoDesc, prometheus.GaugeValue, 1, labels...) | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 				deviceRoutes[ifName]++ | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 			ifName := "" | 
					
						
							|  |  |  | 			for _, link := range links { | 
					
						
							|  |  |  | 				if link.Index == route.Attributes.OutIface { | 
					
						
							|  |  |  | 					ifName = link.Attributes.Name | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 			labels := []string{ | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 				ifName, // if
 | 
					
						
							|  |  |  | 				networkRouteIPToString(route.Attributes.Src),                            // src
 | 
					
						
							|  |  |  | 				networkRouteIPWithPrefixToString(route.Attributes.Dst, route.DstLength), // dest
 | 
					
						
							|  |  |  | 				networkRouteIPToString(route.Attributes.Gateway),                        // gw
 | 
					
						
							|  |  |  | 				strconv.FormatUint(uint64(route.Attributes.Priority), 10),               // priority(metrics)
 | 
					
						
							|  |  |  | 				networkRouteProtocolToString(route.Protocol),                            // proto
 | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 				"", // weight
 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 			ch <- prometheus.MustNewConstMetric(n.routeInfoDesc, prometheus.GaugeValue, 1, labels...) | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 			deviceRoutes[ifName]++ | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for dev, total := range deviceRoutes { | 
					
						
							| 
									
										
										
										
											2021-02-05 09:10:42 -08:00
										 |  |  | 		ch <- prometheus.MustNewConstMetric(n.routesDesc, prometheus.GaugeValue, float64(total), dev) | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | func networkRouteIPWithPrefixToString(ip net.IP, len uint8) string { | 
					
						
							|  |  |  | 	if len == 0 { | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 		return "default" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | 	iplen := net.IPv4len | 
					
						
							|  |  |  | 	if ip.To4() == nil { | 
					
						
							|  |  |  | 		iplen = net.IPv6len | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	network := &net.IPNet{ | 
					
						
							|  |  |  | 		IP:   ip, | 
					
						
							|  |  |  | 		Mask: net.CIDRMask(int(len), iplen*8), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return network.String() | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func networkRouteIPToString(ip net.IP) string { | 
					
						
							|  |  |  | 	if len(ip) == 0 { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ip.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 18:35:33 -08:00
										 |  |  | func networkRouteProtocolToString(protocol uint8) string { | 
					
						
							| 
									
										
										
										
											2020-08-12 23:57:48 -07:00
										 |  |  | 	// from linux kernel 'include/uapi/linux/rtnetlink.h'
 | 
					
						
							|  |  |  | 	switch protocol { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		return "unspec" | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		return "redirect" | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		return "kernel" | 
					
						
							|  |  |  | 	case 3: | 
					
						
							|  |  |  | 		return "boot" | 
					
						
							|  |  |  | 	case 4: | 
					
						
							|  |  |  | 		return "static" | 
					
						
							|  |  |  | 	case 8: | 
					
						
							|  |  |  | 		return "gated" | 
					
						
							|  |  |  | 	case 9: | 
					
						
							|  |  |  | 		return "ra" | 
					
						
							|  |  |  | 	case 10: | 
					
						
							|  |  |  | 		return "mrt" | 
					
						
							|  |  |  | 	case 11: | 
					
						
							|  |  |  | 		return "zebra" | 
					
						
							|  |  |  | 	case 12: | 
					
						
							|  |  |  | 		return "bird" | 
					
						
							|  |  |  | 	case 13: | 
					
						
							|  |  |  | 		return "dnrouted" | 
					
						
							|  |  |  | 	case 14: | 
					
						
							|  |  |  | 		return "xorp" | 
					
						
							|  |  |  | 	case 15: | 
					
						
							|  |  |  | 		return "ntk" | 
					
						
							|  |  |  | 	case 16: | 
					
						
							|  |  |  | 		return "dhcp" | 
					
						
							|  |  |  | 	case 17: | 
					
						
							|  |  |  | 		return "mrouted" | 
					
						
							|  |  |  | 	case 42: | 
					
						
							|  |  |  | 		return "babel" | 
					
						
							|  |  |  | 	case 186: | 
					
						
							|  |  |  | 		return "bgp" | 
					
						
							|  |  |  | 	case 187: | 
					
						
							|  |  |  | 		return "isis" | 
					
						
							|  |  |  | 	case 188: | 
					
						
							|  |  |  | 		return "ospf" | 
					
						
							|  |  |  | 	case 189: | 
					
						
							|  |  |  | 		return "rip" | 
					
						
							|  |  |  | 	case 192: | 
					
						
							|  |  |  | 		return "eigrp" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "unknown" | 
					
						
							|  |  |  | } |