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