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