1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Return information about routing table entries 6 7Read and parse the system routing table. There are 8four classes defined here: NetworkRoutes, which contains 9information about all routes; IPv4Route, which describes 10a single IPv4 routing table entry; IPv6Route, which 11does the same for IPv6; and Route, which has common code 12for IPv4Route and IPv6Route. 13""" 14 15ROUTES_V4_FILE = "/proc/net/route" 16ROUTES_V6_FILE = "/proc/net/ipv6_route" 17 18# The following constants are from <net/route.h> 19RTF_UP = 0x0001 20RTF_GATEWAY = 0x0002 21RTF_HOST = 0x0004 22# IPv6 constants from <net/route.h> 23RTF_DEFAULT = 0x10000 24 25import socket 26import struct 27 28class Route(object): 29 def __init__(self, iface, dest, gway, flags, mask): 30 self.interface = iface 31 self.destination = dest 32 self.gateway = gway 33 self.flagbits = flags 34 self.netmask = mask 35 36 def __str__(self): 37 flags = "" 38 if self.flagbits & RTF_UP: 39 flags += "U" 40 if self.flagbits & RTF_GATEWAY: 41 flags += "G" 42 if self.flagbits & RTF_HOST: 43 flags += "H" 44 if self.flagbits & RTF_DEFAULT: 45 flags += "D" 46 return "<%s dest: %s gway: %s mask: %s flags: %s>" % ( 47 self.interface, 48 self._intToIp(self.destination), 49 self._intToIp(self.gateway), 50 self._intToIp(self.netmask), 51 flags) 52 53 def isUsable(self): 54 return self.flagbits & RTF_UP 55 56 def isHostRoute(self): 57 return self.flagbits & RTF_HOST 58 59 def isGatewayRoute(self): 60 return self.flagbits & RTF_GATEWAY 61 62 def isInterfaceRoute(self): 63 return (self.flagbits & RTF_GATEWAY) == 0 64 65 def matches(self, ip): 66 try: 67 return (self._ipToInt(ip) & self.netmask) == self.destination 68 except socket.error: 69 return False 70 71 72class IPv4Route(Route): 73 def __init__(self, iface, dest, gway, flags, mask): 74 super(IPv4Route, self).__init__( 75 iface, int(dest, 16), int(gway, 16), int(flags, 16), int(mask, 16)) 76 77 def _intToIp(self, addr): 78 return socket.inet_ntoa(struct.pack('@I', addr)) 79 80 def _ipToInt(self, ip): 81 return struct.unpack('I', socket.inet_aton(ip))[0] 82 83 def isDefaultRoute(self): 84 return (self.flagbits & RTF_GATEWAY) and self.destination == 0 85 86def parseIPv4Routes(routelist): 87 # The first line is headers that will allow us 88 # to correctly interpret the values in the following 89 # lines 90 headers = routelist[0].split() 91 col_map = {token: pos for (pos, token) in enumerate(headers)} 92 93 routes = [] 94 for routeline in routelist[1:]: 95 route = routeline.split() 96 interface = route[col_map["Iface"]] 97 destination = route[col_map["Destination"]] 98 gateway = route[col_map["Gateway"]] 99 flags = route[col_map["Flags"]] 100 mask = route[col_map["Mask"]] 101 routes.append(IPv4Route(interface, destination, gateway, flags, mask)) 102 103 return routes 104 105 106class IPv6Route(Route): 107 def __init__(self, iface, dest, gway, flags, plen): 108 super(IPv6Route, self).__init__( 109 iface, 110 long(dest, 16), 111 long(gway, 16), 112 long(flags, 16), 113 # netmask = set first plen bits to 1, all following to 0 114 (1 << 128) - (1 << (128 - int(plen, 16)))) 115 116 def _intToIp(self, addr): 117 return socket.inet_ntop(socket.AF_INET6, ("%032x" % addr).decode("hex")) 118 119 def _ipToInt(self, ip): 120 return long(socket.inet_pton(socket.AF_INET6, ip).encode("hex"), 16) 121 122 def isDefaultRoute(self): 123 return self.flagbits & RTF_DEFAULT 124 125def parseIPv6Routes(routelist): 126 # ipv6_route has no headers, so the routing table looks like the following: 127 # Dest DestPrefix Src SrcPrefix Gateway Metric RefCnt UseCnt Flags Iface 128 routes = [] 129 for routeline in routelist: 130 route = routeline.split() 131 interface = route[9] 132 destination = route[0] 133 gateway = route[4] 134 flags = route[8] 135 prefix = route[1] 136 routes.append(IPv6Route(interface, destination, gateway, flags, prefix)) 137 138 return routes 139 140 141class NetworkRoutes(object): 142 def __init__(self, routelist_v4=None, routelist_v6=None): 143 if routelist_v4 is None: 144 with open(ROUTES_V4_FILE) as routef_v4: 145 routelist_v4 = routef_v4.readlines() 146 147 self.routes = parseIPv4Routes(routelist_v4) 148 149 if routelist_v6 is None: 150 with open(ROUTES_V6_FILE) as routef_v6: 151 routelist_v6 = routef_v6.readlines() 152 153 self.routes += parseIPv6Routes(routelist_v6) 154 155 def _filterUsableRoutes(self): 156 return (rr for rr in self.routes if rr.isUsable()) 157 158 def hasDefaultRoute(self, interface): 159 return any(rr for rr in self._filterUsableRoutes() 160 if (rr.interface == interface and rr.isDefaultRoute())) 161 162 def getDefaultRoutes(self): 163 return [rr for rr in self._filterUsableRoutes() if rr.isDefaultRoute()] 164 165 def hasInterfaceRoute(self, interface): 166 return any(rr for rr in self._filterUsableRoutes() 167 if (rr.interface == interface and rr.isInterfaceRoute())) 168 169 def getRouteFor(self, ip): 170 for rr in self._filterUsableRoutes(): 171 if rr.matches(ip): 172 return rr 173 return None 174