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