blob: d14f8e7d9806d75331d3db51f2758d9c898a3836 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (C) 2009 The Android Open Source Project
#
# 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.
#
"""Return information about routing table entries
Read and parse the system routing table. There are
four classes defined here: NetworkRoutes, which contains
information about all routes; IPv4Route, which describes
a single IPv4 routing table entry; IPv6Route, which
does the same for IPv6; and Route, which has common code
for IPv4Route and IPv6Route.
"""
ROUTES_V4_FILE = "/proc/net/route"
ROUTES_V6_FILE = "/proc/net/ipv6_route"
# The following constants are from <net/route.h>
RTF_UP = 0x0001
RTF_GATEWAY = 0x0002
RTF_HOST = 0x0004
# IPv6 constants from <net/route.h>
RTF_DEFAULT = 0x10000
import socket
import struct
class Route(object):
def __init__(self, iface, dest, gway, flags, mask):
self.interface = iface
self.destination = dest
self.gateway = gway
self.flagbits = flags
self.netmask = mask
def __str__(self):
flags = ""
if self.flagbits & RTF_UP:
flags += "U"
if self.flagbits & RTF_GATEWAY:
flags += "G"
if self.flagbits & RTF_HOST:
flags += "H"
if self.flagbits & RTF_DEFAULT:
flags += "D"
return "<%s dest: %s gway: %s mask: %s flags: %s>" % (
self.interface,
self._intToIp(self.destination),
self._intToIp(self.gateway),
self._intToIp(self.netmask),
flags)
def isUsable(self):
return self.flagbits & RTF_UP
def isHostRoute(self):
return self.flagbits & RTF_HOST
def isGatewayRoute(self):
return self.flagbits & RTF_GATEWAY
def isInterfaceRoute(self):
return (self.flagbits & RTF_GATEWAY) == 0
def matches(self, ip):
try:
return (self._ipToInt(ip) & self.netmask) == self.destination
except socket.error:
return False
class IPv4Route(Route):
def __init__(self, iface, dest, gway, flags, mask):
super(IPv4Route, self).__init__(
iface, int(dest, 16), int(gway, 16), int(flags, 16), int(mask, 16))
def _intToIp(self, addr):
return socket.inet_ntoa(struct.pack('@I', addr))
def _ipToInt(self, ip):
return struct.unpack('I', socket.inet_aton(ip))[0]
def isDefaultRoute(self):
return (self.flagbits & RTF_GATEWAY) and self.destination == 0
def parseIPv4Routes(routelist):
# The first line is headers that will allow us
# to correctly interpret the values in the following
# lines
headers = routelist[0].split()
col_map = {token: pos for (pos, token) in enumerate(headers)}
routes = []
for routeline in routelist[1:]:
route = routeline.split()
interface = route[col_map["Iface"]]
destination = route[col_map["Destination"]]
gateway = route[col_map["Gateway"]]
flags = route[col_map["Flags"]]
mask = route[col_map["Mask"]]
routes.append(IPv4Route(interface, destination, gateway, flags, mask))
return routes
class IPv6Route(Route):
def __init__(self, iface, dest, gway, flags, plen):
super(IPv6Route, self).__init__(
iface,
long(dest, 16),
long(gway, 16),
long(flags, 16),
# netmask = set first plen bits to 1, all following to 0
(1 << 128) - (1 << (128 - int(plen, 16))))
def _intToIp(self, addr):
return socket.inet_ntop(socket.AF_INET6, ("%032x" % addr).decode("hex"))
def _ipToInt(self, ip):
return long(socket.inet_pton(socket.AF_INET6, ip).encode("hex"), 16)
def isDefaultRoute(self):
return self.flagbits & RTF_DEFAULT
def parseIPv6Routes(routelist):
# ipv6_route has no headers, so the routing table looks like the following:
# Dest DestPrefix Src SrcPrefix Gateway Metric RefCnt UseCnt Flags Iface
routes = []
for routeline in routelist:
route = routeline.split()
interface = route[9]
destination = route[0]
gateway = route[4]
flags = route[8]
prefix = route[1]
routes.append(IPv6Route(interface, destination, gateway, flags, prefix))
return routes
class NetworkRoutes(object):
def __init__(self, routelist_v4=None, routelist_v6=None):
if routelist_v4 is None:
with open(ROUTES_V4_FILE) as routef_v4:
routelist_v4 = routef_v4.readlines()
self.routes = parseIPv4Routes(routelist_v4)
if routelist_v6 is None:
with open(ROUTES_V6_FILE) as routef_v6:
routelist_v6 = routef_v6.readlines()
self.routes += parseIPv6Routes(routelist_v6)
def _filterUsableRoutes(self):
return (rr for rr in self.routes if rr.isUsable())
def hasDefaultRoute(self, interface):
return any(rr for rr in self._filterUsableRoutes()
if (rr.interface == interface and rr.isDefaultRoute()))
def getDefaultRoutes(self):
return [rr for rr in self._filterUsableRoutes() if rr.isDefaultRoute()]
def hasInterfaceRoute(self, interface):
return any(rr for rr in self._filterUsableRoutes()
if (rr.interface == interface and rr.isInterfaceRoute()))
def getRouteFor(self, ip):
for rr in self._filterUsableRoutes():
if rr.matches(ip):
return rr
return None
if __name__ == "__main__":
routes = NetworkRoutes()
if len(routes.routes) == 0:
print "Failed to read routing table"
else:
for each_route in routes.routes:
print each_route
print "hasDefaultRoute(\"eth0\"):", routes.hasDefaultRoute("eth0")
dflts = routes.getDefaultRoutes()
if len(dflts) == 0:
print "There are no default routes"
else:
print "There are %d default routes" % len(dflts)
print "hasInterfaceRoute(\"eth0\"):", routes.hasInterfaceRoute("eth0")
routes = NetworkRoutes(routelist_v4=[
"Iface Destination Gateway Flags RefCnt "
"Use Metric Mask MTU Window IRTT",
"ones 00010203 FE010203 0007 0 0 0 00FFFFFF 0 0 0\n",
"default 00000000 09080706 0007 0 0 0 00000000 0 0 0\n",
])
print routes.getRouteFor("3.2.1.1")
print routes.getRouteFor("9.2.1.8")
routes = NetworkRoutes(routelist_v6=[
"000102030405060700000000deadbeef 80 "
"00000000000000000000000000000000 00 "
"00000000000000000000000000000000 00000000 "
"00000001 000075c7 00000001 exact",
"00000000000000000000000000000000 00 "
"00000000000000000000000000000000 00 "
"0f0e0d0b0c0a09080706050403020100 00000000 "
"00000005 0000feed 00010003 default",
])
print routes.getRouteFor("1:203:405:607::dead:beef")
print routes.getRouteFor("f00b:a200::abad:1dea")