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