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