3 # Copyright 2014 The Android Open Source Project
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 """Partial Python implementation of iproute functionality."""
19 # pylint: disable=g-bad-todo
31 ### Base netlink constants. See include/uapi/linux/netlink.h.
46 # Data structure formats.
47 # These aren't constants, they're classes. So, pylint: disable=invalid-name
48 NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
49 NLMsgErr = cstruct.Struct("NLMsgErr", "=i", "error")
50 NLAttr = cstruct.Struct("NLAttr", "=HH", "nla_len nla_type")
52 # Alignment / padding.
56 ### rtnetlink constants. See include/uapi/linux/rtnetlink.h.
74 # Routing message type values (rtm_type).
79 # Routing protocol values (rtm_protocol).
83 # Route scope values (rtm_scope).
87 # Named routing tables.
103 # Route metric attributes.
107 # Data structure formats.
108 IfinfoMsg = cstruct.Struct(
109 "IfinfoMsg", "=BBHiII", "family pad type index flags change")
110 RTMsg = cstruct.Struct(
111 "RTMsg", "=BBBBBBBBI",
112 "family dst_len src_len tos table protocol scope type flags")
113 RTACacheinfo = cstruct.Struct(
114 "RTACacheinfo", "=IIiiI", "clntref lastuse expires error used")
117 ### Interface address constants. See include/uapi/linux/if_addr.h.
118 # Interface address attributes.
124 IFA_F_SECONDARY = 0x01
125 IFA_F_TEMPORARY = IFA_F_SECONDARY
127 IFA_F_OPTIMISTIC = 0x04
128 IFA_F_DADFAILED = 0x08
129 IFA_F_HOMEADDRESS = 0x10
130 IFA_F_DEPRECATED = 0x20
131 IFA_F_TENTATIVE = 0x40
132 IFA_F_PERMANENT = 0x80
134 # Data structure formats.
135 IfAddrMsg = cstruct.Struct(
136 "IfAddrMsg", "=BBBBI",
137 "family prefixlen flags scope index")
138 IFACacheinfo = cstruct.Struct(
139 "IFACacheinfo", "=IIII", "prefered valid cstamp tstamp")
140 NDACacheinfo = cstruct.Struct(
141 "NDACacheinfo", "=IIII", "confirmed used updated refcnt")
144 ### Neighbour table entry constants. See include/uapi/linux/neighbour.h.
145 # Neighbour cache entry attributes.
151 # Neighbour cache entry states.
154 # Data structure formats.
155 NdMsg = cstruct.Struct(
156 "NdMsg", "=BxxxiHBB",
157 "family ifindex state flags type")
160 ### FIB rule constants. See include/uapi/linux/fib_rules.h.
164 FRA_SUPPRESS_PREFIXLEN = 14
172 # Link constants. See include/uapi/linux/if_link.h.
187 IFLA_PROMISCUITY = 30
188 IFLA_NUM_TX_QUEUES = 31
189 IFLA_NUM_RX_QUEUES = 32
193 def CommandVerb(command):
194 return ["NEW", "DEL", "GET", "SET"][command % 4]
197 def CommandSubject(command):
198 return ["LINK", "ADDR", "ROUTE", "NEIGH", "RULE"][(command - 16) / 4]
201 def CommandName(command):
203 return "RTM_%s%s" % (CommandVerb(command), CommandSubject(command))
205 return "RTM_%d" % command
208 class IPRoute(netlink.NetlinkSocket):
209 """Provides a tiny subset of iproute functionality."""
211 FAMILY = NETLINK_ROUTE
213 def _NlAttrIPAddress(self, nla_type, family, address):
214 return self._NlAttr(nla_type, socket.inet_pton(family, address))
216 def _NlAttrInterfaceName(self, nla_type, interface):
217 return self._NlAttr(nla_type, interface + "\x00")
219 def _GetConstantName(self, value, prefix):
220 return super(IPRoute, self)._GetConstantName(__name__, value, prefix)
222 def _Decode(self, command, msg, nla_type, nla_data):
223 """Decodes netlink attributes to Python types.
225 Values for which the code knows the type (e.g., the fwmark ID in a
226 RTM_NEWRULE command) are decoded to Python integers, strings, etc. Values
227 of unknown type are returned as raw byte strings.
231 - If positive, the number of the rtnetlink command being carried out.
232 This is used to interpret the attributes. For example, for an
233 RTM_NEWROUTE command, attribute type 3 is the incoming interface and
234 is an integer, but for a RTM_NEWRULE command, attribute type 3 is the
235 incoming interface name and is a string.
236 - If negative, one of the following (negative) values:
237 - RTA_METRICS: Interpret as nested route metrics.
238 family: The address family. Used to convert IP addresses into strings.
239 nla_type: An integer, then netlink attribute type.
240 nla_data: A byte string, the netlink attribute data.
243 A tuple (name, data):
244 - name is a string (e.g., "FRA_PRIORITY") if we understood the attribute,
245 or an integer if we didn't.
246 - data can be an integer, a string, a nested dict of attributes as
247 returned by _ParseAttributes (e.g., for RTA_METRICS), a cstruct.Struct
248 (e.g., RTACacheinfo), etc. If we didn't understand the attribute, it
249 will be the raw byte string.
251 if command == -RTA_METRICS:
252 name = self._GetConstantName(nla_type, "RTAX_")
253 elif CommandSubject(command) == "ADDR":
254 name = self._GetConstantName(nla_type, "IFA_")
255 elif CommandSubject(command) == "LINK":
256 name = self._GetConstantName(nla_type, "IFLA_")
257 elif CommandSubject(command) == "RULE":
258 name = self._GetConstantName(nla_type, "FRA_")
259 elif CommandSubject(command) == "ROUTE":
260 name = self._GetConstantName(nla_type, "RTA_")
261 elif CommandSubject(command) == "NEIGH":
262 name = self._GetConstantName(nla_type, "NDA_")
264 # Don't know what this is. Leave it as an integer.
267 if name in ["FRA_PRIORITY", "FRA_FWMARK", "FRA_TABLE", "FRA_FWMASK",
268 "FRA_UID_START", "FRA_UID_END",
269 "RTA_OIF", "RTA_PRIORITY", "RTA_TABLE", "RTA_MARK",
270 "IFLA_MTU", "IFLA_TXQLEN", "IFLA_GROUP", "IFLA_EXT_MASK",
271 "IFLA_PROMISCUITY", "IFLA_NUM_RX_QUEUES",
272 "IFLA_NUM_TX_QUEUES", "NDA_PROBES", "RTAX_MTU",
274 data = struct.unpack("=I", nla_data)[0]
275 elif name == "FRA_SUPPRESS_PREFIXLEN":
276 data = struct.unpack("=i", nla_data)[0]
277 elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]:
279 elif name in ["IFA_ADDRESS", "IFA_LOCAL", "RTA_DST", "RTA_SRC",
280 "RTA_GATEWAY", "RTA_PREFSRC", "RTA_UID",
282 data = socket.inet_ntop(msg.family, nla_data)
283 elif name in ["FRA_IIFNAME", "FRA_OIFNAME", "IFLA_IFNAME", "IFLA_QDISC"]:
284 data = nla_data.strip("\x00")
285 elif name == "RTA_METRICS":
286 data = self._ParseAttributes(-RTA_METRICS, msg.family, None, nla_data)
287 elif name == "RTA_CACHEINFO":
288 data = RTACacheinfo(nla_data)
289 elif name == "IFA_CACHEINFO":
290 data = IFACacheinfo(nla_data)
291 elif name == "NDA_CACHEINFO":
292 data = NDACacheinfo(nla_data)
293 elif name in ["NDA_LLADDR", "IFLA_ADDRESS"]:
294 data = ":".join(x.encode("hex") for x in nla_data)
301 super(IPRoute, self).__init__()
303 def _AddressFamily(self, version):
304 return {4: socket.AF_INET, 6: socket.AF_INET6}[version]
306 def _SendNlRequest(self, command, data, flags=0):
307 """Sends a netlink request and expects an ack."""
309 flags |= NLM_F_REQUEST
310 if CommandVerb(command) != "GET":
312 if CommandVerb(command) == "NEW":
313 if not flags & NLM_F_REPLACE:
314 flags |= (NLM_F_EXCL | NLM_F_CREATE)
316 super(IPRoute, self)._SendNlRequest(command, data, flags)
318 def _Rule(self, version, is_add, rule_type, table, match_nlattr, priority):
319 """Python equivalent of "ip rule <add|del> <match_cond> lookup <table>".
322 version: An integer, 4 or 6.
323 is_add: True to add a rule, False to delete it.
324 rule_type: Type of rule, e.g., RTN_UNICAST or RTN_UNREACHABLE.
325 table: If nonzero, rule looks up this table.
326 match_nlattr: A blob of struct nlattrs that express the match condition.
327 If None, match everything.
328 priority: An integer, the priority.
331 IOError: If the netlink request returns an error.
332 ValueError: If the kernel's response could not be parsed.
334 # Create a struct rtmsg specifying the table and the given match attributes.
335 family = self._AddressFamily(version)
336 rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
337 RTPROT_STATIC, RT_SCOPE_UNIVERSE, rule_type, 0)).Pack()
338 rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
340 rtmsg += match_nlattr
342 rtmsg += self._NlAttrU32(FRA_TABLE, table)
344 # Create a netlink request containing the rtmsg.
345 command = RTM_NEWRULE if is_add else RTM_DELRULE
346 self._SendNlRequest(command, rtmsg)
348 def DeleteRulesAtPriority(self, version, priority):
349 family = self._AddressFamily(version)
350 rtmsg = RTMsg((family, 0, 0, 0, RT_TABLE_UNSPEC,
351 RTPROT_STATIC, RT_SCOPE_UNIVERSE, RTN_UNICAST, 0)).Pack()
352 rtmsg += self._NlAttrU32(FRA_PRIORITY, priority)
355 self._SendNlRequest(RTM_DELRULE, rtmsg)
357 if e.errno == -errno.ENOENT:
362 def FwmarkRule(self, version, is_add, fwmark, table, priority):
363 nlattr = self._NlAttrU32(FRA_FWMARK, fwmark)
364 return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
366 def IifRule(self, version, is_add, iif, table, priority):
367 nlattr = self._NlAttrInterfaceName(FRA_IIFNAME, iif)
368 return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
370 def OifRule(self, version, is_add, oif, table, priority):
371 nlattr = self._NlAttrInterfaceName(FRA_OIFNAME, oif)
372 return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
374 def UidRangeRule(self, version, is_add, start, end, table, priority):
375 nlattr = (self._NlAttrInterfaceName(FRA_IIFNAME, "lo") +
376 self._NlAttrU32(FRA_UID_START, start) +
377 self._NlAttrU32(FRA_UID_END, end))
378 return self._Rule(version, is_add, RTN_UNICAST, table, nlattr, priority)
380 def UnreachableRule(self, version, is_add, priority):
381 return self._Rule(version, is_add, RTN_UNREACHABLE, None, None, priority)
383 def DefaultRule(self, version, is_add, table, priority):
384 return self.FwmarkRule(version, is_add, 0, table, priority)
386 def CommandToString(self, command, data):
388 name = CommandName(command)
389 subject = CommandSubject(command)
397 parsed = self._ParseNLMsg(data, struct_type)
398 return "%s %s" % (name, str(parsed))
400 raise ValueError("Don't know how to print command type %s" % name)
402 def MaybeDebugCommand(self, command, data):
403 subject = CommandSubject(command)
404 if "ALL" not in self.NL_DEBUG and subject not in self.NL_DEBUG:
406 print self.CommandToString(command, data)
408 def MaybeDebugMessage(self, message):
409 hdr = NLMsgHdr(message)
410 self.MaybeDebugCommand(hdr.type, message)
412 def PrintMessage(self, message):
413 hdr = NLMsgHdr(message)
414 print self.CommandToString(hdr.type, message)
416 def DumpRules(self, version):
417 """Returns the IP rules for the specified IP version."""
418 # Create a struct rtmsg specifying the table and the given match attributes.
419 family = self._AddressFamily(version)
420 rtmsg = RTMsg((family, 0, 0, 0, 0, 0, 0, 0, 0))
421 return self._Dump(RTM_GETRULE, rtmsg, RTMsg, "")
424 ifinfomsg = IfinfoMsg((0, 0, 0, 0, 0, 0))
425 return self._Dump(RTM_GETLINK, ifinfomsg, IfinfoMsg, "")
427 def _Address(self, version, command, addr, prefixlen, flags, scope, ifindex):
428 """Adds or deletes an IP address."""
429 family = self._AddressFamily(version)
430 ifaddrmsg = IfAddrMsg((family, prefixlen, flags, scope, ifindex)).Pack()
431 ifaddrmsg += self._NlAttrIPAddress(IFA_ADDRESS, family, addr)
433 ifaddrmsg += self._NlAttrIPAddress(IFA_LOCAL, family, addr)
434 self._SendNlRequest(command, ifaddrmsg)
436 def AddAddress(self, address, prefixlen, ifindex):
437 self._Address(6 if ":" in address else 4,
438 RTM_NEWADDR, address, prefixlen,
439 IFA_F_PERMANENT, RT_SCOPE_UNIVERSE, ifindex)
441 def DelAddress(self, address, prefixlen, ifindex):
442 self._Address(6 if ":" in address else 4,
443 RTM_DELADDR, address, prefixlen, 0, 0, ifindex)
445 def GetAddress(self, address, ifindex=0):
446 """Returns an ifaddrmsg for the requested address."""
447 if ":" not in address:
448 # The address is likely an IPv4 address. RTM_GETADDR without the
449 # NLM_F_DUMP flag is not supported by the kernel. We do not currently
450 # implement parsing dump results.
451 raise NotImplementedError("IPv4 RTM_GETADDR not implemented.")
452 self._Address(6, RTM_GETADDR, address, 0, 0, RT_SCOPE_UNIVERSE, ifindex)
453 return self._GetMsg(IfAddrMsg)
455 def _Route(self, version, command, table, dest, prefixlen, nexthop, dev,
457 """Adds, deletes, or queries a route."""
458 family = self._AddressFamily(version)
459 scope = RT_SCOPE_UNIVERSE if nexthop else RT_SCOPE_LINK
460 rtmsg = RTMsg((family, prefixlen, 0, 0, RT_TABLE_UNSPEC,
461 RTPROT_STATIC, scope, RTN_UNICAST, 0)).Pack()
462 if command == RTM_NEWROUTE and not table:
463 # Don't allow setting routes in table 0, since its behaviour is confusing
464 # and differs between IPv4 and IPv6.
465 raise ValueError("Cowardly refusing to add a route to table 0")
467 rtmsg += self._NlAttrU32(FRA_TABLE, table)
468 if dest != "default": # The default is the default route.
469 rtmsg += self._NlAttrIPAddress(RTA_DST, family, dest)
471 rtmsg += self._NlAttrIPAddress(RTA_GATEWAY, family, nexthop)
473 rtmsg += self._NlAttrU32(RTA_OIF, dev)
475 rtmsg += self._NlAttrU32(RTA_MARK, mark)
477 rtmsg += self._NlAttrU32(RTA_UID, uid)
478 self._SendNlRequest(command, rtmsg)
480 def AddRoute(self, version, table, dest, prefixlen, nexthop, dev):
481 self._Route(version, RTM_NEWROUTE, table, dest, prefixlen, nexthop, dev,
484 def DelRoute(self, version, table, dest, prefixlen, nexthop, dev):
485 self._Route(version, RTM_DELROUTE, table, dest, prefixlen, nexthop, dev,
488 def GetRoutes(self, dest, oif, mark, uid):
489 version = 6 if ":" in dest else 4
490 prefixlen = {4: 32, 6: 128}[version]
491 self._Route(version, RTM_GETROUTE, 0, dest, prefixlen, None, oif, mark, uid)
493 # The response will either be an error or a list of routes.
494 if NLMsgHdr(data).type == NLMSG_ERROR:
496 routes = self._GetMsgList(RTMsg, data, False)
499 def _Neighbour(self, version, is_add, addr, lladdr, dev, state, flags=0):
500 """Adds or deletes a neighbour cache entry."""
501 family = self._AddressFamily(version)
503 # Convert the link-layer address to a raw byte string.
504 if is_add and lladdr:
505 lladdr = lladdr.split(":")
507 raise ValueError("Invalid lladdr %s" % ":".join(lladdr))
508 lladdr = "".join(chr(int(hexbyte, 16)) for hexbyte in lladdr)
510 ndmsg = NdMsg((family, dev, state, 0, RTN_UNICAST)).Pack()
511 ndmsg += self._NlAttrIPAddress(NDA_DST, family, addr)
512 if is_add and lladdr:
513 ndmsg += self._NlAttr(NDA_LLADDR, lladdr)
514 command = RTM_NEWNEIGH if is_add else RTM_DELNEIGH
515 self._SendNlRequest(command, ndmsg, flags)
517 def AddNeighbour(self, version, addr, lladdr, dev):
518 self._Neighbour(version, True, addr, lladdr, dev, NUD_PERMANENT)
520 def DelNeighbour(self, version, addr, lladdr, dev):
521 self._Neighbour(version, False, addr, lladdr, dev, 0)
523 def UpdateNeighbour(self, version, addr, lladdr, dev, state):
524 self._Neighbour(version, True, addr, lladdr, dev, state,
527 def DumpNeighbours(self, version):
528 ndmsg = NdMsg((self._AddressFamily(version), 0, 0, 0, 0))
529 return self._Dump(RTM_GETNEIGH, ndmsg, NdMsg, "")
531 def ParseNeighbourMessage(self, msg):
532 msg, _ = self._ParseNLMsg(msg, NdMsg)
536 if __name__ == "__main__":
541 print iproute.GetRoutes("2001:4860:4860::8888", 0, 0, None)