]>
Commit | Line | Data |
---|---|---|
f82758bf RP |
1 | #!/usr/bin/env python |
2 | # | |
3 | # Copyright 2014 Cumulus Networks, Inc. All rights reserved. | |
4 | # | |
5 | # Author: Scott Feldman, sfeldma@cumulusnetworks.com | |
6 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com | |
7 | # | |
8 | # | |
9 | from socket import NETLINK_ROUTE, AF_INET, AF_INET6 | |
10 | from string import printable | |
11 | from ipaddr import * | |
12 | from ctypes import * | |
13 | from netlink import * | |
14 | import logging | |
15 | ||
16 | logger = logging.getLogger(__name__) | |
17 | ||
18 | # | |
19 | # from /usr/include/linux/rtnetlink.h | |
20 | # | |
21 | ||
22 | RTMGRP_LINK = 0x1 | |
23 | RTMGRP_IPV4_IFADDR = 0x10 | |
24 | RTMGRP_IPV4_ROUTE = 0x40 | |
25 | RTMGRP_IPV6_IFADDR = 0x100 | |
26 | RTMGRP_IPV6_ROUTE = 0x400 | |
27 | ||
28 | RTM_NEWLINK = 16 | |
29 | RTM_DELLINK = 17 | |
30 | RTM_GETLINK = 18 | |
31 | RTM_SETLINK = 19 | |
32 | RTM_NEWADDR = 20 | |
33 | RTM_DELADDR = 21 | |
34 | RTM_GETADDR = 22 | |
35 | RTM_NEWROUTE = 24 | |
36 | RTM_DELROUTE = 25 | |
37 | RTM_GETROUTE = 26 | |
38 | ||
39 | # Definitions used in routing table administration. | |
40 | ||
41 | class Nlmsg(Structure): | |
42 | ||
43 | def _stringify(self): | |
44 | return string_at(addressof(self), sizeof(self)) | |
45 | ||
46 | def __eq__(self, other): | |
47 | return self._stringify() == other._stringify() and \ | |
48 | self.__dict__ == other.__dict__ | |
49 | ||
50 | def to_rta(self): | |
51 | return Rtattr.from_address(addressof(self) + NLMSG_ALIGN(sizeof(self))) | |
52 | ||
53 | def pack_extra(self, extra, addr): | |
54 | memmove(addr, addressof(extra), sizeof(extra)) | |
55 | return NLMSG_ALIGN(sizeof(extra)) | |
56 | ||
57 | def pack_rtas(self, rtas, addr): | |
58 | total_len = 0 | |
59 | for rta_type, value in rtas.iteritems(): | |
60 | rta = Rtattr.from_address(addr) | |
61 | rta.rta_type = rta_type | |
62 | pack_fn = self.rta_fn(rta_type) | |
63 | rta_len = NLMSG_ALIGN(pack_fn(rta, value)) | |
64 | total_len += rta_len | |
65 | addr += rta_len | |
66 | return total_len | |
67 | ||
68 | def pack_rtas_new(self, rtas, addr, policy): | |
69 | total_len = 0 | |
70 | ||
71 | for rta_type, value in rtas.iteritems(): | |
72 | if type(value) == dict: | |
73 | rta = Rtattr.from_address(addr) | |
74 | rta.rta_type = rta_type | |
75 | rta.rta_len = RTA_LENGTH(0) | |
76 | rta_len = NLMSG_ALIGN(rta.rta_len) | |
77 | total_len += rta_len | |
78 | addr += rta_len | |
79 | pack_fn = policy.get(rta_type) | |
80 | rta_len = NLMSG_ALIGN(pack_fn(addr, value)) | |
81 | ||
82 | rta.rta_len += rta_len | |
83 | else: | |
84 | rta = Rtattr.from_address(addr) | |
85 | rta.rta_type = rta_type | |
86 | pack_fn = policy.get(rta_type) | |
87 | rta_len = NLMSG_ALIGN(pack_fn(rta, value)) | |
88 | total_len += rta_len | |
89 | addr += rta_len | |
90 | return total_len | |
91 | ||
92 | def rta_linkinfo(self, addr, rtas): | |
93 | total_len = 0 | |
94 | ||
95 | # Check interface kind | |
96 | kind = rtas.get(IFLA_INFO_KIND) | |
97 | if kind == 'vlan': | |
98 | data_policy = self.rta_linkinfo_data_vlan_policy() | |
99 | else: | |
100 | data_policy = self.rta_linkinfo_data_macvlan_policy() | |
101 | ||
102 | # Pack info kind | |
103 | rta = Rtattr.from_address(addr) | |
104 | rta.rta_type = IFLA_INFO_KIND | |
105 | rta_len = NLMSG_ALIGN(self.rta_string(rta, kind)) | |
106 | total_len += rta_len | |
107 | addr += rta_len | |
108 | ||
109 | # nest start link info data | |
110 | rta = Rtattr.from_address(addr) | |
111 | rta.rta_type = IFLA_INFO_DATA | |
112 | rta.rta_len = RTA_LENGTH(0) | |
113 | rta_len = NLMSG_ALIGN(rta.rta_len) | |
114 | total_len += rta_len | |
115 | addr += rta_len | |
116 | rta_len = self.pack_rtas_new(rtas.get(IFLA_INFO_DATA), addr, | |
117 | data_policy) | |
118 | rta.rta_len += rta_len | |
119 | ||
120 | total_len += rta_len | |
121 | addr += rta_len | |
122 | ||
123 | return total_len | |
124 | ||
125 | def rta_bridge_vlan_info(self, rta, value): | |
126 | if value: | |
127 | data = RTA_DATA(rta) | |
128 | memmove(data, addressof(value), sizeof(value)) | |
129 | rta.rta_len = RTA_LENGTH(sizeof(value)) | |
130 | return rta.rta_len | |
131 | ||
132 | def rta_af_spec(self, addr, rtas): | |
133 | total_len = 0 | |
134 | ||
135 | # XXX: Check family (Assumes bridge family for now) | |
136 | rta_len = self.pack_rtas_new(rtas, addr, | |
137 | self.rta_bridge_af_spec_policy()) | |
138 | total_len += rta_len | |
139 | return total_len | |
140 | ||
141 | def unpack_rtas(self, which_ones=[]): | |
142 | len = self.nlh.nlmsg_len - NLMSG_LENGTH(sizeof(self)) | |
143 | rta = self.to_rta() | |
144 | rtas = {} | |
145 | while RTA_OK(rta, len): | |
146 | rta_type = rta.rta_type | |
147 | if not which_ones or rta_type in which_ones: | |
148 | unpack_fn = self.rta_fn(rta_type) | |
149 | rtas[rta_type] = unpack_fn(rta) | |
150 | len, rta = RTA_NEXT(rta, len) | |
151 | return rtas | |
152 | ||
153 | def dump_rtas(self): | |
154 | rtas = self.unpack_rtas() | |
155 | for type, value in rtas.iteritems(): | |
156 | print "rta", type, ":", value | |
157 | ||
158 | class _IPv6Addr(BigEndianStructure): | |
159 | _fields_ = [ | |
160 | ('upper', c_uint64), | |
161 | ('lower', c_uint64), | |
162 | ] | |
163 | ||
164 | class _IPv4Addr(BigEndianStructure): | |
165 | _fields_ = [ | |
166 | ('addr', c_uint32), | |
167 | ] | |
168 | ||
169 | def rta_uint8(self, rta, value=None): | |
170 | data = RTA_DATA(rta) | |
171 | if value: | |
172 | c_uint8.from_address(data).value = value | |
173 | rta.rta_len = RTA_LENGTH(sizeof(c_uint8)) | |
174 | return rta.rta_len | |
175 | else: | |
176 | return c_uint8.from_address(data).value | |
177 | ||
178 | def rta_uint16(self, rta, value=None): | |
179 | data = RTA_DATA(rta) | |
180 | if value: | |
181 | c_uint16.from_address(data).value = value | |
182 | rta.rta_len = RTA_LENGTH(sizeof(c_uint16)) | |
183 | return rta.rta_len | |
184 | else: | |
185 | return c_uint16.from_address(data).value | |
186 | ||
187 | def rta_uint32(self, rta, value=None): | |
188 | data = RTA_DATA(rta) | |
189 | if value: | |
190 | c_uint32.from_address(data).value = value | |
191 | rta.rta_len = RTA_LENGTH(sizeof(c_uint32)) | |
192 | return rta.rta_len | |
193 | else: | |
194 | return c_uint32.from_address(data).value | |
195 | ||
196 | def rta_string(self, rta, value=None): | |
197 | data = RTA_DATA(rta) | |
198 | if value: | |
199 | s = create_string_buffer(value) | |
200 | memmove(data, addressof(s), len(value)) | |
201 | rta.rta_len = RTA_LENGTH(len(value)) | |
202 | return rta.rta_len | |
203 | else: | |
204 | return c_char_p(data).value | |
205 | ||
206 | def rta_addr(self, rta, value=None): | |
207 | data = RTA_DATA(rta) | |
208 | if value: | |
209 | if isinstance(value, IPv4Address): | |
210 | self._IPv4Addr.from_address(data).addr = value._ip | |
211 | rta.rta_len = RTA_LENGTH(sizeof(self._IPv4Addr)) | |
212 | elif isinstance(value, IPv6Address): | |
213 | addr = self._IPv6Addr.from_address(data) | |
214 | addr.upper = value._ip >> 64 | |
215 | addr.lower = value._ip & 0xffffffffffffffff | |
216 | rta.rta_len = RTA_LENGTH(sizeof(self._IPv6Addr)) | |
217 | else: | |
218 | assert(False) | |
219 | return rta.rta_len | |
220 | else: | |
221 | if RTA_PAYLOAD(rta) == 4: | |
222 | addr = c_uint32.__ctype_be__.from_address(data).value | |
223 | addr = IPv4Address(addr) | |
224 | else: | |
225 | addr = self._IPv6Addr.from_address(data) | |
226 | addr = IPv6Address((addr.upper << 64) + addr.lower) | |
227 | return addr | |
228 | ||
229 | def rta_uint8_array(self, rta, value=None): | |
230 | data = RTA_DATA(rta) | |
231 | if value: | |
232 | s = (c_uint8 * len(value)).from_buffer_copy(value) | |
233 | memmove(data, addressof(s), len(value)) | |
234 | rta.rta_len = RTA_LENGTH(len(value)) | |
235 | return rta.rta_len | |
236 | else: | |
237 | array = (c_uint8 * RTA_PAYLOAD(rta))() | |
238 | memmove(array, data, RTA_PAYLOAD(rta)) | |
239 | return array | |
240 | ||
241 | def rta_uint32_array(self, rta, value=None): | |
242 | if value: | |
243 | assert(False) | |
244 | else: | |
245 | data = RTA_DATA(rta) | |
246 | size = RTA_PAYLOAD(rta) / sizeof(c_uint32) | |
247 | array = (c_uint32 * size)() | |
248 | memmove(array, data, RTA_PAYLOAD(rta)) | |
249 | return array | |
250 | ||
251 | def rta_multipath(self, rta, value=None): | |
252 | # XXX implement this | |
253 | return None | |
254 | ||
255 | def rta_wtf(self, rta, value=None): | |
256 | return None | |
257 | ||
258 | def rta_none(self, rta, value=None): | |
259 | return None | |
260 | ||
261 | def rta_fn(self, rta_type): | |
262 | return None | |
263 | ||
264 | ||
265 | # rtm_type | |
266 | ||
267 | RTN_UNSPEC = 0 | |
268 | RTN_UNICAST = 1 # Gateway or direct route | |
269 | RTN_LOCAL = 2 # Accept locally | |
270 | RTN_BROADCAST = 3 # Accept locally as broadcast, | |
271 | # send as broadcast | |
272 | RTN_ANYCAST = 4 # Accept locally as broadcast, | |
273 | # but send as unicast | |
274 | RTN_MULTICAST = 5 # Multicast route | |
275 | RTN_BLACKHOLE = 6 # Drop | |
276 | RTN_UNREACHABLE = 7 # Destination is unreachable | |
277 | RTN_PROHIBIT = 8 # Administratively prohibited | |
278 | RTN_THROW = 9 # Not in this table | |
279 | RTN_NAT = 10 # Translate this address | |
280 | RTN_XRESOLVE = 11 # Use external resolver | |
281 | RTN_MAX = 11 | |
282 | ||
283 | # rtm_protocol | |
284 | ||
285 | RTPROT_UNSPEC = 0 | |
286 | RTPROT_REDIRECT = 1 # Route installed by ICMP redirects; | |
287 | # not used by current IPv4 | |
288 | RTPROT_KERNEL = 2 # Route installed by kernel | |
289 | RTPROT_BOOT = 3 # Route installed during boot | |
290 | RTPROT_STATIC = 4 # Route installed by administrator | |
291 | ||
292 | # Values of protocol >= RTPROT_STATIC are not interpreted by kernel; | |
293 | # they are just passed from user and back as is. | |
294 | # It will be used by hypothetical multiple routing daemons. | |
295 | # Note that protocol values should be standardized in order to | |
296 | # avoid conflicts. | |
297 | ||
298 | RTPROT_GATED = 8 # Apparently, GateD | |
299 | RTPROT_RA = 9 # RDISC/ND router advertisements | |
300 | RTPROT_MRT = 10 # Merit MRT | |
301 | RTPROT_ZEBRA = 11 # Zebra | |
302 | RTPROT_BIRD = 12 # BIRD | |
303 | RTPROT_DNROUTED = 13 # DECnet routing daemon | |
304 | RTPROT_XORP = 14 # XORP | |
305 | RTPROT_NTK = 15 # Netsukuku | |
306 | RTPROT_DHCP = 16 # DHCP client | |
307 | ||
308 | # rtm_scope | |
309 | ||
310 | # Really it is not scope, but sort of distance to the destination. | |
311 | # NOWHERE are reserved for not existing destinations, HOST is our | |
312 | # local addresses, LINK are destinations, located on directly attached | |
313 | # link and UNIVERSE is everywhere in the Universe. | |
314 | ||
315 | # Intermediate values are also possible f.e. interior routes | |
316 | # could be assigned a value between UNIVERSE and LINK. | |
317 | ||
318 | RT_SCOPE_UNIVERSE = 0 | |
319 | # User defined values | |
320 | RT_SCOPE_SITE = 200 | |
321 | RT_SCOPE_LINK = 253 | |
322 | RT_SCOPE_HOST = 254 | |
323 | RT_SCOPE_NOWHERE=255 | |
324 | ||
325 | # rtm_flags | |
326 | ||
327 | RTM_F_NOTIFY = 0x100 # Notify user of route change | |
328 | RTM_F_CLONED = 0x200 # This route is cloned | |
329 | RTM_F_EQUALIZE = 0x400 # Multipath equalizer: NI | |
330 | RTM_F_PREFIX = 0x800 # Prefix addresses | |
331 | ||
332 | # Reserved table identifiers | |
333 | ||
334 | RT_TABLE_UNSPEC = 0 | |
335 | # User defined values | |
336 | RT_TABLE_COMPAT = 252 | |
337 | RT_TABLE_DEFAULT = 253 | |
338 | RT_TABLE_MAIN = 254 | |
339 | RT_TABLE_LOCAL = 255 | |
340 | RT_TABLE_MAX = 0xFFFFFFFF | |
341 | ||
342 | # Generic structure for encapsulation of optional route information. | |
343 | # It is reminiscent of sockaddr, but with sa_family replaced | |
344 | # with attribute type. | |
345 | ||
346 | class Rtattr(Structure): | |
347 | ||
348 | _fields_ = [ | |
349 | ('rta_len', c_uint16), | |
350 | ('rta_type', c_uint16), | |
351 | ] | |
352 | ||
353 | # Routing message attributes | |
354 | ||
355 | RTA_UNSPEC = 0 | |
356 | RTA_DST = 1 | |
357 | RTA_SRC = 2 | |
358 | RTA_IIF = 3 | |
359 | RTA_OIF = 4 | |
360 | RTA_GATEWAY = 5 | |
361 | RTA_PRIORITY = 6 | |
362 | RTA_PREFSRC = 7 | |
363 | RTA_METRICS = 8 | |
364 | RTA_MULTIPATH = 9 | |
365 | RTA_PROTOINFO = 10 # no longer used | |
366 | RTA_FLOW = 11 | |
367 | RTA_CACHEINFO = 12 | |
368 | RTA_SESSION = 13 # no longer used | |
369 | RTA_MP_ALGO = 14 # no longer used | |
370 | RTA_TABLE = 15 | |
371 | RTA_MAX = 15 | |
372 | ||
373 | # Macros to handle rtattributes | |
374 | ||
375 | RTA_ALIGNTO = 4 | |
376 | def RTA_ALIGN(len): | |
377 | return (len + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1) | |
378 | def RTA_OK(rta, len): | |
379 | return len >= sizeof(Rtattr) and \ | |
380 | rta.rta_len >= sizeof(Rtattr) and \ | |
381 | rta.rta_len <= len | |
382 | def RTA_NEXT(rta, len): | |
383 | cur = RTA_ALIGN(rta.rta_len) | |
384 | rta = Rtattr.from_address(addressof(rta) + cur) | |
385 | return len - cur, rta | |
386 | def RTA_LENGTH(len): | |
387 | return len + RTA_ALIGN(sizeof(Rtattr)) | |
388 | def RTA_SPACE(len): | |
389 | return RTA_ALIGN(RTA_LENGTH(len)) | |
390 | def RTA_DATA(rta): | |
391 | return addressof(rta) + RTA_LENGTH(0) | |
392 | def RTA_PAYLOAD(rta): | |
393 | return rta.rta_len - RTA_LENGTH(0) | |
394 | ||
395 | RTNH_F_DEAD = 1 # Nexthop is dead (used by multipath) | |
396 | RTNH_F_PERVASIVE = 2 # Do recursive gateway lookup | |
397 | RTNH_F_ONLINK = 4 # Gateway is forced on link | |
398 | ||
399 | # Reserved table identifiers | |
400 | ||
401 | RT_TABLE_UNSPEC = 0 | |
402 | # User defined values | |
403 | RT_TABLE_COMPAT = 252 | |
404 | RT_TABLE_DEFAULT = 253 | |
405 | RT_TABLE_MAIN = 254 | |
406 | RT_TABLE_LOCAL = 255 | |
407 | RT_TABLE_MAX = 0xFFFFFFFF | |
408 | ||
409 | class Rtmsg(Nlmsg): | |
410 | ||
411 | _fields_ = [ | |
412 | ('rtm_family', c_uint8), | |
413 | ('rtm_dst_len', c_uint8), | |
414 | ('rtm_src_len', c_uint8), | |
415 | ('rtm_tos', c_uint8), | |
416 | ('rtm_table', c_uint8), | |
417 | ('rtm_protocol', c_uint8), | |
418 | ('rtm_scope', c_uint8), | |
419 | ('rtm_type', c_uint8), | |
420 | ('rtm_flags', c_uint32), | |
421 | ] | |
422 | ||
423 | _table_str = { | |
424 | RT_TABLE_UNSPEC: "unspecified", | |
425 | RT_TABLE_COMPAT: "compat", | |
426 | RT_TABLE_DEFAULT: "default", | |
427 | RT_TABLE_MAIN: "main", | |
428 | RT_TABLE_LOCAL: "local", | |
429 | } | |
430 | ||
431 | _proto_str = { | |
432 | RTPROT_UNSPEC: "none", | |
433 | RTPROT_REDIRECT: "redirect", | |
434 | RTPROT_KERNEL: "kernel", | |
435 | RTPROT_BOOT: "boot", | |
436 | RTPROT_STATIC: "static", | |
437 | RTPROT_GATED: "gated", | |
438 | RTPROT_RA: "ra", | |
439 | RTPROT_MRT: "mrtmrt", | |
440 | RTPROT_ZEBRA: "zebra", | |
441 | RTPROT_BIRD: "bird", | |
442 | RTPROT_DNROUTED: "dnrouted", | |
443 | RTPROT_XORP: "xorp", | |
444 | RTPROT_NTK: "ntk", | |
445 | RTPROT_DHCP: "dhcp", | |
446 | } | |
447 | ||
448 | _scope_str = { | |
449 | RT_SCOPE_UNIVERSE: "universe", | |
450 | RT_SCOPE_SITE: "site", | |
451 | RT_SCOPE_LINK: "link", | |
452 | RT_SCOPE_HOST: "host", | |
453 | RT_SCOPE_NOWHERE: "nowhere", | |
454 | } | |
455 | ||
456 | _type_str = { | |
457 | RTN_UNSPEC: "unspecified", | |
458 | RTN_UNICAST: "unicast", | |
459 | RTN_LOCAL: "local", | |
460 | RTN_BROADCAST: "broadcast", | |
461 | RTN_ANYCAST: "anycast", | |
462 | RTN_MULTICAST: "multicast", | |
463 | RTN_BLACKHOLE: "blackhole", | |
464 | RTN_UNREACHABLE: "unreachable", | |
465 | RTN_PROHIBIT: "prohibit", | |
466 | RTN_THROW: "throw", | |
467 | RTN_NAT: "nat", | |
468 | RTN_XRESOLVE: "xresolve", | |
469 | } | |
470 | ||
471 | def dump(self): | |
472 | print 'rtm_family', self.rtm_family | |
473 | print 'rtm_dst_len', self.rtm_dst_len | |
474 | print 'rtm_src_len', self.rtm_src_len | |
475 | print 'rtm_tos', self.rtm_tos | |
476 | print 'rtm_table', self._table_str.get(self.rtm_table, self.rtm_table) | |
477 | print 'rtm_protocol', self._proto_str.get(self.rtm_protocol) | |
478 | print 'rtm_scope', self._scope_str.get(self.rtm_scope) | |
479 | print 'rtm_type', self._type_str.get(self.rtm_type) | |
480 | print 'rtm_flags 0x%08x' % self.rtm_flags | |
481 | ||
482 | def rta_fn(self, rta_type): | |
483 | fns = { | |
484 | RTA_DST: self.rta_addr, | |
485 | RTA_SRC: self.rta_addr, | |
486 | RTA_IIF: self.rta_uint32, | |
487 | RTA_OIF: self.rta_uint32, | |
488 | RTA_GATEWAY: self.rta_addr, | |
489 | RTA_PRIORITY: self.rta_uint32, | |
490 | RTA_PREFSRC: self.rta_addr, | |
491 | RTA_METRICS: self.rta_uint32_array, | |
492 | RTA_MULTIPATH: self.rta_multipath, | |
493 | RTA_PROTOINFO: self.rta_none, | |
494 | RTA_FLOW: self.rta_uint32, | |
495 | RTA_CACHEINFO: self.rta_none, | |
496 | RTA_SESSION: self.rta_none, | |
497 | RTA_MP_ALGO: self.rta_none, | |
498 | RTA_TABLE: self.rta_uint32, | |
499 | } | |
500 | ||
501 | return fns.get(rta_type) | |
502 | ||
503 | class Rtgenmsg(Nlmsg): | |
504 | ||
505 | _fields_ = [ | |
506 | ('rtgen_family', c_uint8), | |
507 | ] | |
508 | ||
509 | def dump(self): | |
510 | print 'rtgen_family', self.rtgen_family | |
511 | ||
512 | # New extended info filters for IFLA_EXT_MASK | |
513 | RTEXT_FILTER_VF = (1 << 0) | |
514 | ||
515 | # passes link level specific information, not dependent | |
516 | # on network protocol. | |
517 | ||
518 | IFLA_UNSPEC = 0 | |
519 | IFLA_ADDRESS = 1 | |
520 | IFLA_BROADCAST = 2 | |
521 | IFLA_IFNAME = 3 | |
522 | IFLA_MTU = 4 | |
523 | IFLA_LINK = 5 | |
524 | IFLA_QDISC = 6 | |
525 | IFLA_STATS = 7 | |
526 | IFLA_COST = 8 | |
527 | IFLA_PRIORITY = 9 | |
528 | IFLA_MASTER = 10 | |
529 | IFLA_WIRELESS = 11 # Wireless Extension event - see wireless.h | |
530 | IFLA_PROTINFO = 12 # Protocol specific information for a link | |
531 | IFLA_TXQLEN = 13 | |
532 | IFLA_MAP = 14 | |
533 | IFLA_WEIGHT = 15 | |
534 | IFLA_OPERSTATE = 16 | |
535 | IFLA_LINKMODE = 17 | |
536 | IFLA_LINKINFO = 18 | |
537 | IFLA_NET_NS_PID = 19 | |
538 | IFLA_IFALIAS = 20 | |
539 | IFLA_NUM_VF = 21 # Number of VFs if device is SR-IOV PF | |
540 | IFLA_VFINFO_LIST = 22 | |
541 | IFLA_STATS64 = 23 | |
542 | IFLA_VF_PORTS = 24 | |
543 | IFLA_PORT_SELF = 25 | |
544 | IFLA_AF_SPEC = 26 | |
545 | IFLA_GROUP = 27 # Group the device belongs to | |
546 | IFLA_NET_NS_FD = 28 | |
547 | IFLA_EXT_MASK = 29 # Extended info mask, VFs, etc | |
548 | IFLA_MAX = 29 | |
549 | ||
550 | ||
551 | # IFLA_LINKINFO attributes | |
552 | IFLA_INFO_UNSPEC = 0 | |
553 | IFLA_INFO_KIND = 1 | |
554 | IFLA_INFO_DATA = 2 | |
555 | IFLA_INFO_XSTATS = 3 | |
556 | IFLA_INFO_MAX = 4 | |
557 | ||
558 | # IFLA_LINKINFO_DATA attributes for vlan | |
559 | IFLA_VLAN_UNSPEC = 0 | |
560 | IFLA_VLAN_ID = 1 | |
561 | ||
562 | # IFLA_LINKINFO_DATA attributes for macvlan | |
563 | IFLA_MACVLAN_UNSPEC = 0 | |
564 | IFLA_MACVLAN_MODE = 1 | |
565 | ||
566 | # macvlan modes | |
567 | MACVLAN_MODE_PRIVATE = 1 | |
568 | MACVLAN_MODE_VEPA = 2 | |
569 | MACVLAN_MODE_BRIDGE = 3 | |
570 | MACVLAN_MODE_PASSTHRU = 4 | |
571 | ||
572 | # BRIDGE IFLA_AF_SPEC attributes | |
573 | IFLA_BRIDGE_FLAGS = 0 | |
574 | IFLA_BRIDGE_MODE = 1 | |
575 | IFLA_BRIDGE_VLAN_INFO = 2 | |
576 | ||
577 | # BRIDGE_VLAN_INFO flags | |
578 | BRIDGE_VLAN_INFO_MASTER = 1 | |
579 | BRIDGE_VLAN_INFO_PVID = 2 | |
580 | BRIDGE_VLAN_INFO_UNTAGGED = 4 | |
581 | ||
582 | # Bridge flags | |
583 | BRIDGE_FLAGS_MASTER = 1 | |
584 | BRIDGE_FLAGS_SELF = 2 | |
585 | ||
586 | class BridgeVlanInfo(Structure): | |
587 | _fields_ = [ | |
588 | ('flags', c_uint16), | |
589 | ('vid', c_uint16), | |
590 | ('vid_end', c_uint16), | |
591 | ] | |
592 | ||
593 | class Ifinfomsg(Nlmsg): | |
594 | ||
595 | _fields_ = [ | |
596 | ('ifi_family', c_uint8), | |
597 | ('__ifi_pad', c_uint8), | |
598 | ('ifi_type', c_uint16), # ARPHRD_* | |
599 | ('ifi_index', c_int32), # Link index | |
600 | ('ifi_flags', c_uint32), # IFF_* flags | |
601 | ('ifi_change', c_uint32), # IFF_* change mask | |
602 | ] | |
603 | ||
604 | def dump(self): | |
605 | print 'ifi_family', self.ifi_family | |
606 | print 'ifi_type', self.ifi_type | |
607 | print 'ifi_index', self.ifi_index | |
608 | print 'ifi_flags 0x%08x' % self.ifi_flags | |
609 | print 'ifi_change 0x%08x' % self.ifi_change | |
610 | ||
611 | def rta_linkinfo_data_vlan_policy(self): | |
612 | fns = { | |
613 | IFLA_VLAN_ID : self.rta_uint16, | |
614 | } | |
615 | return fns | |
616 | ||
617 | def rta_linkinfo_data_macvlan_policy(self): | |
618 | fns = { | |
619 | IFLA_MACVLAN_MODE : self.rta_uint32, | |
620 | } | |
621 | return fns | |
622 | ||
623 | def rta_linkinfo_policy(self): | |
624 | fns = { | |
625 | IFLA_INFO_KIND : self.rta_string, | |
626 | IFLA_INFO_DATA : self.rta_linkinfo_data, | |
627 | } | |
628 | return fns | |
629 | ||
630 | def rta_bridge_af_spec_policy(self): | |
631 | # Assume bridge family for now | |
632 | fns = { | |
633 | IFLA_BRIDGE_FLAGS : self.rta_uint16, | |
634 | IFLA_BRIDGE_VLAN_INFO : self.rta_bridge_vlan_info, | |
635 | } | |
636 | return fns | |
637 | ||
638 | def rta_policy(self): | |
639 | fns = { | |
640 | IFLA_UNSPEC: self.rta_wtf, | |
641 | IFLA_ADDRESS: self.rta_uint8_array, | |
642 | IFLA_BROADCAST: self.rta_uint8_array, | |
643 | IFLA_IFNAME: self.rta_string, | |
644 | IFLA_MTU: self.rta_uint32, | |
645 | IFLA_LINK: self.rta_uint32, | |
646 | IFLA_QDISC: self.rta_string, | |
647 | IFLA_STATS: self.rta_none, | |
648 | IFLA_COST: self.rta_none, | |
649 | IFLA_PRIORITY: self.rta_none, | |
650 | IFLA_MASTER: self.rta_uint32, | |
651 | IFLA_WIRELESS: self.rta_none, | |
652 | IFLA_PROTINFO: self.rta_none, | |
653 | IFLA_TXQLEN: self.rta_uint32, | |
654 | IFLA_MAP: self.rta_none, | |
655 | IFLA_WEIGHT: self.rta_uint32, | |
656 | IFLA_OPERSTATE: self.rta_uint8, | |
657 | IFLA_LINKMODE: self.rta_uint8, | |
658 | IFLA_LINKINFO: self.rta_linkinfo, | |
659 | IFLA_NET_NS_PID: self.rta_uint32, | |
660 | IFLA_IFALIAS: self.rta_string, | |
661 | IFLA_NUM_VF: self.rta_uint32, | |
662 | IFLA_VFINFO_LIST: self.rta_none, | |
663 | IFLA_STATS64: self.rta_none, | |
664 | IFLA_VF_PORTS: self.rta_none, | |
665 | IFLA_PORT_SELF: self.rta_none, | |
666 | IFLA_AF_SPEC: self.rta_af_spec, | |
667 | IFLA_GROUP: self.rta_none, | |
668 | IFLA_NET_NS_FD: self.rta_none, | |
669 | IFLA_EXT_MASK: self.rta_none, | |
670 | } | |
671 | return fns; | |
672 | ||
673 | def rta_fn(self, rta_type): | |
674 | fns = { | |
675 | IFLA_UNSPEC: self.rta_wtf, | |
676 | IFLA_ADDRESS: self.rta_uint8_array, | |
677 | IFLA_BROADCAST: self.rta_uint8_array, | |
678 | IFLA_IFNAME: self.rta_string, | |
679 | IFLA_MTU: self.rta_uint32, | |
680 | IFLA_LINK: self.rta_uint32, | |
681 | IFLA_QDISC: self.rta_string, | |
682 | IFLA_STATS: self.rta_none, | |
683 | IFLA_COST: self.rta_none, | |
684 | IFLA_PRIORITY: self.rta_none, | |
685 | IFLA_MASTER: self.rta_uint32, | |
686 | IFLA_WIRELESS: self.rta_none, | |
687 | IFLA_PROTINFO: self.rta_none, | |
688 | IFLA_TXQLEN: self.rta_uint32, | |
689 | IFLA_MAP: self.rta_none, | |
690 | IFLA_WEIGHT: self.rta_uint32, | |
691 | IFLA_OPERSTATE: self.rta_uint8, | |
692 | IFLA_LINKMODE: self.rta_uint8, | |
693 | IFLA_LINKINFO: self.rta_linkinfo, | |
694 | IFLA_NET_NS_PID: self.rta_uint32, | |
695 | IFLA_IFALIAS: self.rta_string, | |
696 | IFLA_NUM_VF: self.rta_uint32, | |
697 | IFLA_VFINFO_LIST: self.rta_none, | |
698 | IFLA_STATS64: self.rta_none, | |
699 | IFLA_VF_PORTS: self.rta_none, | |
700 | IFLA_PORT_SELF: self.rta_none, | |
701 | IFLA_AF_SPEC: self.rta_af_spec, | |
702 | IFLA_GROUP: self.rta_none, | |
703 | IFLA_NET_NS_FD: self.rta_none, | |
704 | IFLA_EXT_MASK: self.rta_none, | |
705 | } | |
706 | return fns.get(rta_type) | |
707 | ||
708 | # passes address specific information | |
709 | ||
710 | # Important comment: | |
711 | # IFA_ADDRESS is prefix address, rather than local interface address. | |
712 | # It makes no difference for normally configured broadcast interfaces, | |
713 | # but for point-to-point IFA_ADDRESS is DESTINATION address, | |
714 | # local address is supplied in IFA_LOCAL attribute. | |
715 | ||
716 | IFA_UNSPEC = 0 | |
717 | IFA_ADDRESS = 1 | |
718 | IFA_LOCAL = 2 | |
719 | IFA_LABEL = 3 | |
720 | IFA_BROADCAST = 4 | |
721 | IFA_ANYCAST = 5 | |
722 | IFA_CACHEINFO = 6 | |
723 | IFA_MULTICAST = 7 | |
724 | IFA_MAX = 7 | |
725 | ||
726 | class Ifaddrmsg(Nlmsg): | |
727 | ||
728 | _fields_ = [ | |
729 | ('ifa_family', c_uint8), | |
730 | ('ifa_prefixlen', c_uint8), # The prefix length | |
731 | ('ifa_flags', c_uint8), # Flags | |
732 | ('ifa_scope', c_uint8), # Address scope | |
733 | ('ifa_index', c_uint32), # Link index | |
734 | ] | |
735 | ||
736 | _family_str = { | |
737 | AF_INET: "inet", | |
738 | AF_INET6: "inet6", | |
739 | } | |
740 | ||
741 | def dump(self): | |
742 | print 'ifa_family', self.ifa_family | |
743 | print 'ifa_prefixlen', self.ifa_prefixlen | |
744 | print 'ifa_flags 0x%02x' % self.ifa_flags | |
745 | print 'ifa_scope', self.ifa_scope | |
746 | print 'ifa_index', self.ifa_index | |
747 | ||
748 | def rta_fn(self, rta_type): | |
749 | fns = { | |
750 | IFA_ADDRESS: self.rta_addr, | |
751 | IFA_LOCAL: self.rta_addr, | |
752 | IFA_LABEL: self.rta_string, | |
753 | IFA_BROADCAST: self.rta_addr, | |
754 | IFA_ANYCAST: self.rta_addr, | |
755 | IFA_CACHEINFO: self.rta_none, | |
756 | IFA_MULTICAST: self.rta_addr, | |
757 | } | |
758 | return fns.get(rta_type) | |
759 | ||
760 | class RtNetlinkError(Exception): | |
761 | ||
762 | def __init__(self, message): | |
763 | Exception.__init__(self, message) | |
764 | logger.error(message) | |
765 | ||
766 | class RtNetlink(Netlink): | |
767 | ||
768 | def __init__(self, pid): | |
769 | Netlink.__init__(self, pid, NETLINK_ROUTE) | |
770 | ||
771 | _rt_nlmsg_type_str = { | |
772 | RTM_NEWROUTE: "RTM_NEWROUTE", | |
773 | RTM_DELROUTE: "RTM_DELROUTE", | |
774 | RTM_NEWLINK: "RTM_NEWLINK", | |
775 | RTM_SETLINK: "RTM_SETLINK", | |
776 | RTM_DELLINK: "RTM_DELLINK", | |
777 | RTM_GETLINK: "RTM_GETLINK", | |
778 | RTM_NEWADDR: "RTM_NEWADDR", | |
779 | RTM_DELADDR: "RTM_DELADDR", | |
780 | } | |
781 | ||
782 | def _hexdump(self, buf): | |
783 | while buf: | |
784 | chunk = buf[:16] | |
785 | buf = buf[16:] | |
786 | nums = ["%02x" % c for c in chunk] | |
787 | txt = [chr(c) if chr(c) in printable[:-5] else '.' for c in chunk] | |
788 | print " ".join(nums).ljust(48), "".join(txt) | |
789 | ||
790 | def dump(self, nlh): | |
791 | nlmsg = self.nlmsg(nlh) | |
792 | ||
793 | self._hexdump(self.sendbuf[:nlh.nlmsg_len]) | |
794 | ||
795 | nlh.dump() | |
796 | ||
797 | nlmsg.dump() | |
798 | ||
799 | nlmsg.dump_rtas() | |
800 | ||
801 | def nlmsg(self, nlh): | |
802 | nlmsg_struct = { | |
803 | RTM_NEWROUTE: Rtmsg, | |
804 | RTM_DELROUTE: Rtmsg, | |
805 | RTM_GETROUTE: Rtmsg, | |
806 | RTM_NEWLINK: Ifinfomsg, | |
807 | RTM_SETLINK: Ifinfomsg, | |
808 | RTM_DELLINK: Ifinfomsg, | |
809 | RTM_GETLINK: Rtgenmsg, | |
810 | RTM_NEWADDR: Ifaddrmsg, | |
811 | RTM_DELADDR: Ifaddrmsg, | |
812 | RTM_GETADDR: Rtgenmsg, | |
813 | } | |
814 | nldata = NLMSG_DATA(nlh) | |
815 | nlmsg = nlmsg_struct[nlh.nlmsg_type].from_address(nldata) | |
816 | nlmsg.nlh = nlh | |
817 | return nlmsg | |
818 | ||
819 | def _nl_cb(self, nlh): | |
820 | # print "nl cb", self._rt_nlmsg_type_str[nlh.nlmsg_type] | |
821 | ||
822 | if nlh.nlmsg_type in self._cbs: | |
823 | ||
824 | nlmsg = self.nlmsg(nlh) | |
825 | ||
826 | # validate nl length | |
827 | if nlh.nlmsg_len - NLMSG_LENGTH(sizeof(nlmsg)) < 0: | |
828 | raise RtNetlinkError("invalid nl length") | |
829 | ||
830 | self._cbs[nlh.nlmsg_type](nlh, nlmsg) | |
831 | ||
832 | def bind(self, groups, cbs): | |
833 | self._cbs = cbs | |
834 | Netlink.bind(self, groups, self._nl_cb) | |
835 | ||
836 | def request(self, nlmsg_type, flags, extra, rtas={}): | |
837 | ||
838 | nlh = Nlmsghdr.from_buffer(self.sendbuf) | |
839 | nlh_p = addressof(nlh) | |
840 | ||
841 | seq = self.seq | |
842 | pid = self.pid | |
843 | ||
844 | nlh.nlmsg_len = NLMSG_HDRLEN() | |
845 | nlh.nlmsg_type = nlmsg_type | |
846 | nlh.nlmsg_flags = flags | |
847 | nlh.nlmsg_pid = pid | |
848 | nlh.nlmsg_seq = seq | |
849 | ||
850 | nlmsg = self.nlmsg(nlh) | |
851 | ||
852 | nlh.nlmsg_len += nlmsg.pack_extra(extra, nlh_p + nlh.nlmsg_len) | |
853 | nlh.nlmsg_len += nlmsg.pack_rtas_new(rtas, nlh_p + nlh.nlmsg_len, | |
854 | nlmsg.rta_policy()) | |
855 | #self.dump(nlh) | |
856 | self.sendall(string_at(nlh_p, nlh.nlmsg_len)) | |
857 | self.seq += 1 | |
858 | ||
859 | token = (pid, seq) | |
860 | return token |