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
21import errno
22import os
23import socket
24import struct
25import sys
26
27import cstruct
28
29
30### Base netlink constants. See include/uapi/linux/netlink.h.
31NETLINK_ROUTE = 0
32
33# Request constants.
34NLM_F_REQUEST = 1
35NLM_F_ACK = 4
36NLM_F_EXCL = 0x200
37NLM_F_CREATE = 0x400
38NLM_F_DUMP = 0x300
39
40# Message types.
41NLMSG_ERROR = 2
42NLMSG_DONE = 3
43
44# Data structure formats.
45# These aren't constants, they're classes. So, pylint: disable=invalid-name
46NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
47NLMsgErr = cstruct.Struct("NLMsgErr", "=i", "error")
48NLAttr = cstruct.Struct("NLAttr", "=HH", "nla_len nla_type")
49
50# Alignment / padding.
51NLA_ALIGNTO = 4
52
53
54### rtnetlink constants. See include/uapi/linux/rtnetlink.h.
55# Message types.
56RTM_NEWLINK = 16
57RTM_DELLINK = 17
58RTM_GETLINK = 18
59RTM_NEWADDR = 20
60RTM_DELADDR = 21
61RTM_GETADDR = 22
62RTM_NEWROUTE = 24
63RTM_DELROUTE = 25
64RTM_GETROUTE = 26
65RTM_NEWNEIGH = 28
66RTM_DELNEIGH = 29
67RTM_NEWRULE = 32
68RTM_DELRULE = 33
69RTM_GETRULE = 34
70
71# Routing message type values (rtm_type).
72RTN_UNSPEC = 0
73RTN_UNICAST = 1
74RTN_UNREACHABLE = 7
75
76# Routing protocol values (rtm_protocol).
77RTPROT_UNSPEC = 0
78RTPROT_STATIC = 4
79
80# Route scope values (rtm_scope).
81RT_SCOPE_UNIVERSE = 0
82RT_SCOPE_LINK = 253
83
84# Named routing tables.
85RT_TABLE_UNSPEC = 0
86
87# Routing attributes.
88RTA_DST = 1
89RTA_SRC = 2
90RTA_OIF = 4
91RTA_GATEWAY = 5
92RTA_PRIORITY = 6
93RTA_PREFSRC = 7
94RTA_METRICS = 8
95RTA_CACHEINFO = 12
96RTA_TABLE = 15
97RTA_MARK = 16
98RTA_UID = 18
99
100# Route metric attributes.
101RTAX_MTU = 2
102
103# Data structure formats.
104IfinfoMsg = cstruct.Struct(
105    "IfinfoMsg", "=BBHiII", "family pad type index flags change")
106RTMsg = cstruct.Struct(
107    "RTMsg", "=BBBBBBBBI",
108    "family dst_len src_len tos table protocol scope type flags")
109RTACacheinfo = cstruct.Struct(
110    "RTACacheinfo", "=IIiiI", "clntref lastuse expires error used")
111
112
113### Interface address constants. See include/uapi/linux/if_addr.h.
114# Interface address attributes.
115IFA_ADDRESS = 1
116IFA_LOCAL = 2
117IFA_CACHEINFO = 6
118
119# Address flags.
120IFA_F_SECONDARY = 0x01
121IFA_F_TEMPORARY = IFA_F_SECONDARY
122IFA_F_NODAD = 0x02
123IFA_F_OPTIMISTIC = 0x04
124IFA_F_DADFAILED = 0x08
125IFA_F_HOMEADDRESS = 0x10
126IFA_F_DEPRECATED = 0x20
127IFA_F_TENTATIVE = 0x40
128IFA_F_PERMANENT = 0x80
129
130# Data structure formats.
131IfAddrMsg = cstruct.Struct(
132    "IfAddrMsg", "=BBBBI",
133    "family prefixlen flags scope index")
134IFACacheinfo = cstruct.Struct(
135    "IFACacheinfo", "=IIII", "prefered valid cstamp tstamp")
136
137
138### Neighbour table entry constants. See include/uapi/linux/neighbour.h.
139# Neighbour cache entry attributes.
140NDA_DST = 1
141NDA_LLADDR = 2
142
143# Neighbour cache entry states.
144NUD_PERMANENT = 0x80
145
146# Data structure formats.
147NdMsg = cstruct.Struct(
148    "NdMsg", "=BxxxiHBB",
149    "family ifindex state flags type")
150
151
152### FIB rule constants. See include/uapi/linux/fib_rules.h.
153FRA_IIFNAME = 3
154FRA_PRIORITY = 6
155FRA_FWMARK = 10
156FRA_SUPPRESS_PREFIXLEN = 14
157FRA_TABLE = 15
158FRA_OIFNAME = 17
159FRA_UID_START = 18
160FRA_UID_END = 19
161
162
163# Link constants. See include/uapi/linux/if_link.h.
164IFLA_ADDRESS = 1
165IFLA_BROADCAST = 2
166IFLA_IFNAME = 3
167IFLA_MTU = 4
168IFLA_QDISC = 6
169IFLA_STATS = 7
170IFLA_TXQLEN = 13
171IFLA_MAP = 14
172IFLA_OPERSTATE = 16
173IFLA_LINKMODE = 17
174IFLA_STATS64 = 23
175IFLA_AF_SPEC = 26
176IFLA_GROUP = 27
177IFLA_EXT_MASK = 29
178IFLA_PROMISCUITY = 30
179IFLA_NUM_TX_QUEUES = 31
180IFLA_NUM_RX_QUEUES = 32
181IFLA_CARRIER = 33
182
183def CommandVerb(command):
184  return ["NEW", "DEL", "GET", "SET"][command % 4]
185
186
187def CommandSubject(command):
188  return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) / 4]
189
190
191def CommandName(command):
192  try:
193    return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
194  except KeyError:
195    return "RTM_%d" % command
196
197
198def PaddedLength(length):
199  # TODO: This padding is probably overly simplistic.
200  return NLA_ALIGNTO * ((length / NLA_ALIGNTO) + (length % NLA_ALIGNTO != 0))
201
202
203class IPRoute(object):
204
205  """Provides a tiny subset of iproute functionality."""
206
207  BUFSIZE = 65536
208  DEBUG = False
209  # List of netlink messages to print, e.g., [], ["NEIGH", "ROUTE"], or ["ALL"]
210  NL_DEBUG = []
211
212  def _Debug(self, s):
213    if self.DEBUG:
214      print s
215
216  def _NlAttr(self, nla_type, data):
217    datalen = len(data)
218    # Pad the data if it's not a multiple of NLA_ALIGNTO bytes long.
219    padding = "\x00" * (PaddedLength(datalen) - datalen)
220    nla_len = datalen + len(NLAttr)
221    return NLAttr((nla_len, nla_type)).Pack() + data + padding
222
223  def _NlAttrU32(self, nla_type, value):
224    return self._NlAttr(nla_type, struct.pack("=I", value))
225
226  def _NlAttrIPAddress(self, nla_type, family, address):
227    return self._NlAttr(nla_type, socket.inet_pton(family, address))
228
229  def _NlAttrInterfaceName(self, nla_type, interface):
230    return self._NlAttr(nla_type, interface + "\x00")
231
232  def _GetConstantName(self, value, prefix):
233    thismodule = sys.modules[__name__]
234    for name in dir(thismodule):
235      if (name.startswith(prefix) and
236          not name.startswith(prefix + "F_") and
237          name.isupper() and
238          getattr(thismodule, name) == value):
239        return name
240    return value
241
242  def _Decode(self, command, family, nla_type, nla_data):
243    """Decodes netlink attributes to Python types.
244
245    Values for which the code knows the type (e.g., the fwmark ID in a
246    RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
247    of unknown type are returned as raw byte strings.
248
249    Args:
250      command: An integer.
251        - If positive, the number of the rtnetlink command being carried out.
252          This is used to interpret the attributes. For example, for an
253          RTM_NEWROUTE command, attribute type 3 is the incoming interface and
254          is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
255          incoming interface name and is a string.
256        - If negative, one of the following (negative) values:
257          - RTA_METRICS: Interpret as nested route metrics.
258      family: The address family. Used to convert IP addresses into strings.
259      nla_type: An integer, then netlink attribute type.
260      nla_data: A byte string, the netlink attribute data.
261
262    Returns:
263      A tuple (name, data):
264       - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute,
265         or an integer if we didn't.
266       - data can be an integer, a string, a nested dict of attributes as
267         returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct
268         (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it
269         will be the raw byte string.
270    """
271    if command == -RTA_METRICS:
272      if nla_type == RTAX_MTU:
273        return ("RTAX_MTU", struct.unpack("=I", nla_data)[0])
274
275    if command == -RTA_METRICS:
276      name = self._GetConstantName(nla_type, "RTAX_")
277    elif CommandSubject(command) == "ADDR":
278      name = self._GetConstantName(nla_type, "IFA_")
279    elif CommandSubject(command) == "LINK":
280      name = self._GetConstantName(nla_type, "IFLA_")
281    elif CommandSubject(command) == "RULE":
282      name = self._GetConstantName(nla_type, "FRA_")
283    elif CommandSubject(command) == "ROUTE":
284      name = self._GetConstantName(nla_type, "RTA_")
285    elif CommandSubject(command) == "NEIGH":
286      name = self._GetConstantName(nla_type, "NDA_")
287    else:
288      # Don't know what this is. Leave it as an integer.
289      name = nla_type
290
291    if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE",
292                "FRA_UID_START", "FRA_UID_END",
293                "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK",
294                "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK",
295                "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES",
296                "IFLA_NUM_TX_QUEUES"]:
297      data = struct.unpack("=I", nla_data)[0]
298    elif name == "FRA_SUPPRESS_PREFIXLEN":
299      data = struct.unpack("=i", nla_data)[0]
300    elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]:
301      data = ord(nla_data)
302    elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC",
303                  "RTA_GATEWAY", "RTA_PREFSRC", "RTA_UID",
304                  "NDA_DST"]:
305      data = socket.inet_ntop(family, nla_data)
306    elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC"]:
307      data = nla_data.strip("\x00")
308    elif name == "RTA_METRICS":
309      data = self._ParseAttributes(-RTA_METRICS, family, nla_data)
310    elif name == "RTA_CACHEINFO":
311      data = RTACacheinfo(nla_data)
312    elif name == "IFA_CACHEINFO":
313      data = IFACacheinfo(nla_data)
314    elif name in ["NDA_LLADDR", "IFLA_ADDRESS"]:
315      data = ":".join(x.encode("hex") for x in nla_data)
316    else:
317      data = nla_data
318
319    return name, data
320
321  def _ParseAttributes(self, command, family, data):
322    """Parses and decodes netlink attributes.
323
324    Takes a block of NLAttr data structures, decodes them using Decode, and
325    returns the result in a dict keyed by attribute number.
326
327    Args:
328      command: An integer, the rtnetlink command being carried out.
329      family: The address family.
330      data: A byte string containing a sequence of NLAttr data structures.
331
332    Returns:
333      A dictionary mapping attribute types (integers) to decoded values.
334
335    Raises:
336      ValueError: There was a duplicate attribute type.
337    """
338    attributes = {}
339    while data:
340      # Read the nlattr header.
341      nla, data = cstruct.Read(data, NLAttr)
342
343      # Read the data.
344      datalen = nla.nla_len - len(nla)
345      padded_len = PaddedLength(nla.nla_len) - len(nla)
346      nla_data, data = data[:datalen], data[padded_len:]
347
348      # If it's an attribute we know about, try to decode it.
349      nla_name, nla_data = self._Decode(command, family, nla.nla_type, nla_data)
350
351      # We only support unique attributes for now.
352      if nla_name in attributes:
353        raise ValueError("Duplicate attribute %d" % nla_name)
354
355      attributes[nla_name] = nla_data
356      self._Debug("      %s" % str((nla_name, nla_data)))
357
358    return attributes
359
360  def __init__(self):
361    # Global sequence number.
362    self.seq = 0
363    self.sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW,
364                              socket.NETLINK_ROUTE)
365    self.sock.connect((0, 0))  # The kernel.
366    self.pid = self.sock.getsockname()[1]
367
368  def _Send(self, msg):
369    # self._Debug(msg.encode("hex"))
370    self.seq += 1
371    self.sock.send(msg)
372
373  def _Recv(self):
374    data = self.sock.recv(self.BUFSIZE)
375    # self._Debug(data.encode("hex"))
376    return data
377
378  def _ExpectDone(self):
379    response = self._Recv()
380    hdr = NLMsgHdr(response)
381    if hdr.type != NLMSG_DONE:
382      raise ValueError("Expected DONE, got type %d" % hdr.type)
383
384  def _ParseAck(self, response):
385    # Find the error code.
386    hdr, data = cstruct.Read(response, NLMsgHdr)
387    if hdr.type == NLMSG_ERROR:
388      error = NLMsgErr(data).error
389      if error:
390        raise IOError(error, os.strerror(-error))
391    else:
392      raise ValueError("Expected ACK, got type %d" % hdr.type)
393
394  def _ExpectAck(self):
395    response = self._Recv()
396    self._ParseAck(response)
397
398  def _AddressFamily(self, version):
399    return {4: socket.AF_INET, 6: socket.AF_INET6}[version]
400
401  def _SendNlRequest(self, command, data):
402    """Sends a netlink request and expects an ack."""
403    flags = NLM_F_REQUEST
404    if CommandVerb(command) != "GET":
405      flags |= NLM_F_ACK
406    if CommandVerb(command) == "NEW":
407      flags |= (NLM_F_EXCL | NLM_F_CREATE)
408
409    length = len(NLMsgHdr) + len(data)
410    nlmsg = NLMsgHdr((length, command, flags, self.seq, self.pid)).Pack()
411
412    self.MaybeDebugCommand(command, nlmsg + data)
413
414    # Send the message.
415    self._Send(nlmsg + data)
416
417    if flags & NLM_F_ACK:
418      self._ExpectAck()
419
420  def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
421    """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
422
423    Args:
424      version: An integer, 4 or 6.
425      is_add: True to add a rule, False to delete it.
426      rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
427      table: If nonzero, rule looks up this table.
428      match_nlattr: A blob of struct nlattrs that express the match condition.
429        If None, match everything.
430      priority: An integer, the priority.
431
432    Raises:
433      IOError: If the netlink request returns an error.
434      ValueError: If the kernel's response could not be parsed.
435    """
436    # Create a struct rtmsg specifying the table and the given match attributes.
437    family = self._AddressFamily(version)
438    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
439                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
440    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
441    if match_nlattr:
442      rtmsg += match_nlattr
443    if table:
444      rtmsg += self._NlAttrU32(FRA_TABLE, table)
445
446    # Create a netlink request containing the rtmsg.
447    command = RTM_NEWRULE if is_add else RTM_DELRULE
448    self._SendNlRequest(command, rtmsg)
449
450  def DeleteRulesAtPriority(self, version, priority):
451    family = self._AddressFamily(version)
452    rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
453                   RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
454    rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
455    while True:
456      try:
457        self._SendNlRequest(RTM_DELRULE, rtmsg)
458      except IOError, e:
459        if e.errno == -errno.ENOENT:
460          break
461        else:
462          raise
463
464  def FwmarkRule(self, version, is_add, fwmark, table, priority):
465    nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
466    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
467
468  def OifRule(self, version, is_add, oif, table, priority):
469    nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif)
470    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
471
472  def UidRangeRule(self, version, is_add, start, end, table, priority):
473    nlattr = (self._NlAttrInterfaceName(FRA_IIFNAME, "lo") +
474              self._NlAttrU32(FRA_UID_START, start) +
475              self._NlAttrU32(FRA_UID_END, end))
476    return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
477
478  def UnreachableRule(self, version, is_add, priority):
479    return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority)
480
481  def DefaultRule(self, version, is_add, table, priority):
482    return self.FwmarkRule(version, is_add, 0, table, priority)
483
484  def _ParseNLMsg(self, data, msgtype):
485    """Parses a Netlink message into a header and a dictionary of attributes."""
486    nlmsghdr, data = cstruct.Read(data, NLMsgHdr)
487    self._Debug("  %s" % nlmsghdr)
488
489    if nlmsghdr.type == NLMSG_ERROR or nlmsghdr.type == NLMSG_DONE:
490      print "done"
491      return None, data
492
493    nlmsg, data = cstruct.Read(data, msgtype)
494    self._Debug("    %s" % nlmsg)
495
496    # Parse the attributes in the nlmsg.
497    attrlen = nlmsghdr.length - len(nlmsghdr) - len(nlmsg)
498    attributes = self._ParseAttributes(nlmsghdr.type, nlmsg.family,
499                                       data[:attrlen])
500    data = data[attrlen:]
501    return (nlmsg, attributes), data
502
503  def _GetMsgList(self, msgtype, data, expect_done):
504    out = []
505    while data:
506      msg, data = self._ParseNLMsg(data, msgtype)
507      if msg is None:
508        break
509      out.append(msg)
510    if expect_done:
511      self._ExpectDone()
512    return out
513
514  def MaybeDebugCommand(self, command, data):
515    subject = CommandSubject(command)
516    if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
517      return
518    name = CommandName(command)
519    try:
520      struct_type = {
521          "ADDR": IfAddrMsg,
522          "LINK": IfinfoMsg,
523          "NEIGH": NdMsg,
524          "ROUTE": RTMsg,
525          "RULE": RTMsg,
526      }[subject]
527      parsed = self._ParseNLMsg(data, struct_type)
528      print "%s %s" % (name, str(parsed))
529    except KeyError:
530      raise ValueError("Don't know how to print command type %s" % name)
531
532  def MaybeDebugMessage(self, message):
533    hdr = NLMsgHdr(message)
534    self.MaybeDebugCommand(hdr.type, message)
535
536  def _Dump(self, command, msg, msgtype):
537    """Sends a dump request and returns a list of decoded messages."""
538    # Create a netlink dump request containing the msg.
539    flags = NLM_F_DUMP | NLM_F_REQUEST
540    length = len(NLMsgHdr) + len(msg)
541    nlmsghdr = NLMsgHdr((length, command, flags, self.seq, self.pid))
542
543    # Send the request.
544    self._Send(nlmsghdr.Pack() + msg.Pack())
545
546    # Keep reading netlink messages until we get a NLMSG_DONE.
547    out = []
548    while True:
549      data = self._Recv()
550      response_type = NLMsgHdr(data).type
551      if response_type == NLMSG_DONE:
552        break
553      out.extend(self._GetMsgList(msgtype, data, False))
554
555    return out
556
557  def DumpRules(self, version):
558    """Returns the IP rules for the specified IP version."""
559    # Create a struct rtmsg specifying the table and the given match attributes.
560    family = self._AddressFamily(version)
561    rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0))
562    return self._Dump(RTM_GETRULE, rtmsg, RTMsg)
563
564  def DumpLinks(self):
565    ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0))
566    return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg)
567
568  def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex):
569    """Adds or deletes an IP address."""
570    family = self._AddressFamily(version)
571    ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack()
572    ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr)
573    if version == 4:
574      ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr)
575    self._SendNlRequest(command, ifaddrmsg)
576
577  def AddAddress(self, address, prefixlen, ifindex):
578    self._Address(6 if ":" in address else 4,
579                  RTM_NEWADDR, address, prefixlen,
580                  IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, ifindex)
581
582  def DelAddress(self, address, prefixlen, ifindex):
583    self._Address(6 if ":" in address else 4,
584                  RTM_DELADDR, address, prefixlen, 0, 0, ifindex)
585
586  def GetAddress(self, address, ifindex=0):
587    """Returns an ifaddrmsg for the requested address."""
588    if ":" not in address:
589      # The address is likely an IPv4 address.  RTM_GETADDR without the
590      # NLM_F_DUMP flag is not supported by the kernel.  We do not currently
591      # implement parsing dump results.
592      raise NotImplementedError("IPv4 RTM_GETADDR not implemented.")
593    self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex)
594    data = self._Recv()
595    if NLMsgHdr(data).type == NLMSG_ERROR:
596      self._ParseAck(data)
597    return self._ParseNLMsg(data, IfAddrMsg)[0]
598
599  def _Route(self, version, command, table, dest, prefixlen, nexthop, dev,
600             mark, uid):
601    """Adds, deletes, or queries a route."""
602    family = self._AddressFamily(version)
603    scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK
604    rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC,
605                   RTPROT_STATIC, scope, RTN_UNICAST, 0)).Pack()
606    if command == RTM_NEWROUTE and not table:
607      # Don't allow setting routes in table 0, since its behaviour is confusing
608      # and differs between IPv4 and IPv6.
609      raise ValueError("Cowardly refusing to add a route to table 0")
610    if table:
611      rtmsg += self._NlAttrU32(FRA_TABLE, table)
612    if dest != "default":  # The default is the default route.
613      rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest)
614    if nexthop:
615      rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop)
616    if dev:
617      rtmsg += self._NlAttrU32(RTA_OIF, dev)
618    if mark is not None:
619      rtmsg += self._NlAttrU32(RTA_MARK, mark)
620    if uid is not None:
621      rtmsg += self._NlAttrU32(RTA_UID, uid)
622    self._SendNlRequest(command, rtmsg)
623
624  def AddRoute(self, version, table, dest, prefixlen, nexthop, dev):
625    self._Route(version, RTM_NEWROUTE, table, dest, prefixlen, nexthop, dev,
626                None, None)
627
628  def DelRoute(self, version, table, dest, prefixlen, nexthop, dev):
629    self._Route(version, RTM_DELROUTE, table, dest, prefixlen, nexthop, dev,
630                None, None)
631
632  def GetRoutes(self, dest, oif, mark, uid):
633    version = 6 if ":" in dest else 4
634    prefixlen = {4: 32, 6: 128}[version]
635    self._Route(version, RTM_GETROUTE, 0, dest, prefixlen, None, oif, mark, uid)
636    data = self._Recv()
637    # The response will either be an error or a list of routes.
638    if NLMsgHdr(data).type == NLMSG_ERROR:
639      self._ParseAck(data)
640    routes = self._GetMsgList(RTMsg, data, False)
641    return routes
642
643  def _Neighbour(self, version, is_add, addr, lladdr, dev, state):
644    """Adds or deletes a neighbour cache entry."""
645    family = self._AddressFamily(version)
646
647    # Convert the link-layer address to a raw byte string.
648    if is_add:
649      lladdr = lladdr.split(":")
650      if len(lladdr) != 6:
651        raise ValueError("Invalid lladdr %s" % ":".join(lladdr))
652      lladdr = "".join(chr(int(hexbyte, 16)) for hexbyte in lladdr)
653
654    ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack()
655    ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr)
656    if is_add:
657      ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
658    command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
659    self._SendNlRequest(command, ndmsg)
660
661  def AddNeighbour(self, version, addr, lladdr, dev):
662    self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT)
663
664  def DelNeighbour(self, version, addr, lladdr, dev):
665    self._Neighbour(version, False, addr, lladdr, dev, 0)
666
667
668if __name__ == "__main__":
669  iproute = IPRoute()
670  iproute.DEBUG = True
671  iproute.DumpRules(6)
672  iproute.DumpLinks()
673  print iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None)
674