3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
7 from ipaddr
import IPNetwork
, IPv4Network
, IPv6Network
, _BaseV6
10 from ifupdown2
.ifupdown
.iface
import *
11 from ifupdown2
.ifupdown
.utils
import utils
12 from ifupdown2
.ifupdown
.netlink
import netlink
14 from ifupdown2
.ifupdownaddons
.dhclient
import dhclient
15 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
16 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
18 import ifupdown2
.ifupdown
.statemanager
as statemanager
19 import ifupdown2
.ifupdown
.policymanager
as policymanager
20 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
21 import ifupdown2
.ifupdown
.ifupdownconfig
as ifupdownconfig
23 from ifupdown
.iface
import *
24 from ifupdown
.utils
import utils
25 from ifupdown
.netlink
import netlink
27 from ifupdownaddons
.dhclient
import dhclient
28 from ifupdownaddons
.LinkUtils
import LinkUtils
29 from ifupdownaddons
.modulebase
import moduleBase
31 import ifupdown
.statemanager
as statemanager
32 import ifupdown
.policymanager
as policymanager
33 import ifupdown
.ifupdownflags
as ifupdownflags
34 import ifupdown
.ifupdownconfig
as ifupdownconfig
37 class address(moduleBase
):
38 """ ifupdown2 addon module to configure address, mtu, hwaddress, alias
39 (description) on an interface """
41 _modinfo
= {'mhelp' : 'address configuration module for interfaces',
44 {'help' : 'ipv4 or ipv6 addresses',
45 'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
47 'example' : ['address 10.0.12.3/24',
48 'address 2000:1000:1000:1000:3::5/128']},
51 'example' : ['netmask 255.255.255.0'],
54 {'help': 'broadcast address',
55 'validvals' : ['<ipv4>', ],
56 'example' : ['broadcast 10.0.1.255']},
59 'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'],
60 'example' : ['scope host']},
61 'preferred-lifetime' :
62 {'help': 'preferred lifetime',
63 'validrange' : ['0', '65535'],
64 'example' : ['preferred-lifetime forever',
65 'preferred-lifetime 10']},
67 {'help': 'default gateway',
68 'validvals' : ['<ipv4>', '<ipv6>'],
70 'example' : ['gateway 255.255.255.0']},
72 { 'help': 'interface mtu',
73 'validrange' : ['552', '9216'],
74 'example' : ['mtu 1600'],
77 {'help' : 'hw address',
78 'validvals' : ['<mac>',],
79 'example': ['hwaddress 44:38:39:00:27:b8']},
81 { 'help': 'description/alias',
82 'example' : ['alias testnetwork']},
84 { 'help': 'purge existing addresses. By default ' +
85 'any existing ip addresses on an interface are ' +
86 'purged to match persistant addresses in the ' +
87 'interfaces file. Set this attribute to \'no\'' +
88 'if you want to preserve existing addresses',
89 'validvals' : ['yes', 'no'],
91 'example' : ['address-purge yes/no']},
92 'clagd-vxlan-anycast-ip' :
93 { 'help' : 'Anycast local IP address for ' +
94 'dual connected VxLANs',
95 'validvals' : ['<ipv4>', ],
96 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']},
98 { 'help': 'ip forwarding flag',
99 'validvals': ['on', 'off'],
101 'example' : ['ip-forward off']},
103 { 'help': 'ipv6 forwarding flag',
104 'validvals': ['on', 'off'],
106 'example' : ['ip6-forward off']},
108 { 'help': 'mpls enable flag',
109 'validvals': ['yes', 'no'],
111 'example' : ['mpls-enable yes']},
113 'help': 'enable disable ipv6 link addrgenmode',
114 'validvals': ['on', 'off'],
123 def __init__(self
, *args
, **kargs
):
124 moduleBase
.__init
__(self
, *args
, **kargs
)
126 self
._bridge
_fdb
_query
_cache
= {}
127 self
.default_mtu
= policymanager
.policymanager_api
.get_attr_default(module_name
=self
.__class
__.__name
__, attr
='mtu')
128 self
.max_mtu
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='max_mtu')
129 self
.ipforward
= policymanager
.policymanager_api
.get_attr_default(module_name
=self
.__class
__.__name
__, attr
='ip-forward')
130 self
.ip6forward
= policymanager
.policymanager_api
.get_attr_default(module_name
=self
.__class
__.__name
__, attr
='ip6-forward')
131 self
.ifaces_defaults
= policymanager
.policymanager_api
.get_iface_defaults(module_name
=self
.__class
__.__name
__)
132 self
.enable_l3_iface_forwarding_checks
= utils
.get_boolean_from_string(
133 policymanager
.policymanager_api
.get_module_globals(
134 self
.__class
__.__name
__,
135 'enable_l3_iface_forwarding_checks'
139 if not self
.default_mtu
:
140 self
.default_mtu
= '1500'
142 self
.logger
.info('address: using default mtu %s' %self
.default_mtu
)
145 self
.logger
.info('address: using max mtu %s' %self
.max_mtu
)
147 self
.lower_iface_mtu_checked_list
= list()
149 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
=None):
150 return (self
.syntax_check_multiple_gateway(ifaceobj
)
151 and self
.syntax_check_addr_allowed_on(ifaceobj
, True)
152 and self
.syntax_check_mtu(ifaceobj
, ifaceobj_getfunc
)
153 and self
.syntax_check_sysctls(ifaceobj
)
154 and self
.syntax_check_enable_l3_iface_forwardings(ifaceobj
, ifaceobj_getfunc
, syntax_check
=True))
156 def syntax_check_enable_l3_iface_forwardings(self
, ifaceobj
, ifaceobj_getfunc
, syntax_check
=False):
157 if (self
.enable_l3_iface_forwarding_checks
158 and (ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
159 or ifaceobj
.link_kind
& ifaceLinkKind
.BRIDGE
)
160 and not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
):
162 ifname
= ifaceobj
.name
164 vlan_ipforward_off
= None
166 for obj
in ifaceobj_getfunc(ifname
) or [ifaceobj
]:
168 vlan_addr
= obj
.get_attr_value('address')
170 if not vlan_ipforward_off
:
171 ip_forward_value
= obj
.get_attr_value_first('ip-forward')
173 if ip_forward_value
and not utils
.get_boolean_from_string(ip_forward_value
):
174 vlan_ipforward_off
= True
176 if vlan_addr
and vlan_ipforward_off
:
179 'configuring ip-forward off and ip address(es) (%s) is not compatible'
180 % (', '.join(vlan_addr
))
184 '%s: configuring ip-forward off and ip address(es) (%s) is not compatible'
185 % (ifname
, ', '.join(vlan_addr
))
190 def syntax_check_sysctls(self
, ifaceobj
):
192 bridge_port
= (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
)
193 ipforward
= ifaceobj
.get_attr_value_first('ip-forward')
194 if bridge_port
and ipforward
:
196 self
.log_error('%s: \'ip-forward\' is not supported for '
197 'bridge port' %ifaceobj
.name
)
198 ip6forward
= ifaceobj
.get_attr_value_first('ip6-forward')
199 if bridge_port
and ip6forward
:
201 self
.log_error('%s: \'ip6-forward\' is not supported for '
202 'bridge port' %ifaceobj
.name
)
205 def syntax_check_mtu(self
, ifaceobj
, ifaceobj_getfunc
):
206 mtu
= ifaceobj
.get_attr_value_first('mtu')
208 return self
._check
_mtu
_config
(ifaceobj
, mtu
, ifaceobj_getfunc
,
212 def syntax_check_addr_allowed_on(self
, ifaceobj
, syntax_check
=False):
213 if ifaceobj
.get_attr_value('address'):
214 return utils
.is_addr_ip_allowed_on(ifaceobj
, syntax_check
=syntax_check
)
217 def _syntax_check_multiple_gateway(self
, family
, found
, addr
, type_obj
):
218 if type(IPNetwork(addr
)) == type_obj
:
220 raise Exception('%s: multiple gateways for %s family'
225 def syntax_check_multiple_gateway(self
, ifaceobj
):
229 gateways
= ifaceobj
.get_attr_value('gateway')
230 for addr
in gateways
if gateways
else []:
232 if self
._syntax
_check
_multiple
_gateway
('inet', inet
, addr
,
235 if self
._syntax
_check
_multiple
_gateway
('inet6', inet6
, addr
,
238 except Exception as e
:
239 self
.logger
.warning('%s: address: %s' % (ifaceobj
.name
, str(e
)))
243 def _address_valid(self
, addrs
):
246 if any(map(lambda a
: True if a
[:7] != '0.0.0.0'
251 def _get_hwaddress(self
, ifaceobj
):
252 return utils
.strip_hwaddress(ifaceobj
.get_attr_value_first('hwaddress'))
254 def _process_bridge(self
, ifaceobj
, up
):
255 hwaddress
= self
._get
_hwaddress
(ifaceobj
)
256 addrs
= ifaceobj
.get_attr_value_first('address')
257 is_vlan_dev_on_vlan_aware_bridge
= False
258 is_bridge
= self
.ipcmd
.is_bridge(ifaceobj
.name
)
260 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
261 bridgename
= ifaceobj
.lowerifaces
[0]
262 vlan
= self
._get
_vlan
_id
(ifaceobj
)
263 is_vlan_dev_on_vlan_aware_bridge
= self
.ipcmd
.bridge_is_vlan_aware(bridgename
)
264 if ((is_bridge
and not self
.ipcmd
.bridge_is_vlan_aware(ifaceobj
.name
))
265 or is_vlan_dev_on_vlan_aware_bridge
):
266 if self
._address
_valid
(addrs
):
268 self
.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj
.name
+
271 self
.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj
.name
+
273 if hwaddress
and is_vlan_dev_on_vlan_aware_bridge
:
275 self
.ipcmd
.bridge_fdb_add(bridgename
, hwaddress
, vlan
)
277 self
.ipcmd
.bridge_fdb_del(bridgename
, hwaddress
, vlan
)
279 def _get_anycast_addr(self
, ifaceobjlist
):
280 for ifaceobj
in ifaceobjlist
:
281 anycast_addr
= ifaceobj
.get_attr_value_first('clagd-vxlan-anycast-ip')
283 anycast_addr
= anycast_addr
+'/32'
287 def _inet_address_convert_to_cidr(self
, ifaceobjlist
):
291 for ifaceobj
in ifaceobjlist
:
292 addrs
= ifaceobj
.get_attr_value('address')
296 if not self
.syntax_check_addr_allowed_on(ifaceobj
,
298 return (False, newaddrs
, newaddr_attrs
)
299 # If user address is not in CIDR notation, convert them to CIDR
300 for addr_index
in range(0, len(addrs
)):
301 addr
= addrs
[addr_index
]
304 newaddrs
.append(addr
)
306 netmask
= ifaceobj
.get_attr_value_n('netmask', addr_index
)
308 prefixlen
= IPNetwork('%s' %addr
+
309 '/%s' %netmask
).prefixlen
310 newaddr
= addr
+ '/%s' %prefixlen
312 # we are here because there is no slash (/xx) and no netmask
313 # just let IPNetwork handle the ipv4 or ipv6 address mask
314 prefixlen
= IPNetwork(addr
).prefixlen
315 newaddr
= addr
+ '/%s' %prefixlen
316 newaddrs
.append(newaddr
)
319 for a
in ['broadcast', 'pointopoint', 'scope',
320 'preferred-lifetime']:
321 aval
= ifaceobj
.get_attr_value_n(a
, addr_index
)
326 newaddr_attrs
[newaddr
]= attrs
327 return (True, newaddrs
, newaddr_attrs
)
329 def _inet_address_list_config(self
, ifaceobj
, newaddrs
, newaddr_attrs
):
330 for addr_index
in range(0, len(newaddrs
)):
333 self
.ipcmd
.addr_add(ifaceobj
.name
, newaddrs
[addr_index
],
334 newaddr_attrs
.get(newaddrs
[addr_index
],
335 {}).get('broadcast'),
336 newaddr_attrs
.get(newaddrs
[addr_index
],
337 {}).get('pointopoint'),
338 newaddr_attrs
.get(newaddrs
[addr_index
],
340 newaddr_attrs
.get(newaddrs
[addr_index
],
341 {}).get('preferred-lifetime'))
343 self
.ipcmd
.addr_add(ifaceobj
.name
, newaddrs
[addr_index
])
345 self
.log_error(str(e
), ifaceobj
)
347 def _inet_address_config(self
, ifaceobj
, ifaceobj_getfunc
=None,
348 force_reapply
=False):
349 squash_addr_config
= (True if \
350 ifupdownconfig
.config
.get('addr_config_squash', \
351 '0') == '1' else False)
353 if (squash_addr_config
and
354 not (ifaceobj
.flags
& ifaceobj
.YOUNGEST_SIBLING
)):
357 purge_addresses
= ifaceobj
.get_attr_value_first('address-purge')
358 if not purge_addresses
:
359 purge_addresses
= 'yes'
361 if squash_addr_config
and ifaceobj
.flags
& iface
.HAS_SIBLINGS
:
362 ifaceobjlist
= ifaceobj_getfunc(ifaceobj
.name
)
364 ifaceobjlist
= [ifaceobj
]
366 module_name
= self
.__class
__.__name
__
367 ifname
= ifaceobj
.name
369 (addr_supported
, newaddrs
, newaddr_attrs
) = self
._inet
_address
_convert
_to
_cidr
(ifaceobjlist
)
370 newaddrs
= utils
.get_ip_objs(module_name
, ifname
, newaddrs
)
372 if not addr_supported
:
374 if (not squash_addr_config
and (ifaceobj
.flags
& iface
.HAS_SIBLINGS
)):
375 # if youngest sibling and squash addr is not set
376 # print a warning that addresses will not be purged
377 if (ifaceobj
.flags
& iface
.YOUNGEST_SIBLING
):
378 self
.logger
.warn('%s: interface has multiple ' %ifaceobj
.name
+
379 'iface stanzas, skip purging existing addresses')
380 purge_addresses
= 'no'
382 if not ifupdownflags
.flags
.PERFMODE
and purge_addresses
== 'yes':
383 # if perfmode is not set and purge addresses is not set to 'no'
384 # lets purge addresses not in the config
385 runningaddrs
= self
.ipcmd
.get_running_addrs(ifaceobj
, details
=False)
387 # if anycast address is configured on 'lo' and is in running config
388 # add it to newaddrs so that ifreload doesn't wipe it out
389 anycast_addr
= utils
.get_normalized_ip_addr(ifaceobj
.name
, self
._get
_anycast
_addr
(ifaceobjlist
))
391 if runningaddrs
and anycast_addr
and anycast_addr
in runningaddrs
:
392 newaddrs
.append(anycast_addr
)
394 user_ip4
, user_ip6
, newaddrs
= self
.order_user_configured_addrs(newaddrs
)
396 if newaddrs
== runningaddrs
or self
.compare_running_ips_and_user_config(user_ip4
, user_ip6
, runningaddrs
):
398 self
._inet
_address
_list
_config
(ifaceobj
, newaddrs
, newaddr_attrs
)
401 # if primary address is not same, there is no need to keep any.
402 # reset all addresses
403 if newaddrs
and runningaddrs
and newaddrs
[0] != runningaddrs
[0]:
406 skip_addrs
= newaddrs
or []
407 for addr
in runningaddrs
or []:
408 if addr
in skip_addrs
:
410 self
.ipcmd
.addr_del(ifaceobj
.name
, addr
)
412 self
.log_warn(str(e
))
415 self
._inet
_address
_list
_config
(ifaceobj
, newaddrs
, newaddr_attrs
)
417 def compare_running_ips_and_user_config(self
, user_ip4
, user_ip6
, running_addrs
):
419 We need to compare the user config ips and the running ips.
420 ip4 ordering matters (primary etc) but ip6 order doesn't matter
422 this function replaces the strict comparison previously in place
423 if newaddrs == running_addrs ?
425 We will compare if the ip4 ordering is correct, then check if all
426 ip6 are present in the list (without checking the ordering)
428 if (user_ip4
or user_ip6
) and not running_addrs
:
430 elif running_addrs
and not user_ip4
and not user_ip6
:
432 elif not running_addrs
and not user_ip4
and not user_ip6
:
435 len_ip4
= len(user_ip4
)
436 len_running_addrs
= len(running_addrs
)
438 if len_ip4
> len_running_addrs
:
443 if user_ip4
[i
] != running_addrs
[i
]:
448 running_ip6
= running_addrs
[len_ip4
:]
450 running_ip6
= running_addrs
453 len_ip6
= len(user_ip6
)
455 for ip6
in running_ip6
:
456 if ip6
not in user_ip6
:
462 def order_user_configured_addrs(self
, user_config_addrs
):
466 for a
in user_config_addrs
:
467 if isinstance(a
, _BaseV6
):
472 return ip4
, ip6
, ip4
+ ip6
474 def _delete_gateway(self
, ifaceobj
, gateways
, vrf
, metric
):
475 for del_gw
in gateways
:
477 self
.ipcmd
.route_del_gateway(ifaceobj
.name
, del_gw
, vrf
, metric
)
478 except Exception as e
:
479 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
481 def _add_delete_gateway(self
, ifaceobj
, gateways
=[], prev_gw
=[]):
482 vrf
= ifaceobj
.get_attr_value_first('vrf')
483 metric
= ifaceobj
.get_attr_value_first('metric')
484 self
._delete
_gateway
(ifaceobj
, list(set(prev_gw
) - set(gateways
)),
486 for add_gw
in gateways
:
488 self
.ipcmd
.route_add_gateway(ifaceobj
.name
, add_gw
, vrf
, metric
)
489 except Exception as e
:
490 self
.log_error('%s: %s' % (ifaceobj
.name
, str(e
)))
492 def _get_prev_gateway(self
, ifaceobj
, gateways
):
494 saved_ifaceobjs
= statemanager
.statemanager_api
.get_ifaceobjs(ifaceobj
.name
)
495 if not saved_ifaceobjs
:
497 prev_gateways
= saved_ifaceobjs
[0].get_attr_value('gateway')
498 if not prev_gateways
:
502 def _check_mtu_config(self
, ifaceobj
, mtu
, ifaceobj_getfunc
, syntaxcheck
=False):
504 if (ifaceobj
.link_kind
& ifaceLinkKind
.BRIDGE
):
506 self
.logger
.warn('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj
.name
)
509 self
.logger
.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj
.name
)
510 elif ifaceobj_getfunc
:
511 if ((ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BOND_SLAVE
) and
512 ifaceobj
.upperifaces
):
513 masterobj
= ifaceobj_getfunc(ifaceobj
.upperifaces
[0])
515 master_mtu
= masterobj
[0].get_attr_value_first('mtu')
516 if master_mtu
and master_mtu
!= mtu
:
518 self
.logger
.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj
.name
, mtu
, masterobj
[0].name
, master_mtu
))
521 self
.logger
.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj
.name
, mtu
, masterobj
[0].name
, master_mtu
))
522 elif ((ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
) and
523 ifaceobj
.lowerifaces
):
524 lowerobj
= ifaceobj_getfunc(ifaceobj
.lowerifaces
[0])
527 lowerdev_mtu
= lowerobj
[0].get_attr_value_first('mtu')
529 lowerdev_mtu
= self
.ipcmd
.link_get_mtu_sysfs(lowerobj
[0].name
)
530 if lowerdev_mtu
and int(mtu
) > int(lowerdev_mtu
):
531 self
.logger
.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
532 %(ifaceobj
.name
, mtu
, lowerobj
[0].name
, lowerdev_mtu
))
534 elif (not lowerobj
[0].link_kind
and
535 not (lowerobj
[0].link_privflags
& ifaceLinkPrivFlags
.LOOPBACK
) and
536 not lowerdev_mtu
and self
.default_mtu
and
537 (int(mtu
) > int(self
.default_mtu
))):
538 # only check default mtu on lower device which is a physical interface
539 self
.logger
.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
540 %(ifaceobj
.name
, mtu
, lowerobj
[0].name
, self
.default_mtu
))
542 if self
.max_mtu
and mtu
> self
.max_mtu
:
543 self
.logger
.warn('%s: specified mtu %s is greater than max mtu %s'
544 %(ifaceobj
.name
, mtu
, self
.max_mtu
))
548 def _propagate_mtu_to_upper_devs(self
, ifaceobj
, mtu
, ifaceobj_getfunc
):
549 if (not ifaceobj
.upperifaces
or
550 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BOND_SLAVE
) or
551 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
) or
552 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
)):
554 for u
in ifaceobj
.upperifaces
:
555 upperobjs
= ifaceobj_getfunc(u
)
557 not (upperobjs
[0].link_kind
& ifaceLinkKind
.VLAN
)):
559 # only adjust mtu for vlan devices on ifaceobj
560 umtu
= upperobjs
[0].get_attr_value_first('mtu')
562 running_mtu
= self
.ipcmd
.link_get_mtu(upperobjs
[0].name
)
563 if not running_mtu
or (running_mtu
!= mtu
):
564 self
.ipcmd
.link_set(u
, 'mtu', mtu
)
566 def _process_mtu_config(self
, ifaceobj
, ifaceobj_getfunc
, mtu
):
568 if not self
._check
_mtu
_config
(ifaceobj
, mtu
, ifaceobj_getfunc
):
570 cached_running_mtu
= self
.ipcmd
.link_get_mtu(ifaceobj
.name
)
571 running_mtu
= self
.ipcmd
.link_get_mtu_sysfs(ifaceobj
.name
)
572 if not running_mtu
or (running_mtu
and running_mtu
!= mtu
):
573 force
= cached_running_mtu
!= running_mtu
574 self
.ipcmd
.link_set(ifaceobj
.name
, 'mtu', mtu
, force
=force
)
575 if (not ifupdownflags
.flags
.ALL
and
576 not ifaceobj
.link_kind
and
577 ifupdownconfig
.config
.get('adjust_logical_dev_mtu', '1') != '0'):
578 # This is additional cost to us, so do it only when
579 # ifupdown2 is called on a particular interface and
580 # it is a physical interface
581 self
._propagate
_mtu
_to
_upper
_devs
(ifaceobj
, mtu
, ifaceobj_getfunc
)
584 if ifaceobj
.link_kind
:
585 # bonds and vxlan devices need an explicit set of mtu.
586 # bridges don't need mtu set
587 if (ifaceobj
.link_kind
& ifaceLinkKind
.BOND
or
588 ifaceobj
.link_kind
& ifaceLinkKind
.VXLAN
):
589 running_mtu
= self
.ipcmd
.link_get_mtu(ifaceobj
.name
)
590 if (self
.default_mtu
and running_mtu
!= self
.default_mtu
):
591 self
.ipcmd
.link_set(ifaceobj
.name
, 'mtu', self
.default_mtu
)
593 if (ifupdownconfig
.config
.get('adjust_logical_dev_mtu', '1') != '0'
594 and ifaceobj
.lowerifaces
):
595 # set vlan interface mtu to lower device mtu
596 if (ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
):
597 lower_iface
= ifaceobj
.lowerifaces
[0]
598 if lower_iface
not in self
.lower_iface_mtu_checked_list
:
599 lower_iface_mtu
= self
.ipcmd
.link_get_mtu_sysfs(lower_iface
)
600 self
.ipcmd
.cache_update([lower_iface
, 'mtu'], lower_iface_mtu
)
601 self
.lower_iface_mtu_checked_list
.append(lower_iface
)
603 lower_iface_mtu
= self
.ipcmd
.link_get_mtu(lower_iface
)
605 if lower_iface_mtu
!= self
.ipcmd
.link_get_mtu_sysfs(ifaceobj
.name
):
606 self
.ipcmd
.link_set_mtu(ifaceobj
.name
, lower_iface_mtu
)
608 elif (not (ifaceobj
.name
== 'lo') and not ifaceobj
.link_kind
and
609 not (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BOND_SLAVE
) and
611 # logical devices like bridges and vlan devices rely on mtu
612 # from their lower devices. ie mtu travels from
613 # lower devices to upper devices. For bonds mtu travels from
614 # upper to lower devices. running mtu depends on upper and
615 # lower device mtu. With all this implicit mtu
616 # config by the kernel in play, we try to be cautious here
617 # on which devices we want to reset mtu to default.
618 # essentially only physical interfaces which are not bond slaves
619 running_mtu
= self
.ipcmd
.link_get_mtu(ifaceobj
.name
)
620 if running_mtu
!= self
.default_mtu
:
621 self
.ipcmd
.link_set(ifaceobj
.name
, 'mtu', self
.default_mtu
)
623 def _set_bridge_forwarding(self
, ifaceobj
):
624 """ set ip forwarding to 0 if bridge interface does not have a
626 if not ifaceobj
.upperifaces
and not ifaceobj
.get_attr_value('address'):
628 if self
.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj
.name
) == '1':
629 self
.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj
.name
, 0)
630 if self
.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj
.name
) == '1':
631 self
.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj
.name
, 0)
633 if self
.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj
.name
) == '0':
634 self
.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj
.name
, 1)
635 if self
.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj
.name
) == '0':
636 self
.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj
.name
, 1)
638 def _sysctl_config(self
, ifaceobj
):
639 setting_default_value
= False
640 mpls_enable
= ifaceobj
.get_attr_value_first('mpls-enable');
642 setting_default_value
= True
643 mpls_enable
= self
.get_mod_subattr('mpls-enable', 'default')
644 mpls_enable
= utils
.boolean_support_binary(mpls_enable
)
645 # File read has been used for better performance
646 # instead of using sysctl command
647 if ifupdownflags
.flags
.PERFMODE
:
648 running_mpls_enable
= '0'
650 running_mpls_enable
= self
.read_file_oneline(
651 '/proc/sys/net/mpls/conf/%s/input'
655 if mpls_enable
!= running_mpls_enable
:
657 self
.sysctl_set('net.mpls.conf.%s.input'
658 %('/'.join(ifaceobj
.name
.split("."))),
660 except Exception as e
:
661 if not setting_default_value
:
662 ifaceobj
.status
= ifaceStatus
.ERROR
663 self
.logger
.error('%s: %s' %(ifaceobj
.name
, str(e
)))
665 if (ifaceobj
.link_kind
& ifaceLinkKind
.BRIDGE
):
666 self
._set
_bridge
_forwarding
(ifaceobj
)
668 if not self
.syntax_check_sysctls(ifaceobj
):
670 ipforward
= ifaceobj
.get_attr_value_first('ip-forward')
671 ip6forward
= ifaceobj
.get_attr_value_first('ip6-forward')
672 if ifupdownflags
.flags
.PERFMODE
:
674 self
.sysctl_set('net.ipv4.conf.%s.forwarding'
675 %('/'.join(ifaceobj
.name
.split("."))),
676 utils
.boolean_support_binary(ipforward
))
678 self
.sysctl_set('net.ipv6.conf.%s.forwarding'
679 %('/'.join(ifaceobj
.name
.split("."))),
680 utils
.boolean_support_binary(ip6forward
))
682 bridge_port
= ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
685 self
.log_error('%s: \'ip-forward\' is not supported for '
686 'bridge port' %ifaceobj
.name
)
688 self
.log_error('%s: \'ip6-forward\' is not supported for '
689 'bridge port' %ifaceobj
.name
)
691 setting_default_value
= False
693 setting_default_value
= True
694 ipforward
= (self
.ipforward
or
695 self
.get_mod_subattr('ip-forward', 'default'))
696 ipforward
= utils
.boolean_support_binary(ipforward
)
697 # File read has been used for better performance
698 # instead of using sysctl command
699 running_ipforward
= self
.read_file_oneline(
700 '/proc/sys/net/ipv4/conf/%s/forwarding'
702 if ipforward
!= running_ipforward
:
704 self
.sysctl_set('net.ipv4.conf.%s.forwarding'
705 %('/'.join(ifaceobj
.name
.split("."))),
707 except Exception as e
:
708 if not setting_default_value
:
709 ifaceobj
.status
= ifaceStatus
.ERROR
710 self
.logger
.error('%s: %s' %(ifaceobj
.name
, str(e
)))
712 setting_default_value
= False
714 setting_default_value
= True
715 ip6forward
= (self
.ip6forward
or
716 self
.get_mod_subattr('ip6-forward', 'default'))
717 ip6forward
= utils
.boolean_support_binary(ip6forward
)
718 # File read has been used for better performance
719 # instead of using sysctl command
720 running_ip6forward
= self
.read_file_oneline(
721 '/proc/sys/net/ipv6/conf/%s/forwarding'
723 if ip6forward
!= running_ip6forward
:
725 self
.sysctl_set('net.ipv6.conf.%s.forwarding'
726 %('/'.join(ifaceobj
.name
.split("."))),
728 except Exception as e
:
729 # There is chance of ipv6 being removed because of,
730 # for example, setting mtu < 1280
731 # In such cases, log error only if user has configured
733 if not setting_default_value
:
734 ifaceobj
.status
= ifaceStatus
.ERROR
735 self
.logger
.error('%s: %s' %(ifaceobj
.name
, str(e
)))
737 def process_mtu(self
, ifaceobj
, ifaceobj_getfunc
):
738 mtu
= ifaceobj
.get_attr_value_first('mtu')
741 default_iface_mtu
= self
.ifaces_defaults
.get(ifaceobj
.name
, {}).get('mtu')
743 if default_iface_mtu
:
745 mtu
= default_iface_mtu
746 int(default_iface_mtu
)
747 except Exception as e
:
748 self
.logger
.warning('%s: MTU value from policy file: %s' % (ifaceobj
.name
, str(e
)))
751 self
._process
_mtu
_config
(ifaceobj
, ifaceobj_getfunc
, mtu
)
753 def up_ipv6_addrgen(self
, ifaceobj
):
754 ipv6_addrgen
= ifaceobj
.get_attr_value_first('ipv6-addrgen')
756 self
.ipcmd
.ipv6_addrgen(ifaceobj
.name
, utils
.get_boolean_from_string(ipv6_addrgen
))
758 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
759 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
762 if not self
.syntax_check_enable_l3_iface_forwardings(ifaceobj
, ifaceobj_getfunc
):
765 alias
= ifaceobj
.get_attr_value_first('alias')
766 current_alias
= self
.ipcmd
.link_get_alias(ifaceobj
.name
)
767 if alias
and alias
!= current_alias
:
768 self
.ipcmd
.link_set_alias(ifaceobj
.name
, alias
)
769 elif not alias
and current_alias
:
770 self
.ipcmd
.link_set_alias(ifaceobj
.name
, '')
772 self
._sysctl
_config
(ifaceobj
)
774 addr_method
= ifaceobj
.addr_method
775 force_reapply
= False
777 # release any stale dhcp addresses if present
778 if (addr_method
not in ["dhcp", "ppp"] and not ifupdownflags
.flags
.PERFMODE
and
779 not (ifaceobj
.flags
& iface
.HAS_SIBLINGS
)):
780 # if not running in perf mode and ifaceobj does not have
781 # any sibling iface objects, kill any stale dhclient
783 dhclientcmd
= dhclient()
784 if dhclientcmd
.is_running(ifaceobj
.name
):
785 # release any dhcp leases
786 dhclientcmd
.release(ifaceobj
.name
)
788 elif dhclientcmd
.is_running6(ifaceobj
.name
):
789 dhclientcmd
.release6(ifaceobj
.name
)
794 self
.ipcmd
.batch_start()
795 self
.up_ipv6_addrgen(ifaceobj
)
797 if addr_method
not in ["dhcp", "ppp"]:
798 self
._inet
_address
_config
(ifaceobj
, ifaceobj_getfunc
,
801 self
.process_mtu(ifaceobj
, ifaceobj_getfunc
)
804 self
.ipcmd
.batch_commit()
805 except Exception as e
:
806 self
.log_error('%s: %s' % (ifaceobj
.name
, str(e
)), ifaceobj
, raise_error
=False)
809 hwaddress
= self
._get
_hwaddress
(ifaceobj
)
811 running_hwaddress
= None
812 if not ifupdownflags
.flags
.PERFMODE
: # system is clean
813 running_hwaddress
= self
.ipcmd
.link_get_hwaddress(ifaceobj
.name
)
814 if hwaddress
!= running_hwaddress
:
816 netlink
.link_set_updown(ifaceobj
.name
, "down")
817 if ifaceobj
.link_kind
& ifaceLinkKind
.BOND
:
818 # if bond, down all the slaves
819 if ifaceobj
.lowerifaces
:
820 for l
in ifaceobj
.lowerifaces
:
821 netlink
.link_set_updown(l
, "down")
824 self
.ipcmd
.link_set(ifaceobj
.name
, 'address', hwaddress
)
826 netlink
.link_set_updown(ifaceobj
.name
, "up")
828 for l
in ifaceobj
.lowerifaces
:
829 netlink
.link_set_updown(l
, "up")
831 # Handle special things on a bridge
832 self
._process
_bridge
(ifaceobj
, True)
834 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
836 gateways
= ifaceobj
.get_attr_value('gateway')
839 prev_gw
= self
._get
_prev
_gateway
(ifaceobj
, gateways
)
840 self
._add
_delete
_gateway
(ifaceobj
, gateways
, prev_gw
)
842 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
844 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
846 addr_method
= ifaceobj
.addr_method
847 if addr_method
not in ["dhcp", "ppp"]:
848 if ifaceobj
.get_attr_value_first('address-purge')=='no':
849 addrlist
= ifaceobj
.get_attr_value('address')
850 for addr
in addrlist
:
851 self
.ipcmd
.addr_del(ifaceobj
.name
, addr
)
852 #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0])
853 elif not ifaceobj
.link_kind
:
854 # for logical interfaces we don't need to remove the ip addresses
855 # kernel will do it for us on 'ip link del'
856 self
.ipcmd
.del_addr_all(ifaceobj
.name
)
857 gateways
= ifaceobj
.get_attr_value('gateway')
859 self
._delete
_gateway
(ifaceobj
, gateways
,
860 ifaceobj
.get_attr_value_first('vrf'),
861 ifaceobj
.get_attr_value_first('metric'))
862 mtu
= ifaceobj
.get_attr_value_first('mtu')
863 if (not ifaceobj
.link_kind
and mtu
and
864 self
.default_mtu
and (mtu
!= self
.default_mtu
)):
865 self
.ipcmd
.link_set(ifaceobj
.name
, 'mtu', self
.default_mtu
)
866 alias
= ifaceobj
.get_attr_value_first('alias')
868 self
.write_file('/sys/class/net/%s/ifalias' % ifaceobj
.name
, '\n')
869 # XXX hwaddress reset cannot happen because we dont know last
872 # Handle special things on a bridge
873 self
._process
_bridge
(ifaceobj
, False)
875 self
.logger
.debug('%s : %s' %(ifaceobj
.name
, str(e
)))
878 def _get_iface_addresses(self
, ifaceobj
):
879 addrlist
= ifaceobj
.get_attr_value('address')
882 if not addrlist
: return None
883 for addrindex
in range(0, len(addrlist
)):
884 addr
= addrlist
[addrindex
]
885 netmask
= ifaceobj
.get_attr_value_n('netmask', addrindex
)
887 prefixlen
= IPNetwork('%s' %addr
+
888 '/%s' %netmask
).prefixlen
889 addr
= addr
+ '/%s' %prefixlen
890 outaddrlist
.append(addr
)
893 def _get_bridge_fdbs(self
, bridgename
, vlan
):
894 fdbs
= self
._bridge
_fdb
_query
_cache
.get(bridgename
)
896 fdbs
= self
.ipcmd
.bridge_fdb_show_dev(bridgename
)
899 self
._bridge
_fdb
_query
_cache
[bridgename
] = fdbs
900 return fdbs
.get(vlan
)
902 def _check_addresses_in_bridge(self
, ifaceobj
, hwaddress
):
903 """ If the device is a bridge, make sure the addresses
904 are in the bridge """
905 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
906 bridgename
= ifaceobj
.lowerifaces
[0]
907 vlan
= self
._get
_vlan
_id
(ifaceobj
)
908 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
909 fdb_addrs
= self
._get
_bridge
_fdbs
(bridgename
, str(vlan
))
910 if not fdb_addrs
or hwaddress
not in fdb_addrs
:
914 def _query_sysctl(self
, ifaceobj
, ifaceobjcurr
):
915 bridge_port
= ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
916 ipforward
= ifaceobj
.get_attr_value_first('ip-forward')
919 ifaceobjcurr
.status
= ifaceStatus
.ERROR
920 ifaceobjcurr
.status_str
= ('\'ip-forward\' not supported ' +
922 ifaceobjcurr
.update_config_with_status('ip-forward', 1, None)
924 running_ipforward
= self
.read_file_oneline(
925 '/proc/sys/net/ipv4/conf/%s/forwarding'
927 running_ipforward
= utils
.get_onff_from_onezero(
929 ifaceobjcurr
.update_config_with_status('ip-forward',
931 ipforward
!= running_ipforward
)
933 ip6forward
= ifaceobj
.get_attr_value_first('ip6-forward')
936 ifaceobjcurr
.status
= ifaceStatus
.ERROR
937 ifaceobjcurr
.status_str
= ('\'ip6-forward\' not supported ' +
939 ifaceobjcurr
.update_config_with_status('ip6-forward', 1, None)
941 running_ip6forward
= self
.read_file_oneline(
942 '/proc/sys/net/ipv6/conf/%s/forwarding'
944 running_ip6forward
= utils
.get_onff_from_onezero(
946 ifaceobjcurr
.update_config_with_status('ip6-forward',
948 ip6forward
!= running_ip6forward
)
949 mpls_enable
= ifaceobj
.get_attr_value_first('mpls-enable');
951 running_mpls_enable
= self
.read_file_oneline(
952 '/proc/sys/net/mpls/conf/%s/input'
954 running_mpls_enable
= utils
.get_yesno_from_onezero(
956 ifaceobjcurr
.update_config_with_status('mpls-enable',
958 mpls_enable
!= running_mpls_enable
)
961 def _query_check(self
, ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
=None):
962 runningaddrsdict
= None
963 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
964 self
.logger
.debug('iface %s not found' %ifaceobj
.name
)
966 addr_method
= ifaceobj
.addr_method
967 self
.query_n_update_ifaceobjcurr_attr(ifaceobj
, ifaceobjcurr
,
968 'mtu', self
.ipcmd
.link_get_mtu
)
969 hwaddress
= self
._get
_hwaddress
(ifaceobj
)
971 rhwaddress
= self
.ipcmd
.link_get_hwaddress(ifaceobj
.name
)
972 if not rhwaddress
or rhwaddress
!= hwaddress
:
973 ifaceobjcurr
.update_config_with_status('hwaddress', rhwaddress
,
975 elif not self
._check
_addresses
_in
_bridge
(ifaceobj
, hwaddress
):
976 # XXX: hw address is not in bridge
977 ifaceobjcurr
.update_config_with_status('hwaddress', rhwaddress
,
979 ifaceobjcurr
.status_str
= 'bridge fdb error'
981 ifaceobjcurr
.update_config_with_status('hwaddress', rhwaddress
,
983 self
.query_n_update_ifaceobjcurr_attr(ifaceobj
, ifaceobjcurr
,
984 'alias', self
.ipcmd
.link_get_alias
)
985 self
._query
_sysctl
(ifaceobj
, ifaceobjcurr
)
987 if addr_method
in ["dhcp", "ppp"]:
989 addrs
= utils
.get_normalized_ip_addr(ifaceobj
.name
,
990 self
._get
_iface
_addresses
(ifaceobj
))
991 runningaddrsdict
= self
.ipcmd
.get_running_addrs(ifaceobj
)
992 # if anycast address is configured on 'lo' and is in running config
993 # add it to addrs so that query_check doesn't fail
994 anycast_addr
= utils
.get_normalized_ip_addr(ifaceobj
.name
, ifaceobj
.get_attr_value_first('clagd-vxlan-anycast-ip'))
996 anycast_addr
= anycast_addr
+'/32'
997 if runningaddrsdict
and anycast_addr
and runningaddrsdict
.get(anycast_addr
):
998 addrs
.append(anycast_addr
)
1000 # Set ifaceobjcurr method and family
1001 ifaceobjcurr
.addr_method
= ifaceobj
.addr_method
1002 ifaceobjcurr
.addr_family
= ifaceobj
.addr_family
1003 if not runningaddrsdict
and not addrs
:
1005 runningaddrs
= runningaddrsdict
.keys() if runningaddrsdict
else []
1006 # Add /32 netmask to configured address without netmask.
1007 # This may happen on interfaces where pointopoint is used.
1008 runningaddrs
= [ addr
if '/' in addr
else addr
+ '/32' for addr
in runningaddrs
]
1009 if runningaddrs
!= addrs
:
1010 runningaddrsset
= set(runningaddrs
) if runningaddrs
else set([])
1011 addrsset
= set(addrs
) if addrs
else set([])
1012 if (ifaceobj
.flags
& iface
.HAS_SIBLINGS
):
1015 # only check for addresses present in running config
1016 addrsdiff
= addrsset
.difference(runningaddrsset
)
1018 if addr
in addrsdiff
:
1019 ifaceobjcurr
.update_config_with_status('address',
1022 ifaceobjcurr
.update_config_with_status('address',
1025 addrsdiff
= addrsset
.symmetric_difference(runningaddrsset
)
1026 for addr
in addrsset
.union(runningaddrsset
):
1027 if addr
in addrsdiff
:
1028 ifaceobjcurr
.update_config_with_status('address',
1031 ifaceobjcurr
.update_config_with_status('address',
1034 [ifaceobjcurr
.update_config_with_status('address',
1035 addr
, 0) for addr
in addrs
]
1036 #XXXX Check broadcast address, scope, etc
1039 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
1040 if not self
.ipcmd
.link_exists(ifaceobjrunning
.name
):
1041 self
.logger
.debug('iface %s not found' %ifaceobjrunning
.name
)
1043 dhclientcmd
= dhclient()
1044 if (dhclientcmd
.is_running(ifaceobjrunning
.name
) or
1045 dhclientcmd
.is_running6(ifaceobjrunning
.name
)):
1046 # If dhcp is configured on the interface, we skip it
1048 isloopback
= self
.ipcmd
.link_isloopback(ifaceobjrunning
.name
)
1050 default_addrs
= ['127.0.0.1/8', '::1/128']
1051 ifaceobjrunning
.addr_family
.append('inet')
1052 ifaceobjrunning
.addr_method
= 'loopback'
1055 runningaddrsdict
= self
.ipcmd
.get_running_addrs(ifaceobjrunning
)
1056 if runningaddrsdict
:
1057 [ifaceobjrunning
.update_config('address', addr
)
1058 for addr
, addrattrs
in runningaddrsdict
.items()
1059 if addr
not in default_addrs
]
1060 mtu
= self
.ipcmd
.link_get_mtu(ifaceobjrunning
.name
)
1062 (ifaceobjrunning
.name
== 'lo' and mtu
!= '16436') or
1063 (ifaceobjrunning
.name
!= 'lo' and
1064 mtu
!= self
.get_mod_subattr('mtu', 'default'))):
1065 ifaceobjrunning
.update_config('mtu', mtu
)
1066 alias
= self
.ipcmd
.link_get_alias(ifaceobjrunning
.name
)
1068 ifaceobjrunning
.update_config('alias', alias
)
1070 ipforward
= self
.read_file_oneline(
1071 '/proc/sys/net/ipv4/conf/%s/forwarding'
1072 %ifaceobjrunning
.name
)
1075 _run_ops
= {'up' : _up
,
1077 'query-checkcurr' : _query_check
,
1078 'query-running' : _query_running
}
1081 """ returns list of ops supported by this module """
1082 return self
._run
_ops
.keys()
1084 def _init_command_handlers(self
):
1086 self
.ipcmd
= LinkUtils()
1088 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, ifaceobj_getfunc
=None):
1089 """ run address configuration on the interface object passed as argument
1092 **ifaceobj** (object): iface object
1094 **operation** (str): any of 'up', 'down', 'query-checkcurr',
1097 query_ifaceobj (object): query check ifaceobject. This is only
1098 valid when op is 'query-checkcurr'. It is an object same as
1099 ifaceobj, but contains running attribute values and its config
1100 status. The modules can use it to return queried running state
1101 of interfaces. status is success if the running state is same
1102 as user required state in ifaceobj. error otherwise.
1104 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
1106 op_handler
= self
._run
_ops
.get(operation
)
1109 self
._init
_command
_handlers
()
1110 if operation
== 'query-checkcurr':
1111 op_handler(self
, ifaceobj
, query_ifaceobj
,
1112 ifaceobj_getfunc
=ifaceobj_getfunc
)
1114 op_handler(self
, ifaceobj
,
1115 ifaceobj_getfunc
=ifaceobj_getfunc
)