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