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_INET_CONF = 1
225
226IFLA_INET6_FLAGS = 1
227IFLA_INET6_CONF = 2
228IFLA_INET6_STATS = 3
229IFLA_INET6_MCAST = 4
230IFLA_INET6_CACHEINFO = 5
231IFLA_INET6_ICMP6STATS = 6
232IFLA_INET6_TOKEN = 7
233IFLA_INET6_ADDR_GEN_MODE = 8
234IFLA_INET6_RA_MTU = 9
235
236IFLA_XFRM_UNSPEC = 0
237IFLA_XFRM_LINK = 1
238IFLA_XFRM_IF_ID = 2
239
240# include/uapi/linux/if_tunnel.h
241IFLA_VTI_UNSPEC = 0
242IFLA_VTI_LINK = 1
243IFLA_VTI_IKEY = 2
244IFLA_VTI_OKEY = 3
245IFLA_VTI_LOCAL = 4
246IFLA_VTI_REMOTE = 5
247
248# include/net/if_inet6.h
249IF_RA_OTHERCONF = 0x80
250IF_RA_MANAGED   = 0x40
251IF_RA_RCVD      = 0x20
252IF_RS_SENT      = 0x10
253IF_READY        = 0x80000000
254
255# Hack to use _ParseAttributes to parse family-specific interface attributes.
256# These are not actual kernel constants.
257IFLA_AF_SPEC_AF_INET = AF_INET
258IFLA_AF_SPEC_AF_INET6 = AF_INET6
259
260
261CONSTANT_PREFIXES = netlink.MakeConstantPrefixes(
262    ["RTM_", "RTN_", "RTPROT_", "RT_SCOPE_", "RT_TABLE_", "RTA_", "RTMGRP_",
263     "RTNLGRP_", "RTAX_", "IFA_", "IFA_F_", "NDA_", "FRA_", "IFLA_",
264     "IFLA_INFO_", "IFLA_XFRM_", "IFLA_VTI_", "IFLA_AF_SPEC_", "IFLA_INET_",
265     "IFLA_INET6_"])
266
267
268def CommandVerb(command):
269  return ["NEW", "DEL", "GET", "SET"][command % 4]
270
271
272def CommandSubject(command):
273  return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) // 4]
274
275
276def CommandName(command):
277  try:
278    return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
279  except IndexError:
280    return "RTM_%d" % command
281
282
283class IPRoute(netlink.NetlinkSocket):
284  """Provides a tiny subset of iproute functionality."""
285
286  def _NlAttrInterfaceName(self, nla_type, interface):
287    return self._NlAttr(nla_type, interface.encode() + b"\x00")
288
289  def _GetConstantName(self, value, prefix):
290    return super(IPRoute, self)._GetConstantName(__name__, value, prefix)
291
292  def _Decode(self, command, msg, nla_type, nla_data, nested):
293    """Decodes netlink attributes to Python types.
294
295    Values for which the code knows the type (e.g., the fwmark ID in a
296    RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
297    of unknown type are returned as raw byte strings.
298
299    Args:
300      command: An integer.
301        - If positive, the number of the rtnetlink command being carried out.
302          This is used to interpret the attributes. For example, for an
303          RTM_NEWROUTE command, attribute type 3 is the incoming interface and
304          is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
305          incoming interface name and is a string.
306      family: The address family. Used to convert IP addresses into strings.
307      nla_type: An integer, then netlink attribute type.
308      nla_data: A byte string, the netlink attribute data.
309      nested: A list, outermost first, of each of the attributes the NLAttrs are
310              nested inside. Empty for non-nested attributes.
311
312    Returns:
313      A tuple (name, data):
314       - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute,
315         or an integer if we didn't.
316       - data can be an integer, a string, a nested dict of attributes as
317         returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct
318         (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it
319         will be the raw byte string.
320    """
321    lastnested = nested[-1] if nested else None
322    if lastnested == "RTA_METRICS":
323      name = self._GetConstantName(nla_type, "RTAX_")
324    elif lastnested == "IFLA_LINKINFO":
325      name = self._GetConstantName(nla_type, "IFLA_INFO_")
326    elif lastnested == "IFLA_INFO_DATA":
327      name = self._GetConstantName(nla_type, "IFLA_VTI_")
328    elif lastnested == "IFLA_AF_SPEC":
329      name = self._GetConstantName(nla_type, "IFLA_AF_SPEC_")
330    elif lastnested == "IFLA_AF_SPEC_AF_INET":
331      name = self._GetConstantName(nla_type, "IFLA_INET_")
332    elif lastnested == "IFLA_AF_SPEC_AF_INET6":
333      name = self._GetConstantName(nla_type, "IFLA_INET6_")
334    elif CommandSubject(command) == "ADDR":
335      name = self._GetConstantName(nla_type, "IFA_")
336    elif CommandSubject(command) == "LINK":
337      name = self._GetConstantName(nla_type, "IFLA_")
338    elif CommandSubject(command) == "RULE":
339      name = self._GetConstantName(nla_type, "FRA_")
340    elif CommandSubject(command) == "ROUTE":
341      name = self._GetConstantName(nla_type, "RTA_")
342    elif CommandSubject(command) == "NEIGH":
343      name = self._GetConstantName(nla_type, "NDA_")
344    else:
345      # Don't know what this is. Leave it as an integer.
346      name = nla_type
347
348    if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE", "FRA_FWMASK",
349                "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK",
350                "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK",
351                "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES",
352                "IFLA_NUM_TX_QUEUES", "NDA_PROBES", "RTAX_MTU",
353                "RTAX_HOPLIMIT", "IFLA_CARRIER_CHANGES", "IFLA_GSO_MAX_SEGS",
354                "IFLA_GSO_MAX_SIZE", "RTA_UID", "IFLA_INET6_FLAGS"]:
355      data = struct.unpack("=I", nla_data)[0]
356    # HACK: the code cannot distinguish between IFLA_VTI_OKEY and
357    # IFLA_INET6_STATS, because they have the same values and similar context:
358    # they're both in an IFLA_INFO_DATA attribute, and knowing which one is
359    # being used requires remembering the IFLA_INFO_KIND attribute which is a
360    # peer of the IFLA_INFO_DATA).
361    # TODO: support parsing attributes whose meaning depends on the value of
362    # attributes that don't directly contain them.
363    # For now, disambiguate by checking the length.
364    elif name in ["IFLA_VTI_OKEY", "IFLA_VTI_IKEY"] and len(nla_data) == 4:
365      data = struct.unpack("!I", nla_data)[0]
366    elif name == "FRA_SUPPRESS_PREFIXLEN":
367      data = struct.unpack("=i", nla_data)[0]
368    elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER",
369                  "IFLA_INET6_ADDR_GEN_MODE"]:
370      data = ord(nla_data)
371    elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC",
372                  "RTA_GATEWAY", "RTA_PREFSRC", "NDA_DST"]:
373      data = socket.inet_ntop(msg.family, nla_data)
374    elif name in ["IFLA_INET_CONF", "IFLA_INET6_CONF"]:
375      data = [struct.unpack("=I", nla_data[i:i+4])[0]
376              for i in range(0, len(nla_data), 4)]
377    elif name == "IFLA_INET6_TOKEN":
378      data = socket.inet_ntop(AF_INET6, nla_data)
379    elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC",
380                  "IFA_LABEL", "IFLA_INFO_KIND"]:
381      data = nla_data.strip(b"\x00")
382    elif name in ["RTA_METRICS", "IFLA_LINKINFO", "IFLA_INFO_DATA",
383                  "IFLA_AF_SPEC", "IFLA_AF_SPEC_AF_INET",
384                  "IFLA_AF_SPEC_AF_INET6"]:
385      data = self._ParseAttributes(command, None, nla_data, nested + [name])
386    elif name == "RTA_CACHEINFO":
387      data = RTACacheinfo(nla_data)
388    elif name == "IFA_CACHEINFO":
389      data = IFACacheinfo(nla_data)
390    elif name == "NDA_CACHEINFO":
391      data = NDACacheinfo(nla_data)
392    elif name in ["NDA_LLADDR", "IFLA_ADDRESS", "IFLA_BROADCAST"]:
393      data = ":".join(net_test.ByteToHex(x) for x in nla_data)
394    elif name == "FRA_UID_RANGE":
395      data = FibRuleUidRange(nla_data)
396    elif name == "IFLA_STATS":
397      data = RtnlLinkStats(nla_data)
398    elif name == "IFLA_STATS64":
399      data = RtnlLinkStats64(nla_data)
400    else:
401      data = nla_data
402
403    return name, data
404
405  def __init__(self):
406    super(IPRoute, self).__init__(netlink.NETLINK_ROUTE)
407
408  def _AddressFamily(self, version):
409    return {4: AF_INET, 6: AF_INET6}[version]
410
411  def _SendNlRequest(self, command, data, flags=0):
412    """Sends a netlink request and expects an ack."""
413
414    flags |= netlink.NLM_F_REQUEST
415    if CommandVerb(command) != "GET":
416      flags |= netlink.NLM_F_ACK
417    if CommandVerb(command) == "NEW":
418      if flags & (netlink.NLM_F_REPLACE | netlink.NLM_F_CREATE) == 0:
419        flags |= netlink.NLM_F_CREATE | netlink.NLM_F_EXCL
420
421    super(IPRoute, self)._SendNlRequest(command, data, flags)
422
423  def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
424    """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
425
426    Args:
427      version: An integer, 4 or 6.
428      is_add: True to add a rule, False to delete it.
429      rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
430      table: If nonzero, rule looks up this table.
431      match_nlattr: A blob of struct nlattrs that express the match condition.
432        If None, match everything.
433      priority: An integer, the priority.
434
435    Raises:
436      IOError: If the netlink request returns an error.
437      ValueError: If the kernel's response could not be parsed.
438    """
439    # Create a struct rtmsg specifying the table and the given match attributes.
440    family = self._AddressFamily(version)
441    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
442                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
443    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
444    if match_nlattr:
445      rtmsg += match_nlattr
446    if table:
447      rtmsg += self._NlAttrU32(FRA_TABLE, table)
448
449    # Create a netlink request containing the rtmsg.
450    command = RTM_NEWRULE if is_add else RTM_DELRULE
451    self._SendNlRequest(command, rtmsg)
452
453  def DeleteRulesAtPriority(self, version, priority):
454    family = self._AddressFamily(version)
455    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
456                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
457    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
458    while True:
459      try:
460        self._SendNlRequest(RTM_DELRULE, rtmsg)
461      except IOError as e:
462        if e.errno == errno.ENOENT:
463          break
464        else:
465          raise
466
467  def FwmarkRule(self, version, is_add, fwmark, fwmask, table, priority):
468    nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
469    nlattr += self._NlAttrU32(FRA_FWMASK, fwmask)
470    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
471
472  def IifRule(self, version, is_add, iif, table, priority):
473    nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, iif)
474    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
475
476  def OifRule(self, version, is_add, oif, table, priority):
477    nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif)
478    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
479
480  def UidRangeRule(self, version, is_add, start, end, table, priority):
481    nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, "lo")
482    nlattr += self._NlAttr(FRA_UID_RANGE,
483                           FibRuleUidRange((start, end)).Pack())
484    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
485
486  def UnreachableRule(self, version, is_add, priority):
487    return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority)
488
489  def DefaultRule(self, version, is_add, table, priority):
490    return self.FwmarkRule(version, is_add, 0, 0, table, priority)
491
492  def CommandToString(self, command, data):
493    try:
494      name = CommandName(command)
495      subject = CommandSubject(command)
496      struct_type = {
497          "ADDR": IfAddrMsg,
498          "LINK": IfinfoMsg,
499          "NEIGH": NdMsg,
500          "ROUTE": RTMsg,
501          "RULE": RTMsg,
502      }[subject]
503      parsed = self._ParseNLMsg(data, struct_type)
504      return "%s %s" % (name, str(parsed))
505    except IndexError:
506      raise ValueError("Don't know how to print command type %s" % name)
507
508  def MaybeDebugCommand(self, command, unused_flags, data):
509    subject = CommandSubject(command)
510    if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
511      return
512    print(self.CommandToString(command, data))
513
514  def MaybeDebugMessage(self, message):
515    hdr = netlink.NLMsgHdr(message)
516    self.MaybeDebugCommand(hdr.type, message)
517
518  def PrintMessage(self, message):
519    hdr = netlink.NLMsgHdr(message)
520    print(self.CommandToString(hdr.type, message))
521
522  def DumpRules(self, version):
523    """Returns the IP rules for the specified IP version."""
524    # Create a struct rtmsg specifying the table and the given match attributes.
525    family = self._AddressFamily(version)
526    rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0))
527    return self._Dump(RTM_GETRULE, rtmsg, RTMsg)
528
529  def DumpLinks(self):
530    ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0))
531    return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg)
532
533  def DumpAddresses(self, version):
534    family = self._AddressFamily(version)
535    ifaddrmsg = IfAddrMsg((family, 0, 0, 0, 0))
536    return self._Dump(RTM_GETADDR, ifaddrmsg, IfAddrMsg)
537
538  def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex):
539    """Adds or deletes an IP address."""
540    family = self._AddressFamily(version)
541    ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack()
542    ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr)
543    if version == 4:
544      ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr)
545    self._SendNlRequest(command, ifaddrmsg)
546
547  def _WaitForAddress(self, sock, address, ifindex):
548    # IPv6 addresses aren't immediately usable when the netlink ACK comes back.
549    # Even if DAD is disabled via IFA_F_NODAD or on the interface, when the ACK
550    # arrives the input route has not yet been added to the local table. The
551    # route is added in addrconf_dad_begin with a delayed timer of 0, but if
552    # the system is under load, we could win the race against that timer and
553    # cause the tests to be flaky. So, wait for RTM_NEWADDR to arrive
554    csocket.SetSocketTimeout(sock, 100)
555    while True:
556      try:
557        data = sock.recv(4096)
558      except EnvironmentError as e:
559        raise AssertionError("Address %s did not appear on ifindex %d: %s" %
560                             (address, ifindex, e.strerror))
561      msg, attrs = self._ParseNLMsg(data, IfAddrMsg)[0]
562      if msg.index == ifindex and attrs["IFA_ADDRESS"] == address:
563        return
564
565  def AddAddress(self, address, prefixlen, ifindex):
566    """Adds a statically-configured IP address to an interface.
567
568    The address is created with flags IFA_F_PERMANENT, and, if IPv6,
569    IFA_F_NODAD. The requested scope is RT_SCOPE_UNIVERSE, but at least for
570    IPv6, is instead determined by the kernel.
571
572    In order to avoid races (see comments in _WaitForAddress above), when
573    configuring IPv6 addresses, the method blocks until it receives an
574    RTM_NEWADDR from the kernel confirming that the address has been added.
575    If the address does not appear within 100ms, AssertionError is thrown.
576
577    Args:
578      address: A string, the IP address to configure.
579      prefixlen: The prefix length passed to the kernel. If not /32 for IPv4 or
580        /128 for IPv6, the kernel creates an implicit directly-connected route.
581      ifindex: The interface index to add the address to.
582
583    Raises:
584      AssertionError: An IPv6 address was requested, and it did not appear
585        within the timeout.
586    """
587    version = csocket.AddressVersion(address)
588
589    flags = IFA_F_PERMANENT
590    if version == 6:
591      flags |= IFA_F_NODAD
592      sock = self._OpenNetlinkSocket(netlink.NETLINK_ROUTE, RTMGRP_IPV6_IFADDR)
593
594    self._Address(version, RTM_NEWADDR, address, prefixlen, flags,
595                  RT_SCOPE_UNIVERSE, ifindex)
596
597    if version == 6:
598      self._WaitForAddress(sock, address, ifindex)
599      sock.close()
600
601  def DelAddress(self, address, prefixlen, ifindex):
602    self._Address(csocket.AddressVersion(address),
603                  RTM_DELADDR, address, prefixlen, 0, 0, ifindex)
604
605  def GetAddress(self, address, ifindex=0):
606    """Returns an ifaddrmsg for the requested address."""
607    if ":" not in address:
608      # The address is likely an IPv4 address.  RTM_GETADDR without the
609      # NLM_F_DUMP flag is not supported by the kernel.  We do not currently
610      # implement parsing dump results.
611      raise NotImplementedError("IPv4 RTM_GETADDR not implemented.")
612    self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex)
613    return self._GetMsg(IfAddrMsg)
614
615  def _Route(self, version, proto, command, table, dest, prefixlen, nexthop,
616             dev, mark, uid, route_type=RTN_UNICAST, priority=None, iif=None):
617    """Adds, deletes, or queries a route."""
618    family = self._AddressFamily(version)
619    scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK
620    rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC,
621                   proto, scope, route_type, 0)).Pack()
622    if command == RTM_NEWROUTE and not table:
623      # Don't allow setting routes in table 0, since its behaviour is confusing
624      # and differs between IPv4 and IPv6.
625      raise ValueError("Cowardly refusing to add a route to table 0")
626    if table:
627      rtmsg += self._NlAttrU32(FRA_TABLE, table)
628    if dest != "default":  # The default is the default route.
629      rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest)
630    if nexthop:
631      rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop)
632    if dev:
633      rtmsg += self._NlAttrU32(RTA_OIF, dev)
634    if mark is not None:
635      rtmsg += self._NlAttrU32(RTA_MARK, mark)
636    if uid is not None:
637      rtmsg += self._NlAttrU32(RTA_UID, uid)
638    if priority is not None:
639      rtmsg += self._NlAttrU32(RTA_PRIORITY, priority)
640    if iif is not None:
641      rtmsg += self._NlAttrU32(RTA_IIF, iif)
642    self._SendNlRequest(command, rtmsg)
643
644  def AddRoute(self, version, table, dest, prefixlen, nexthop, dev):
645    self._Route(version, RTPROT_STATIC, RTM_NEWROUTE, table, dest, prefixlen,
646                nexthop, dev, None, None)
647
648  def DelRoute(self, version, table, dest, prefixlen, nexthop, dev):
649    self._Route(version, RTPROT_STATIC, RTM_DELROUTE, table, dest, prefixlen,
650                nexthop, dev, None, None)
651
652  def GetRoutes(self, dest, oif, mark, uid, iif=None):
653    version = csocket.AddressVersion(dest)
654    prefixlen = {4: 32, 6: 128}[version]
655    self._Route(version, RTPROT_STATIC, RTM_GETROUTE, 0, dest, prefixlen, None,
656                oif, mark, uid, iif=iif)
657    data = self._Recv()
658    # The response will either be an error or a list of routes.
659    if netlink.NLMsgHdr(data).type == netlink.NLMSG_ERROR:
660      self._ParseAck(data)
661    routes = self._GetMsgList(RTMsg, data, False)
662    return routes
663
664  def DumpRoutes(self, version, ifindex):
665    rtmsg = RTMsg(family=self._AddressFamily(version))
666    return [(m, r) for (m, r) in self._Dump(RTM_GETROUTE, rtmsg, RTMsg)
667            if r['RTA_TABLE'] == ifindex]
668
669  def _Neighbour(self, version, is_add, addr, lladdr, dev, state, flags=0):
670    """Adds or deletes a neighbour cache entry."""
671    family = self._AddressFamily(version)
672
673    # Convert the link-layer address to a raw byte string.
674    if is_add and lladdr:
675      lladdr = lladdr.split(":")
676      if len(lladdr) != 6 or any (len(b) not in range(1, 3) for b in lladdr):
677        raise ValueError("Invalid lladdr %s" % ":".join(lladdr))
678      lladdr = binascii.unhexlify("".join(lladdr))
679
680    ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack()
681    ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr)
682    if is_add and lladdr:
683      ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
684    command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
685    self._SendNlRequest(command, ndmsg, flags)
686
687  def AddNeighbour(self, version, addr, lladdr, dev):
688    self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT)
689
690  def DelNeighbour(self, version, addr, lladdr, dev):
691    self._Neighbour(version, False, addr, lladdr, dev, 0)
692
693  def UpdateNeighbour(self, version, addr, lladdr, dev, state):
694    self._Neighbour(version, True, addr, lladdr, dev, state,
695                    flags=netlink.NLM_F_REPLACE)
696
697  def DumpNeighbours(self, version, ifindex):
698    ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0))
699    attrs = self._NlAttrU32(NDA_IFINDEX, ifindex) if ifindex else b""
700    return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, attrs)
701
702  def ParseNeighbourMessage(self, msg):
703    msg, _ = self._ParseNLMsg(msg, NdMsg)
704    return msg
705
706  def DeleteLink(self, dev_name):
707    ifinfo = IfinfoMsg().Pack()
708    ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
709    return self._SendNlRequest(RTM_DELLINK, ifinfo)
710
711  def GetIfinfo(self, dev_name):
712    """Fetches information about the specified interface.
713
714    Args:
715      dev_name: A string, the name of the interface.
716
717    Returns:
718      A tuple containing an IfinfoMsg struct and raw, undecoded attributes.
719    """
720    ifinfo = IfinfoMsg().Pack()
721    ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
722    self._SendNlRequest(RTM_GETLINK, ifinfo)
723    hdr, data = cstruct.Read(self._Recv(), netlink.NLMsgHdr)
724    if hdr.type == RTM_NEWLINK:
725      return cstruct.Read(data, IfinfoMsg)
726    elif hdr.type == netlink.NLMSG_ERROR:
727      error = -netlink.NLMsgErr(data).error
728      raise IOError(error, os.strerror(error))
729    else:
730      raise ValueError("Unknown Netlink Message Type %d" % hdr.type)
731
732  def GetIfIndex(self, dev_name):
733    """Returns the interface index for the specified interface."""
734    ifinfo, _ = self.GetIfinfo(dev_name)
735    return ifinfo.index
736
737  def GetIfaceStats(self, dev_name):
738    """Returns an RtnlLinkStats64 stats object for the specified interface."""
739    _, attrs = self.GetIfinfo(dev_name)
740    attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, [])
741    return attrs["IFLA_STATS64"]
742
743  def GetIfinfoData(self, dev_name):
744    """Returns an IFLA_INFO_DATA dict object for the specified interface."""
745    _, attrs = self.GetIfinfo(dev_name)
746    attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, [])
747    return attrs["IFLA_LINKINFO"]["IFLA_INFO_DATA"]
748
749  def GetRxTxPackets(self, dev_name):
750    stats = self.GetIfaceStats(dev_name)
751    return stats.rx_packets, stats.tx_packets
752
753  def GetIflaAfSpecificData(self, dev_name, family):
754    _, attrs = self.GetIfinfo(dev_name)
755    attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs, [])
756    if family == AF_INET:
757      attrname = "IFLA_AF_SPEC_AF_INET"
758    elif family == AF_INET6:
759      attrname = "IFLA_AF_SPEC_AF_INET6"
760    else:
761      raise ValueError("Unsupported address family %d" % family)
762    return attrs["IFLA_AF_SPEC"][attrname]
763
764  def CreateVirtualTunnelInterface(self, dev_name, local_addr, remote_addr,
765                                   i_key=None, o_key=None, is_update=False):
766    """
767    Create a Virtual Tunnel Interface that provides a proxy interface
768    for IPsec tunnels.
769
770    The VTI Newlink structure is a series of nested netlink
771    attributes following a mostly-ignored 'struct ifinfomsg':
772
773    NLMSGHDR (type=RTM_NEWLINK)
774    |
775    |-{IfinfoMsg}
776    |
777    |-IFLA_IFNAME = <user-provided ifname>
778    |
779    |-IFLA_LINKINFO
780      |
781      |-IFLA_INFO_KIND = "vti"
782      |
783      |-IFLA_INFO_DATA
784        |
785        |-IFLA_VTI_LOCAL = <local addr>
786        |-IFLA_VTI_REMOTE = <remote addr>
787        |-IFLA_VTI_LINK = ????
788        |-IFLA_VTI_OKEY = [outbound mark]
789        |-IFLA_VTI_IKEY = [inbound mark]
790    """
791    family = AF_INET6 if ":" in remote_addr else AF_INET
792
793    ifinfo = IfinfoMsg().Pack()
794    ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
795
796    linkinfo = self._NlAttrStr(IFLA_INFO_KIND,
797        {AF_INET6: "vti6", AF_INET: "vti"}[family])
798
799    ifdata = self._NlAttrIPAddress(IFLA_VTI_LOCAL, family, local_addr)
800    ifdata += self._NlAttrIPAddress(IFLA_VTI_REMOTE, family,
801                                    remote_addr)
802    if i_key is not None:
803      ifdata += self._NlAttrU32(IFLA_VTI_IKEY, socket.htonl(i_key))
804    if o_key is not None:
805      ifdata += self._NlAttrU32(IFLA_VTI_OKEY, socket.htonl(o_key))
806    linkinfo += self._NlAttr(IFLA_INFO_DATA, ifdata)
807
808    ifinfo += self._NlAttr(IFLA_LINKINFO, linkinfo)
809
810    # Always pass CREATE to prevent _SendNlRequest() from incorrectly
811    # guessing the flags.
812    flags = netlink.NLM_F_CREATE
813    if not is_update:
814      flags |= netlink.NLM_F_EXCL
815    return self._SendNlRequest(RTM_NEWLINK, ifinfo, flags)
816
817  def CreateXfrmInterface(self, dev_name, xfrm_if_id, underlying_ifindex):
818    """Creates an XFRM interface with the specified parameters."""
819    # The netlink attribute structure is essentially identical to the one
820    # for VTI above (q.v).
821    ifdata = self._NlAttrU32(IFLA_XFRM_LINK, underlying_ifindex)
822    ifdata += self._NlAttrU32(IFLA_XFRM_IF_ID, xfrm_if_id)
823
824    linkinfo = self._NlAttrStr(IFLA_INFO_KIND, "xfrm")
825    linkinfo += self._NlAttr(IFLA_INFO_DATA, ifdata)
826
827    msg = IfinfoMsg().Pack()
828    msg += self._NlAttrStr(IFLA_IFNAME, dev_name)
829    msg += self._NlAttr(IFLA_LINKINFO, linkinfo)
830
831    return self._SendNlRequest(RTM_NEWLINK, msg)
832
833
834if __name__ == "__main__":
835  iproute = IPRoute()
836  iproute.DEBUG = True
837  iproute.DumpRules(6)
838  iproute.DumpLinks()
839  print(iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None))
840