1#!/usr/bin/env python
2#
3# Copyright (C) 2009 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""Return information about routing table entries
19
20Read and parse the system routing table. There are
21two classes defined here: NetworkRoutes, which contains
22information about all routes, and Route, which describes
23a single routing table entry.
24"""
25
26ROUTES_FILE = "/proc/net/route"
27# The following constants are from <net/route.h>
28RTF_UP      = 0x0001
29RTF_GATEWAY = 0x0002
30RTF_HOST    = 0x0004
31
32import socket
33import struct
34
35def intToDottedQuad(addr):
36    return socket.inet_ntoa(struct.pack('@I', addr))
37
38def convertIpToInt(i):
39    """Convert the supplied argument to an int representing an IP address."""
40    if isinstance(i, int):
41        return i
42    return struct.unpack('I', socket.inet_aton(i))[0]
43
44class Route(object):
45    def __init__(self, iface, dest, gway, flags, mask):
46        self.interface = iface
47        self.destination = int(dest, 16)
48        self.gateway = int(gway, 16)
49        self.flagbits = int(flags, 16)
50        self.netmask = int(mask, 16)
51
52    def __str__(self):
53        flags = ""
54        if self.flagbits & RTF_UP:
55            flags += "U"
56        if self.flagbits & RTF_GATEWAY:
57            flags += "G"
58        if self.flagbits & RTF_HOST:
59            flags += "H"
60        return "<%s dest: %s gway: %s mask: %s flags: %s>" % (
61                self.interface,
62                intToDottedQuad(self.destination),
63                intToDottedQuad(self.gateway),
64                intToDottedQuad(self.netmask),
65                flags)
66
67    def isUsable(self):
68        return self.flagbits & RTF_UP
69
70    def isHostRoute(self):
71        return self.flagbits & RTF_HOST
72
73    def isGatewayRoute(self):
74        return self.flagbits & RTF_GATEWAY
75
76    def isInterfaceRoute(self):
77        return (self.flagbits & RTF_GATEWAY) == 0
78
79    def isDefaultRoute(self):
80        return (self.flagbits & RTF_GATEWAY) and self.destination == 0
81
82    def matches(self, ip):
83        return (ip & self.netmask) == self.destination
84
85class NetworkRoutes(object):
86    def __init__(self, routelist=None):
87        if not routelist:
88            routef = open(ROUTES_FILE)
89            routelist = routef.readlines()
90            routef.close()
91
92        # The first line is headers that will allow us
93        # to correctly interpret the values in the following
94        # lines
95        colMap = {}
96        headers = routelist[0].split()
97        for (pos, token) in enumerate(headers):
98            colMap[token] = pos
99
100        self.routes = []
101        for routeline in routelist[1:]:
102            route = routeline.split()
103            interface = route[colMap["Iface"]]
104            destination = route[colMap["Destination"]]
105            gateway = route[colMap["Gateway"]]
106            flags = route[colMap["Flags"]]
107            mask = route[colMap["Mask"]]
108            self.routes.append(
109                    Route(interface, destination, gateway, flags, mask))
110
111    def hasDefaultRoute(self, interface):
112        for rr in self.routes:
113            if (rr.isUsable() and
114                    rr.interface == interface and
115                    rr.isDefaultRoute()):
116                return True
117        return False
118
119    def getDefaultRoutes(self):
120        defroutes = []
121        for rr in self.routes:
122            if rr.isUsable() and rr.isDefaultRoute():
123                defroutes.append(rr)
124        return defroutes
125
126    def hasInterfaceRoute(self, interface):
127        for rr in self.routes:
128            if (rr.isUsable() and
129                    rr.interface == interface and
130                    rr.isInterfaceRoute()):
131                return True
132            return False
133
134    def getRouteFor(self, ip_as_int_or_string):
135        ip = convertIpToInt(ip_as_int_or_string)
136        for rr in self.routes:
137            if rr.isUsable() and rr.matches(ip):
138                return rr
139        return None
140
141
142if __name__ == "__main__":
143    routes = NetworkRoutes()
144    if routes == None:
145        print "Failed to read routing table"
146    else:
147        for each_route in routes.routes:
148            print each_route
149
150        print "hasDefaultRoute(\"eth0\"):", routes.hasDefaultRoute("eth0")
151
152        dflts = routes.getDefaultRoutes()
153        if dflts == None:
154            print "There are no default routes"
155        else:
156            print "There are %d default routes" % (len(dflts))
157
158
159        print "hasInterfaceRoute(\"eth0\"):", routes.hasInterfaceRoute("eth0")
160
161    routes = NetworkRoutes([
162        "Iface Destination Gateway  Flags RefCnt "
163        "Use Metric Mask MTU Window IRTT",
164        "ones 00010203 FE010203 0007 0 0 0 00FFFFFF 0 0 0\n",
165        "default 00000000 09080706 0007 0 0 0 00000000 0 0 0\n",
166        ])
167
168    print routes.getRouteFor(0x01010203)
169    print routes.getRouteFor("3.2.1.1")
170    print routes.getRouteFor(0x08010209)
171