• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
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
21from socket import AF_INET
22from socket import AF_INET6
23
24import binascii
25import errno
26import os
27import socket
28import struct
29
30import net_test
31import csocket
32import cstruct
33import netlink
34
35### rtnetlink constants. See include/uapi/linux/rtnetlink.h.
36# Message types.
37RTM_NEWLINK = 16
38RTM_DELLINK = 17
39RTM_GETLINK = 18
40RTM_NEWADDR = 20
41RTM_DELADDR = 21
42RTM_GETADDR = 22
43RTM_NEWROUTE = 24
44RTM_DELROUTE = 25
45RTM_GETROUTE = 26
46RTM_NEWNEIGH = 28
47RTM_DELNEIGH = 29
48RTM_GETNEIGH = 30
49RTM_NEWRULE = 32
50RTM_DELRULE = 33
51RTM_GETRULE = 34
52RTM_NEWNDUSEROPT = 68
53
54# Routing message type values (rtm_type).
55RTN_UNSPEC = 0
56RTN_UNICAST = 1
57RTN_UNREACHABLE = 7
58RTN_THROW = 9
59
60# Routing protocol values (rtm_protocol).
61RTPROT_UNSPEC = 0
62RTPROT_BOOT = 3
63RTPROT_STATIC = 4
64RTPROT_RA = 9
65
66# Route scope values (rtm_scope).
67RT_SCOPE_UNIVERSE = 0
68RT_SCOPE_LINK = 253
69
70# Named routing tables.
71RT_TABLE_UNSPEC = 0
72
73# Routing attributes.
74RTA_DST = 1
75RTA_SRC = 2
76RTA_IIF = 3
77RTA_OIF = 4
78RTA_GATEWAY = 5
79RTA_PRIORITY = 6
80RTA_PREFSRC = 7
81RTA_METRICS = 8
82RTA_CACHEINFO = 12
83RTA_TABLE = 15
84RTA_MARK = 16
85RTA_PREF = 20
86RTA_UID = 25
87
88# Netlink groups.
89RTMGRP_IPV6_IFADDR = 0x100
90RTNLGRP_ND_USEROPT = 20
91RTMGRP_ND_USEROPT = (1 << (RTNLGRP_ND_USEROPT - 1))  # Not a kernel constant
92
93# Route metric attributes.
94RTAX_MTU = 2
95RTAX_HOPLIMIT = 10
96
97# Data structure formats.
98IfinfoMsg = cstruct.Struct(
99    "IfinfoMsg", "=BBHiII", "family pad type index flags change")
100RTMsg = cstruct.Struct(
101    "RTMsg", "=BBBBBBBBI",
102    "family dst_len src_len tos table protocol scope type flags")
103RTACacheinfo = cstruct.Struct(
104    "RTACacheinfo", "=IIiiI", "clntref lastuse expires error used")
105NdUseroptMsg = cstruct.Struct("nduseroptmsg", "=BxHiBBxxxxxx",
106                              "family opts_len ifindex icmp_type icmp_code")
107
108### Interface address constants. See include/uapi/linux/if_addr.h.
109# Interface address attributes.
110IFA_ADDRESS = 1
111IFA_LOCAL = 2
112IFA_LABEL = 3
113IFA_CACHEINFO = 6
114
115# Address flags.
116IFA_F_SECONDARY = 0x01
117IFA_F_TEMPORARY = IFA_F_SECONDARY
118IFA_F_NODAD = 0x02
119IFA_F_OPTIMISTIC = 0x04
120IFA_F_DADFAILED = 0x08
121IFA_F_HOMEADDRESS = 0x10
122IFA_F_DEPRECATED = 0x20
123IFA_F_TENTATIVE = 0x40
124IFA_F_PERMANENT = 0x80
125
126# This cannot contain members that do not yet exist in older kernels, because
127# GetIfaceStats will fail if the kernel returns fewer bytes than the size of
128# RtnlLinkStats[64].
129_LINK_STATS_MEMBERS = (
130    "rx_packets tx_packets rx_bytes tx_bytes rx_errors tx_errors "
131    "rx_dropped tx_dropped multicast collisions "
132    "rx_length_errors rx_over_errors rx_crc_errors rx_frame_errors "
133    "rx_fifo_errors rx_missed_errors tx_aborted_errors tx_carrier_errors "
134    "tx_fifo_errors tx_heartbeat_errors tx_window_errors "
135    "rx_compressed tx_compressed")
136
137# Data structure formats.
138IfAddrMsg = cstruct.Struct(
139    "IfAddrMsg", "=BBBBI",
140    "family prefixlen flags scope index")
141IFACacheinfo = cstruct.Struct(
142    "IFACacheinfo", "=IIII", "prefered valid cstamp tstamp")
143NDACacheinfo = cstruct.Struct(
144    "NDACacheinfo", "=IIII", "confirmed used updated refcnt")
145RtnlLinkStats = cstruct.Struct(
146    "RtnlLinkStats", "=IIIIIIIIIIIIIIIIIIIIIII", _LINK_STATS_MEMBERS)
147RtnlLinkStats64 = cstruct.Struct(
148    "RtnlLinkStats64", "=QQQQQQQQQQQQQQQQQQQQQQQ", _LINK_STATS_MEMBERS)
149
150### Neighbour table entry constants. See include/uapi/linux/neighbour.h.
151# Neighbour cache entry attributes.
152NDA_DST = 1
153NDA_LLADDR = 2
154NDA_CACHEINFO = 3
155NDA_PROBES = 4
156NDA_IFINDEX = 8
157
158# Neighbour cache entry states.
159NUD_PERMANENT = 0x80
160
161# Data structure formats.
162NdMsg = cstruct.Struct(
163    "NdMsg", "=BxxxiHBB",
164    "family ifindex state flags type")
165
166
167### FIB rule constants. See include/uapi/linux/fib_rules.h.
168FRA_IIFNAME = 3
169FRA_PRIORITY = 6
170FRA_FWMARK = 10
171FRA_SUPPRESS_PREFIXLEN = 14
172FRA_TABLE = 15
173FRA_FWMASK = 16
174FRA_OIFNAME = 17
175FRA_UID_RANGE = 20
176
177# Data structure formats.
178FibRuleUidRange = cstruct.Struct("FibRuleUidRange", "=II", "start end")
179
180# Link constants. See include/uapi/linux/if_link.h.
181IFLA_UNSPEC = 0
182IFLA_ADDRESS = 1
183IFLA_BROADCAST = 2
184IFLA_IFNAME = 3
185IFLA_MTU = 4
186IFLA_LINK = 5
187IFLA_QDISC = 6
188IFLA_STATS = 7
189IFLA_COST = 8
190IFLA_PRIORITY = 9
191IFLA_MASTER = 10
192IFLA_WIRELESS = 11
193IFLA_PROTINFO = 12
194IFLA_TXQLEN = 13
195IFLA_MAP = 14
196IFLA_WEIGHT = 15
197IFLA_OPERSTATE = 16
198IFLA_LINKMODE = 17
199IFLA_LINKINFO = 18
200IFLA_NET_NS_PID = 19
201IFLA_IFALIAS = 20
202IFLA_STATS64 = 23
203IFLA_AF_SPEC = 26
204IFLA_GROUP = 27
205IFLA_EXT_MASK = 29
206IFLA_PROMISCUITY = 30
207IFLA_NUM_TX_QUEUES = 31
208IFLA_NUM_RX_QUEUES = 32
209IFLA_CARRIER = 33
210IFLA_CARRIER_CHANGES = 35
211IFLA_PROTO_DOWN = 39
212IFLA_GSO_MAX_SEGS = 40
213IFLA_GSO_MAX_SIZE = 41
214IFLA_PAD = 42
215IFLA_XDP = 43
216IFLA_EVENT = 44
217
218# include/uapi/linux/if_link.h
219IFLA_INFO_UNSPEC = 0
220IFLA_INFO_KIND = 1
221IFLA_INFO_DATA = 2
222IFLA_INFO_XSTATS = 3
223
224IFLA_XFRM_UNSPEC = 0
225IFLA_XFRM_LINK = 1
226IFLA_XFRM_IF_ID = 2
227
228# include/uapi/linux/if_tunnel.h
229IFLA_VTI_UNSPEC = 0
230IFLA_VTI_LINK = 1
231IFLA_VTI_IKEY = 2
232IFLA_VTI_OKEY = 3
233IFLA_VTI_LOCAL = 4
234IFLA_VTI_REMOTE = 5
235
236
237CONSTANT_PREFIXES = netlink.MakeConstantPrefixes(
238    ["RTM_", "RTN_", "RTPROT_", "RT_SCOPE_", "RT_TABLE_", "RTA_", "RTMGRP_",
239     "RTNLGRP_", "RTAX_", "IFA_", "IFA_F_", "NDA_", "FRA_", "IFLA_",
240     "IFLA_INFO_", "IFLA_XFRM_", "IFLA_VTI_"])
241
242
243def CommandVerb(command):
244  return ["NEW", "DEL", "GET", "SET"][command % 4]
245
246
247def CommandSubject(command):
248  return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) // 4]
249
250
251def CommandName(command):
252  try:
253    return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
254  except IndexError:
255    return "RTM_%d" % command
256
257
258class IPRoute(netlink.NetlinkSocket):
259  """Provides a tiny subset of iproute functionality."""
260
261  def _NlAttrInterfaceName(self, nla_type, interface):
262    return self._NlAttr(nla_type, interface.encode() + b"\x00")
263
264  def _GetConstantName(self, value, prefix):
265    return super(IPRoute, self)._GetConstantName(__name__, value, prefix)
266
267  def _Decode(self, command, msg, nla_type, nla_data, nested):
268    """Decodes netlink attributes to Python types.
269
270    Values for which the code knows the type (e.g., the fwmark ID in a
271    RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
272    of unknown type are returned as raw byte strings.
273
274    Args:
275      command: An integer.
276        - If positive, the number of the rtnetlink command being carried out.
277          This is used to interpret the attributes. For example, for an
278          RTM_NEWROUTE command, attribute type 3 is the incoming interface and
279          is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
280          incoming interface name and is a string.
281      family: The address family. Used to convert IP addresses into strings.
282      nla_type: An integer, then netlink attribute type.
283      nla_data: A byte string, the netlink attribute data.
284      nested: A list, outermost first, of each of the attributes the NLAttrs are
285              nested inside. Empty for non-nested attributes.
286
287    Returns:
288      A tuple (name, data):
289       - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute,
290         or an integer if we didn't.
291       - data can be an integer, a string, a nested dict of attributes as
292         returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct
293         (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it
294         will be the raw byte string.
295    """
296    lastnested = nested[-1] if nested else None
297    if lastnested == "RTA_METRICS":
298      name = self._GetConstantName(nla_type, "RTAX_")
299    elif lastnested == "IFLA_LINKINFO":
300      name = self._GetConstantName(nla_type, "IFLA_INFO_")
301    elif lastnested == "IFLA_INFO_DATA":
302      name = self._GetConstantName(nla_type, "IFLA_VTI_")
303    elif CommandSubject(command) == "ADDR":
304      name = self._GetConstantName(nla_type, "IFA_")
305    elif CommandSubject(command) == "LINK":
306      name = self._GetConstantName(nla_type, "IFLA_")
307    elif CommandSubject(command) == "RULE":
308      name = self._GetConstantName(nla_type, "FRA_")
309    elif CommandSubject(command) == "ROUTE":
310      name = self._GetConstantName(nla_type, "RTA_")
311    elif CommandSubject(command) == "NEIGH":
312      name = self._GetConstantName(nla_type, "NDA_")
313    else:
314      # Don't know what this is. Leave it as an integer.
315      name = nla_type
316
317    if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE", "FRA_FWMASK",
318                "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK",
319                "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK",
320                "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES",
321                "IFLA_NUM_TX_QUEUES", "NDA_PROBES", "RTAX_MTU",
322                "RTAX_HOPLIMIT", "IFLA_CARRIER_CHANGES", "IFLA_GSO_MAX_SEGS",
323                "IFLA_GSO_MAX_SIZE", "RTA_UID"]:
324      data = struct.unpack("=I", nla_data)[0]
325    elif name in ["IFLA_VTI_OKEY", "IFLA_VTI_IKEY"]:
326      data = struct.unpack("!I", nla_data)[0]
327    elif name == "FRA_SUPPRESS_PREFIXLEN":
328      data = struct.unpack("=i", nla_data)[0]
329    elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]:
330      data = ord(nla_data)
331    elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC",
332                  "RTA_GATEWAY", "RTA_PREFSRC", "NDA_DST"]:
333      data = socket.inet_ntop(msg.family, nla_data)
334    elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC",
335                  "IFA_LABEL", "IFLA_INFO_KIND"]:
336      data = nla_data.strip(b"\x00")
337    elif name in ["RTA_METRICS", "IFLA_LINKINFO", "IFLA_INFO_DATA"]:
338      data = self._ParseAttributes(command, None, nla_data, nested + [name])
339    elif name == "RTA_CACHEINFO":
340      data = RTACacheinfo(nla_data)
341    elif name == "IFA_CACHEINFO":
342      data = IFACacheinfo(nla_data)
343    elif name == "NDA_CACHEINFO":
344      data = NDACacheinfo(nla_data)
345    elif name in ["NDA_LLADDR", "IFLA_ADDRESS", "IFLA_BROADCAST"]:
346      data = ":".join(net_test.ByteToHex(x) for x in nla_data)
347    elif name == "FRA_UID_RANGE":
348      data = FibRuleUidRange(nla_data)
349    elif name == "IFLA_STATS":
350      data = RtnlLinkStats(nla_data)
351    elif name == "IFLA_STATS64":
352      data = RtnlLinkStats64(nla_data)
353    else:
354      data = nla_data
355
356    return name, data
357
358  def __init__(self):
359    super(IPRoute, self).__init__(netlink.NETLINK_ROUTE)
360
361  def _AddressFamily(self, version):
362    return {4: AF_INET, 6: AF_INET6}[version]
363
364  def _SendNlRequest(self, command, data, flags=0):
365    """Sends a netlink request and expects an ack."""
366
367    flags |= netlink.NLM_F_REQUEST
368    if CommandVerb(command) != "GET":
369      flags |= netlink.NLM_F_ACK
370    if CommandVerb(command) == "NEW":
371      if flags & (netlink.NLM_F_REPLACE | netlink.NLM_F_CREATE) == 0:
372        flags |= netlink.NLM_F_CREATE | netlink.NLM_F_EXCL
373
374    super(IPRoute, self)._SendNlRequest(command, data, flags)
375
376  def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
377    """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
378
379    Args:
380      version: An integer, 4 or 6.
381      is_add: True to add a rule, False to delete it.
382      rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
383      table: If nonzero, rule looks up this table.
384      match_nlattr: A blob of struct nlattrs that express the match condition.
385        If None, match everything.
386      priority: An integer, the priority.
387
388    Raises:
389      IOError: If the netlink request returns an error.
390      ValueError: If the kernel's response could not be parsed.
391    """
392    # Create a struct rtmsg specifying the table and the given match attributes.
393    family = self._AddressFamily(version)
394    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
395                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
396    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
397    if match_nlattr:
398      rtmsg += match_nlattr
399    if table:
400      rtmsg += self._NlAttrU32(FRA_TABLE, table)
401
402    # Create a netlink request containing the rtmsg.
403    command = RTM_NEWRULE if is_add else RTM_DELRULE
404    self._SendNlRequest(command, rtmsg)
405
406  def DeleteRulesAtPriority(self, version, priority):
407    family = self._AddressFamily(version)
408    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
409                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
410    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
411    while True:
412      try:
413        self._SendNlRequest(RTM_DELRULE, rtmsg)
414      except IOError as e:
415        if e.errno == errno.ENOENT:
416          break
417        else:
418          raise
419
420  def FwmarkRule(self, version, is_add, fwmark, fwmask, table, priority):
421    nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
422    nlattr += self._NlAttrU32(FRA_FWMASK, fwmask)
423    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
424
425  def IifRule(self, version, is_add, iif, table, priority):
426    nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, iif)
427    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
428
429  def OifRule(self, version, is_add, oif, table, priority):
430    nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif)
431    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
432
433  def UidRangeRule(self, version, is_add, start, end, table, priority):
434    nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, "lo")
435    nlattr += self._NlAttr(FRA_UID_RANGE,
436                           FibRuleUidRange((start, end)).Pack())
437    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
438
439  def UnreachableRule(self, version, is_add, priority):
440    return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority)
441
442  def DefaultRule(self, version, is_add, table, priority):
443    return self.FwmarkRule(version, is_add, 0, 0, table, priority)
444
445  def CommandToString(self, command, data):
446    try:
447      name = CommandName(command)
448      subject = CommandSubject(command)
449      struct_type = {
450          "ADDR": IfAddrMsg,
451          "LINK": IfinfoMsg,
452          "NEIGH": NdMsg,
453          "ROUTE": RTMsg,
454          "RULE": RTMsg,
455      }[subject]
456      parsed = self._ParseNLMsg(data, struct_type)
457      return "%s %s" % (name, str(parsed))
458    except IndexError:
459      raise ValueError("Don't know how to print command type %s" % name)
460
461  def MaybeDebugCommand(self, command, unused_flags, data):
462    subject = CommandSubject(command)
463    if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
464      return
465    print(self.CommandToString(command, data))
466
467  def MaybeDebugMessage(self, message):
468    hdr = netlink.NLMsgHdr(message)
469    self.MaybeDebugCommand(hdr.type, message)
470
471  def PrintMessage(self, message):
472    hdr = netlink.NLMsgHdr(message)
473    print(self.CommandToString(hdr.type, message))
474
475  def DumpRules(self, version):
476    """Returns the IP rules for the specified IP version."""
477    # Create a struct rtmsg specifying the table and the given match attributes.
478    family = self._AddressFamily(version)
479    rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0))
480    return self._Dump(RTM_GETRULE, rtmsg, RTMsg)
481
482  def DumpLinks(self):
483    ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0))
484    return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg)
485
486  def DumpAddresses(self, version):
487    family = self._AddressFamily(version)
488    ifaddrmsg = IfAddrMsg((family, 0, 0, 0, 0))
489    return self._Dump(RTM_GETADDR, ifaddrmsg, IfAddrMsg)
490
491  def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex):
492    """Adds or deletes an IP address."""
493    family = self._AddressFamily(version)
494    ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack()
495    ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr)
496    if version == 4:
497      ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr)
498    self._SendNlRequest(command, ifaddrmsg)
499
500  def _WaitForAddress(self, sock, address, ifindex):
501    # IPv6 addresses aren't immediately usable when the netlink ACK comes back.
502    # Even if DAD is disabled via IFA_F_NODAD or on the interface, when the ACK
503    # arrives the input route has not yet been added to the local table. The
504    # route is added in addrconf_dad_begin with a delayed timer of 0, but if
505    # the system is under load, we could win the race against that timer and
506    # cause the tests to be flaky. So, wait for RTM_NEWADDR to arrive
507    csocket.SetSocketTimeout(sock, 100)
508    while True:
509      try:
510        data = sock.recv(4096)
511      except EnvironmentError as e:
512        raise AssertionError("Address %s did not appear on ifindex %d: %s" %
513                             (address, ifindex, e.strerror))
514      msg, attrs = self._ParseNLMsg(data, IfAddrMsg)[0]
515      if msg.index == ifindex and attrs["IFA_ADDRESS"] == address:
516        return
517
518  def AddAddress(self, address, prefixlen, ifindex):
519    """Adds a statically-configured IP address to an interface.
520
521    The address is created with flags IFA_F_PERMANENT, and, if IPv6,
522    IFA_F_NODAD. The requested scope is RT_SCOPE_UNIVERSE, but at least for
523    IPv6, is instead determined by the kernel.
524
525    In order to avoid races (see comments in _WaitForAddress above), when
526    configuring IPv6 addresses, the method blocks until it receives an
527    RTM_NEWADDR from the kernel confirming that the address has been added.
528    If the address does not appear within 100ms, AssertionError is thrown.
529
530    Args:
531      address: A string, the IP address to configure.
532      prefixlen: The prefix length passed to the kernel. If not /32 for IPv4 or
533        /128 for IPv6, the kernel creates an implicit directly-connected route.
534      ifindex: The interface index to add the address to.
535
536    Raises:
537      AssertionError: An IPv6 address was requested, and it did not appear
538        within the timeout.
539    """
540    version = csocket.AddressVersion(address)
541
542    flags = IFA_F_PERMANENT
543    if version == 6:
544      flags |= IFA_F_NODAD
545      sock = self._OpenNetlinkSocket(netlink.NETLINK_ROUTE, RTMGRP_IPV6_IFADDR)
546
547    self._Address(version, RTM_NEWADDR, address, prefixlen, flags,
548                  RT_SCOPE_UNIVERSE, ifindex)
549
550    if version == 6:
551      self._WaitForAddress(sock, address, ifindex)
552
553  def DelAddress(self, address, prefixlen, ifindex):
554    self._Address(csocket.AddressVersion(address),
555                  RTM_DELADDR, address, prefixlen, 0, 0, ifindex)
556
557  def GetAddress(self, address, ifindex=0):
558    """Returns an ifaddrmsg for the requested address."""
559    if ":" not in address:
560      # The address is likely an IPv4 address.  RTM_GETADDR without the
561      # NLM_F_DUMP flag is not supported by the kernel.  We do not currently
562      # implement parsing dump results.
563      raise NotImplementedError("IPv4 RTM_GETADDR not implemented.")
564    self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex)
565    return self._GetMsg(IfAddrMsg)
566
567  def _Route(self, version, proto, command, table, dest, prefixlen, nexthop,
568             dev, mark, uid, route_type=RTN_UNICAST, priority=None, iif=None):
569    """Adds, deletes, or queries a route."""
570    family = self._AddressFamily(version)
571    scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK
572    rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC,
573                   proto, scope, route_type, 0)).Pack()
574    if command == RTM_NEWROUTE and not table:
575      # Don't allow setting routes in table 0, since its behaviour is confusing
576      # and differs between IPv4 and IPv6.
577      raise ValueError("Cowardly refusing to add a route to table 0")
578    if table:
579      rtmsg += self._NlAttrU32(FRA_TABLE, table)
580    if dest != "default":  # The default is the default route.
581      rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest)
582    if nexthop:
583      rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop)
584    if dev:
585      rtmsg += self._NlAttrU32(RTA_OIF, dev)
586    if mark is not None:
587      rtmsg += self._NlAttrU32(RTA_MARK, mark)
588    if uid is not None:
589      rtmsg += self._NlAttrU32(RTA_UID, uid)
590    if priority is not None:
591      rtmsg += self._NlAttrU32(RTA_PRIORITY, priority)
592    if iif is not None:
593      rtmsg += self._NlAttrU32(RTA_IIF, iif)
594    self._SendNlRequest(command, rtmsg)
595
596  def AddRoute(self, version, table, dest, prefixlen, nexthop, dev):
597    self._Route(version, RTPROT_STATIC, RTM_NEWROUTE, table, dest, prefixlen,
598                nexthop, dev, None, None)
599
600  def DelRoute(self, version, table, dest, prefixlen, nexthop, dev):
601    self._Route(version, RTPROT_STATIC, RTM_DELROUTE, table, dest, prefixlen,
602                nexthop, dev, None, None)
603
604  def GetRoutes(self, dest, oif, mark, uid, iif=None):
605    version = csocket.AddressVersion(dest)
606    prefixlen = {4: 32, 6: 128}[version]
607    self._Route(version, RTPROT_STATIC, RTM_GETROUTE, 0, dest, prefixlen, None,
608                oif, mark, uid, iif=iif)
609    data = self._Recv()
610    # The response will either be an error or a list of routes.
611    if netlink.NLMsgHdr(data).type == netlink.NLMSG_ERROR:
612      self._ParseAck(data)
613    routes = self._GetMsgList(RTMsg, data, False)
614    return routes
615
616  def DumpRoutes(self, version, ifindex):
617    rtmsg = RTMsg(family=self._AddressFamily(version))
618    return [(m, r) for (m, r) in self._Dump(RTM_GETROUTE, rtmsg, RTMsg)
619            if r['RTA_TABLE'] == ifindex]
620
621  def _Neighbour(self, version, is_add, addr, lladdr, dev, state, flags=0):
622    """Adds or deletes a neighbour cache entry."""
623    family = self._AddressFamily(version)
624
625    # Convert the link-layer address to a raw byte string.
626    if is_add and lladdr:
627      lladdr = lladdr.split(":")
628      if len(lladdr) != 6 or any (len(b) not in range(1, 3) for b in lladdr):
629        raise ValueError("Invalid lladdr %s" % ":".join(lladdr))
630      lladdr = binascii.unhexlify("".join(lladdr))
631
632    ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack()
633    ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr)
634    if is_add and lladdr:
635      ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
636    command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
637    self._SendNlRequest(command, ndmsg, flags)
638
639  def AddNeighbour(self, version, addr, lladdr, dev):
640    self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT)
641
642  def DelNeighbour(self, version, addr, lladdr, dev):
643    self._Neighbour(version, False, addr, lladdr, dev, 0)
644
645  def UpdateNeighbour(self, version, addr, lladdr, dev, state):
646    self._Neighbour(version, True, addr, lladdr, dev, state,
647                    flags=netlink.NLM_F_REPLACE)
648
649  def DumpNeighbours(self, version, ifindex):
650    ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0))
651    attrs = self._NlAttrU32(NDA_IFINDEX, ifindex) if ifindex else b""
652    return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, attrs)
653
654  def ParseNeighbourMessage(self, msg):
655    msg, _ = self._ParseNLMsg(msg, NdMsg)
656    return msg
657
658  def DeleteLink(self, dev_name):
659    ifinfo = IfinfoMsg().Pack()
660    ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
661    return self._SendNlRequest(RTM_DELLINK, ifinfo)
662
663  def GetIfinfo(self, dev_name):
664    """Fetches information about the specified interface.
665
666    Args:
667      dev_name: A string, the name of the interface.
668
669    Returns:
670      A tuple containing an IfinfoMsg struct and raw, undecoded attributes.
671    """
672    ifinfo = IfinfoMsg().Pack()
673    ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
674    self._SendNlRequest(RTM_GETLINK, ifinfo)
675    hdr, data = cstruct.Read(self._Recv(), netlink.NLMsgHdr)
676    if hdr.type == RTM_NEWLINK:
677      return cstruct.Read(data, IfinfoMsg)
678    elif hdr.type == netlink.NLMSG_ERROR:
679      error = -netlink.NLMsgErr(data).error
680      raise IOError(error, os.strerror(error))
681    else:
682      raise ValueError("Unknown Netlink Message Type %d" % hdr.type)
683
684  def GetIfIndex(self, dev_name):
685    """Returns the interface index for the specified interface."""
686    ifinfo, _ = self.GetIfinfo(dev_name)
687    return ifinfo.index
688
689  def GetIfaceStats(self, dev_name):
690    """Returns an RtnlLinkStats64 stats object for the specified interface."""
691    _, attrs = self.GetIfinfo(dev_name)
692    attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, [])
693    return attrs["IFLA_STATS64"]
694
695  def GetIfinfoData(self, dev_name):
696    """Returns an IFLA_INFO_DATA dict object for the specified interface."""
697    _, attrs = self.GetIfinfo(dev_name)
698    attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, [])
699    return attrs["IFLA_LINKINFO"]["IFLA_INFO_DATA"]
700
701  def GetRxTxPackets(self, dev_name):
702    stats = self.GetIfaceStats(dev_name)
703    return stats.rx_packets, stats.tx_packets
704
705  def CreateVirtualTunnelInterface(self, dev_name, local_addr, remote_addr,
706                                   i_key=None, o_key=None, is_update=False):
707    """
708    Create a Virtual Tunnel Interface that provides a proxy interface
709    for IPsec tunnels.
710
711    The VTI Newlink structure is a series of nested netlink
712    attributes following a mostly-ignored 'struct ifinfomsg':
713
714    NLMSGHDR (type=RTM_NEWLINK)
715    |
716    |-{IfinfoMsg}
717    |
718    |-IFLA_IFNAME = <user-provided ifname>
719    |
720    |-IFLA_LINKINFO
721      |
722      |-IFLA_INFO_KIND = "vti"
723      |
724      |-IFLA_INFO_DATA
725        |
726        |-IFLA_VTI_LOCAL = <local addr>
727        |-IFLA_VTI_REMOTE = <remote addr>
728        |-IFLA_VTI_LINK = ????
729        |-IFLA_VTI_OKEY = [outbound mark]
730        |-IFLA_VTI_IKEY = [inbound mark]
731    """
732    family = AF_INET6 if ":" in remote_addr else AF_INET
733
734    ifinfo = IfinfoMsg().Pack()
735    ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
736
737    linkinfo = self._NlAttrStr(IFLA_INFO_KIND,
738        {AF_INET6: "vti6", AF_INET: "vti"}[family])
739
740    ifdata = self._NlAttrIPAddress(IFLA_VTI_LOCAL, family, local_addr)
741    ifdata += self._NlAttrIPAddress(IFLA_VTI_REMOTE, family,
742                                    remote_addr)
743    if i_key is not None:
744      ifdata += self._NlAttrU32(IFLA_VTI_IKEY, socket.htonl(i_key))
745    if o_key is not None:
746      ifdata += self._NlAttrU32(IFLA_VTI_OKEY, socket.htonl(o_key))
747    linkinfo += self._NlAttr(IFLA_INFO_DATA, ifdata)
748
749    ifinfo += self._NlAttr(IFLA_LINKINFO, linkinfo)
750
751    # Always pass CREATE to prevent _SendNlRequest() from incorrectly
752    # guessing the flags.
753    flags = netlink.NLM_F_CREATE
754    if not is_update:
755      flags |= netlink.NLM_F_EXCL
756    return self._SendNlRequest(RTM_NEWLINK, ifinfo, flags)
757
758  def CreateXfrmInterface(self, dev_name, xfrm_if_id, underlying_ifindex):
759    """Creates an XFRM interface with the specified parameters."""
760    # The netlink attribute structure is essentially identical to the one
761    # for VTI above (q.v).
762    ifdata = self._NlAttrU32(IFLA_XFRM_LINK, underlying_ifindex)
763    ifdata += self._NlAttrU32(IFLA_XFRM_IF_ID, xfrm_if_id)
764
765    linkinfo = self._NlAttrStr(IFLA_INFO_KIND, "xfrm")
766    linkinfo += self._NlAttr(IFLA_INFO_DATA, ifdata)
767
768    msg = IfinfoMsg().Pack()
769    msg += self._NlAttrStr(IFLA_IFNAME, dev_name)
770    msg += self._NlAttr(IFLA_LINKINFO, linkinfo)
771
772    return self._SendNlRequest(RTM_NEWLINK, msg)
773
774
775if __name__ == "__main__":
776  iproute = IPRoute()
777  iproute.DEBUG = True
778  iproute.DumpRules(6)
779  iproute.DumpLinks()
780  print(iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None))
781