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