]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/netlink.py
9dcc8d5a16c1c0995af6f296fad9b9599b5363b0
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / netlink.py
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,
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,
68 'xfrm': self._link_dump_info_data_xfrm
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)))
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)))
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,
288 group=None, learning=True, ageing=None, physdev=None, ttl=None):
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 ''
296 cmd += ' dev %s' % physdev if physdev else ''
297
298 if ttl is not None:
299 cmd += ' ttl %s' % ttl
300
301 self.logger.info('%s: netlink: %s' % (ifacename, cmd))
302 if ifupdownflags.flags.DRYRUN: return
303 try:
304 if physdev:
305 physdev = self.get_iface_index(physdev)
306 return self._nlmanager_api.link_add_vxlan(ifacename,
307 vxlanid,
308 dstport=dstport,
309 local=local,
310 group=group,
311 learning=learning,
312 ageing=ageing,
313 physdev=physdev,
314 ttl=ttl)
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:]
365 },
366 {
367 'attr': Link.IFLA_AF_SPEC,
368 'name': 'af_spec',
369 'func': dict
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
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)),
537 "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
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)),
547 "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
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)),
556 "tunnel-physdev": self.get_iface_name(tunnel_link_ifindex) if tunnel_link_ifindex else ""
557 }
558
559 def _link_dump_info_data_xfrm(self, ifname, linkdata):
560 xfrm_physdev_link_ifindex = linkdata.get(Link.IFLA_XFRM_LINK)
561
562 return {
563 'xfrmid': str(linkdata.get(Link.IFLA_XFRM_IF_ID, '')),
564 'xfrm-physdev': self.get_iface_name(xfrm_physdev_link_ifindex) if xfrm_physdev_link_ifindex else ""
565 }
566
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()