OSDN Git Service

ANRdaemon: move trace result from /sdcard to /data am: d93aa41807
[android-x86/system-extras.git] / tests / net_test / iproute.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 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 """Partial Python implementation of iproute functionality."""
18
19 # pylint: disable=g-bad-todo
20
21 import errno
22 import os
23 import socket
24 import struct
25 import sys
26
27 import cstruct
28 import netlink
29
30
31 ### Base netlink constants. See include/uapi/linux/netlink.h.
32 NETLINK_ROUTE = 0
33
34 # Request constants.
35 NLM_F_REQUEST = 1
36 NLM_F_ACK = 4
37 NLM_F_REPLACE = 0x100
38 NLM_F_EXCL = 0x200
39 NLM_F_CREATE = 0x400
40 NLM_F_DUMP = 0x300
41
42 # Message types.
43 NLMSG_ERROR = 2
44 NLMSG_DONE = 3
45
46 # Data structure formats.
47 # These aren't constants, they're classes. So, pylint: disable=invalid-name
48 NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
49 NLMsgErr = cstruct.Struct("NLMsgErr", "=i", "error")
50 NLAttr = cstruct.Struct("NLAttr", "=HH", "nla_len nla_type")
51
52 # Alignment / padding.
53 NLA_ALIGNTO = 4
54
55
56 ### rtnetlink constants. See include/uapi/linux/rtnetlink.h.
57 # Message types.
58 RTM_NEWLINK = 16
59 RTM_DELLINK = 17
60 RTM_GETLINK = 18
61 RTM_NEWADDR = 20
62 RTM_DELADDR = 21
63 RTM_GETADDR = 22
64 RTM_NEWROUTE = 24
65 RTM_DELROUTE = 25
66 RTM_GETROUTE = 26
67 RTM_NEWNEIGH = 28
68 RTM_DELNEIGH = 29
69 RTM_GETNEIGH = 30
70 RTM_NEWRULE = 32
71 RTM_DELRULE = 33
72 RTM_GETRULE = 34
73
74 # Routing message type values (rtm_type).
75 RTN_UNSPEC = 0
76 RTN_UNICAST = 1
77 RTN_UNREACHABLE = 7
78
79 # Routing protocol values (rtm_protocol).
80 RTPROT_UNSPEC = 0
81 RTPROT_STATIC = 4
82
83 # Route scope values (rtm_scope).
84 RT_SCOPE_UNIVERSE = 0
85 RT_SCOPE_LINK = 253
86
87 # Named routing tables.
88 RT_TABLE_UNSPEC = 0
89
90 # Routing attributes.
91 RTA_DST = 1
92 RTA_SRC = 2
93 RTA_OIF = 4
94 RTA_GATEWAY = 5
95 RTA_PRIORITY = 6
96 RTA_PREFSRC = 7
97 RTA_METRICS = 8
98 RTA_CACHEINFO = 12
99 RTA_TABLE = 15
100 RTA_MARK = 16
101 RTA_UID = 18
102
103 # Route metric attributes.
104 RTAX_MTU = 2
105 RTAX_HOPLIMIT = 10
106
107 # Data structure formats.
108 IfinfoMsg = cstruct.Struct(
109     "IfinfoMsg", "=BBHiII", "family pad type index flags change")
110 RTMsg = cstruct.Struct(
111     "RTMsg", "=BBBBBBBBI",
112     "family dst_len src_len tos table protocol scope type flags")
113 RTACacheinfo = cstruct.Struct(
114     "RTACacheinfo", "=IIiiI", "clntref lastuse expires error used")
115
116
117 ### Interface address constants. See include/uapi/linux/if_addr.h.
118 # Interface address attributes.
119 IFA_ADDRESS = 1
120 IFA_LOCAL = 2
121 IFA_CACHEINFO = 6
122
123 # Address flags.
124 IFA_F_SECONDARY = 0x01
125 IFA_F_TEMPORARY = IFA_F_SECONDARY
126 IFA_F_NODAD = 0x02
127 IFA_F_OPTIMISTIC = 0x04
128 IFA_F_DADFAILED = 0x08
129 IFA_F_HOMEADDRESS = 0x10
130 IFA_F_DEPRECATED = 0x20
131 IFA_F_TENTATIVE = 0x40
132 IFA_F_PERMANENT = 0x80
133
134 # Data structure formats.
135 IfAddrMsg = cstruct.Struct(
136     "IfAddrMsg", "=BBBBI",
137     "family prefixlen flags scope index")
138 IFACacheinfo = cstruct.Struct(
139     "IFACacheinfo", "=IIII", "prefered valid cstamp tstamp")
140 NDACacheinfo = cstruct.Struct(
141     "NDACacheinfo", "=IIII", "confirmed used updated refcnt")
142
143
144 ### Neighbour table entry constants. See include/uapi/linux/neighbour.h.
145 # Neighbour cache entry attributes.
146 NDA_DST = 1
147 NDA_LLADDR = 2
148 NDA_CACHEINFO = 3
149 NDA_PROBES = 4
150
151 # Neighbour cache entry states.
152 NUD_PERMANENT = 0x80
153
154 # Data structure formats.
155 NdMsg = cstruct.Struct(
156     "NdMsg", "=BxxxiHBB",
157     "family ifindex state flags type")
158
159
160 ### FIB rule constants. See include/uapi/linux/fib_rules.h.
161 FRA_IIFNAME = 3
162 FRA_PRIORITY = 6
163 FRA_FWMARK = 10
164 FRA_SUPPRESS_PREFIXLEN = 14
165 FRA_TABLE = 15
166 FRA_FWMASK = 16
167 FRA_OIFNAME = 17
168 FRA_UID_START = 18
169 FRA_UID_END = 19
170
171
172 # Link constants. See include/uapi/linux/if_link.h.
173 IFLA_ADDRESS = 1
174 IFLA_BROADCAST = 2
175 IFLA_IFNAME = 3
176 IFLA_MTU = 4
177 IFLA_QDISC = 6
178 IFLA_STATS = 7
179 IFLA_TXQLEN = 13
180 IFLA_MAP = 14
181 IFLA_OPERSTATE = 16
182 IFLA_LINKMODE = 17
183 IFLA_STATS64 = 23
184 IFLA_AF_SPEC = 26
185 IFLA_GROUP = 27
186 IFLA_EXT_MASK = 29
187 IFLA_PROMISCUITY = 30
188 IFLA_NUM_TX_QUEUES = 31
189 IFLA_NUM_RX_QUEUES = 32
190 IFLA_CARRIER = 33
191
192
193 def CommandVerb(command):
194   return ["NEW", "DEL", "GET", "SET"][command % 4]
195
196
197 def CommandSubject(command):
198   return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) / 4]
199
200
201 def CommandName(command):
202   try:
203     return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
204   except IndexError:
205     return "RTM_%d" % command
206
207
208 class IPRoute(netlink.NetlinkSocket):
209   """Provides a tiny subset of iproute functionality."""
210
211   FAMILY = NETLINK_ROUTE
212
213   def _NlAttrIPAddress(self, nla_type, family, address):
214     return self._NlAttr(nla_type, socket.inet_pton(family, address))
215
216   def _NlAttrInterfaceName(self, nla_type, interface):
217     return self._NlAttr(nla_type, interface + "\x00")
218
219   def _GetConstantName(self, value, prefix):
220     return super(IPRoute, self)._GetConstantName(__name__, value, prefix)
221
222   def _Decode(self, command, msg, nla_type, nla_data):
223     """Decodes netlink attributes to Python types.
224
225     Values for which the code knows the type (e.g., the fwmark ID in a
226     RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
227     of unknown type are returned as raw byte strings.
228
229     Args:
230       command: An integer.
231         - If positive, the number of the rtnetlink command being carried out.
232           This is used to interpret the attributes. For example, for an
233           RTM_NEWROUTE command, attribute type 3 is the incoming interface and
234           is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
235           incoming interface name and is a string.
236         - If negative, one of the following (negative) values:
237           - RTA_METRICS: Interpret as nested route metrics.
238       family: The address family. Used to convert IP addresses into strings.
239       nla_type: An integer, then netlink attribute type.
240       nla_data: A byte string, the netlink attribute data.
241
242     Returns:
243       A tuple (name, data):
244        - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute,
245          or an integer if we didn't.
246        - data can be an integer, a string, a nested dict of attributes as
247          returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct
248          (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it
249          will be the raw byte string.
250     """
251     if command == -RTA_METRICS:
252       name = self._GetConstantName(nla_type, "RTAX_")
253     elif CommandSubject(command) == "ADDR":
254       name = self._GetConstantName(nla_type, "IFA_")
255     elif CommandSubject(command) == "LINK":
256       name = self._GetConstantName(nla_type, "IFLA_")
257     elif CommandSubject(command) == "RULE":
258       name = self._GetConstantName(nla_type, "FRA_")
259     elif CommandSubject(command) == "ROUTE":
260       name = self._GetConstantName(nla_type, "RTA_")
261     elif CommandSubject(command) == "NEIGH":
262       name = self._GetConstantName(nla_type, "NDA_")
263     else:
264       # Don't know what this is. Leave it as an integer.
265       name = nla_type
266
267     if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE", "FRA_FWMASK",
268                 "FRA_UID_START", "FRA_UID_END",
269                 "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK",
270                 "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK",
271                 "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES",
272                 "IFLA_NUM_TX_QUEUES", "NDA_PROBES", "RTAX_MTU",
273                 "RTAX_HOPLIMIT"]:
274       data = struct.unpack("=I", nla_data)[0]
275     elif name == "FRA_SUPPRESS_PREFIXLEN":
276       data = struct.unpack("=i", nla_data)[0]
277     elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]:
278       data = ord(nla_data)
279     elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC",
280                   "RTA_GATEWAY", "RTA_PREFSRC", "RTA_UID",
281                   "NDA_DST"]:
282       data = socket.inet_ntop(msg.family, nla_data)
283     elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC"]:
284       data = nla_data.strip("\x00")
285     elif name == "RTA_METRICS":
286       data = self._ParseAttributes(-RTA_METRICS, msg.family, None, nla_data)
287     elif name == "RTA_CACHEINFO":
288       data = RTACacheinfo(nla_data)
289     elif name == "IFA_CACHEINFO":
290       data = IFACacheinfo(nla_data)
291     elif name == "NDA_CACHEINFO":
292       data = NDACacheinfo(nla_data)
293     elif name in ["NDA_LLADDR", "IFLA_ADDRESS"]:
294       data = ":".join(x.encode("hex") for x in nla_data)
295     else:
296       data = nla_data
297
298     return name, data
299
300   def __init__(self):
301     super(IPRoute, self).__init__()
302
303   def _AddressFamily(self, version):
304     return {4: socket.AF_INET, 6: socket.AF_INET6}[version]
305
306   def _SendNlRequest(self, command, data, flags=0):
307     """Sends a netlink request and expects an ack."""
308
309     flags |= NLM_F_REQUEST
310     if CommandVerb(command) != "GET":
311       flags |= NLM_F_ACK
312     if CommandVerb(command) == "NEW":
313       if not flags & NLM_F_REPLACE:
314         flags |= (NLM_F_EXCL | NLM_F_CREATE)
315
316     super(IPRoute, self)._SendNlRequest(command, data, flags)
317
318   def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
319     """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
320
321     Args:
322       version: An integer, 4 or 6.
323       is_add: True to add a rule, False to delete it.
324       rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
325       table: If nonzero, rule looks up this table.
326       match_nlattr: A blob of struct nlattrs that express the match condition.
327         If None, match everything.
328       priority: An integer, the priority.
329
330     Raises:
331       IOError: If the netlink request returns an error.
332       ValueError: If the kernel's response could not be parsed.
333     """
334     # Create a struct rtmsg specifying the table and the given match attributes.
335     family = self._AddressFamily(version)
336     rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
337                    RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
338     rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
339     if match_nlattr:
340       rtmsg += match_nlattr
341     if table:
342       rtmsg += self._NlAttrU32(FRA_TABLE, table)
343
344     # Create a netlink request containing the rtmsg.
345     command = RTM_NEWRULE if is_add else RTM_DELRULE
346     self._SendNlRequest(command, rtmsg)
347
348   def DeleteRulesAtPriority(self, version, priority):
349     family = self._AddressFamily(version)
350     rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
351                    RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
352     rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
353     while True:
354       try:
355         self._SendNlRequest(RTM_DELRULE, rtmsg)
356       except IOError, e:
357         if e.errno == -errno.ENOENT:
358           break
359         else:
360           raise
361
362   def FwmarkRule(self, version, is_add, fwmark, table, priority):
363     nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
364     return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
365
366   def IifRule(self, version, is_add, iif, table, priority):
367     nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, iif)
368     return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
369
370   def OifRule(self, version, is_add, oif, table, priority):
371     nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif)
372     return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
373
374   def UidRangeRule(self, version, is_add, start, end, table, priority):
375     nlattr = (self._NlAttrInterfaceName(FRA_IIFNAME, "lo") +
376               self._NlAttrU32(FRA_UID_START, start) +
377               self._NlAttrU32(FRA_UID_END, end))
378     return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
379
380   def UnreachableRule(self, version, is_add, priority):
381     return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority)
382
383   def DefaultRule(self, version, is_add, table, priority):
384     return self.FwmarkRule(version, is_add, 0, table, priority)
385
386   def CommandToString(self, command, data):
387     try:
388       name = CommandName(command)
389       subject = CommandSubject(command)
390       struct_type = {
391           "ADDR": IfAddrMsg,
392           "LINK": IfinfoMsg,
393           "NEIGH": NdMsg,
394           "ROUTE": RTMsg,
395           "RULE": RTMsg,
396       }[subject]
397       parsed = self._ParseNLMsg(data, struct_type)
398       return "%s %s" % (name, str(parsed))
399     except IndexError:
400       raise ValueError("Don't know how to print command type %s" % name)
401
402   def MaybeDebugCommand(self, command, data):
403     subject = CommandSubject(command)
404     if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
405       return
406     print self.CommandToString(command, data)
407
408   def MaybeDebugMessage(self, message):
409     hdr = NLMsgHdr(message)
410     self.MaybeDebugCommand(hdr.type, message)
411
412   def PrintMessage(self, message):
413     hdr = NLMsgHdr(message)
414     print self.CommandToString(hdr.type, message)
415
416   def DumpRules(self, version):
417     """Returns the IP rules for the specified IP version."""
418     # Create a struct rtmsg specifying the table and the given match attributes.
419     family = self._AddressFamily(version)
420     rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0))
421     return self._Dump(RTM_GETRULE, rtmsg, RTMsg, "")
422
423   def DumpLinks(self):
424     ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0))
425     return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg, "")
426
427   def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex):
428     """Adds or deletes an IP address."""
429     family = self._AddressFamily(version)
430     ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack()
431     ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr)
432     if version == 4:
433       ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr)
434     self._SendNlRequest(command, ifaddrmsg)
435
436   def AddAddress(self, address, prefixlen, ifindex):
437     self._Address(6 if ":" in address else 4,
438                   RTM_NEWADDR, address, prefixlen,
439                   IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, ifindex)
440
441   def DelAddress(self, address, prefixlen, ifindex):
442     self._Address(6 if ":" in address else 4,
443                   RTM_DELADDR, address, prefixlen, 0, 0, ifindex)
444
445   def GetAddress(self, address, ifindex=0):
446     """Returns an ifaddrmsg for the requested address."""
447     if ":" not in address:
448       # The address is likely an IPv4 address.  RTM_GETADDR without the
449       # NLM_F_DUMP flag is not supported by the kernel.  We do not currently
450       # implement parsing dump results.
451       raise NotImplementedError("IPv4 RTM_GETADDR not implemented.")
452     self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex)
453     return self._GetMsg(IfAddrMsg)
454
455   def _Route(self, version, command, table, dest, prefixlen, nexthop, dev,
456              mark, uid):
457     """Adds, deletes, or queries a route."""
458     family = self._AddressFamily(version)
459     scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK
460     rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC,
461                    RTPROT_STATIC, scope, RTN_UNICAST, 0)).Pack()
462     if command == RTM_NEWROUTE and not table:
463       # Don't allow setting routes in table 0, since its behaviour is confusing
464       # and differs between IPv4 and IPv6.
465       raise ValueError("Cowardly refusing to add a route to table 0")
466     if table:
467       rtmsg += self._NlAttrU32(FRA_TABLE, table)
468     if dest != "default":  # The default is the default route.
469       rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest)
470     if nexthop:
471       rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop)
472     if dev:
473       rtmsg += self._NlAttrU32(RTA_OIF, dev)
474     if mark is not None:
475       rtmsg += self._NlAttrU32(RTA_MARK, mark)
476     if uid is not None:
477       rtmsg += self._NlAttrU32(RTA_UID, uid)
478     self._SendNlRequest(command, rtmsg)
479
480   def AddRoute(self, version, table, dest, prefixlen, nexthop, dev):
481     self._Route(version, RTM_NEWROUTE, table, dest, prefixlen, nexthop, dev,
482                 None, None)
483
484   def DelRoute(self, version, table, dest, prefixlen, nexthop, dev):
485     self._Route(version, RTM_DELROUTE, table, dest, prefixlen, nexthop, dev,
486                 None, None)
487
488   def GetRoutes(self, dest, oif, mark, uid):
489     version = 6 if ":" in dest else 4
490     prefixlen = {4: 32, 6: 128}[version]
491     self._Route(version, RTM_GETROUTE, 0, dest, prefixlen, None, oif, mark, uid)
492     data = self._Recv()
493     # The response will either be an error or a list of routes.
494     if NLMsgHdr(data).type == NLMSG_ERROR:
495       self._ParseAck(data)
496     routes = self._GetMsgList(RTMsg, data, False)
497     return routes
498
499   def _Neighbour(self, version, is_add, addr, lladdr, dev, state, flags=0):
500     """Adds or deletes a neighbour cache entry."""
501     family = self._AddressFamily(version)
502
503     # Convert the link-layer address to a raw byte string.
504     if is_add and lladdr:
505       lladdr = lladdr.split(":")
506       if len(lladdr) != 6:
507         raise ValueError("Invalid lladdr %s" % ":".join(lladdr))
508       lladdr = "".join(chr(int(hexbyte, 16)) for hexbyte in lladdr)
509
510     ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack()
511     ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr)
512     if is_add and lladdr:
513       ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
514     command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
515     self._SendNlRequest(command, ndmsg, flags)
516
517   def AddNeighbour(self, version, addr, lladdr, dev):
518     self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT)
519
520   def DelNeighbour(self, version, addr, lladdr, dev):
521     self._Neighbour(version, False, addr, lladdr, dev, 0)
522
523   def UpdateNeighbour(self, version, addr, lladdr, dev, state):
524     self._Neighbour(version, True, addr, lladdr, dev, state,
525                     flags=NLM_F_REPLACE)
526
527   def DumpNeighbours(self, version):
528     ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0))
529     return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, "")
530
531   def ParseNeighbourMessage(self, msg):
532     msg, _ = self._ParseNLMsg(msg, NdMsg)
533     return msg
534
535
536 if __name__ == "__main__":
537   iproute = IPRoute()
538   iproute.DEBUG = True
539   iproute.DumpRules(6)
540   iproute.DumpLinks()
541   print iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None)