]>
Commit | Line | Data |
---|---|---|
15ef32ea RP |
1 | #!/usr/bin/python |
2 | # | |
d486dd0d | 3 | # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. |
15ef32ea RP |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com |
5 | # | |
6 | ||
007cae35 JF |
7 | import socket |
8 | ||
d486dd0d | 9 | from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6 |
9087e727 | 10 | |
15ef32ea | 11 | try: |
d486dd0d JF |
12 | from ifupdown2.ifupdown.iface import * |
13 | from ifupdown2.ifupdown.utils import utils | |
14 | from ifupdown2.ifupdown.netlink import netlink | |
15 | ||
16 | from ifupdown2.ifupdownaddons.dhclient import dhclient | |
17 | from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils | |
18 | from ifupdown2.ifupdownaddons.modulebase import moduleBase | |
19 | ||
20 | import ifupdown2.ifupdown.statemanager as statemanager | |
21 | import ifupdown2.ifupdown.policymanager as policymanager | |
22 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags | |
23 | import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig | |
24 | except ImportError: | |
15ef32ea | 25 | from ifupdown.iface import * |
82908a2d | 26 | from ifupdown.utils import utils |
d486dd0d JF |
27 | from ifupdown.netlink import netlink |
28 | ||
15ef32ea | 29 | from ifupdownaddons.dhclient import dhclient |
d486dd0d JF |
30 | from ifupdownaddons.LinkUtils import LinkUtils |
31 | from ifupdownaddons.modulebase import moduleBase | |
32 | ||
33 | import ifupdown.statemanager as statemanager | |
84f33af6 | 34 | import ifupdown.policymanager as policymanager |
fc5e1735 | 35 | import ifupdown.ifupdownflags as ifupdownflags |
d486dd0d JF |
36 | import ifupdown.ifupdownconfig as ifupdownconfig |
37 | ||
15ef32ea RP |
38 | |
39 | class address(moduleBase): | |
40 | """ ifupdown2 addon module to configure address, mtu, hwaddress, alias | |
41 | (description) on an interface """ | |
42 | ||
43 | _modinfo = {'mhelp' : 'address configuration module for interfaces', | |
44 | 'attrs': { | |
45 | 'address' : | |
46 | {'help' : 'ipv4 or ipv6 addresses', | |
482b2fab | 47 | 'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'], |
c6370b56 | 48 | 'multiline' : True, |
15ef32ea RP |
49 | 'example' : ['address 10.0.12.3/24', |
50 | 'address 2000:1000:1000:1000:3::5/128']}, | |
51 | 'netmask' : | |
52 | {'help': 'netmask', | |
53 | 'example' : ['netmask 255.255.255.0'], | |
54 | 'compat' : True}, | |
55 | 'broadcast' : | |
56 | {'help': 'broadcast address', | |
482b2fab | 57 | 'validvals' : ['<ipv4>', ], |
15ef32ea RP |
58 | 'example' : ['broadcast 10.0.1.255']}, |
59 | 'scope' : | |
60 | {'help': 'scope', | |
c6370b56 | 61 | 'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'], |
15ef32ea RP |
62 | 'example' : ['scope host']}, |
63 | 'preferred-lifetime' : | |
64 | {'help': 'preferred lifetime', | |
c6370b56 | 65 | 'validrange' : ['0', '65535'], |
15ef32ea RP |
66 | 'example' : ['preferred-lifetime forever', |
67 | 'preferred-lifetime 10']}, | |
68 | 'gateway' : | |
69 | {'help': 'default gateway', | |
482b2fab | 70 | 'validvals' : ['<ipv4>', '<ipv6>'], |
2ed2adeb | 71 | 'multiline' : True, |
15ef32ea RP |
72 | 'example' : ['gateway 255.255.255.0']}, |
73 | 'mtu' : | |
74 | { 'help': 'interface mtu', | |
c6370b56 | 75 | 'validrange' : ['552', '9216'], |
15ef32ea RP |
76 | 'example' : ['mtu 1600'], |
77 | 'default' : '1500'}, | |
78 | 'hwaddress' : | |
79 | {'help' : 'hw address', | |
c6370b56 | 80 | 'validvals' : ['<mac>',], |
15ef32ea RP |
81 | 'example': ['hwaddress 44:38:39:00:27:b8']}, |
82 | 'alias' : | |
83 | { 'help': 'description/alias', | |
394e68b5 RP |
84 | 'example' : ['alias testnetwork']}, |
85 | 'address-purge' : | |
86 | { 'help': 'purge existing addresses. By default ' + | |
87 | 'any existing ip addresses on an interface are ' + | |
88 | 'purged to match persistant addresses in the ' + | |
89 | 'interfaces file. Set this attribute to \'no\'' + | |
90 | 'if you want to preserve existing addresses', | |
c6370b56 | 91 | 'validvals' : ['yes', 'no'], |
394e68b5 | 92 | 'default' : 'yes', |
a794fb31 BR |
93 | 'example' : ['address-purge yes/no']}, |
94 | 'clagd-vxlan-anycast-ip' : | |
95 | { 'help' : 'Anycast local IP address for ' + | |
96 | 'dual connected VxLANs', | |
482b2fab | 97 | 'validvals' : ['<ipv4>', ], |
d486dd0d JF |
98 | 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']}, |
99 | 'ip-forward' : | |
100 | { 'help': 'ip forwarding flag', | |
7b444c7c | 101 | 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], |
d486dd0d JF |
102 | 'default' : 'off', |
103 | 'example' : ['ip-forward off']}, | |
104 | 'ip6-forward' : | |
105 | { 'help': 'ipv6 forwarding flag', | |
7b444c7c | 106 | 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], |
d486dd0d JF |
107 | 'default' : 'off', |
108 | 'example' : ['ip6-forward off']}, | |
109 | 'mpls-enable' : | |
110 | { 'help': 'mpls enable flag', | |
111 | 'validvals': ['yes', 'no'], | |
112 | 'default' : 'no', | |
113 | 'example' : ['mpls-enable yes']}, | |
3fc54eef JF |
114 | 'ipv6-addrgen': { |
115 | 'help': 'enable disable ipv6 link addrgenmode', | |
116 | 'validvals': ['on', 'off'], | |
117 | 'default': 'on', | |
118 | 'example': [ | |
119 | 'ipv6-addrgen on', | |
120 | 'ipv6-addrgen off' | |
121 | ] | |
122 | } | |
d486dd0d | 123 | }} |
15ef32ea RP |
124 | |
125 | def __init__(self, *args, **kargs): | |
126 | moduleBase.__init__(self, *args, **kargs) | |
127 | self.ipcmd = None | |
8e113d63 | 128 | self._bridge_fdb_query_cache = {} |
84f33af6 | 129 | self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu') |
9f30b2cc | 130 | self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu') |
d486dd0d JF |
131 | self.ipforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip-forward') |
132 | self.ip6forward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-forward') | |
133 | self.ifaces_defaults = policymanager.policymanager_api.get_iface_defaults(module_name=self.__class__.__name__) | |
134 | self.enable_l3_iface_forwarding_checks = utils.get_boolean_from_string( | |
135 | policymanager.policymanager_api.get_module_globals( | |
136 | self.__class__.__name__, | |
137 | 'enable_l3_iface_forwarding_checks' | |
138 | ) | |
139 | ) | |
9f30b2cc RP |
140 | |
141 | if not self.default_mtu: | |
142 | self.default_mtu = '1500' | |
143 | ||
144 | self.logger.info('address: using default mtu %s' %self.default_mtu) | |
145 | ||
146 | if self.max_mtu: | |
147 | self.logger.info('address: using max mtu %s' %self.max_mtu) | |
15ef32ea | 148 | |
d486dd0d JF |
149 | self.lower_iface_mtu_checked_list = list() |
150 | ||
8b57a467 JF |
151 | self.l3_intf_arp_accept = utils.get_boolean_from_string( |
152 | policymanager.policymanager_api.get_module_globals( | |
153 | module_name=self.__class__.__name__, | |
154 | attr='l3_intf_arp_accept' | |
155 | ), | |
156 | default=False | |
157 | ) | |
158 | ||
9d505185 JF |
159 | self.l3_intf_default_gateway_set_onlink = utils.get_boolean_from_string( |
160 | policymanager.policymanager_api.get_module_globals( | |
161 | module_name=self.__class__.__name__, | |
162 | attr='l3_intf_default_gateway_set_onlink' | |
163 | ), | |
164 | default=True | |
165 | ) | |
166 | ||
09096420 | 167 | def syntax_check(self, ifaceobj, ifaceobj_getfunc=None): |
22b49c28 | 168 | return (self.syntax_check_multiple_gateway(ifaceobj) |
09096420 | 169 | and self.syntax_check_addr_allowed_on(ifaceobj, True) |
d486dd0d JF |
170 | and self.syntax_check_mtu(ifaceobj, ifaceobj_getfunc) |
171 | and self.syntax_check_sysctls(ifaceobj) | |
172 | and self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc, syntax_check=True)) | |
173 | ||
174 | def syntax_check_enable_l3_iface_forwardings(self, ifaceobj, ifaceobj_getfunc, syntax_check=False): | |
175 | if (self.enable_l3_iface_forwarding_checks | |
176 | and (ifaceobj.link_kind & ifaceLinkKind.VLAN | |
177 | or ifaceobj.link_kind & ifaceLinkKind.BRIDGE) | |
178 | and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT): | |
179 | ||
180 | ifname = ifaceobj.name | |
181 | vlan_addr = None | |
182 | vlan_ipforward_off = None | |
183 | ||
184 | for obj in ifaceobj_getfunc(ifname) or [ifaceobj]: | |
185 | if not vlan_addr: | |
186 | vlan_addr = obj.get_attr_value('address') | |
187 | ||
188 | if not vlan_ipforward_off: | |
189 | ip_forward_value = obj.get_attr_value_first('ip-forward') | |
190 | ||
191 | if ip_forward_value and not utils.get_boolean_from_string(ip_forward_value): | |
192 | vlan_ipforward_off = True | |
193 | ||
194 | if vlan_addr and vlan_ipforward_off: | |
195 | if syntax_check: | |
196 | raise Exception( | |
197 | 'configuring ip-forward off and ip address(es) (%s) is not compatible' | |
198 | % (', '.join(vlan_addr)) | |
199 | ) | |
200 | else: | |
201 | raise Exception( | |
202 | '%s: configuring ip-forward off and ip address(es) (%s) is not compatible' | |
203 | % (ifname, ', '.join(vlan_addr)) | |
204 | ) | |
205 | ||
206 | return True | |
207 | ||
208 | def syntax_check_sysctls(self, ifaceobj): | |
209 | result = True | |
210 | bridge_port = (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT) | |
211 | ipforward = ifaceobj.get_attr_value_first('ip-forward') | |
212 | if bridge_port and ipforward: | |
213 | result = False | |
214 | self.log_error('%s: \'ip-forward\' is not supported for ' | |
215 | 'bridge port' %ifaceobj.name) | |
216 | ip6forward = ifaceobj.get_attr_value_first('ip6-forward') | |
217 | if bridge_port and ip6forward: | |
218 | result = False | |
219 | self.log_error('%s: \'ip6-forward\' is not supported for ' | |
220 | 'bridge port' %ifaceobj.name) | |
221 | return result | |
09096420 RP |
222 | |
223 | def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc): | |
224 | mtu = ifaceobj.get_attr_value_first('mtu') | |
225 | if mtu: | |
226 | return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc, | |
227 | syntaxcheck=True) | |
228 | return True | |
22b49c28 JF |
229 | |
230 | def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False): | |
231 | if ifaceobj.get_attr_value('address'): | |
232 | return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=syntax_check) | |
233 | return True | |
234 | ||
38365d4a JF |
235 | def _syntax_check_multiple_gateway(self, family, found, addr, type_obj): |
236 | if type(IPNetwork(addr)) == type_obj: | |
237 | if found: | |
238 | raise Exception('%s: multiple gateways for %s family' | |
239 | % (addr, family)) | |
240 | return True | |
241 | return False | |
242 | ||
22b49c28 | 243 | def syntax_check_multiple_gateway(self, ifaceobj): |
38365d4a JF |
244 | result = True |
245 | inet = False | |
246 | inet6 = False | |
247 | gateways = ifaceobj.get_attr_value('gateway') | |
248 | for addr in gateways if gateways else []: | |
249 | try: | |
250 | if self._syntax_check_multiple_gateway('inet', inet, addr, | |
251 | IPv4Network): | |
252 | inet = True | |
253 | if self._syntax_check_multiple_gateway('inet6', inet6, addr, | |
254 | IPv6Network): | |
255 | inet6 = True | |
256 | except Exception as e: | |
257 | self.logger.warning('%s: address: %s' % (ifaceobj.name, str(e))) | |
258 | result = False | |
259 | return result | |
260 | ||
75afe2a7 RP |
261 | def _address_valid(self, addrs): |
262 | if not addrs: | |
263 | return False | |
264 | if any(map(lambda a: True if a[:7] != '0.0.0.0' | |
265 | else False, addrs)): | |
266 | return True | |
267 | return False | |
268 | ||
428206bf | 269 | def _get_hwaddress(self, ifaceobj): |
d486dd0d | 270 | return utils.strip_hwaddress(ifaceobj.get_attr_value_first('hwaddress')) |
428206bf JF |
271 | |
272 | def _process_bridge(self, ifaceobj, up): | |
273 | hwaddress = self._get_hwaddress(ifaceobj) | |
75afe2a7 RP |
274 | addrs = ifaceobj.get_attr_value_first('address') |
275 | is_vlan_dev_on_vlan_aware_bridge = False | |
276 | is_bridge = self.ipcmd.is_bridge(ifaceobj.name) | |
277 | if not is_bridge: | |
d486dd0d JF |
278 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
279 | bridgename = ifaceobj.lowerifaces[0] | |
280 | vlan = self._get_vlan_id(ifaceobj) | |
75afe2a7 | 281 | is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename) |
8c2c9f26 RP |
282 | if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)) |
283 | or is_vlan_dev_on_vlan_aware_bridge): | |
8b57a467 JF |
284 | if self._address_valid(addrs): |
285 | if self.l3_intf_arp_accept: | |
286 | if up: | |
287 | self.write_file('/proc/sys/net/ipv4/conf/%s' % ifaceobj.name + | |
288 | '/arp_accept', '1') | |
289 | else: | |
290 | self.write_file('/proc/sys/net/ipv4/conf/%s' % ifaceobj.name + | |
291 | '/arp_accept', '0') | |
292 | else: | |
293 | self.write_file('/proc/sys/net/ipv4/conf/%s/arp_accept' % ifaceobj.name, '0') | |
75afe2a7 | 294 | if hwaddress and is_vlan_dev_on_vlan_aware_bridge: |
15897163 JF |
295 | if up: |
296 | # check statemanager to delete the old entry if necessary | |
297 | try: | |
298 | for old_obj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: | |
299 | old_hwaddress = old_obj.get_attr_value_first("hwaddress") | |
300 | if old_hwaddress and self.ipcmd.mac_str_to_int(old_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress): | |
301 | self.ipcmd.bridge_fdb_del(bridgename, old_hwaddress, vlan) | |
302 | break | |
303 | except: | |
304 | pass | |
305 | self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan) | |
306 | else: | |
307 | self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan) | |
cb46a208 | 308 | |
0582f185 RP |
309 | def _get_anycast_addr(self, ifaceobjlist): |
310 | for ifaceobj in ifaceobjlist: | |
311 | anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip') | |
312 | if anycast_addr: | |
313 | anycast_addr = anycast_addr+'/32' | |
314 | return anycast_addr | |
315 | return None | |
316 | ||
317 | def _inet_address_convert_to_cidr(self, ifaceobjlist): | |
15ef32ea | 318 | newaddrs = [] |
0582f185 RP |
319 | newaddr_attrs = {} |
320 | ||
321 | for ifaceobj in ifaceobjlist: | |
322 | addrs = ifaceobj.get_attr_value('address') | |
323 | if not addrs: | |
324 | continue | |
325 | ||
22b49c28 JF |
326 | if not self.syntax_check_addr_allowed_on(ifaceobj, |
327 | syntax_check=False): | |
0582f185 | 328 | return (False, newaddrs, newaddr_attrs) |
15ef32ea RP |
329 | # If user address is not in CIDR notation, convert them to CIDR |
330 | for addr_index in range(0, len(addrs)): | |
331 | addr = addrs[addr_index] | |
8de397ef | 332 | newaddr = addr |
15ef32ea RP |
333 | if '/' in addr: |
334 | newaddrs.append(addr) | |
42ae7838 | 335 | else: |
8de397ef MW |
336 | netmask = ifaceobj.get_attr_value_n('netmask', addr_index) |
337 | if netmask: | |
338 | prefixlen = IPNetwork('%s' %addr + | |
339 | '/%s' %netmask).prefixlen | |
340 | newaddr = addr + '/%s' %prefixlen | |
341 | else: | |
342 | # we are here because there is no slash (/xx) and no netmask | |
343 | # just let IPNetwork handle the ipv4 or ipv6 address mask | |
344 | prefixlen = IPNetwork(addr).prefixlen | |
345 | newaddr = addr + '/%s' %prefixlen | |
346 | newaddrs.append(newaddr) | |
15ef32ea | 347 | |
0582f185 RP |
348 | attrs = {} |
349 | for a in ['broadcast', 'pointopoint', 'scope', | |
350 | 'preferred-lifetime']: | |
351 | aval = ifaceobj.get_attr_value_n(a, addr_index) | |
352 | if aval: | |
72c964c2 | 353 | attrs[a] = aval |
0582f185 RP |
354 | |
355 | if attrs: | |
356 | newaddr_attrs[newaddr]= attrs | |
357 | return (True, newaddrs, newaddr_attrs) | |
358 | ||
77021aa1 RP |
359 | def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs): |
360 | for addr_index in range(0, len(newaddrs)): | |
361 | try: | |
362 | if newaddr_attrs: | |
363 | self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index], | |
364 | newaddr_attrs.get(newaddrs[addr_index], | |
365 | {}).get('broadcast'), | |
366 | newaddr_attrs.get(newaddrs[addr_index], | |
367 | {}).get('pointopoint'), | |
368 | newaddr_attrs.get(newaddrs[addr_index], | |
369 | {}).get('scope'), | |
370 | newaddr_attrs.get(newaddrs[addr_index], | |
371 | {}).get('preferred-lifetime')) | |
372 | else: | |
373 | self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index]) | |
374 | except Exception, e: | |
bf3eda91 | 375 | self.log_error(str(e), ifaceobj) |
77021aa1 RP |
376 | |
377 | def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None, | |
378 | force_reapply=False): | |
0582f185 | 379 | squash_addr_config = (True if \ |
d486dd0d JF |
380 | ifupdownconfig.config.get('addr_config_squash', \ |
381 | '0') == '1' else False) | |
0582f185 RP |
382 | |
383 | if (squash_addr_config and | |
384 | not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)): | |
385 | return | |
386 | ||
387 | purge_addresses = ifaceobj.get_attr_value_first('address-purge') | |
388 | if not purge_addresses: | |
389 | purge_addresses = 'yes' | |
390 | ||
391 | if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS: | |
392 | ifaceobjlist = ifaceobj_getfunc(ifaceobj.name) | |
393 | else: | |
394 | ifaceobjlist = [ifaceobj] | |
395 | ||
d486dd0d JF |
396 | module_name = self.__class__.__name__ |
397 | ifname = ifaceobj.name | |
398 | ||
0582f185 | 399 | (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist) |
d486dd0d JF |
400 | newaddrs = utils.get_ip_objs(module_name, ifname, newaddrs) |
401 | ||
0582f185 RP |
402 | if not addr_supported: |
403 | return | |
404 | if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)): | |
405 | # if youngest sibling and squash addr is not set | |
406 | # print a warning that addresses will not be purged | |
407 | if (ifaceobj.flags & iface.YOUNGEST_SIBLING): | |
408 | self.logger.warn('%s: interface has multiple ' %ifaceobj.name + | |
409 | 'iface stanzas, skip purging existing addresses') | |
410 | purge_addresses = 'no' | |
411 | ||
fc5e1735 | 412 | if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes': |
0582f185 RP |
413 | # if perfmode is not set and purge addresses is not set to 'no' |
414 | # lets purge addresses not in the config | |
d486dd0d | 415 | runningaddrs = self.ipcmd.get_running_addrs(ifaceobj, details=False) |
0582f185 | 416 | |
a794fb31 BR |
417 | # if anycast address is configured on 'lo' and is in running config |
418 | # add it to newaddrs so that ifreload doesn't wipe it out | |
82908a2d | 419 | anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist)) |
0582f185 | 420 | |
a794fb31 BR |
421 | if runningaddrs and anycast_addr and anycast_addr in runningaddrs: |
422 | newaddrs.append(anycast_addr) | |
d486dd0d JF |
423 | |
424 | user_ip4, user_ip6, newaddrs = self.order_user_configured_addrs(newaddrs) | |
425 | ||
426 | if newaddrs == runningaddrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, runningaddrs): | |
77021aa1 RP |
427 | if force_reapply: |
428 | self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs) | |
15ef32ea RP |
429 | return |
430 | try: | |
431 | # if primary address is not same, there is no need to keep any. | |
432 | # reset all addresses | |
d486dd0d JF |
433 | if newaddrs and runningaddrs and newaddrs[0] != runningaddrs[0]: |
434 | skip_addrs = [] | |
15ef32ea | 435 | else: |
d486dd0d JF |
436 | skip_addrs = newaddrs or [] |
437 | for addr in runningaddrs or []: | |
438 | if addr in skip_addrs: | |
439 | continue | |
440 | self.ipcmd.addr_del(ifaceobj.name, addr) | |
15ef32ea RP |
441 | except Exception, e: |
442 | self.log_warn(str(e)) | |
443 | if not newaddrs: | |
444 | return | |
77021aa1 | 445 | self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs) |
15ef32ea | 446 | |
d486dd0d JF |
447 | def compare_running_ips_and_user_config(self, user_ip4, user_ip6, running_addrs): |
448 | """ | |
449 | We need to compare the user config ips and the running ips. | |
450 | ip4 ordering matters (primary etc) but ip6 order doesn't matter | |
451 | ||
452 | this function replaces the strict comparison previously in place | |
453 | if newaddrs == running_addrs ? | |
454 | ||
455 | We will compare if the ip4 ordering is correct, then check if all | |
456 | ip6 are present in the list (without checking the ordering) | |
457 | """ | |
458 | if (user_ip4 or user_ip6) and not running_addrs: | |
459 | return False | |
460 | elif running_addrs and not user_ip4 and not user_ip6: | |
461 | return False | |
462 | elif not running_addrs and not user_ip4 and not user_ip6: | |
463 | return True | |
464 | ||
465 | len_ip4 = len(user_ip4) | |
466 | len_running_addrs = len(running_addrs) | |
467 | ||
468 | if len_ip4 > len_running_addrs: | |
469 | return False | |
470 | ||
471 | i = 0 | |
472 | while i < len_ip4: | |
473 | if user_ip4[i] != running_addrs[i]: | |
474 | return False | |
475 | i += 1 | |
476 | ||
477 | if len_ip4 > 0: | |
478 | running_ip6 = running_addrs[len_ip4:] | |
479 | else: | |
480 | running_ip6 = running_addrs | |
481 | ||
482 | i = 0 | |
483 | len_ip6 = len(user_ip6) | |
484 | ||
485 | for ip6 in running_ip6: | |
486 | if ip6 not in user_ip6: | |
487 | return False | |
488 | i += 1 | |
489 | ||
490 | return i == len_ip6 | |
491 | ||
492 | def order_user_configured_addrs(self, user_config_addrs): | |
493 | ip4 = [] | |
494 | ip6 = [] | |
495 | ||
496 | for a in user_config_addrs: | |
497 | if isinstance(a, _BaseV6): | |
498 | ip6.append(str(a)) | |
499 | else: | |
500 | ip4.append(str(a)) | |
501 | ||
502 | return ip4, ip6, ip4 + ip6 | |
503 | ||
504 | def _delete_gateway(self, ifaceobj, gateways, vrf, metric): | |
505 | for del_gw in gateways: | |
0232d1bb N |
506 | try: |
507 | self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric) | |
5b666749 JF |
508 | except Exception as e: |
509 | self.logger.debug('%s: %s' % (ifaceobj.name, str(e))) | |
d486dd0d JF |
510 | |
511 | def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]): | |
512 | vrf = ifaceobj.get_attr_value_first('vrf') | |
513 | metric = ifaceobj.get_attr_value_first('metric') | |
514 | self._delete_gateway(ifaceobj, list(set(prev_gw) - set(gateways)), | |
515 | vrf, metric) | |
4b875c17 | 516 | for add_gw in gateways: |
0232d1bb | 517 | try: |
9d505185 | 518 | self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink) |
5b666749 JF |
519 | except Exception as e: |
520 | self.log_error('%s: %s' % (ifaceobj.name, str(e))) | |
0232d1bb N |
521 | |
522 | def _get_prev_gateway(self, ifaceobj, gateways): | |
523 | ipv = [] | |
524 | saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) | |
525 | if not saved_ifaceobjs: | |
526 | return ipv | |
527 | prev_gateways = saved_ifaceobjs[0].get_attr_value('gateway') | |
528 | if not prev_gateways: | |
529 | return ipv | |
530 | return prev_gateways | |
531 | ||
8d1e346f RP |
532 | def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False): |
533 | retval = True | |
534 | if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): | |
535 | if syntaxcheck: | |
536 | self.logger.warn('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name) | |
537 | retval = False | |
538 | else: | |
9f30b2cc | 539 | self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name) |
8d1e346f RP |
540 | elif ifaceobj_getfunc: |
541 | if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and | |
542 | ifaceobj.upperifaces): | |
9f30b2cc RP |
543 | masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0]) |
544 | if masterobj: | |
545 | master_mtu = masterobj[0].get_attr_value_first('mtu') | |
546 | if master_mtu and master_mtu != mtu: | |
8d1e346f RP |
547 | if syntaxcheck: |
548 | 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)) | |
549 | retval = False | |
550 | else: | |
551 | 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)) | |
552 | elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and | |
553 | ifaceobj.lowerifaces): | |
554 | lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0]) | |
555 | if lowerobj: | |
556 | if syntaxcheck: | |
557 | lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu') | |
558 | else: | |
d486dd0d | 559 | lowerdev_mtu = self.ipcmd.link_get_mtu_sysfs(lowerobj[0].name) |
8d1e346f RP |
560 | if lowerdev_mtu and int(mtu) > int(lowerdev_mtu): |
561 | self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' | |
562 | %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu)) | |
563 | retval = False | |
564 | elif (not lowerobj[0].link_kind and | |
565 | not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and | |
d486dd0d JF |
566 | not lowerdev_mtu and self.default_mtu and |
567 | (int(mtu) > int(self.default_mtu))): | |
8d1e346f RP |
568 | # only check default mtu on lower device which is a physical interface |
569 | self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' | |
570 | %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu)) | |
571 | retval = False | |
9f30b2cc RP |
572 | if self.max_mtu and mtu > self.max_mtu: |
573 | self.logger.warn('%s: specified mtu %s is greater than max mtu %s' | |
574 | %(ifaceobj.name, mtu, self.max_mtu)) | |
8d1e346f RP |
575 | retval = False |
576 | return retval | |
577 | ||
578 | def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc): | |
579 | if (not ifaceobj.upperifaces or | |
580 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or | |
581 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or | |
582 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)): | |
583 | return | |
584 | for u in ifaceobj.upperifaces: | |
585 | upperobjs = ifaceobj_getfunc(u) | |
586 | if (not upperobjs or | |
587 | not (upperobjs[0].link_kind & ifaceLinkKind.VLAN)): | |
588 | continue | |
589 | # only adjust mtu for vlan devices on ifaceobj | |
590 | umtu = upperobjs[0].get_attr_value_first('mtu') | |
591 | if not umtu: | |
592 | running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name) | |
593 | if not running_mtu or (running_mtu != mtu): | |
594 | self.ipcmd.link_set(u, 'mtu', mtu) | |
595 | ||
d486dd0d | 596 | def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc, mtu): |
8d1e346f RP |
597 | if mtu: |
598 | if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc): | |
599 | return | |
d486dd0d JF |
600 | cached_running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) |
601 | running_mtu = self.ipcmd.link_get_mtu_sysfs(ifaceobj.name) | |
8d1e346f | 602 | if not running_mtu or (running_mtu and running_mtu != mtu): |
d486dd0d JF |
603 | force = cached_running_mtu != running_mtu |
604 | self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu, force=force) | |
8d1e346f RP |
605 | if (not ifupdownflags.flags.ALL and |
606 | not ifaceobj.link_kind and | |
d486dd0d | 607 | ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'): |
8d1e346f RP |
608 | # This is additional cost to us, so do it only when |
609 | # ifupdown2 is called on a particular interface and | |
610 | # it is a physical interface | |
611 | self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc) | |
9f30b2cc RP |
612 | return |
613 | ||
614 | if ifaceobj.link_kind: | |
19ee2b11 | 615 | # bonds, vxlan and custom devices (like dummy) need an explicit set of mtu. |
9f30b2cc RP |
616 | # bridges don't need mtu set |
617 | if (ifaceobj.link_kind & ifaceLinkKind.BOND or | |
19ee2b11 JF |
618 | ifaceobj.link_kind & ifaceLinkKind.VXLAN or |
619 | ifaceobj.link_kind & ifaceLinkKind.OTHER | |
620 | ): | |
9f30b2cc RP |
621 | running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) |
622 | if (self.default_mtu and running_mtu != self.default_mtu): | |
623 | self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) | |
624 | return | |
d486dd0d | 625 | if (ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0' |
9f30b2cc RP |
626 | and ifaceobj.lowerifaces): |
627 | # set vlan interface mtu to lower device mtu | |
628 | if (ifaceobj.link_kind & ifaceLinkKind.VLAN): | |
d486dd0d JF |
629 | lower_iface = ifaceobj.lowerifaces[0] |
630 | if lower_iface not in self.lower_iface_mtu_checked_list: | |
631 | lower_iface_mtu = self.ipcmd.link_get_mtu_sysfs(lower_iface) | |
632 | self.ipcmd.cache_update([lower_iface, 'mtu'], lower_iface_mtu) | |
633 | self.lower_iface_mtu_checked_list.append(lower_iface) | |
634 | else: | |
635 | lower_iface_mtu = self.ipcmd.link_get_mtu(lower_iface) | |
636 | ||
637 | if lower_iface_mtu != self.ipcmd.link_get_mtu_sysfs(ifaceobj.name): | |
9f30b2cc RP |
638 | self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu) |
639 | ||
640 | elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and | |
641 | not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and | |
642 | self.default_mtu): | |
643 | # logical devices like bridges and vlan devices rely on mtu | |
644 | # from their lower devices. ie mtu travels from | |
645 | # lower devices to upper devices. For bonds mtu travels from | |
646 | # upper to lower devices. running mtu depends on upper and | |
647 | # lower device mtu. With all this implicit mtu | |
648 | # config by the kernel in play, we try to be cautious here | |
649 | # on which devices we want to reset mtu to default. | |
650 | # essentially only physical interfaces which are not bond slaves | |
651 | running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) | |
652 | if running_mtu != self.default_mtu: | |
653 | self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) | |
654 | ||
d486dd0d JF |
655 | def _set_bridge_forwarding(self, ifaceobj): |
656 | """ set ip forwarding to 0 if bridge interface does not have a | |
657 | ip nor svi """ | |
2185a108 | 658 | ifname = ifaceobj.name |
d486dd0d | 659 | if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address'): |
2185a108 JF |
660 | if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '1': |
661 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0) | |
662 | if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '1': | |
663 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 0) | |
d486dd0d | 664 | else: |
2185a108 JF |
665 | if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv4") == '0': |
666 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 1) | |
667 | if self.sysctl_get_forwarding_value_from_proc(ifname, "ipv6") == '0': | |
668 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 1) | |
669 | ||
670 | def sysctl_get_forwarding_value_from_proc(self, ifname, family): | |
671 | return self.read_file_oneline("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname)) | |
672 | ||
673 | def sysctl_write_forwarding_value_to_proc(self, ifname, family, value): | |
674 | self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value) | |
d486dd0d JF |
675 | |
676 | def _sysctl_config(self, ifaceobj): | |
677 | setting_default_value = False | |
678 | mpls_enable = ifaceobj.get_attr_value_first('mpls-enable'); | |
679 | if not mpls_enable: | |
680 | setting_default_value = True | |
681 | mpls_enable = self.get_mod_subattr('mpls-enable', 'default') | |
682 | mpls_enable = utils.boolean_support_binary(mpls_enable) | |
683 | # File read has been used for better performance | |
684 | # instead of using sysctl command | |
685 | if ifupdownflags.flags.PERFMODE: | |
686 | running_mpls_enable = '0' | |
687 | else: | |
688 | running_mpls_enable = self.read_file_oneline( | |
689 | '/proc/sys/net/mpls/conf/%s/input' | |
690 | % ifaceobj.name | |
691 | ) | |
692 | ||
693 | if mpls_enable != running_mpls_enable: | |
694 | try: | |
695 | self.sysctl_set('net.mpls.conf.%s.input' | |
696 | %('/'.join(ifaceobj.name.split("."))), | |
697 | mpls_enable) | |
698 | except Exception as e: | |
699 | if not setting_default_value: | |
700 | ifaceobj.status = ifaceStatus.ERROR | |
701 | self.logger.error('%s: %s' %(ifaceobj.name, str(e))) | |
702 | ||
703 | if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): | |
704 | self._set_bridge_forwarding(ifaceobj) | |
705 | return | |
706 | if not self.syntax_check_sysctls(ifaceobj): | |
707 | return | |
708 | ipforward = ifaceobj.get_attr_value_first('ip-forward') | |
709 | ip6forward = ifaceobj.get_attr_value_first('ip6-forward') | |
710 | if ifupdownflags.flags.PERFMODE: | |
711 | if ipforward: | |
712 | self.sysctl_set('net.ipv4.conf.%s.forwarding' | |
713 | %('/'.join(ifaceobj.name.split("."))), | |
714 | utils.boolean_support_binary(ipforward)) | |
715 | if ip6forward: | |
716 | self.sysctl_set('net.ipv6.conf.%s.forwarding' | |
717 | %('/'.join(ifaceobj.name.split("."))), | |
718 | utils.boolean_support_binary(ip6forward)) | |
719 | return | |
720 | bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT | |
721 | if bridge_port: | |
722 | if ipforward: | |
723 | self.log_error('%s: \'ip-forward\' is not supported for ' | |
724 | 'bridge port' %ifaceobj.name) | |
725 | if ip6forward: | |
726 | self.log_error('%s: \'ip6-forward\' is not supported for ' | |
727 | 'bridge port' %ifaceobj.name) | |
728 | return | |
42ef1cce | 729 | |
52712b1a AD |
730 | setting_default_value = False |
731 | if not ipforward: | |
732 | setting_default_value = True | |
733 | ipforward = self.ipforward | |
734 | ||
42ef1cce AD |
735 | if ipforward: |
736 | ipforward = utils.boolean_support_binary(ipforward) | |
737 | # File read has been used for better performance | |
738 | # instead of using sysctl command | |
739 | running_ipforward = self.read_file_oneline( | |
740 | '/proc/sys/net/ipv4/conf/%s/forwarding' | |
741 | %ifaceobj.name) | |
742 | ||
743 | if ipforward != running_ipforward: | |
744 | try: | |
745 | ||
746 | self.sysctl_set('net.ipv4.conf.%s.forwarding' | |
747 | %('/'.join(ifaceobj.name.split("."))), | |
748 | ipforward) | |
749 | except Exception as e: | |
52712b1a AD |
750 | if not setting_default_value: |
751 | ifaceobj.status = ifaceStatus.ERROR | |
752 | self.logger.error('%s: %s' %(ifaceobj.name, str(e))) | |
753 | ||
754 | ||
755 | setting_default_value = False | |
756 | if not ip6forward: | |
757 | setting_default_value = True | |
758 | ip6forward = self.ip6forward | |
d486dd0d | 759 | |
42ef1cce AD |
760 | if ip6forward: |
761 | ip6forward = utils.boolean_support_binary(ip6forward) | |
762 | # File read has been used for better performance | |
763 | # instead of using sysctl command | |
764 | running_ip6forward = self.read_file_oneline( | |
765 | '/proc/sys/net/ipv6/conf/%s/forwarding' | |
766 | %ifaceobj.name) | |
767 | if ip6forward != running_ip6forward: | |
768 | try: | |
769 | self.sysctl_set('net.ipv6.conf.%s.forwarding' | |
770 | %('/'.join(ifaceobj.name.split("."))), | |
771 | ip6forward) | |
772 | except Exception as e: | |
773 | # There is chance of ipv6 being removed because of, | |
774 | # for example, setting mtu < 1280 | |
775 | # In such cases, log error only if user has configured | |
776 | # ip6-forward | |
52712b1a AD |
777 | if not setting_default_value: |
778 | ifaceobj.status = ifaceStatus.ERROR | |
779 | self.logger.error('%s: %s' %(ifaceobj.name, str(e))) | |
d486dd0d JF |
780 | |
781 | def process_mtu(self, ifaceobj, ifaceobj_getfunc): | |
782 | mtu = ifaceobj.get_attr_value_first('mtu') | |
783 | ||
784 | if not mtu: | |
785 | default_iface_mtu = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu') | |
786 | ||
787 | if default_iface_mtu: | |
788 | try: | |
789 | mtu = default_iface_mtu | |
790 | int(default_iface_mtu) | |
791 | except Exception as e: | |
792 | self.logger.warning('%s: MTU value from policy file: %s' % (ifaceobj.name, str(e))) | |
793 | return | |
794 | ||
795 | self._process_mtu_config(ifaceobj, ifaceobj_getfunc, mtu) | |
796 | ||
3fc54eef | 797 | def up_ipv6_addrgen(self, ifaceobj): |
007cae35 JF |
798 | user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') |
799 | ||
b306a8b6 JF |
800 | if not user_configured_ipv6_addrgen and ifupdownflags.flags.PERFMODE: |
801 | # no need to go further during perfmode (boot) | |
802 | return | |
803 | ||
cd890b06 JF |
804 | if not user_configured_ipv6_addrgen and ifaceobj.addr_method == 'dhcp': |
805 | return | |
806 | ||
007cae35 | 807 | if not user_configured_ipv6_addrgen: |
17da0561 JF |
808 | # if user didn't configure ipv6-addrgen, should we reset to default? |
809 | user_configured_ipv6_addrgen = self.get_attr_default_value('ipv6-addrgen') | |
007cae35 JF |
810 | |
811 | ipv6_addrgen_nl = { | |
812 | 'on': 0, | |
813 | 'yes': 0, | |
814 | '0': 0, | |
815 | 'off': 1, | |
816 | 'no': 1, | |
817 | '1': 1 | |
818 | }.get(user_configured_ipv6_addrgen.lower(), None) | |
819 | ||
820 | if ipv6_addrgen_nl is not None: | |
821 | self.ipcmd.ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True) | |
822 | # link_create=False will flush the addr cache of that intf | |
823 | else: | |
824 | self.logger.warning('%s: invalid value "%s" for attribute ipv6-addrgen' % (ifaceobj.name, user_configured_ipv6_addrgen)) | |
3fc54eef | 825 | |
0582f185 | 826 | def _up(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea RP |
827 | if not self.ipcmd.link_exists(ifaceobj.name): |
828 | return | |
2ff98bb9 | 829 | |
d486dd0d JF |
830 | if not self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc): |
831 | return | |
832 | ||
2ff98bb9 JF |
833 | alias = ifaceobj.get_attr_value_first('alias') |
834 | current_alias = self.ipcmd.link_get_alias(ifaceobj.name) | |
835 | if alias and alias != current_alias: | |
836 | self.ipcmd.link_set_alias(ifaceobj.name, alias) | |
837 | elif not alias and current_alias: | |
838 | self.ipcmd.link_set_alias(ifaceobj.name, '') | |
839 | ||
d486dd0d JF |
840 | self._sysctl_config(ifaceobj) |
841 | ||
68d9fee0 | 842 | addr_method = ifaceobj.addr_method |
77021aa1 | 843 | force_reapply = False |
15ef32ea RP |
844 | try: |
845 | # release any stale dhcp addresses if present | |
77054f7f | 846 | if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and |
15ef32ea RP |
847 | not (ifaceobj.flags & iface.HAS_SIBLINGS)): |
848 | # if not running in perf mode and ifaceobj does not have | |
849 | # any sibling iface objects, kill any stale dhclient | |
850 | # processes | |
75afe2a7 | 851 | dhclientcmd = dhclient() |
7c1135ea | 852 | if dhclientcmd.is_running(ifaceobj.name): |
15ef32ea RP |
853 | # release any dhcp leases |
854 | dhclientcmd.release(ifaceobj.name) | |
77021aa1 | 855 | force_reapply = True |
7c1135ea | 856 | elif dhclientcmd.is_running6(ifaceobj.name): |
15ef32ea | 857 | dhclientcmd.release6(ifaceobj.name) |
77021aa1 | 858 | force_reapply = True |
15ef32ea RP |
859 | except: |
860 | pass | |
8e113d63 | 861 | |
15ef32ea | 862 | self.ipcmd.batch_start() |
3fc54eef JF |
863 | self.up_ipv6_addrgen(ifaceobj) |
864 | ||
77054f7f | 865 | if addr_method not in ["dhcp", "ppp"]: |
77021aa1 RP |
866 | self._inet_address_config(ifaceobj, ifaceobj_getfunc, |
867 | force_reapply) | |
2e2dcdaf JF |
868 | else: |
869 | # remove old addresses added by ifupdown2 | |
870 | # (if intf was moved from static config to dhcp) | |
871 | for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: | |
872 | for addr in old_ifaceobj.get_attr_value("address") or []: | |
873 | self.ipcmd.addr_del(ifaceobj.name, addr) | |
d486dd0d JF |
874 | |
875 | self.process_mtu(ifaceobj, ifaceobj_getfunc) | |
13e22530 | 876 | |
093ffa00 JF |
877 | try: |
878 | self.ipcmd.batch_commit() | |
879 | except Exception as e: | |
03092649 | 880 | self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False) |
264dcaa0 | 881 | |
d1477c4b JF |
882 | self.up_hwaddress(ifaceobj) |
883 | ||
884 | gateways = ifaceobj.get_attr_value('gateway') | |
885 | if not gateways: | |
886 | gateways = [] | |
887 | prev_gw = self._get_prev_gateway(ifaceobj, gateways) | |
888 | self._add_delete_gateway(ifaceobj, gateways, prev_gw) | |
889 | ||
890 | def up_hwaddress(self, ifaceobj): | |
68d9fee0 | 891 | try: |
707aeb73 | 892 | hwaddress = self._get_hwaddress(ifaceobj) |
d1477c4b | 893 | |
707aeb73 | 894 | if hwaddress: |
d1477c4b | 895 | if not ifupdownflags.flags.PERFMODE: # system is clean |
707aeb73 | 896 | running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) |
d1477c4b JF |
897 | else: |
898 | running_hwaddress = None | |
899 | ||
900 | if self.ipcmd.mac_str_to_int(running_hwaddress) != self.ipcmd.mac_str_to_int(hwaddress): | |
707aeb73 JF |
901 | slave_down = False |
902 | netlink.link_set_updown(ifaceobj.name, "down") | |
903 | if ifaceobj.link_kind & ifaceLinkKind.BOND: | |
904 | # if bond, down all the slaves | |
905 | if ifaceobj.lowerifaces: | |
906 | for l in ifaceobj.lowerifaces: | |
907 | netlink.link_set_updown(l, "down") | |
908 | slave_down = True | |
909 | try: | |
d1477c4b | 910 | self.ipcmd.link_set_hwaddress(ifaceobj.name, hwaddress, force=True) |
707aeb73 JF |
911 | finally: |
912 | netlink.link_set_updown(ifaceobj.name, "up") | |
913 | if slave_down: | |
914 | for l in ifaceobj.lowerifaces: | |
915 | netlink.link_set_updown(l, "up") | |
916 | ||
68d9fee0 RP |
917 | # Handle special things on a bridge |
918 | self._process_bridge(ifaceobj, True) | |
919 | except Exception, e: | |
d1477c4b | 920 | self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) |
15ef32ea | 921 | |
0582f185 | 922 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea RP |
923 | try: |
924 | if not self.ipcmd.link_exists(ifaceobj.name): | |
925 | return | |
68d9fee0 | 926 | addr_method = ifaceobj.addr_method |
77054f7f | 927 | if addr_method not in ["dhcp", "ppp"]: |
aa052170 N |
928 | if ifaceobj.get_attr_value_first('address-purge')=='no': |
929 | addrlist = ifaceobj.get_attr_value('address') | |
930 | for addr in addrlist: | |
931 | self.ipcmd.addr_del(ifaceobj.name, addr) | |
932 | #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0]) | |
d486dd0d JF |
933 | elif not ifaceobj.link_kind: |
934 | # for logical interfaces we don't need to remove the ip addresses | |
935 | # kernel will do it for us on 'ip link del' | |
aa052170 | 936 | self.ipcmd.del_addr_all(ifaceobj.name) |
d486dd0d JF |
937 | gateways = ifaceobj.get_attr_value('gateway') |
938 | if gateways: | |
939 | self._delete_gateway(ifaceobj, gateways, | |
940 | ifaceobj.get_attr_value_first('vrf'), | |
941 | ifaceobj.get_attr_value_first('metric')) | |
84f33af6 | 942 | mtu = ifaceobj.get_attr_value_first('mtu') |
00c12960 RP |
943 | if (not ifaceobj.link_kind and mtu and |
944 | self.default_mtu and (mtu != self.default_mtu)): | |
84f33af6 | 945 | self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) |
15ef32ea RP |
946 | alias = ifaceobj.get_attr_value_first('alias') |
947 | if alias: | |
d486dd0d | 948 | self.write_file('/sys/class/net/%s/ifalias' % ifaceobj.name, '\n') |
75afe2a7 RP |
949 | # XXX hwaddress reset cannot happen because we dont know last |
950 | # address. | |
951 | ||
952 | # Handle special things on a bridge | |
953 | self._process_bridge(ifaceobj, False) | |
15ef32ea | 954 | except Exception, e: |
bcf11b14 RP |
955 | self.logger.debug('%s : %s' %(ifaceobj.name, str(e))) |
956 | pass | |
15ef32ea RP |
957 | |
958 | def _get_iface_addresses(self, ifaceobj): | |
959 | addrlist = ifaceobj.get_attr_value('address') | |
960 | outaddrlist = [] | |
961 | ||
962 | if not addrlist: return None | |
963 | for addrindex in range(0, len(addrlist)): | |
964 | addr = addrlist[addrindex] | |
965 | netmask = ifaceobj.get_attr_value_n('netmask', addrindex) | |
966 | if netmask: | |
967 | prefixlen = IPNetwork('%s' %addr + | |
968 | '/%s' %netmask).prefixlen | |
969 | addr = addr + '/%s' %prefixlen | |
970 | outaddrlist.append(addr) | |
971 | return outaddrlist | |
972 | ||
8e113d63 RP |
973 | def _get_bridge_fdbs(self, bridgename, vlan): |
974 | fdbs = self._bridge_fdb_query_cache.get(bridgename) | |
975 | if not fdbs: | |
976 | fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename) | |
977 | if not fdbs: | |
978 | return | |
979 | self._bridge_fdb_query_cache[bridgename] = fdbs | |
980 | return fdbs.get(vlan) | |
981 | ||
982 | def _check_addresses_in_bridge(self, ifaceobj, hwaddress): | |
983 | """ If the device is a bridge, make sure the addresses | |
984 | are in the bridge """ | |
d486dd0d JF |
985 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
986 | bridgename = ifaceobj.lowerifaces[0] | |
987 | vlan = self._get_vlan_id(ifaceobj) | |
8e113d63 | 988 | if self.ipcmd.bridge_is_vlan_aware(bridgename): |
d1477c4b JF |
989 | fdb_addrs = [self.ipcmd.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))] |
990 | if not fdb_addrs: | |
991 | return False | |
992 | hwaddress_int = self.ipcmd.mac_str_to_int(hwaddress) | |
993 | if hwaddress_int not in fdb_addrs: | |
994 | return False | |
8e113d63 RP |
995 | return True |
996 | ||
d486dd0d JF |
997 | def _query_sysctl(self, ifaceobj, ifaceobjcurr): |
998 | bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT | |
999 | ipforward = ifaceobj.get_attr_value_first('ip-forward') | |
1000 | if ipforward: | |
1001 | if bridge_port: | |
1002 | ifaceobjcurr.status = ifaceStatus.ERROR | |
1003 | ifaceobjcurr.status_str = ('\'ip-forward\' not supported ' + | |
1004 | 'for bridge port') | |
1005 | ifaceobjcurr.update_config_with_status('ip-forward', 1, None) | |
1006 | else: | |
1007 | running_ipforward = self.read_file_oneline( | |
1008 | '/proc/sys/net/ipv4/conf/%s/forwarding' | |
1009 | %ifaceobj.name) | |
307e814c JF |
1010 | running_ipforward = utils.get_boolean_from_string(running_ipforward) |
1011 | config_ipforward = utils.get_boolean_from_string(ipforward) | |
1012 | ifaceobjcurr.update_config_with_status( | |
1013 | 'ip-forward', | |
1014 | 'on' if running_ipforward else 'off', | |
1015 | running_ipforward != config_ipforward | |
1016 | ) | |
d486dd0d JF |
1017 | |
1018 | ip6forward = ifaceobj.get_attr_value_first('ip6-forward') | |
1019 | if ip6forward: | |
1020 | if bridge_port: | |
1021 | ifaceobjcurr.status = ifaceStatus.ERROR | |
1022 | ifaceobjcurr.status_str = ('\'ip6-forward\' not supported ' + | |
1023 | 'for bridge port') | |
1024 | ifaceobjcurr.update_config_with_status('ip6-forward', 1, None) | |
1025 | else: | |
1026 | running_ip6forward = self.read_file_oneline( | |
1027 | '/proc/sys/net/ipv6/conf/%s/forwarding' | |
1028 | %ifaceobj.name) | |
307e814c JF |
1029 | running_ip6forward = utils.get_boolean_from_string(running_ip6forward) |
1030 | config_ip6forward = utils.get_boolean_from_string(ip6forward) | |
1031 | ifaceobjcurr.update_config_with_status( | |
1032 | 'ip6-forward', | |
1033 | 'on' if running_ip6forward else 'off', | |
1034 | running_ip6forward != config_ip6forward | |
1035 | ) | |
d486dd0d JF |
1036 | mpls_enable = ifaceobj.get_attr_value_first('mpls-enable'); |
1037 | if mpls_enable: | |
1038 | running_mpls_enable = self.read_file_oneline( | |
1039 | '/proc/sys/net/mpls/conf/%s/input' | |
1040 | %ifaceobj.name) | |
1041 | running_mpls_enable = utils.get_yesno_from_onezero( | |
1042 | running_mpls_enable) | |
1043 | ifaceobjcurr.update_config_with_status('mpls-enable', | |
1044 | running_mpls_enable, | |
1045 | mpls_enable != running_mpls_enable) | |
1046 | return | |
1047 | ||
007cae35 JF |
1048 | def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr): |
1049 | ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') | |
1050 | ||
1051 | if not ipv6_addrgen: | |
1052 | return | |
1053 | ||
1054 | if ipv6_addrgen in utils._string_values: | |
1055 | ifaceobjcurr.update_config_with_status( | |
1056 | 'ipv6-addrgen', | |
1057 | ipv6_addrgen, | |
1058 | utils.get_boolean_from_string(ipv6_addrgen) == self.ipcmd.get_ipv6_addrgen_mode(ifaceobj.name) | |
1059 | ) | |
1060 | else: | |
1061 | ifaceobjcurr.update_config_with_status('ipv6-addrgen', ipv6_addrgen, 1) | |
1062 | ||
0582f185 | 1063 | def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): |
15ef32ea RP |
1064 | runningaddrsdict = None |
1065 | if not self.ipcmd.link_exists(ifaceobj.name): | |
1066 | self.logger.debug('iface %s not found' %ifaceobj.name) | |
1067 | return | |
007cae35 JF |
1068 | |
1069 | self.query_check_ipv6_addrgen(ifaceobj, ifaceobjcurr) | |
1070 | ||
16d854b4 | 1071 | addr_method = ifaceobj.addr_method |
15ef32ea RP |
1072 | self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, |
1073 | 'mtu', self.ipcmd.link_get_mtu) | |
428206bf | 1074 | hwaddress = self._get_hwaddress(ifaceobj) |
8e113d63 | 1075 | if hwaddress: |
2876ca35 | 1076 | rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) |
d1477c4b | 1077 | if not rhwaddress or self.ipcmd.mac_str_to_int(rhwaddress) != self.ipcmd.mac_str_to_int(hwaddress): |
8e113d63 RP |
1078 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, |
1079 | 1) | |
1080 | elif not self._check_addresses_in_bridge(ifaceobj, hwaddress): | |
1081 | # XXX: hw address is not in bridge | |
1082 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
1083 | 1) | |
1084 | ifaceobjcurr.status_str = 'bridge fdb error' | |
1085 | else: | |
1086 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
1087 | 0) | |
15ef32ea RP |
1088 | self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, |
1089 | 'alias', self.ipcmd.link_get_alias) | |
d486dd0d | 1090 | self._query_sysctl(ifaceobj, ifaceobjcurr) |
15ef32ea | 1091 | # compare addresses |
77054f7f | 1092 | if addr_method in ["dhcp", "ppp"]: |
16d854b4 | 1093 | return |
82908a2d JF |
1094 | addrs = utils.get_normalized_ip_addr(ifaceobj.name, |
1095 | self._get_iface_addresses(ifaceobj)) | |
d486dd0d | 1096 | runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobj) |
a794fb31 BR |
1097 | # if anycast address is configured on 'lo' and is in running config |
1098 | # add it to addrs so that query_check doesn't fail | |
82908a2d | 1099 | anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')) |
a794fb31 BR |
1100 | if anycast_addr: |
1101 | anycast_addr = anycast_addr+'/32' | |
1102 | if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr): | |
1103 | addrs.append(anycast_addr) | |
15ef32ea RP |
1104 | |
1105 | # Set ifaceobjcurr method and family | |
1106 | ifaceobjcurr.addr_method = ifaceobj.addr_method | |
1107 | ifaceobjcurr.addr_family = ifaceobj.addr_family | |
1108 | if not runningaddrsdict and not addrs: | |
1109 | return | |
1110 | runningaddrs = runningaddrsdict.keys() if runningaddrsdict else [] | |
8de397ef MW |
1111 | # Add /32 netmask to configured address without netmask. |
1112 | # This may happen on interfaces where pointopoint is used. | |
1113 | runningaddrs = [ addr if '/' in addr else addr + '/32' for addr in runningaddrs] | |
15ef32ea RP |
1114 | if runningaddrs != addrs: |
1115 | runningaddrsset = set(runningaddrs) if runningaddrs else set([]) | |
1116 | addrsset = set(addrs) if addrs else set([]) | |
1117 | if (ifaceobj.flags & iface.HAS_SIBLINGS): | |
1118 | if not addrsset: | |
1119 | return | |
1120 | # only check for addresses present in running config | |
1121 | addrsdiff = addrsset.difference(runningaddrsset) | |
1122 | for addr in addrs: | |
1123 | if addr in addrsdiff: | |
1124 | ifaceobjcurr.update_config_with_status('address', | |
1125 | addr, 1) | |
1126 | else: | |
1127 | ifaceobjcurr.update_config_with_status('address', | |
1128 | addr, 0) | |
1129 | else: | |
1130 | addrsdiff = addrsset.symmetric_difference(runningaddrsset) | |
1131 | for addr in addrsset.union(runningaddrsset): | |
1132 | if addr in addrsdiff: | |
1133 | ifaceobjcurr.update_config_with_status('address', | |
1134 | addr, 1) | |
1135 | else: | |
1136 | ifaceobjcurr.update_config_with_status('address', | |
1137 | addr, 0) | |
1138 | elif addrs: | |
1139 | [ifaceobjcurr.update_config_with_status('address', | |
1140 | addr, 0) for addr in addrs] | |
1141 | #XXXX Check broadcast address, scope, etc | |
1142 | return | |
1143 | ||
007cae35 JF |
1144 | def query_running_ipv6_addrgen(self, ifaceobjrunning): |
1145 | ipv6_addrgen = self.ipcmd.get_ipv6_addrgen_mode(ifaceobjrunning.name) | |
1146 | ||
1147 | if ipv6_addrgen: | |
1148 | ifaceobjrunning.update_config('ipv6-addrgen', 'off') | |
1149 | ||
0582f185 | 1150 | def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): |
15ef32ea RP |
1151 | if not self.ipcmd.link_exists(ifaceobjrunning.name): |
1152 | self.logger.debug('iface %s not found' %ifaceobjrunning.name) | |
15ef32ea | 1153 | return |
007cae35 JF |
1154 | |
1155 | self.query_running_ipv6_addrgen(ifaceobjrunning) | |
1156 | ||
15ef32ea RP |
1157 | dhclientcmd = dhclient() |
1158 | if (dhclientcmd.is_running(ifaceobjrunning.name) or | |
1159 | dhclientcmd.is_running6(ifaceobjrunning.name)): | |
1160 | # If dhcp is configured on the interface, we skip it | |
84f33af6 | 1161 | return |
15ef32ea RP |
1162 | isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name) |
1163 | if isloopback: | |
1164 | default_addrs = ['127.0.0.1/8', '::1/128'] | |
004d1e65 | 1165 | ifaceobjrunning.addr_family.append('inet') |
15ef32ea RP |
1166 | ifaceobjrunning.addr_method = 'loopback' |
1167 | else: | |
1168 | default_addrs = [] | |
d486dd0d | 1169 | runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobjrunning) |
15ef32ea RP |
1170 | if runningaddrsdict: |
1171 | [ifaceobjrunning.update_config('address', addr) | |
1172 | for addr, addrattrs in runningaddrsdict.items() | |
1173 | if addr not in default_addrs] | |
1174 | mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name) | |
1175 | if (mtu and | |
1176 | (ifaceobjrunning.name == 'lo' and mtu != '16436') or | |
1177 | (ifaceobjrunning.name != 'lo' and | |
1178 | mtu != self.get_mod_subattr('mtu', 'default'))): | |
1179 | ifaceobjrunning.update_config('mtu', mtu) | |
1180 | alias = self.ipcmd.link_get_alias(ifaceobjrunning.name) | |
84f33af6 | 1181 | if alias: |
15ef32ea RP |
1182 | ifaceobjrunning.update_config('alias', alias) |
1183 | ||
d486dd0d JF |
1184 | ipforward = self.read_file_oneline( |
1185 | '/proc/sys/net/ipv4/conf/%s/forwarding' | |
1186 | %ifaceobjrunning.name) | |
1187 | ||
8d1e346f | 1188 | |
15ef32ea RP |
1189 | _run_ops = {'up' : _up, |
1190 | 'down' : _down, | |
1191 | 'query-checkcurr' : _query_check, | |
1192 | 'query-running' : _query_running } | |
1193 | ||
1194 | def get_ops(self): | |
1195 | """ returns list of ops supported by this module """ | |
1196 | return self._run_ops.keys() | |
1197 | ||
1198 | def _init_command_handlers(self): | |
1199 | if not self.ipcmd: | |
d486dd0d | 1200 | self.ipcmd = LinkUtils() |
15ef32ea | 1201 | |
6e16e5ae | 1202 | def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): |
15ef32ea RP |
1203 | """ run address configuration on the interface object passed as argument |
1204 | ||
1205 | Args: | |
1206 | **ifaceobj** (object): iface object | |
1207 | ||
1208 | **operation** (str): any of 'up', 'down', 'query-checkcurr', | |
1209 | 'query-running' | |
1210 | Kwargs: | |
1211 | query_ifaceobj (object): query check ifaceobject. This is only | |
1212 | valid when op is 'query-checkcurr'. It is an object same as | |
1213 | ifaceobj, but contains running attribute values and its config | |
1214 | status. The modules can use it to return queried running state | |
1215 | of interfaces. status is success if the running state is same | |
1216 | as user required state in ifaceobj. error otherwise. | |
1217 | """ | |
8e113d63 RP |
1218 | if ifaceobj.type == ifaceType.BRIDGE_VLAN: |
1219 | return | |
15ef32ea RP |
1220 | op_handler = self._run_ops.get(operation) |
1221 | if not op_handler: | |
1222 | return | |
15ef32ea RP |
1223 | self._init_command_handlers() |
1224 | if operation == 'query-checkcurr': | |
0582f185 RP |
1225 | op_handler(self, ifaceobj, query_ifaceobj, |
1226 | ifaceobj_getfunc=ifaceobj_getfunc) | |
15ef32ea | 1227 | else: |
0582f185 RP |
1228 | op_handler(self, ifaceobj, |
1229 | ifaceobj_getfunc=ifaceobj_getfunc) |