]>
Commit | Line | Data |
---|---|---|
d486dd0d JF |
1 | #!/usr/bin/python |
2 | # | |
3 | # Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved. | |
4 | # Author: Julien Fortin, julien@cumulusnetworks.com | |
5 | # | |
6 | ||
7 | import sys | |
8 | import socket | |
9 | import logging | |
10 | ||
11 | from collections import OrderedDict | |
12 | ||
13 | try: | |
14 | import ifupdown2.nlmanager.nlpacket | |
15 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags | |
16 | ||
17 | from ifupdown2.ifupdownaddons.cache import * | |
18 | from ifupdown2.ifupdownaddons.utilsbase import utilsBase | |
19 | ||
20 | from ifupdown2.nlmanager.nlmanager import Link, Address, Route, NetlinkPacket | |
21 | except ImportError: | |
22 | import nlmanager.nlpacket | |
23 | import ifupdown.ifupdownflags as ifupdownflags | |
24 | ||
25 | from ifupdownaddons.cache import * | |
26 | from ifupdownaddons.utilsbase import utilsBase | |
27 | ||
28 | from nlmanager.nlmanager import Link, Address, Route, NetlinkPacket | |
29 | ||
30 | ||
31 | class Netlink(utilsBase): | |
32 | VXLAN_UDP_PORT = 4789 | |
33 | ||
34 | def __init__(self, *args, **kargs): | |
35 | utilsBase.__init__(self, *args, **kargs) | |
36 | try: | |
37 | sys.path.insert(0, '/usr/share/ifupdown2/') | |
38 | try: | |
39 | from ifupdown2.nlmanager.nlmanager import NetlinkManager | |
40 | # Override the nlmanager's mac_int_to_str function to print the MACs | |
41 | # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx | |
42 | ifupdown2.nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str | |
43 | except ImportError: | |
44 | from nlmanager.nlmanager import NetlinkManager | |
45 | # Override the nlmanager's mac_int_to_str function to print the MACs | |
46 | # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx | |
47 | nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str | |
48 | ||
49 | # this should force the use of the local nlmanager | |
50 | self._nlmanager_api = NetlinkManager(log_level=logging.WARNING) | |
51 | ||
52 | self.link_kind_handlers = { | |
53 | 'vlan': self._link_dump_info_data_vlan, | |
54 | 'vrf': self._link_dump_info_data_vrf, | |
55 | 'vxlan': self._link_dump_info_data_vxlan, | |
56 | 'bond': self._link_dump_info_data_bond, | |
5a4147c4 JF |
57 | 'bridge': self._link_dump_info_data_bridge, |
58 | 'gre': self._link_dump_info_data_gre_tunnel, | |
59 | 'gretap': self._link_dump_info_data_gre_tunnel, | |
60 | "ip6gre": self._link_dump_info_data_gre_tunnel, | |
61 | "ip6gretap": self._link_dump_info_data_gre_tunnel, | |
62 | "ip6erspan": self._link_dump_info_data_gre_tunnel, | |
63 | 'ipip': self._link_dump_info_data_iptun_tunnel, | |
64 | 'sit': self._link_dump_info_data_iptun_tunnel, | |
65 | 'ip6tnl': self._link_dump_info_data_iptun_tunnel, | |
66 | 'vti': self._link_dump_info_data_vti_tunnel, | |
67 | 'vti6': self._link_dump_info_data_vti_tunnel | |
d486dd0d JF |
68 | } |
69 | ||
70 | except Exception as e: | |
71 | self.logger.error('cannot initialize ifupdown2\'s ' | |
72 | 'netlink manager: %s' % str(e)) | |
73 | raise | |
74 | ||
75 | @staticmethod | |
76 | def IN_MULTICAST(a): | |
77 | """ | |
78 | /include/uapi/linux/in.h | |
79 | ||
80 | #define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) == 0xe0000000) | |
81 | #define IN_MULTICAST(a) IN_CLASSD(a) | |
82 | """ | |
83 | return (int(a) & 0xf0000000) == 0xe0000000 | |
84 | ||
85 | @staticmethod | |
86 | def mac_int_to_str(mac_int): | |
87 | """ | |
88 | Return an integer in MAC string format: xx:xx:xx:xx:xx:xx | |
89 | """ | |
90 | return ':'.join(("%012x" % mac_int)[i:i + 2] for i in range(0, 12, 2)) | |
91 | ||
92 | def get_iface_index(self, ifacename): | |
93 | if ifupdownflags.flags.DRYRUN: return | |
94 | try: | |
95 | return self._nlmanager_api.get_iface_index(ifacename) | |
96 | except Exception as e: | |
97 | raise Exception('%s: netlink: %s: cannot get ifindex: %s' | |
98 | % (ifacename, ifacename, str(e))) | |
99 | ||
100 | def get_iface_name(self, ifindex): | |
101 | if ifupdownflags.flags.DRYRUN: return | |
102 | try: | |
103 | return self._nlmanager_api.get_iface_name(ifindex) | |
104 | except Exception as e: | |
105 | raise Exception('netlink: cannot get ifname for index %s: %s' % (ifindex, str(e))) | |
106 | ||
107 | def get_bridge_vlan(self, ifname): | |
108 | self.logger.info('%s: netlink: /sbin/bridge -d -c -json vlan show' % ifname) | |
109 | if ifupdownflags.flags.DRYRUN: return | |
110 | try: | |
111 | return self._nlmanager_api.vlan_get() | |
112 | except Exception as e: | |
113 | raise Exception('netlink: get bridge vlan: %s' % str(e)) | |
114 | ||
115 | def bridge_set_vlan_filtering(self, ifname, vlan_filtering): | |
116 | self.logger.info('%s: netlink: ip link set dev %s type bridge vlan_filtering %s' | |
117 | % (ifname, ifname, vlan_filtering)) | |
118 | if ifupdownflags.flags.DRYRUN: return | |
119 | try: | |
120 | ifla_info_data = {Link.IFLA_BR_VLAN_FILTERING: int(vlan_filtering)} | |
121 | return self._nlmanager_api.link_set_attrs(ifname, 'bridge', ifla_info_data=ifla_info_data) | |
122 | except Exception as e: | |
123 | raise Exception('%s: cannot set %s vlan_filtering %s' % (ifname, ifname, vlan_filtering)) | |
124 | ||
125 | def link_add_set(self, | |
126 | ifname=None, ifindex=0, | |
127 | kind=None, slave_kind=None, | |
128 | ifla={}, | |
129 | ifla_info_data={}, | |
130 | ifla_info_slave_data={}, | |
131 | link_exists=False): | |
132 | action = 'set' if ifindex or link_exists else 'add' | |
133 | ||
134 | if slave_kind: | |
135 | self.logger.info('%s: netlink: ip link set dev %s: %s slave attributes' % (ifname, ifname, slave_kind)) | |
136 | else: | |
137 | self.logger.info('%s: netlink: ip link %s %s type %s with attributes' % (ifname, action, ifname, kind)) | |
138 | if ifla: | |
139 | self.logger.debug('%s: ifla attributes a %s' % (ifname, ifla)) | |
140 | if ifla_info_data: | |
141 | self.logger.debug('%s: ifla_info_data %s' % (ifname, ifla_info_data)) | |
142 | if ifla_info_slave_data: | |
143 | self.logger.debug('%s: ifla_info_slave_data %s' % (ifname, ifla_info_slave_data)) | |
144 | ||
145 | if ifupdownflags.flags.DRYRUN: return | |
146 | try: | |
147 | self._nlmanager_api.link_add_set(ifname=ifname, | |
148 | ifindex=ifindex, | |
149 | kind=kind, | |
150 | slave_kind=slave_kind, | |
151 | ifla=ifla, | |
152 | ifla_info_data=ifla_info_data, | |
153 | ifla_info_slave_data=ifla_info_slave_data) | |
154 | except Exception as e: | |
155 | if kind and not slave_kind: | |
156 | kind_str = kind | |
157 | elif kind and slave_kind: | |
158 | kind_str = '%s (%s slave)' % (kind, slave_kind) | |
159 | else: | |
160 | kind_str = '(%s slave)' % slave_kind | |
161 | ||
162 | raise Exception('netlink: cannot %s %s %s with options: %s' % (action, kind_str, ifname, str(e))) | |
163 | ||
164 | def link_del(self, ifname): | |
165 | self.logger.info('%s: netlink: ip link del %s' % (ifname, ifname)) | |
166 | if ifupdownflags.flags.DRYRUN: return | |
167 | try: | |
168 | self._nlmanager_api.link_del(ifname=ifname) | |
169 | except Exception as e: | |
170 | raise Exception('netlink: cannot delete link %s: %s' % (ifname, str(e))) | |
171 | ||
172 | def link_set_master(self, ifacename, master_dev, state=None): | |
173 | self.logger.info('%s: netlink: ip link set dev %s master %s %s' | |
174 | % (ifacename, ifacename, master_dev, | |
175 | state if state else '')) | |
176 | if ifupdownflags.flags.DRYRUN: return | |
177 | try: | |
178 | master = 0 if not master_dev else self.get_iface_index(master_dev) | |
179 | return self._nlmanager_api.link_set_master(ifacename, | |
180 | master_ifindex=master, | |
181 | state=state) | |
182 | except Exception as e: | |
183 | raise Exception('netlink: %s: cannot set %s master %s: %s' | |
184 | % (ifacename, ifacename, master_dev, str(e))) | |
185 | ||
186 | def link_set_nomaster(self, ifacename, state=None): | |
187 | self.logger.info('%s: netlink: ip link set dev %s nomaster %s' | |
188 | % (ifacename, ifacename, state if state else '')) | |
189 | if ifupdownflags.flags.DRYRUN: return | |
190 | try: | |
191 | return self._nlmanager_api.link_set_master(ifacename, | |
192 | master_ifindex=0, | |
193 | state=state) | |
194 | except Exception as e: | |
195 | raise Exception('netlink: %s: cannot set %s nomaster: %s' | |
196 | % (ifacename, ifacename, str(e))) | |
197 | ||
198 | def link_add_vlan(self, vlanrawdevice, ifacename, vlanid, vlan_protocol): | |
199 | if vlan_protocol: | |
200 | self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s protocol %s' | |
201 | % (ifacename, vlanrawdevice, ifacename, vlanid, vlan_protocol)) | |
202 | ||
203 | else: | |
204 | self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s' | |
205 | % (ifacename, vlanrawdevice, ifacename, vlanid)) | |
206 | if ifupdownflags.flags.DRYRUN: return | |
207 | ifindex = self.get_iface_index(vlanrawdevice) | |
208 | try: | |
209 | return self._nlmanager_api.link_add_vlan(ifindex, ifacename, vlanid, vlan_protocol) | |
210 | except Exception as e: | |
211 | raise Exception('netlink: %s: cannot create vlan %s: %s' | |
212 | % (vlanrawdevice, vlanid, str(e))) | |
213 | ||
214 | def link_add_macvlan(self, ifacename, macvlan_ifacename): | |
215 | self.logger.info('%s: netlink: ip link add link %s name %s type macvlan mode private' | |
216 | % (ifacename, ifacename, macvlan_ifacename)) | |
217 | if ifupdownflags.flags.DRYRUN: return | |
218 | ifindex = self.get_iface_index(ifacename) | |
219 | try: | |
220 | return self._nlmanager_api.link_add_macvlan(ifindex, macvlan_ifacename) | |
221 | except Exception as e: | |
222 | raise Exception('netlink: %s: cannot create macvlan %s: %s' | |
223 | % (ifacename, macvlan_ifacename, str(e))) | |
224 | ||
225 | def link_set_updown(self, ifacename, state): | |
226 | self.logger.info('%s: netlink: ip link set dev %s %s' | |
227 | % (ifacename, ifacename, state)) | |
228 | if ifupdownflags.flags.DRYRUN: return | |
229 | try: | |
230 | return self._nlmanager_api.link_set_updown(ifacename, state) | |
231 | except Exception as e: | |
232 | raise Exception('netlink: cannot set link %s %s: %s' | |
233 | % (ifacename, state, str(e))) | |
234 | ||
235 | def link_set_protodown(self, ifacename, state): | |
236 | self.logger.info('%s: netlink: set link %s protodown %s' | |
237 | % (ifacename, ifacename, state)) | |
238 | if ifupdownflags.flags.DRYRUN: return | |
239 | try: | |
240 | return self._nlmanager_api.link_set_protodown(ifacename, state) | |
241 | except Exception as e: | |
242 | raise Exception('netlink: cannot set link %s protodown %s: %s' | |
243 | % (ifacename, state, str(e))) | |
244 | ||
245 | def link_add_bridge(self, ifname): | |
246 | self.logger.info('%s: netlink: ip link add %s type bridge' % (ifname, ifname)) | |
247 | if ifupdownflags.flags.DRYRUN: return | |
248 | try: | |
249 | return self._nlmanager_api.link_add_bridge(ifname) | |
250 | except Exception as e: | |
251 | raise Exception('netlink: cannot create bridge %s: %s' % (ifname, str(e))) | |
252 | ||
253 | def link_add_bridge_vlan(self, ifacename, vlanid): | |
254 | self.logger.info('%s: netlink: bridge vlan add vid %s dev %s' | |
255 | % (ifacename, vlanid, ifacename)) | |
256 | if ifupdownflags.flags.DRYRUN: return | |
257 | ifindex = self.get_iface_index(ifacename) | |
258 | try: | |
259 | return self._nlmanager_api.link_add_bridge_vlan(ifindex, vlanid) | |
260 | except Exception as e: | |
261 | raise Exception('netlink: %s: cannot create bridge vlan %s: %s' | |
262 | % (ifacename, vlanid, str(e))) | |
263 | ||
264 | def link_del_bridge_vlan(self, ifacename, vlanid): | |
265 | self.logger.info('%s: netlink: bridge vlan del vid %s dev %s' | |
266 | % (ifacename, vlanid, ifacename)) | |
267 | if ifupdownflags.flags.DRYRUN: return | |
268 | ifindex = self.get_iface_index(ifacename) | |
269 | try: | |
270 | return self._nlmanager_api.link_del_bridge_vlan(ifindex, vlanid) | |
271 | except Exception as e: | |
272 | raise Exception('netlink: %s: cannot remove bridge vlan %s: %s' | |
273 | % (ifacename, vlanid, str(e))) | |
274 | ||
275 | def link_add_vxlan(self, ifacename, vxlanid, local=None, dstport=VXLAN_UDP_PORT, | |
a382b488 | 276 | group=None, learning=True, ageing=None, physdev=None): |
d486dd0d JF |
277 | cmd = 'ip link add %s type vxlan id %s dstport %s' % (ifacename, |
278 | vxlanid, | |
279 | dstport) | |
280 | cmd += ' local %s' % local if local else '' | |
281 | cmd += ' ageing %s' % ageing if ageing else '' | |
282 | cmd += ' remote %s' % group if group else ' noremote' | |
283 | cmd += ' nolearning' if not learning else '' | |
a382b488 | 284 | cmd += ' dev %s' % physdev if physdev else '' |
d486dd0d JF |
285 | self.logger.info('%s: netlink: %s' % (ifacename, cmd)) |
286 | if ifupdownflags.flags.DRYRUN: return | |
287 | try: | |
a382b488 JF |
288 | if physdev: |
289 | physdev = self.get_iface_index(physdev) | |
d486dd0d JF |
290 | return self._nlmanager_api.link_add_vxlan(ifacename, |
291 | vxlanid, | |
292 | dstport=dstport, | |
293 | local=local, | |
294 | group=group, | |
295 | learning=learning, | |
a382b488 JF |
296 | ageing=ageing, |
297 | physdev=physdev) | |
d486dd0d JF |
298 | except Exception as e: |
299 | raise Exception('netlink: %s: cannot create vxlan %s: %s' | |
300 | % (ifacename, vxlanid, str(e))) | |
301 | ||
302 | @staticmethod | |
303 | def _link_dump_attr(link, ifla_attributes, dump): | |
304 | for obj in ifla_attributes: | |
305 | attr = link.attributes.get(obj['attr']) | |
306 | if attr: | |
307 | dump[obj['name']] = attr.get_pretty_value(obj=obj.get('func')) | |
308 | ||
309 | @staticmethod | |
310 | def _link_dump_linkdata_attr(linkdata, ifla_linkdata_attr, dump): | |
311 | for obj in ifla_linkdata_attr: | |
312 | attr = obj['attr'] | |
313 | if attr in linkdata: | |
314 | func = obj.get('func') | |
315 | value = linkdata.get(attr) | |
316 | ||
317 | if func: | |
318 | value = func(value) | |
319 | ||
320 | if value or obj['accept_none']: | |
321 | dump[obj['name']] = value | |
322 | ||
323 | ifla_attributes = [ | |
324 | { | |
325 | 'attr': Link.IFLA_LINK, | |
326 | 'name': 'link', | |
327 | 'func': lambda x: netlink.get_iface_name(x) if x > 0 else None | |
328 | }, | |
329 | { | |
330 | 'attr': Link.IFLA_MASTER, | |
331 | 'name': 'master', | |
332 | 'func': lambda x: netlink.get_iface_name(x) if x > 0 else None | |
333 | }, | |
334 | { | |
335 | 'attr': Link.IFLA_IFNAME, | |
336 | 'name': 'ifname', | |
337 | 'func': str, | |
338 | }, | |
339 | { | |
340 | 'attr': Link.IFLA_MTU, | |
341 | 'name': 'mtu', | |
342 | 'func': str | |
343 | }, | |
344 | { | |
345 | 'attr': Link.IFLA_OPERSTATE, | |
346 | 'name': 'state', | |
347 | 'func': lambda x: '0%x' % int(x) if x > len(Link.oper_to_string) else Link.oper_to_string[x][8:] | |
007cae35 JF |
348 | }, |
349 | { | |
350 | 'attr': Link.IFLA_AF_SPEC, | |
351 | 'name': 'af_spec', | |
352 | 'func': dict | |
d486dd0d JF |
353 | } |
354 | ] | |
355 | ||
356 | ifla_address = {'attr': Link.IFLA_ADDRESS, 'name': 'hwaddress', 'func': str} | |
357 | ||
358 | ifla_vxlan_attributes = [ | |
359 | { | |
360 | 'attr': Link.IFLA_VXLAN_LOCAL, | |
361 | 'name': 'local', | |
362 | 'func': str, | |
363 | 'accept_none': True | |
364 | }, | |
365 | { | |
366 | 'attr': Link.IFLA_VXLAN_LOCAL6, | |
367 | 'name': 'local', | |
368 | 'func': str, | |
369 | 'accept_none': True | |
370 | }, | |
371 | { | |
372 | 'attr': Link.IFLA_VXLAN_GROUP, | |
373 | 'name': 'svcnode', | |
374 | 'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None, | |
375 | 'accept_none': False | |
376 | }, | |
377 | { | |
378 | 'attr': Link.IFLA_VXLAN_GROUP6, | |
379 | 'name': 'svcnode', | |
380 | 'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None, | |
381 | 'accept_none': False | |
382 | }, | |
383 | { | |
384 | 'attr': Link.IFLA_VXLAN_LEARNING, | |
385 | 'name': 'learning', | |
386 | 'func': lambda x: 'on' if x else 'off', | |
387 | 'accept_none': True | |
388 | } | |
389 | ] | |
390 | ||
391 | def _link_dump_info_data_vlan(self, ifname, linkdata): | |
392 | return { | |
393 | 'vlanid': str(linkdata.get(Link.IFLA_VLAN_ID, '')), | |
394 | 'vlan_protocol': linkdata.get(Link.IFLA_VLAN_PROTOCOL) | |
395 | } | |
396 | ||
397 | def _link_dump_info_data_vrf(self, ifname, linkdata): | |
398 | vrf_info = {'table': str(linkdata.get(Link.IFLA_VRF_TABLE, ''))} | |
399 | ||
400 | # to remove later when moved to a true netlink cache | |
401 | linkCache.vrfs[ifname] = vrf_info | |
402 | return vrf_info | |
403 | ||
404 | def _link_dump_info_data_vxlan(self, ifname, linkdata): | |
405 | for attr, value in ( | |
406 | ('learning', 'on'), | |
407 | ('svcnode', None), | |
408 | ('vxlanid', str(linkdata.get(Link.IFLA_VXLAN_ID, ''))), | |
409 | ('ageing', str(linkdata.get(Link.IFLA_VXLAN_AGEING, ''))), | |
410 | (Link.IFLA_VXLAN_PORT, linkdata.get(Link.IFLA_VXLAN_PORT)) | |
411 | ): | |
412 | linkdata[attr] = value | |
413 | self._link_dump_linkdata_attr(linkdata, self.ifla_vxlan_attributes, linkdata) | |
414 | return linkdata | |
415 | ||
416 | ifla_bond_attributes = ( | |
417 | Link.IFLA_BOND_MODE, | |
418 | Link.IFLA_BOND_MIIMON, | |
419 | Link.IFLA_BOND_USE_CARRIER, | |
420 | Link.IFLA_BOND_AD_LACP_RATE, | |
421 | Link.IFLA_BOND_XMIT_HASH_POLICY, | |
422 | Link.IFLA_BOND_MIN_LINKS, | |
423 | Link.IFLA_BOND_NUM_PEER_NOTIF, | |
424 | Link.IFLA_BOND_AD_ACTOR_SYSTEM, | |
425 | Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, | |
426 | Link.IFLA_BOND_AD_LACP_BYPASS, | |
427 | Link.IFLA_BOND_UPDELAY, | |
428 | Link.IFLA_BOND_DOWNDELAY, | |
429 | ) | |
430 | ||
431 | def _link_dump_info_data_bond(self, ifname, linkdata): | |
432 | linkinfo = {} | |
433 | for nl_attr in self.ifla_bond_attributes: | |
434 | try: | |
435 | linkinfo[nl_attr] = linkdata.get(nl_attr) | |
436 | except Exception as e: | |
437 | self.logger.debug('%s: parsing bond IFLA_INFO_DATA (%s): %s' | |
438 | % (ifname, nl_attr, str(e))) | |
439 | return linkinfo | |
440 | ||
441 | # this dict contains the netlink attribute, cache key, | |
442 | # and a callable to translate the netlink value into | |
443 | # whatever value we need to store in the old cache to | |
444 | # make sure we don't break anything | |
445 | ifla_bridge_attributes = ( | |
446 | (Link.IFLA_BR_UNSPEC, Link.IFLA_BR_UNSPEC, None), | |
447 | (Link.IFLA_BR_FORWARD_DELAY, "fd", lambda x: str(x / 100)), | |
448 | (Link.IFLA_BR_HELLO_TIME, "hello", lambda x: str(x / 100)), | |
449 | (Link.IFLA_BR_MAX_AGE, "maxage", lambda x: str(x / 100)), | |
450 | (Link.IFLA_BR_AGEING_TIME, "ageing", lambda x: str(x / 100)), | |
451 | (Link.IFLA_BR_STP_STATE, "stp", lambda x: 'yes' if x else 'no'), | |
452 | (Link.IFLA_BR_PRIORITY, "bridgeprio", str), | |
453 | (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering', str), | |
454 | (Link.IFLA_BR_VLAN_PROTOCOL, "vlan-protocol", str), | |
455 | (Link.IFLA_BR_GROUP_FWD_MASK, Link.IFLA_BR_GROUP_FWD_MASK, None), | |
456 | (Link.IFLA_BR_ROOT_ID, Link.IFLA_BR_ROOT_ID, None), | |
457 | (Link.IFLA_BR_BRIDGE_ID, Link.IFLA_BR_BRIDGE_ID, None), | |
458 | (Link.IFLA_BR_ROOT_PORT, Link.IFLA_BR_ROOT_PORT, None), | |
459 | (Link.IFLA_BR_ROOT_PATH_COST, Link.IFLA_BR_ROOT_PATH_COST, None), | |
460 | (Link.IFLA_BR_TOPOLOGY_CHANGE, Link.IFLA_BR_TOPOLOGY_CHANGE, None), | |
461 | (Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, None), | |
462 | (Link.IFLA_BR_HELLO_TIMER, Link.IFLA_BR_HELLO_TIMER, None), | |
463 | (Link.IFLA_BR_TCN_TIMER, Link.IFLA_BR_TCN_TIMER, None), | |
464 | (Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, None), | |
465 | (Link.IFLA_BR_GC_TIMER, Link.IFLA_BR_GC_TIMER, None), | |
466 | (Link.IFLA_BR_GROUP_ADDR, Link.IFLA_BR_GROUP_ADDR, None), | |
467 | (Link.IFLA_BR_FDB_FLUSH, Link.IFLA_BR_FDB_FLUSH, None), | |
468 | (Link.IFLA_BR_MCAST_ROUTER, "mcrouter", str), | |
469 | (Link.IFLA_BR_MCAST_SNOOPING, "mcsnoop", str), | |
470 | (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, "mcqifaddr", str), | |
471 | (Link.IFLA_BR_MCAST_QUERIER, "mcquerier", str), | |
472 | (Link.IFLA_BR_MCAST_HASH_ELASTICITY, "hashel", str), | |
473 | (Link.IFLA_BR_MCAST_HASH_MAX, "hashmax", str), | |
474 | (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, "mclmc", str), | |
475 | (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, "mcsqc", str), | |
476 | (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, "mclmi", lambda x: str(x / 100)), | |
477 | (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, "mcmi", lambda x: str(x / 100)), | |
478 | (Link.IFLA_BR_MCAST_QUERIER_INTVL, "mcqpi", lambda x: str(x / 100)), | |
479 | (Link.IFLA_BR_MCAST_QUERY_INTVL, "mcqi", lambda x: str(x / 100)), | |
480 | (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, "mcqri", lambda x: str(x / 100)), | |
481 | (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, "mcsqi", lambda x: str(x / 100)), | |
482 | (Link.IFLA_BR_NF_CALL_IPTABLES, Link.IFLA_BR_NF_CALL_IPTABLES, None), | |
483 | (Link.IFLA_BR_NF_CALL_IP6TABLES, Link.IFLA_BR_NF_CALL_IP6TABLES, None), | |
484 | (Link.IFLA_BR_NF_CALL_ARPTABLES, Link.IFLA_BR_NF_CALL_ARPTABLES, None), | |
485 | (Link.IFLA_BR_VLAN_DEFAULT_PVID, Link.IFLA_BR_VLAN_DEFAULT_PVID, None), | |
486 | (Link.IFLA_BR_PAD, Link.IFLA_BR_PAD, None), | |
487 | (Link.IFLA_BR_VLAN_STATS_ENABLED, "vlan-stats", str), | |
488 | (Link.IFLA_BR_MCAST_STATS_ENABLED, "mcstats", str), | |
489 | (Link.IFLA_BR_MCAST_IGMP_VERSION, "igmp-version", str), | |
490 | (Link.IFLA_BR_MCAST_MLD_VERSION, "mld-version", str) | |
491 | ) | |
492 | ||
493 | def _link_dump_info_data_bridge(self, ifname, linkdata): | |
494 | linkinfo = {} | |
495 | for nl_attr, cache_key, func in self.ifla_bridge_attributes: | |
496 | try: | |
497 | if func: | |
498 | linkinfo[cache_key] = func(linkdata.get(nl_attr)) | |
499 | else: | |
500 | linkinfo[cache_key] = linkdata.get(nl_attr) | |
501 | ||
502 | # we also store the value in pure netlink, | |
503 | # to make the transition easier in the future | |
504 | linkinfo[nl_attr] = linkdata.get(nl_attr) | |
505 | except Exception as e: | |
506 | self.logger.error('%s: parsing birdge IFLA_INFO_DATA %s: %s' | |
507 | % (ifname, nl_attr, str(e))) | |
508 | return linkinfo | |
509 | ||
510 | def _link_dump_info_slave_data_bridge(self, ifname, info_slave_data): | |
511 | return info_slave_data | |
512 | ||
5a4147c4 JF |
513 | def _link_dump_info_data_gre_tunnel(self, ifname, info_slave_data): |
514 | tunnel_link_ifindex = info_slave_data.get(Link.IFLA_GRE_LINK) | |
515 | ||
516 | return { | |
517 | "endpoint": str(info_slave_data.get(Link.IFLA_GRE_REMOTE)), | |
518 | "local": str(info_slave_data.get(Link.IFLA_GRE_LOCAL)), | |
519 | "ttl": str(info_slave_data.get(Link.IFLA_GRE_TTL)), | |
6039c4d7 | 520 | "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else "" |
5a4147c4 JF |
521 | } |
522 | ||
523 | def _link_dump_info_data_iptun_tunnel(self, ifname, info_slave_data): | |
524 | tunnel_link_ifindex = info_slave_data.get(Link.IFLA_IPTUN_LINK) | |
525 | ||
526 | return { | |
527 | "endpoint": str(info_slave_data.get(Link.IFLA_IPTUN_REMOTE)), | |
528 | "local": str(info_slave_data.get(Link.IFLA_IPTUN_LOCAL)), | |
529 | "ttl": str(info_slave_data.get(Link.IFLA_IPTUN_TTL)), | |
6039c4d7 | 530 | "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else "" |
5a4147c4 JF |
531 | } |
532 | ||
533 | def _link_dump_info_data_vti_tunnel(self, ifname, info_slave_data): | |
534 | tunnel_link_ifindex = info_slave_data.get(Link.IFLA_VTI_LINK) | |
535 | ||
536 | return { | |
537 | "endpoint": str(info_slave_data.get(Link.IFLA_VTI_REMOTE)), | |
538 | "local": str(info_slave_data.get(Link.IFLA_VTI_LOCAL)), | |
6039c4d7 | 539 | "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else "" |
5a4147c4 JF |
540 | } |
541 | ||
d486dd0d JF |
542 | def _link_dump_linkinfo(self, link, dump): |
543 | linkinfo = link.attributes[Link.IFLA_LINKINFO].get_pretty_value(dict) | |
544 | ||
545 | if linkinfo: | |
546 | info_kind = linkinfo.get(Link.IFLA_INFO_KIND) | |
547 | info_data = linkinfo.get(Link.IFLA_INFO_DATA) | |
548 | ||
549 | info_slave_kind = linkinfo.get(Link.IFLA_INFO_SLAVE_KIND) | |
550 | info_slave_data = linkinfo.get(Link.IFLA_INFO_SLAVE_DATA) | |
551 | ||
552 | dump['kind'] = info_kind | |
553 | dump['slave_kind'] = info_slave_kind | |
554 | ||
555 | if info_data: | |
556 | link_kind_handler = self.link_kind_handlers.get(info_kind) | |
557 | if callable(link_kind_handler): | |
558 | dump['linkinfo'] = link_kind_handler(dump['ifname'], info_data) | |
559 | ||
560 | if info_slave_data: | |
561 | dump['info_slave_data'] = info_slave_data | |
562 | ||
563 | def link_dump(self, ifname=None): | |
564 | if ifname: | |
565 | self.logger.info('netlink: ip link show dev %s' % ifname) | |
566 | else: | |
567 | self.logger.info('netlink: ip link show') | |
568 | ||
569 | if ifupdownflags.flags.DRYRUN: return {} | |
570 | ||
571 | links = dict() | |
572 | ||
573 | try: | |
574 | links_dump = self._nlmanager_api.link_dump(ifname) | |
575 | except Exception as e: | |
576 | raise Exception('netlink: link dump failed: %s' % str(e)) | |
577 | ||
578 | for link in links_dump: | |
579 | try: | |
580 | dump = dict() | |
581 | ||
582 | flags = [] | |
583 | for flag, string in Link.flag_to_string.items(): | |
584 | if link.flags & flag: | |
585 | flags.append(string[4:]) | |
586 | ||
587 | dump['flags'] = flags | |
588 | dump['ifflag'] = 'UP' if 'UP' in flags else 'DOWN' | |
589 | dump['ifindex'] = str(link.ifindex) | |
590 | ||
591 | if link.device_type == Link.ARPHRD_ETHER: | |
592 | self._link_dump_attr(link, [self.ifla_address], dump) | |
593 | ||
594 | self._link_dump_attr(link, self.ifla_attributes, dump) | |
595 | ||
596 | if Link.IFLA_LINKINFO in link.attributes: | |
597 | self._link_dump_linkinfo(link, dump) | |
598 | ||
599 | links[dump['ifname']] = dump | |
600 | except Exception as e: | |
601 | self.logger.warning('netlink: ip link show: %s' % str(e)) | |
602 | return links | |
603 | ||
604 | def _addr_dump_extract_ifname(self, addr_packet): | |
605 | addr_ifname_attr = addr_packet.attributes.get(Address.IFA_LABEL) | |
606 | ||
607 | if addr_ifname_attr: | |
608 | return addr_ifname_attr.get_pretty_value(str) | |
609 | else: | |
610 | return self.get_iface_name(addr_packet.ifindex) | |
611 | ||
612 | @staticmethod | |
613 | def _addr_filter(addr_ifname, addr): | |
614 | return addr_ifname == 'lo' and addr in ['127.0.0.1/8', '::1/128', '0.0.0.0'] | |
615 | ||
616 | def _addr_dump_entry(self, ifaces, addr_packet, addr_ifname, ifa_attr): | |
617 | attribute = addr_packet.attributes.get(ifa_attr) | |
618 | ||
619 | if attribute: | |
620 | address = attribute.get_pretty_value(str) | |
621 | ||
622 | if hasattr(addr_packet, 'prefixlen'): | |
623 | address = '%s/%d' % (address, addr_packet.prefixlen) | |
624 | ||
625 | if self._addr_filter(addr_ifname, address): | |
626 | return | |
627 | ||
628 | addr_family = NetlinkPacket.af_family_to_string.get(addr_packet.family) | |
629 | if not addr_family: | |
630 | return | |
631 | ||
632 | ifaces[addr_ifname]['addrs'][address] = { | |
633 | 'type': addr_family, | |
634 | 'scope': addr_packet.scope | |
635 | } | |
636 | ||
637 | ifa_address_attributes = [ | |
638 | Address.IFA_ADDRESS, | |
639 | Address.IFA_LOCAL, | |
640 | Address.IFA_BROADCAST, | |
641 | Address.IFA_ANYCAST, | |
642 | Address.IFA_MULTICAST | |
643 | ] | |
644 | ||
645 | def addr_dump(self, ifname=None): | |
646 | if ifname: | |
647 | self.logger.info('netlink: ip addr show dev %s' % ifname) | |
648 | else: | |
649 | self.logger.info('netlink: ip addr show') | |
650 | ||
651 | ifaces = dict() | |
652 | addr_dump = self._nlmanager_api.addr_dump() | |
653 | ||
654 | for addr_packet in addr_dump: | |
655 | addr_ifname = self._addr_dump_extract_ifname(addr_packet) | |
656 | ||
657 | if addr_packet.family not in [socket.AF_INET, socket.AF_INET6]: | |
658 | continue | |
659 | ||
660 | if ifname and ifname != addr_ifname: | |
661 | continue | |
662 | ||
663 | if addr_ifname not in ifaces: | |
664 | ifaces[addr_ifname] = {'addrs': OrderedDict({})} | |
665 | ||
666 | for ifa_attr in self.ifa_address_attributes: | |
667 | self._addr_dump_entry(ifaces, addr_packet, addr_ifname, ifa_attr) | |
668 | ||
669 | if ifname: | |
670 | return {ifname: ifaces.get(ifname, {})} | |
671 | ||
672 | return ifaces | |
673 | ||
674 | ||
675 | netlink = Netlink() |