]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/vxlan.py
addons: vxlan: add null list check when mcastgrp map not present
[mirror_ifupdown2.git] / ifupdown2 / addons / vxlan.py
CommitLineData
35681c06 1#!/usr/bin/env python3
d486dd0d
JF
2#
3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
66eb9ce3 7from ipaddress import IPv4Network, IPv4Address, AddressValueError, ip_address
d486dd0d 8try:
0e936c3f 9 import ifupdown2.nlmanager.ipnetwork as ipnetwork
d486dd0d 10 import ifupdown2.ifupdown.policymanager as policymanager
3fb83a7a 11 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
d486dd0d 12
223ba5af
JF
13 from ifupdown2.lib.addon import Addon
14 from ifupdown2.lib.nlcache import NetlinkCacheIfnameNotFoundError
15
d486dd0d
JF
16 from ifupdown2.nlmanager.nlmanager import Link
17
18 from ifupdown2.ifupdown.iface import *
19 from ifupdown2.ifupdown.utils import utils
56f34349 20 from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
d486dd0d 21 from ifupdown2.ifupdownaddons.cache import *
d486dd0d 22 from ifupdown2.ifupdownaddons.modulebase import moduleBase
56f34349 23
bd441a51 24except (ImportError, ModuleNotFoundError):
0e936c3f 25 import nlmanager.ipnetwork as ipnetwork
d486dd0d 26 import ifupdown.policymanager as policymanager
3fb83a7a 27 import ifupdown.ifupdownflags as ifupdownflags
d486dd0d 28
db4371de 29 from lib.addon import Vxlan
223ba5af
JF
30 from lib.nlcache import NetlinkCacheIfnameNotFoundError
31
d486dd0d
JF
32 from nlmanager.nlmanager import Link
33
34 from ifupdown.iface import *
35 from ifupdown.utils import utils
56f34349 36 from ifupdown.statemanager import statemanager_api as statemanager
d486dd0d
JF
37
38 from ifupdownaddons.cache import *
d486dd0d
JF
39 from ifupdownaddons.modulebase import moduleBase
40
41
db4371de 42class vxlan(Vxlan, moduleBase):
223ba5af
JF
43 _modinfo = {
44 "mhelp": "vxlan module configures vxlan interfaces.",
45 "attrs": {
46 "vxlan-id": {
47 "help": "vxlan id",
48 "validrange": ["1", "16777214"],
49 "required": True,
50 "example": ["vxlan-id 100"]
51 },
52 "vxlan-local-tunnelip": {
53 "help": "vxlan local tunnel ip",
54 "validvals": ["<ipv4>"],
55 "example": ["vxlan-local-tunnelip 172.16.20.103"]
56 },
57 "vxlan-svcnodeip": {
969257a4 58 "help": "vxlan svc node id",
223ba5af
JF
59 "validvals": ["<ipv4>"],
60 "example": ["vxlan-svcnodeip 172.16.22.125"]
61 },
40658337
JF
62 "vxlan-svcnodeip6": {
63 "help": "vxlan svc node ip",
64 "validvals": ["<ipv6>"],
65 "example": ["vxlan-svcnodeip6 2001:DB8:8086:6502::"]
66 },
223ba5af
JF
67 "vxlan-remoteip": {
68 "help": "vxlan remote ip",
69 "validvals": ["<ipv4>"],
70 "example": ["vxlan-remoteip 172.16.22.127"],
71 "multiline": True
72 },
73 "vxlan-learning": {
74 "help": "vxlan learning yes/no",
75 "validvals": ["yes", "no", "on", "off"],
76 "example": ["vxlan-learning no"],
77 "default": "yes"
78 },
79 "vxlan-ageing": {
80 "help": "vxlan aging timer",
81 "validrange": ["0", "4096"],
82 "example": ["vxlan-ageing 300"],
83 "default": "300"
84 },
85 "vxlan-purge-remotes": {
86 "help": "vxlan purge existing remote entries",
87 "validvals": ["yes", "no"],
88 "example": ["vxlan-purge-remotes yes"],
89 },
90 "vxlan-port": {
91 "help": "vxlan UDP port (transmitted to vxlan driver)",
92 "example": ["vxlan-port 4789"],
93 "validrange": ["1", "65536"],
94 "default": "4789",
95 },
96 "vxlan-physdev": {
97 "help": "vxlan physical device",
98 "example": ["vxlan-physdev eth1"]
99 },
100 "vxlan-ttl": {
101 "help": "specifies the TTL value to use in outgoing packets "
102 "(range 0..255), 0=auto",
103 "default": "0",
b20f9836
SO
104 "validrange": ["0", "255"],
105 "validvals": ["<number>", "auto"],
223ba5af
JF
106 "example": ['vxlan-ttl 42'],
107 },
e521508b 108 "vxlan-tos": {
a8dd54b0 109 "help": "specifies the ToS value (range 0..255), 1=inherit",
b20f9836
SO
110 "validrange": ["0", "255"],
111 "validvals": ["<number>", "inherit"],
e521508b
SO
112 "example": ['vxlan-tos 42'],
113 },
223ba5af
JF
114 "vxlan-mcastgrp": {
115 "help": "vxlan multicast group",
116 "validvals": ["<ip>"],
117 "example": ["vxlan-mcastgrp 172.16.22.127"],
40658337
JF
118 },
119 "vxlan-mcastgrp6": {
120 "help": "vxlan multicast group",
121 "validvals": ["<ip6>"],
122 "example": ["vxlan-mcastgrp ff02::15c"],
ca436937
JF
123 },
124 "vxlan-mcastgrp-map": {
56f34349
JF
125 "help": "vxlan multicast group for single-vxlan device -"
126 "doesn't support multiline attribute",
ca436937
JF
127 "validvals": ["<number-ipv4-list>"],
128 "example": ["vxlan-mcastgrp-map 1000=239.1.1.100 1001=239.1.1.200"],
129 },
84c47c4f
RP
130 "vxlan-vnifilter": {
131 "help": "vxlan vni filter for single-vxlan device",
132 "validvals": ["on", "off"],
133 "default": "off",
134 "example": ["vxlan-vnifilter yes"],
135 },
66eb9ce3
JF
136 "vxlan-remoteip-map": {
137 "help": "static HREP entries for static single vxlan device",
138 "example": ["vxlan-remoteip-map 1000-1002=27.0.0.10-27.0.0.12"],
139 },
e521508b
SO
140 "vxlan-udp-csum": {
141 "help": "whether to perform checksumming or not",
e521508b
SO
142 "validvals": ["yes", "no"],
143 "example": ["vxlan-udp-csum no"]
144 }
223ba5af
JF
145 }
146 }
147
148 VXLAN_PHYSDEV_MCASTGRP_DEFAULT = "ipmr-lo"
d486dd0d
JF
149
150 def __init__(self, *args, **kargs):
db4371de 151 Vxlan.__init__(self)
d486dd0d 152 moduleBase.__init__(self, *args, **kargs)
223ba5af
JF
153
154 self._vxlan_purge_remotes = utils.get_boolean_from_string(
155 policymanager.policymanager_api.get_module_globals(
156 module_name=self.__class__.__name__,
157 attr="vxlan-purge-remotes"
158 )
159 )
160 self._vxlan_local_tunnelip = None
161 self._clagd_vxlan_anycast_ip = ""
162
163 # If mcastgrp is specified we need to rely on a user-configred device (via physdev)
164 # or via a policy variable "vxlan-physdev_mcastgrp". If the device doesn't exist we
165 # create it as a dummy device. We need to keep track of the user configuration to
166 # know when to delete this dummy device (when user remove mcastgrp from it's config)
167 self.vxlan_mcastgrp_ref = False
168 self.vxlan_physdev_mcast = policymanager.policymanager_api.get_module_globals(
169 module_name=self.__class__.__name__,
170 attr="vxlan-physdev-mcastgrp"
171 ) or self.VXLAN_PHYSDEV_MCASTGRP_DEFAULT
172
782aff35
JF
173 self.tvd_svd_mix_support = utils.get_boolean_from_string(
174 policymanager.policymanager_api.get_module_globals(
175 module_name=self.__class__.__name__,
176 attr="vxlan-support-mix-dev-types"
177 ),
178 default=True
179 )
180
db4371de
JF
181 self.svd_tvd_errors = {}
182
223ba5af
JF
183 def reset(self):
184 # in daemon mode we need to reset mcastgrp_ref for every new command
185 # this variable has to be set in get_dependent_ifacenames
186 self.vxlan_mcastgrp_ref = False
d486dd0d
JF
187
188 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
189 if self._is_vxlan_device(ifaceobj):
223ba5af 190 if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not self._vxlan_local_tunnelip:
d486dd0d
JF
191 self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name)
192 return False
782aff35
JF
193
194 self.check_and_raise_svd_tvd_errors(ifaceobj)
195
d486dd0d
JF
196 return self.syntax_check_localip_anycastip_equal(
197 ifaceobj.name,
223ba5af
JF
198 ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or self._vxlan_local_tunnelip,
199 self._clagd_vxlan_anycast_ip
d486dd0d
JF
200 )
201 return True
202
203 def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip):
204 try:
0e936c3f 205 if local_ip and anycast_ip and ipnetwork.IPNetwork(local_ip) == ipnetwork.IPNetwork(anycast_ip):
d486dd0d
JF
206 self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)'
207 % (ifname, local_ip))
208 return False
3218f49d 209 except Exception:
d486dd0d
JF
210 pass
211 return True
212
59ab29fb 213 def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None, old_ifaceobjs=False):
e537a6e6
JF
214 if ifaceobj.get_attr_value_first("bridge-vlan-vni-map"):
215 ifaceobj.link_privflags |= ifaceLinkPrivFlags.SINGLE_VXLAN
216
d486dd0d
JF
217 if self._is_vxlan_device(ifaceobj):
218 ifaceobj.link_kind |= ifaceLinkKind.VXLAN
219 self._set_global_local_ip(ifaceobj)
223ba5af 220
782aff35 221 if not old_ifaceobjs and not self.tvd_svd_mix_support:
db4371de
JF
222 # mixing TVD and SVD is not supported - we need to warn the user
223 # we use a dictionary to make sure to only warn once and prevent each
224 # vxlan from being configured on the system
225
226 if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
227 self.single_vxlan_configured.add(ifaceobj.name)
228
229 if self.traditional_vxlan_configured:
230 self.svd_tvd_errors[ifaceobj.name] = (
231 "%s: mixing single-vxlan-device with tradional %s is not supported (TVD: %s)"
232 % (ifaceobj.name, "vxlans" if len(self.traditional_vxlan_configured) > 1 else "vxlan", ", ".join(self.traditional_vxlan_configured))
233 )
234 else:
235 self.traditional_vxlan_configured.add(ifaceobj.name)
236
237 if self.single_vxlan_configured:
238 self.svd_tvd_errors[ifaceobj.name] = (
239 "%s: mixing traditional vxlan with single vxlan %s is not supported (SVD: %s)"
240 % (ifaceobj.name, "devices" if len(self.single_vxlan_configured) > 1 else "device", ", ".join(self.single_vxlan_configured))
241 )
242
243
223ba5af
JF
244 # if we detect a vxlan we check if mcastgrp is set (if so we set vxlan_mcastgrp_ref)
245 # to know when to delete this device.
1609696f 246 if not self.vxlan_mcastgrp_ref and (ifaceobj.get_attr_value("vxlan-mcastgrp") or ifaceobj.get_attr_value("vxlan-mcastgrp-map")):
223ba5af
JF
247 self.vxlan_mcastgrp_ref = True
248
59ab29fb 249 elif ifaceobj.name == 'lo' and not old_ifaceobjs:
d486dd0d
JF
250 clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip')
251 if clagd_vxlan_list:
252 if len(clagd_vxlan_list) != 1:
253 self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
254 % (ifaceobj.name,))
223ba5af 255 self._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
d486dd0d
JF
256
257 self._set_global_local_ip(ifaceobj)
a382b488
JF
258
259 # If we should use a specific underlay device for the VXLAN
260 # tunnel make sure this device is set up before the VXLAN iface.
261 physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
262
263 if physdev:
264 return [physdev]
265
d486dd0d
JF
266 return None
267
268 def _set_global_local_ip(self, ifaceobj):
269 vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
223ba5af
JF
270 if vxlan_local_tunnel_ip and not self._vxlan_local_tunnelip:
271 self._vxlan_local_tunnelip = vxlan_local_tunnel_ip
d486dd0d 272
223ba5af
JF
273 @staticmethod
274 def _is_vxlan_device(ifaceobj):
e537a6e6
JF
275 return ifaceobj.link_kind & ifaceLinkKind.VXLAN \
276 or ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN \
277 or ifaceobj.get_attr_value_first("vxlan-id") \
278 or ifaceobj.get_attr_value_first("bridge-vlan-vni-map")
d486dd0d 279
223ba5af 280 def __get_vlxan_purge_remotes(self, ifaceobj):
d486dd0d 281 if not ifaceobj:
223ba5af 282 return self._vxlan_purge_remotes
d486dd0d
JF
283 purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes')
284 if purge_remotes:
285 purge_remotes = utils.get_boolean_from_string(purge_remotes)
286 else:
223ba5af 287 purge_remotes = self._vxlan_purge_remotes
d486dd0d
JF
288 return purge_remotes
289
ec25a08c
JF
290 def get_vxlan_ttl_from_string(self, ttl_config):
291 ttl = 0
292 if ttl_config:
293 if ttl_config.lower() == "auto":
294 ttl = 0
295 else:
296 ttl = int(ttl_config)
297 return ttl
298
e521508b 299 def get_vxlan_tos_from_string(self, tos_config):
e521508b
SO
300 if tos_config:
301 if tos_config.lower() == "inherit":
a8dd54b0 302 return 1
e521508b 303 else:
a8dd54b0
JF
304 return int(tos_config)
305 return None
e521508b 306
223ba5af
JF
307 def __config_vxlan_id(self, ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
308 """
309 Get vxlan-id user config and check it's value before inserting it in our netlink dictionary
310 :param ifname:
311 :param ifaceobj:
312 :param vxlan_id_str:
313 :param user_request_vxlan_info_data:
314 :param cached_vxlan_ifla_info_data:
315 :return:
316 """
317 try:
318 vxlan_id = int(vxlan_id_str)
319 cached_vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID)
320
321 if cached_vxlan_id and cached_vxlan_id != vxlan_id:
322 self.log_error(
323 "%s: Cannot change running vxlan id (%s): Operation not supported"
324 % (ifname, cached_vxlan_id),
325 ifaceobj
326 )
327 user_request_vxlan_info_data[Link.IFLA_VXLAN_ID] = vxlan_id
328 except ValueError:
329 self.log_error("%s: invalid vxlan-id '%s'" % (ifname, vxlan_id_str), ifaceobj)
330
331 def __get_vxlan_ageing_int(self, ifname, ifaceobj, link_exists):
332 """
333 Get vxlan-ageing user config or via policy, return integer value, None or raise on error
334 :param ifname:
335 :param ifaceobj:
336 :param link_exists:
337 :return:
338 """
339 vxlan_ageing_str = ifaceobj.get_attr_value_first("vxlan-ageing")
340 try:
341 if vxlan_ageing_str:
342 return int(vxlan_ageing_str)
343
344 vxlan_ageing_str = policymanager.policymanager_api.get_attr_default(
345 module_name=self.__class__.__name__,
346 attr="vxlan-ageing"
347 )
348
349 if not vxlan_ageing_str and link_exists:
350 # if link doesn't exist we let the kernel define ageing
351 vxlan_ageing_str = self.get_attr_default_value("vxlan-ageing")
352
353 if vxlan_ageing_str:
354 return int(vxlan_ageing_str)
3218f49d 355 except Exception:
223ba5af 356 self.log_error("%s: invalid vxlan-ageing '%s'" % (ifname, vxlan_ageing_str), ifaceobj)
d486dd0d 357
223ba5af
JF
358 def __config_vxlan_ageing(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
359 """
360 Check user config vxlan-ageing and insert it in our netlink dictionary if needed
361 """
362 vxlan_ageing = self.__get_vxlan_ageing_int(ifname, ifaceobj, link_exists)
363
364 if not vxlan_ageing or (link_exists and vxlan_ageing == cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_AGEING)):
365 return
366
367 self.logger.info("%s: set vxlan-ageing %s" % (ifname, vxlan_ageing))
368 user_request_vxlan_info_data[Link.IFLA_VXLAN_AGEING] = vxlan_ageing
369
370 def __config_vxlan_port(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
371 """
372 Check vxlan-port user config, validate the integer value and insert it in the netlink dictionary if needed
373 :param ifname:
374 :param ifaceobj:
375 :param link_exists:
376 :param user_request_vxlan_info_data:
377 :param cached_vxlan_ifla_info_data:
378 :return:
379 """
380 vxlan_port_str = ifaceobj.get_attr_value_first("vxlan-port")
381 try:
382 if not vxlan_port_str:
383 vxlan_port_str = policymanager.policymanager_api.get_attr_default(
384 module_name=self.__class__.__name__,
385 attr="vxlan-port"
386 )
d486dd0d 387
ec25a08c 388 try:
223ba5af
JF
389 vxlan_port = int(vxlan_port_str)
390 except TypeError:
391 # TypeError means vxlan_port was None
392 # ie: not provided by the user or the policy
393 vxlan_port = self.netlink.VXLAN_UDP_PORT
394 except ValueError as e:
395 self.logger.warning(
396 "%s: vxlan-port: using default %s: invalid configured value %s"
397 % (ifname, self.netlink.VXLAN_UDP_PORT, str(e))
398 )
399 vxlan_port = self.netlink.VXLAN_UDP_PORT
400
401 cached_vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT)
402
403 if link_exists:
404 if vxlan_port != cached_vxlan_port:
405 self.logger.warning(
406 "%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s"
407 % (ifname, cached_vxlan_port, ifname, ifname)
ec25a08c 408 )
ec25a08c
JF
409 return
410
223ba5af
JF
411 self.logger.info("%s: set vxlan-port %s" % (ifname, vxlan_port))
412 user_request_vxlan_info_data[Link.IFLA_VXLAN_PORT] = vxlan_port
3218f49d 413 except Exception:
223ba5af
JF
414 self.log_error("%s: invalid vxlan-port '%s'" % (ifname, vxlan_port_str), ifaceobj)
415
e521508b
SO
416 def __config_vxlan_tos(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
417 """
418 Get vxlan-tos from user config or policy, validate integer value and insert in netlink dict
419 :param ifname:
420 :param ifaceobj:
421 :param user_request_vxlan_info_data:
422 :param cached_vxlan_ifla_info_data:
423 :return:
424 """
425 vxlan_tos_str = ifaceobj.get_attr_value_first("vxlan-tos")
426 try:
427 if vxlan_tos_str:
428 vxlan_tos = self.get_vxlan_tos_from_string(vxlan_tos_str)
429 else:
430 vxlan_tos = self.get_vxlan_tos_from_string(
431 policymanager.policymanager_api.get_attr_default(
432 module_name=self.__class__.__name__,
433 attr="vxlan-tos"
434 )
435 )
436
a8dd54b0
JF
437 if not vxlan_tos_str:
438 return
439
e521508b 440 cached_ifla_vxlan_tos = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TOS)
a8dd54b0 441
e521508b
SO
442 if vxlan_tos != cached_ifla_vxlan_tos:
443
444 if cached_ifla_vxlan_tos is not None:
445 self.logger.info("%s: set vxlan-tos %s (cache %s)" % (ifname, vxlan_tos_str if vxlan_tos_str else vxlan_tos, cached_ifla_vxlan_tos))
446 else:
447 self.logger.info("%s: set vxlan-tos %s" % (ifname, vxlan_tos_str if vxlan_tos_str else vxlan_tos))
448
449 user_request_vxlan_info_data[Link.IFLA_VXLAN_TOS] = vxlan_tos
450 except Exception:
451 self.log_error("%s: invalid vxlan-tos '%s'" % (ifname, vxlan_tos_str), ifaceobj)
452
223ba5af
JF
453 def __config_vxlan_ttl(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
454 """
455 Get vxlan-ttl from user config or policy, validate integer value and insert in netlink dict
456 :param ifname:
457 :param ifaceobj:
458 :param user_request_vxlan_info_data:
459 :param cached_vxlan_ifla_info_data:
460 :return:
461 """
462 vxlan_ttl_str = ifaceobj.get_attr_value_first("vxlan-ttl")
463 try:
464 if vxlan_ttl_str:
465 vxlan_ttl = self.get_vxlan_ttl_from_string(vxlan_ttl_str)
466 else:
467 vxlan_ttl = self.get_vxlan_ttl_from_string(
468 policymanager.policymanager_api.get_attr_default(
469 module_name=self.__class__.__name__,
470 attr="vxlan-ttl"
471 )
472 )
473
474 cached_ifla_vxlan_ttl = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TTL)
475 if vxlan_ttl != cached_ifla_vxlan_ttl:
476
477 if cached_ifla_vxlan_ttl is not None:
478 self.logger.info("%s: set vxlan-ttl %s (cache %s)" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl, cached_ifla_vxlan_ttl))
479 else:
480 self.logger.info("%s: set vxlan-ttl %s" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl))
481
482 user_request_vxlan_info_data[Link.IFLA_VXLAN_TTL] = vxlan_ttl
9144496d 483 return vxlan_ttl
3218f49d 484 except Exception:
223ba5af
JF
485 self.log_error("%s: invalid vxlan-ttl '%s'" % (ifname, vxlan_ttl_str), ifaceobj)
486
59ab29fb
JF
487 def is_vxlan_on_a_clag_bridge(self, ifaceobj) -> bool:
488 return bool(ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and self._clagd_vxlan_anycast_ip and self.is_process_running('clagd'))
489
223ba5af
JF
490 def __config_vxlan_local_tunnelip(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
491 """
492 Get vxlan-local-tunnelip user config or policy, validate ip address format and insert in netlink dict
493 :param ifname:
494 :param ifaceobj:
495 :param link_exists:
496 :param user_request_vxlan_info_data:
497 :param cached_vxlan_ifla_info_data:
498 :return:
499 """
500 local = ifaceobj.get_attr_value_first("vxlan-local-tunnelip")
501
502 if not local and self._vxlan_local_tunnelip:
503 local = self._vxlan_local_tunnelip
504
505 if link_exists:
59ab29fb
JF
506 cached_ifla_vxlan_local = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
507
223ba5af
JF
508 # on ifreload do not overwrite anycast_ip to individual ip
509 # if clagd has modified
59ab29fb 510 if self._clagd_vxlan_anycast_ip and cached_ifla_vxlan_local:
0e936c3f 511 anycastip = ipnetwork.IPNetwork(self._clagd_vxlan_anycast_ip)
59ab29fb
JF
512
513 if (
514 anycastip == cached_ifla_vxlan_local
515 # there's a change that the cache hasn't been updated in
516 # time to reflect the new anycast ip set by clagd, extra checks:
517 or self.is_vxlan_on_a_clag_bridge(ifaceobj)
518 ):
519 local = cached_ifla_vxlan_local = anycastip
520 self.logger.info("%s: clagd-vxlan-anycast-ip (%s) inherited from loopback interface" % (ifname, local))
521 else:
522 cached_ifla_vxlan_local = None
223ba5af
JF
523
524 if not local:
525 local = policymanager.policymanager_api.get_attr_default(
526 module_name=self.__class__.__name__,
527 attr="vxlan-local-tunnelip"
528 )
529
530 if local:
531 try:
0e936c3f
JF
532 local = ipnetwork.IPv4Address(local)
533
534 if local.initialized_with_prefixlen:
223ba5af 535 self.logger.warning("%s: vxlan-local-tunnelip %s: netmask ignored" % (ifname, local))
0e936c3f
JF
536
537 except Exception as e:
538 raise Exception("%s: invalid vxlan-local-tunnelip %s: %s" % (ifname, local, str(e)))
223ba5af 539
223ba5af
JF
540
541 if local:
542 if local != cached_ifla_vxlan_local:
543 self.logger.info("%s: set vxlan-local-tunnelip %s" % (ifname, local))
544 user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = local
545
546 # if both local-ip and anycast-ip are identical the function prints a warning
547 self.syntax_check_localip_anycastip_equal(ifname, local, self._clagd_vxlan_anycast_ip)
548 elif cached_ifla_vxlan_local:
549 self.logger.info("%s: removing vxlan-local-tunnelip (cache %s)" % (ifname, cached_ifla_vxlan_local))
550 user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = None
551
552 return local
553
40658337
JF
554 def __get_vxlan_attribute(self, ifaceobj, attr_name):
555 vxlan_attr_value = ifaceobj.get_attr_value_first(attr_name)
d486dd0d 556
40658337
JF
557 if not vxlan_attr_value:
558 vxlan_attr_value = policymanager.policymanager_api.get_attr_default(
223ba5af 559 module_name=self.__class__.__name__,
40658337 560 attr=attr_name
223ba5af 561 )
d486dd0d 562
40658337 563 return vxlan_attr_value
d486dd0d 564
6015cce2
QZ
565 def __syntax_check_vxlan_mcast_vni(self, ifaceobj, m, vni):
566 try:
567 int(vni)
568 except ValueError:
569 self.log_error('%s: vxlan-mcastgrp-map "%s" vni format is invalid' % (ifaceobj.name, m))
570
571 def __syntax_check_vxlan_mcast_vni_range(self, ifaceobj, m, vni_range):
572 if len(vni_range) != 2:
573 self.log_error('%s: vxlan-mcastgrp-map "%s" vni range format is invalid' % (ifaceobj.name, m))
574 for vni in vni_range:
575 self.__syntax_check_vxlan_mcast_vni(ifaceobj, m, vni)
576 if int(vni_range[0]) >= int(vni_range[1]):
577 self.log_error('%s: vxlan-mcastgrp-map "%s" vni range is invalid' % (ifaceobj.name, m))
578
579 def __syntax_check_vxlan_mcast_grp(self, ifaceobj, m, grp):
580 try:
581 ip = IPv4Address(grp)
582 except AddressValueError:
583 self.log_error('%s: vxlan-mcastgrp-map "%s" group format is invalid' % (ifaceobj.name, m))
584 if not ip.is_multicast:
585 self.log_error('%s: vxlan-mcastgrp-map "%s" group is not multicast' % (ifaceobj.name, m))
586
587 def __syntax_check_vxlan_mcast_grp_range(self, ifaceobj, m, grp_range):
588 if len(grp_range) != 2:
589 self.log_error('%s: vxlan-mcastgrp-map "%s" group format is invalid' % (ifaceobj.name, m))
590 for grp in grp_range:
591 self.__syntax_check_vxlan_mcast_grp(ifaceobj, m, grp)
592 if int(IPv4Address(grp_range[0])) >= int(IPv4Address(grp_range[1])):
593 self.log_error('%s: vxlan-mcastgrp-map "%s" group range is invalid' % (ifaceobj.name, m))
594
595 def __syntax_check_vxlan_mcast_network(self, ifaceobj, m, network, len_vni):
596 try:
597 ip = IPv4Network(network)
598 ip[0]
599 ip[len_vni - 1]
600 except IndexError:
601 self.log_error('%s: vxlan-mcastgrp-map "%s" network range is insufficient' % (ifaceobj.name, m))
602 except AddressValueError:
603 self.log_error('%s: vxlan-mcastgrp-map "%s" network format is invalid' % (ifaceobj.name, m))
604 if not ip.is_multicast:
605 self.log_error('%s: vxlan-mcastgrp-map "%s" network is not multicast' % (ifaceobj.name, m))
606
607 def __get_vxlan_mcastgrp_map(self, ifaceobj):
608 maps = ifaceobj.get_attr_value('vxlan-mcastgrp-map')
609 if not maps:
610 maps = policymanager.policymanager_api.get_attr_default(
611 module_name=self.__class__.__name__,
612 attr='vxlan-mcastgrp-map'
613 )
614 return maps
615
616 parsed_maps = {}
617 for m_line in maps:
618 # Cover single-line multi-entry case
619 map = m_line.split()
620 for m in map:
621 m_parts = m.split('=')
622 if len(m_parts) != 2:
623 self.log_error('%s: vxlan-mcastgrp-map %s format is invalid' % (ifaceobj.name, m))
624 vni = m_parts[0]
625 grp = m_parts[1]
626 _range = "-"
627 _network = "/"
628
629 # One to one mapping case
630 if _range not in vni and _range not in grp:
631 self.__syntax_check_vxlan_mcast_vni(ifaceobj, m, vni)
632 self.__syntax_check_vxlan_mcast_grp(ifaceobj, m, grp)
633 if int(vni) not in parsed_maps:
634 parsed_maps[int(vni)] = IPv4Address(grp)
635 else:
636 self.log_warn('%s: vxlan-mcastgrp-map %s vni %s duplicate' % (ifaceobj.name, vni, m))
637
638 # Many VNI case
639 if _range in vni:
640 v_parts = vni.split(_range)
641 self.__syntax_check_vxlan_mcast_vni_range(ifaceobj, m, v_parts)
642 vnis = list(range(int(v_parts[0]), int(v_parts[1]) + 1))
643
644 if _range not in grp and _network not in grp:
645 self.__syntax_check_vxlan_mcast_grp(ifaceobj, m, grp)
646 for i in vnis:
647 if i not in parsed_maps:
648 parsed_maps[i] = IPv4Address(grp)
649 else:
650 self.log_warn('%s: vxlan-mcastgrp-map %s vni %s duplicate' % (ifaceobj.name, vni, m))
651 else:
652 if _network in grp:
653 self.__syntax_check_vxlan_mcast_network(ifaceobj, m, grp, len(vnis))
654 network = IPv4Network(grp)
655 g_parts = [network[0], network[len(vnis) - 1]]
656 else:
657 g_parts = grp.split(_range)
658
659 self.__syntax_check_vxlan_mcast_grp_range(ifaceobj, m, g_parts)
660 grp_range = list(range(int(IPv4Address(g_parts[0])), int(IPv4Address(g_parts[1])) + 1))
661 if len(grp_range) != len(vnis):
662 self.log_error('%s: vxlan-mcastgrp-map "%s" range lengths do not match.'
663 % (ifaceobj.name, m))
664
665 for v, g in zip(vnis, grp_range):
666 if v not in parsed_maps:
667 parsed_maps[v] = IPv4Address(g)
668 else:
669 self.log_warn('%s: vxlan-mcastgrp-map %s vni %s duplicate' % (ifaceobj.name, v, m))
670
671 return parsed_maps
672
223ba5af
JF
673 def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
674 """
675 vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive
676 this function validates ip format for both attribute and tries to understand
677 what the user really want (remote or group option).
678
679 :param ifname:
680 :param ifaceobj:
681 :param mcast_grp:
682 :param group:
683 :param physdev:
684 :param user_request_vxlan_info_data:
685 :param cached_vxlan_ifla_info_data:
686 :return:
687 """
688 if mcast_grp and group:
689 self.log_error("%s: both group (vxlan-mcastgrp %s) and "
690 "remote (vxlan-svcnodeip %s) cannot be specified"
691 % (ifname, mcast_grp, group), ifaceobj)
692
693 attribute_name = "vxlan-svcnodeip"
694 multicast_group_change = False
695
696 if group:
697 try:
0e936c3f
JF
698 group = ipnetwork.IPv4Address(group)
699
700 if group.initialized_with_prefixlen:
223ba5af 701 self.logger.warning("%s: vxlan-svcnodeip %s: netmask ignored" % (ifname, group))
223ba5af 702
0e936c3f
JF
703 except Exception as e:
704 raise Exception("%s: invalid vxlan-svcnodeip %s: %s" % (ifname, group, str(e)))
705
706 if group.ip.is_multicast:
223ba5af
JF
707 self.logger.warning("%s: vxlan-svcnodeip %s: invalid group address, "
708 "for multicast IP please use attribute \"vxlan-mcastgrp\"" % (ifname, group))
709 # if svcnodeip is used instead of mcastgrp we warn the user
710 # if mcast_grp is not provided by the user we can instead
711 # use the svcnodeip value
712 if not physdev:
713 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname))
714
715 elif mcast_grp:
716 try:
0e936c3f
JF
717 mcast_grp = ipnetwork.IPv4Address(mcast_grp)
718
719 if mcast_grp.initialized_with_prefixlen:
223ba5af 720 self.logger.warning("%s: vxlan-mcastgrp %s: netmask ignored" % (ifname, mcast_grp))
223ba5af 721
0e936c3f
JF
722 except Exception as e:
723 raise Exception("%s: invalid vxlan-mcastgrp %s: %s" % (ifname, mcast_grp, str(e)))
724
725 if not mcast_grp.ip.is_multicast:
223ba5af
JF
726 self.logger.warning("%s: vxlan-mcastgrp %s: invalid group address, "
727 "for non-multicast IP please use attribute \"vxlan-svcnodeip\""
728 % (ifname, mcast_grp))
729 # if mcastgrp is specified with a non-multicast address
730 # we warn the user. If the svcnodeip wasn't specified by
731 # the user we can use the mcastgrp value as svcnodeip
732 if not group:
733 group = mcast_grp
734 mcast_grp = None
d486dd0d 735 else:
223ba5af
JF
736 attribute_name = "vxlan-mcastgrp"
737
738 if mcast_grp:
739 group = mcast_grp
740
741 if not physdev:
742 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname))
743
744 cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
745
746 if group != cached_ifla_vxlan_group:
747
748 if not group:
0e936c3f 749 group = ipnetwork.IPNetwork("0.0.0.0")
223ba5af
JF
750 attribute_name = "vxlan-svcnodeip/vxlan-mcastgrp"
751
752 self.logger.info("%s: set %s %s" % (ifname, attribute_name, group))
753 user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP] = group
754
755 # if the mcastgrp address is changed we need to signal this to the upper function
756 # in this case vxlan needs to be down before applying changes then up'd
757 multicast_group_change = True
d486dd0d
JF
758
759 if link_exists:
223ba5af
JF
760 if cached_ifla_vxlan_group:
761 self.logger.info(
762 "%s: vxlan-mcastgrp configuration changed (cache %s): flapping vxlan device required"
763 % (ifname, cached_ifla_vxlan_group)
764 )
b067bba9 765 else:
223ba5af
JF
766 self.logger.info(
767 "%s: vxlan-mcastgrp configuration changed: flapping vxlan device required" % ifname
768 )
b067bba9 769
223ba5af 770 return group, multicast_group_change
d486dd0d 771
40658337
JF
772 def __config_vxlan_group6(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
773 """
774 vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive
775 this function validates ip format for both attribute and tries to understand
776 what the user really want (remote or group option).
777
778 :param ifname:
779 :param ifaceobj:
780 :param mcast_grp:
781 :param group:
782 :param physdev:
783 :param user_request_vxlan_info_data:
784 :param cached_vxlan_ifla_info_data:
785 :return:
786 """
787 if mcast_grp and group:
788 self.log_error("%s: both group (vxlan-mcastgrp6 %s) and "
789 "remote (vxlan-svcnodeip6 %s) cannot be specified"
790 % (ifname, mcast_grp, group), ifaceobj)
791
792 attribute_name = "vxlan-svcnodeip6"
793 multicast_group_change = False
794
795 if group:
796 try:
797 group = ipnetwork.IPv6Address(group)
798 except Exception:
799 try:
800 group_ip = ipnetwork.IPv6Network(group).ip
801 self.logger.warning("%s: vxlan-svcnodeip6 %s: netmask ignored" % (ifname, group))
802 group = group_ip
1b7f1f34 803 except Exception:
40658337
JF
804 raise Exception("%s: invalid vxlan-svcnodeip6 %s: must be in ipv4 format" % (ifname, group))
805
806 if group.is_multicast:
807 self.logger.warning("%s: vxlan-svcnodeip6 %s: invalid group address, "
808 "for multicast IP please use attribute \"vxlan-mcastgrp6\"" % (ifname, group))
809 # if svcnodeip is used instead of mcastgrp we warn the user
810 # if mcast_grp is not provided by the user we can instead
811 # use the svcnodeip value
812 if not physdev:
813 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp6) requires 'vxlan-physdev' to be specified" % (ifname))
814
815 elif mcast_grp:
816 try:
817 mcast_grp = ipnetwork.IPv6Address(mcast_grp)
818 except Exception:
819 try:
820 group_ip = ipnetwork.IPv6Network(mcast_grp).ip
821 self.logger.warning("%s: vxlan-mcastgrp6 %s: netmask ignored" % (ifname, mcast_grp))
822 mcast_grp = group_ip
1b7f1f34 823 except Exception:
40658337
JF
824 raise Exception("%s: invalid vxlan-mcastgrp6 %s: must be in ipv4 format" % (ifname, mcast_grp))
825
826 if not mcast_grp.is_multicast:
827 self.logger.warning("%s: vxlan-mcastgrp6 %s: invalid group address, "
828 "for non-multicast IP please use attribute \"vxlan-svcnodeip6\""
829 % (ifname, mcast_grp))
830 # if mcastgrp is specified with a non-multicast address
831 # we warn the user. If the svcnodeip wasn't specified by
832 # the user we can use the mcastgrp value as svcnodeip
833 if not group:
834 group = mcast_grp
835 mcast_grp = None
836 else:
837 attribute_name = "vxlan-mcastgrp6"
838
839 if mcast_grp:
840 group = mcast_grp
841
842 if not physdev:
843 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp6) requires 'vxlan-physdev' to be specified" % (ifname))
844
845 cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP6)
846
847 if group != cached_ifla_vxlan_group:
848
849 if not group:
850 group = ipnetwork.IPNetwork("::0", family=6)
851 attribute_name = "vxlan-svcnodeip6/vxlan-mcastgrp6"
852
853 self.logger.info("%s: set %s %s" % (ifname, attribute_name, group))
854 user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP6] = group
855
856 # if the mcastgrp address is changed we need to signal this to the upper function
857 # in this case vxlan needs to be down before applying changes then up'd
858 multicast_group_change = True
859
860 if link_exists:
861 if cached_ifla_vxlan_group:
862 self.logger.info(
863 "%s: vxlan-mcastgrp6 configuration changed (cache %s): flapping vxlan device required"
864 % (ifname, cached_ifla_vxlan_group)
865 )
866 else:
867 self.logger.info(
868 "%s: vxlan-mcastgrp6 configuration changed: flapping vxlan device required" % ifname
869 )
870
871 return group, multicast_group_change
872
223ba5af
JF
873 def __config_vxlan_learning(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
874 if not link_exists or not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
875 vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning')
876 if not vxlan_learning:
877 vxlan_learning = self.get_attr_default_value('vxlan-learning')
878 vxlan_learning = utils.get_boolean_from_string(vxlan_learning)
879 else:
880 vxlan_learning = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING)
d486dd0d 881
223ba5af
JF
882 if vxlan_learning != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING):
883 self.logger.info("%s: set vxlan-learning %s" % (ifaceobj.name, "on" if vxlan_learning else "off"))
884 user_request_vxlan_info_data[Link.IFLA_VXLAN_LEARNING] = vxlan_learning
885
a8dd54b0 886 def __config_vxlan_udp_csum(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
e521508b 887 vxlan_udp_csum = ifaceobj.get_attr_value_first('vxlan-udp-csum')
a8dd54b0
JF
888
889 if not vxlan_udp_csum:
890 vxlan_udp_csum = policymanager.policymanager_api.get_attr_default(
891 module_name=self.__class__.__name__,
892 attr="vxlan-udp-csum"
893 )
894
895 if not vxlan_udp_csum and not link_exists:
896 return
897
e521508b
SO
898 if not vxlan_udp_csum:
899 vxlan_udp_csum = self.get_attr_default_value('vxlan-udp-csum')
e521508b 900
a8dd54b0
JF
901 if vxlan_udp_csum:
902 vxlan_udp_csum = utils.get_boolean_from_string(vxlan_udp_csum)
903 else:
904 return
e521508b
SO
905
906 if vxlan_udp_csum != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_UDP_CSUM):
907 self.logger.info("%s: set vxlan-udp-csum %s" % (ifaceobj.name, "on" if vxlan_udp_csum else "off"))
908 user_request_vxlan_info_data[Link.IFLA_VXLAN_UDP_CSUM] = vxlan_udp_csum
909
1609696f 910 def __get_vxlan_physdev(self, ifaceobj, mcastgrp, mcastgrp_map):
223ba5af
JF
911 """
912 vxlan-physdev wrapper, special handling is required for mcastgrp is provided
913 the vxlan needs to use a dummy or real device for tunnel endpoint communication
914 This wrapper will get the physdev from user config or policy. IF the device
915 doesnt exists we create a dummy device.
916
917 :param ifaceobj:
918 :param mcastgrp:
919 :return physdev:
920 """
921 physdev = ifaceobj.get_attr_value_first("vxlan-physdev")
d486dd0d 922
223ba5af
JF
923 # if the user provided a physdev we need to honor his config
924 # or if mcastgrp wasn't specified we don't need to go further
1609696f 925 if physdev or (not mcastgrp and not mcastgrp_map):
223ba5af
JF
926 return physdev
927
928 physdev = self.vxlan_physdev_mcast
929
930 if not self.cache.link_exists(physdev):
1609696f
JF
931 if mcastgrp_map:
932 self.logger.info("%s: needs a dummy device (%s) to use for "
933 "multicast termination (vxlan-mcastgrp-map %s)"
934 % (ifaceobj.name, physdev, mcastgrp))
935 else:
936 self.logger.info("%s: needs a dummy device (%s) to use for "
937 "multicast termination (vxlan-mcastgrp %s)"
938 % (ifaceobj.name, physdev, mcastgrp))
223ba5af
JF
939 self.netlink.link_add_with_attributes(ifname=physdev, kind="dummy", ifla={Link.IFLA_MTU: 16000, Link.IFLA_LINKMODE: 1})
940 self.netlink.link_up(physdev)
941
942 return physdev
943
944 def __config_vxlan_physdev(self, link_exists, ifaceobj, vxlan_physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
945 if vxlan_physdev:
946 try:
947 vxlan_physdev_ifindex = self.cache.get_ifindex(vxlan_physdev)
948 except NetlinkCacheIfnameNotFoundError:
d486dd0d 949 try:
223ba5af 950 vxlan_physdev_ifindex = int(self.sysfs.read_file_oneline("/sys/class/net/%s/ifindex" % vxlan_physdev))
3218f49d 951 except Exception:
223ba5af
JF
952 self.logger.error("%s: physdev %s doesn't exists" % (ifaceobj.name, vxlan_physdev))
953 return
d486dd0d 954
223ba5af
JF
955 if vxlan_physdev_ifindex != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LINK):
956 self.logger.info("%s: set vxlan-physdev %s" % (ifaceobj.name, vxlan_physdev))
957 user_request_vxlan_info_data[Link.IFLA_VXLAN_LINK] = vxlan_physdev_ifindex
d486dd0d 958
223ba5af
JF
959 # if the vxlan exists we need to return True, meaning that the vxlan
960 # needs to be flapped because we detected a vxlan-physdev change
961 if link_exists:
962 self.logger.info("%s: vxlan-physdev configuration changed: flapping vxlan device required" % ifaceobj.name)
963 return True
964
965 return False
966
66eb9ce3
JF
967 def __get_vxlan_remote_ip_map(self, ifaceobj):
968 attr_name = "vxlan-remoteip-map"
969
970 maps = ifaceobj.get_attr_value(attr_name)
971 if not maps:
972 maps = policymanager.policymanager_api.get_attr_default(
973 module_name=self.__class__.__name__,
974 attr=attr_name
975 )
976 return maps
977
978 parsed_maps = {}
979 for m_line in maps:
980 # Cover single-line multi-entry case
981 map = m_line.split()
982 for m in map:
983 m_parts = m.split('=')
984 if len(m_parts) != 2:
985 self.log_error('%s: %s %s format is invalid' % (ifaceobj.name, attr_name, m))
986
987 vnis = m_parts[0]
988 _range = "-"
989 remote_ips = []
990
991 for config_remote_ip in m_parts[1].split(","):
992 if _range in config_remote_ip:
993 ip_range = config_remote_ip.split("-")
994 try:
995 start = ip_address(ip_range[0])
996 end = ip_address(ip_range[1])
997 except Exception as e:
998 self.log_error("%s: %s: invalid ip range '%s': %s" % (ifaceobj.name, attr_name, config_remote_ip, e), ifaceobj)
999 return
1000 remote_ips.extend([ipnetwork.ip_address(i) for i in range(int(start), int(end) + 1)])
1001 else:
1002 remote_ips.append(ipnetwork.ip_address(config_remote_ip))
1003
1004 # vxlan-remoteip-map 42,84,1000-1005=10.0.0.1,10.0.0.42-45,222.0.0.1-5
1005 # higher priority is the comma
1006 for vni in utils.ranges_to_ints(vnis.split(",")) or []:
1007 parsed_maps.setdefault(vni, []).extend(remote_ips)
1008
1009 return parsed_maps
1010
84c47c4f
RP
1011 def single_vxlan_device_vni_filter(self, ifaceobj):
1012 vnis = []
af8d5db2 1013 vnisd = {}
84c47c4f 1014 for vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
7f0310a7
RP
1015 try:
1016 (vls, vis) = utils.get_vlan_vnis_in_map(vlan_vni_map)
af8d5db2
RP
1017 for v in utils.ranges_to_ints(vis):
1018 vnisd[v] = None
1019 except Exception as e:
1020 self.logger.error("%s: %s (%s)" %(ifaceobj.name, vlan_vni_map, str(e)))
1021 return
8743434a 1022 for vni_mcastgrp_map in ifaceobj.get_attr_value("vxlan-mcastgrp-map") or []:
af8d5db2
RP
1023 try:
1024 vd = utils.get_vni_mcastgrp_in_map(vni_mcastgrp_map)
1025 for v, g in vd.items():
1026 if v not in vnisd.keys():
1027 self.logger.error("%s: group %s configured for a vni not specified in vlan vni map (%s)"
1028 %(ifaceobj.name, g, vni_mcastgrp_map))
1029 return
1030 vnisd[v] = g
7f0310a7
RP
1031 except Exception as e:
1032 self.logger.error("%s: %s (%s)" %(ifaceobj.name, vlan_vni_map, str(e)))
1033 return
af8d5db2
RP
1034
1035 vnis_int = utils.ranges_to_ints(vnis)
1036 self.iproute2.bridge_link_update_vni_filter(ifaceobj.name, vnisd)
84c47c4f 1037
db4371de
JF
1038 def check_and_raise_svd_tvd_errors(self, ifaceobj):
1039 err = self.svd_tvd_errors.get(ifaceobj.name)
1040
1041 if err:
1042 self.log_error(err, ifaceobj)
1043
1044
223ba5af 1045 def _up(self, ifaceobj):
db4371de
JF
1046 self.check_and_raise_svd_tvd_errors(ifaceobj)
1047
223ba5af
JF
1048 vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id")
1049
e537a6e6
JF
1050 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and not vxlan_id_str:
1051 self.logger.warning("%s: missing vxlan-id attribute on vxlan device" % ifaceobj.name)
223ba5af
JF
1052 return
1053
1054 ifname = ifaceobj.name
1055 link_exists = self.cache.link_exists(ifname)
1056
1057 if link_exists:
1058 # if link already exists make sure this is a vxlan
1059 device_link_kind = self.cache.get_link_kind(ifname)
1060
1061 if device_link_kind != "vxlan":
1062 self.logger.error(
1063 "%s: device already exists and is not a vxlan (type %s)"
1064 % (ifname, device_link_kind)
d486dd0d 1065 )
223ba5af
JF
1066 ifaceobj.set_status(ifaceStatus.ERROR)
1067 return
d486dd0d 1068
223ba5af
JF
1069 # get vxlan running attributes
1070 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
1071 else:
1072 cached_vxlan_ifla_info_data = {}
d486dd0d 1073
223ba5af 1074 user_request_vxlan_info_data = {}
d486dd0d 1075
e537a6e6
JF
1076 if vxlan_id_str:
1077 # for single vxlan device we don't have a vxlan-id
1078 self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
1079
223ba5af
JF
1080 self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
1081 self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
1082 self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
9144496d 1083 vxlan_ttl = self.__config_vxlan_ttl(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
e521508b 1084 self.__config_vxlan_tos(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
a8dd54b0 1085 self.__config_vxlan_udp_csum(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
223ba5af
JF
1086 local = self.__config_vxlan_local_tunnelip(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
1087
40658337
JF
1088 vxlan_mcast_grp = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp")
1089 vxlan_svcnodeip = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip")
1090
1091 vxlan_mcast_grp6 = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp6")
1092 vxlan_svcnodeip6 = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip6")
1093
6015cce2 1094 vxlan_mcast_grp_map = self.__get_vxlan_mcastgrp_map(ifaceobj)
1609696f
JF
1095
1096 vxlan_physdev = self.__get_vxlan_physdev(ifaceobj, vxlan_mcast_grp, vxlan_mcast_grp_map)
223ba5af 1097
84c47c4f
RP
1098 vxlan_vnifilter = self.__get_vxlan_attribute(ifaceobj, "vxlan-vnifilter")
1099
223ba5af
JF
1100 vxlan_physdev_changed = self.__config_vxlan_physdev(
1101 link_exists,
1102 ifaceobj,
1103 vxlan_physdev,
1104 user_request_vxlan_info_data,
1105 cached_vxlan_ifla_info_data
1106 )
1107
1108 group, multicast_group_changed = self.__config_vxlan_group(
1109 ifname,
1110 ifaceobj,
1111 link_exists,
1112 vxlan_mcast_grp,
1113 vxlan_svcnodeip,
1114 vxlan_physdev,
1115 user_request_vxlan_info_data,
1116 cached_vxlan_ifla_info_data
1117 )
1118
40658337
JF
1119 group6, multicast_group_changed6 = self.__config_vxlan_group6(
1120 ifname,
1121 ifaceobj,
1122 link_exists,
1123 vxlan_mcast_grp6,
1124 vxlan_svcnodeip6,
1125 vxlan_physdev,
1126 user_request_vxlan_info_data,
1127 cached_vxlan_ifla_info_data
1128 )
1129
1130 flap_vxlan_device = link_exists and (multicast_group_changed or multicast_group_changed6 or vxlan_physdev_changed)
223ba5af
JF
1131
1132 if user_request_vxlan_info_data:
1133
1134 if link_exists and not len(user_request_vxlan_info_data) > 1:
1135 # if the vxlan already exists it's already cached
1136 # user_request_vxlan_info_data always contains at least one
1137 # element: vxlan-id
1138 self.logger.info('%s: vxlan already exists - no change detected' % ifname)
1139 else:
e537a6e6 1140 if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
223ba5af 1141 if link_exists:
aa4c8a2d 1142 self.logger.warning("%s: updating existing single vxlan device is not supported yet "
56f34349 1143 "(please run 'ifdown %s; ifup %s')" % (ifname, ifname, ifname))
223ba5af 1144 else:
e537a6e6
JF
1145 self.iproute2.link_add_single_vxlan(
1146 ifname,
a7e5fb25 1147 local.ip if local else None,
792b9a07
JF
1148 group.ip if group else None,
1149 vxlan_physdev,
84c47c4f 1150 user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT),
9144496d
RP
1151 vxlan_vnifilter,
1152 vxlan_ttl
e537a6e6
JF
1153 )
1154 else:
1155 try:
1156 if flap_vxlan_device:
1157 self.netlink.link_down_force(ifname)
1158
1159 self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data)
1160
1161 if flap_vxlan_device:
1162 self.netlink.link_up_force(ifname)
1163 except Exception as e:
1164 if link_exists:
1165 self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj)
1166 else:
1167 self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj)
1168 return
223ba5af 1169
84c47c4f
RP
1170 if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
1171 if vxlan_vnifilter and utils.get_boolean_from_string(vxlan_vnifilter):
1172 self.single_vxlan_device_vni_filter(ifaceobj)
1173
223ba5af
JF
1174 vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
1175
1176 remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
1177 if remoteips:
1178 try:
1179 for remoteip in remoteips:
0e936c3f 1180 ipnetwork.IPv4Address(remoteip)
223ba5af
JF
1181 except Exception as e:
1182 self.log_error('%s: vxlan-remoteip: %s' % (ifaceobj.name, str(e)))
1183
1184 if vxlan_purge_remotes or remoteips:
1185 # figure out the diff for remotes and do the bridge fdb updates
1186 # only if provisioned by user and not by an vxlan external
1187 # controller.
0e936c3f
JF
1188 local_str = str(local)
1189
1190 if local_str and remoteips and local_str in remoteips:
1191 remoteips.remove(local_str)
1192
1193 peers = self.iproute2.get_vxlan_peers(ifaceobj.name, str(group.ip) if group else None)
1194
223ba5af
JF
1195 cur_peers = set(peers)
1196 if remoteips:
1197 new_peers = set(remoteips)
1198 del_list = cur_peers.difference(new_peers)
1199 add_list = new_peers.difference(cur_peers)
1200 else:
1201 del_list = cur_peers
1202 add_list = []
d486dd0d 1203
223ba5af 1204 for addr in del_list:
d486dd0d 1205 try:
223ba5af
JF
1206 self.iproute2.bridge_fdb_del(
1207 ifaceobj.name,
1208 "00:00:00:00:00:00",
1209 None, True, addr
1210 )
3218f49d 1211 except Exception:
d486dd0d 1212 pass
d486dd0d 1213
223ba5af 1214 for addr in add_list:
d486dd0d 1215 try:
223ba5af
JF
1216 self.iproute2.bridge_fdb_append(
1217 ifaceobj.name,
1218 "00:00:00:00:00:00",
1219 None, True, addr
1220 )
3218f49d 1221 except Exception:
223ba5af 1222 pass
d486dd0d 1223
66eb9ce3
JF
1224 self.vxlan_remote_ip_map(ifaceobj, vxlan_mcast_grp_map)
1225
1226 def vxlan_remote_ip_map(self, ifaceobj, vxlan_mcast_grp_map):
1227 # get user configured remote ip map
1228 vxlan_remote_ip_map = self.__get_vxlan_remote_ip_map(ifaceobj) or {}
1229
1230 # get running fdb config
1231 fdb_running_config = self.iproute2.bridge_fdb_show_dev_raw_with_filters(ifaceobj.name, filters=["src_vni", "self permanent"])
1232
1233 # go through the user config and add new entries while removing existing entries from 'fdb_running_config'
1234 for vni, ips in vxlan_remote_ip_map.items():
1235 for ip in ips:
1236 fdb_entry = "00:00:00:00:00:00 dst %s src_vni %s self permanent" % (ip, vni)
1237 if fdb_entry not in fdb_running_config:
1238 self.iproute2.bridge_fdb_append(ifaceobj.name, "00:00:00:00:00:00", remote=ip, src_vni=vni)
1239 else:
1240 fdb_running_config.remove(fdb_entry)
1241
1242 # in fdb_running_config we have the delta between user config and running config. We should delete those extra
1243 # fdb entries. But first we need to make sure that those are not added by vxlan-mcastgrp-map
1244 if fdb_running_config:
1245 for vni, ip in (vxlan_mcast_grp_map or {}).items():
1246 try:
1247 fdb_running_config.remove("00:00:00:00:00:00 dst %s src_vni %s self permanent" % (ip, vni))
1248 except:
1249 pass
1250
1251 for fdb_entry in fdb_running_config:
1252 try:
1253 self.iproute2.bridge_fdb_del_raw(ifaceobj.name, fdb_entry)
1254 except:
1255 pass
1256
ca436937
JF
1257 @staticmethod
1258 def get_vxlan_fdb_src_vni(vxlan_mcast_grp_map):
1259 fdbs = []
56f34349 1260 if vxlan_mcast_grp_map:
6015cce2 1261 for src_vni, dst_ip in vxlan_mcast_grp_map.items():
ca436937
JF
1262 fdbs.append(("00:00:00:00:00:00", src_vni, dst_ip))
1263 return fdbs
1264
56f34349
JF
1265 @staticmethod
1266 def get_svd_running_fdb(ifname):
1267 vxlan_fdb_data = utils.exec_command("bridge fdb show dev %s" % ifname)
1268 current_fdb = []
1269
1270 if vxlan_fdb_data:
1271 # each entry should look like the following:
1272 # 00:00:00:00:00:00 dst 239.1.1.100 src_vni 1000 self permanent
1273 for entry in [line for line in vxlan_fdb_data.strip().split("\n") if "src_vni" in line and "00:00:00:00:00:00" in line]:
1274 mac, _, dst, _, src_vni = entry.split()[0:5]
1275 current_fdb.append((mac, src_vni, dst))
1276
1277 return current_fdb
1278
1279 def single_vxlan_device_mcast_grp_map_fdb(self, ifaceobj, ifname, vxlan_mcast_grp_map):
1280 # in this piece of code we won't be checking the running state of the fdb table
1281 # dumping all fdb entries would cause scalability issues in certain cases.
1282
1283 # pulling old mcastgrp-map configuration
1284 old_user_config_fdb = []
1285
1286 for old_ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
6015cce2 1287 old_user_config_fdb += self.get_vxlan_fdb_src_vni(self.__get_vxlan_mcastgrp_map(old_ifaceobj))
56f34349
JF
1288
1289 # new user configuration
1290 user_config_fdb = self.get_vxlan_fdb_src_vni(vxlan_mcast_grp_map)
1291
1292 # compare old and new config to know if we should remove any stale fdb entries.
1293 fdb_entries_to_remove = set(old_user_config_fdb) - set(user_config_fdb)
1294
1295 if fdb_entries_to_remove:
1296 for mac, src_vni, dst_ip in fdb_entries_to_remove:
1297 try:
1298 self.iproute2.bridge_fdb_del_src_vni(ifname, mac, src_vni)
1299 except Exception as e:
1300 if "no such file or directory" not in str(e).lower():
1301 self.logger.warning("%s: removing stale fdb entries failed: %s" % (ifname, str(e)))
af8d5db2
RP
1302
1303 if not user_config_fdb:
1304 # if vxlan-mcastgrp-map wasn't configure return
1305 return
1306
1307 for mac, src_vni, dst_ip in user_config_fdb:
1308 try:
1309 self.iproute2.bridge_fdb_add_src_vni(ifname, src_vni, dst_ip)
1310 except Exception as e:
1311 if "file exists" not in str(e).lower():
1312 ifaceobj.set_status(ifaceStatus.ERROR)
1313 self.log_error(
1314 "%s: vxlan-mcastgrp-map: %s=%s: %s"
1315 % (ifname, src_vni, dst_ip, str(e)), raise_error=False
1316 )
1317
1318 def single_vxlan_device_mcast_grp_map_vnifilter(self, ifaceobj, ifname, vxlan_mcast_grp_map):
1319 # in this piece of code we won't be checking the running state of the fdb table
1320 # dumping all fdb entries would cause scalability issues in certain cases.
1321
1322 # pulling old mcastgrp-map configuration
1323 old_user_config_fdb = []
1324
1325 for old_ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
1326 old_user_config_fdb += self.get_vxlan_fdb_src_vni(self.__get_vxlan_mcastgrp_map(old_ifaceobj))
1327
1328 # new user configuration
1329 user_config_fdb = self.get_vxlan_fdb_src_vni(vxlan_mcast_grp_map)
1330
1331 # compare old and new config to know if we should remove any stale fdb entries.
1332 fdb_entries_to_remove = set(old_user_config_fdb) - set(user_config_fdb)
1333
1334 self.logger.info(old_user_config_fdb)
1335 self.logger.info(user_config_fdb)
1336 self.logger.info(fdb_entries_to_remove)
1337
1338 if fdb_entries_to_remove:
1339 for mac, src_vni, dst_ip in fdb_entries_to_remove:
1340 try:
1341 self.iproute2.bridge_fdb_del_src_vni(ifname, mac, src_vni)
1342 except Exception as e:
1343 if "no such file or directory" not in str(e).lower():
1344 self.logger.warning("%s: removing stale fdb entries failed: %s" % (ifname, str(e)))
56f34349
JF
1345
1346 if not user_config_fdb:
1347 # if vxlan-mcastgrp-map wasn't configure return
1348 return
1349
1350 for mac, src_vni, dst_ip in user_config_fdb:
1351 try:
1352 self.iproute2.bridge_fdb_add_src_vni(ifname, src_vni, dst_ip)
1353 except Exception as e:
1354 if "file exists" not in str(e).lower():
1355 ifaceobj.set_status(ifaceStatus.ERROR)
1356 self.log_error(
1357 "%s: vxlan-mcastgrp-map: %s=%s: %s"
1358 % (ifname, src_vni, dst_ip, str(e)), raise_error=False
1359 )
1360
d486dd0d
JF
1361 def _down(self, ifaceobj):
1362 try:
223ba5af 1363 self.netlink.link_del(ifaceobj.name)
3b01ed76 1364 except Exception as e:
d486dd0d
JF
1365 self.log_warn(str(e))
1366
223ba5af
JF
1367 @staticmethod
1368 def _query_check_n_update(ifaceobj, ifaceobjcurr, attrname, attrval, running_attrval):
d486dd0d
JF
1369 if not ifaceobj.get_attr_value_first(attrname):
1370 return
1371 if running_attrval and attrval == running_attrval:
223ba5af 1372 ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
d486dd0d 1373 else:
223ba5af 1374 ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
d486dd0d 1375
223ba5af
JF
1376 @staticmethod
1377 def _query_check_n_update_addresses(ifaceobjcurr, attrname, addresses, running_addresses):
d486dd0d
JF
1378 if addresses:
1379 for a in addresses:
1380 if a in running_addresses:
1381 ifaceobjcurr.update_config_with_status(attrname, a, 0)
1382 else:
1383 ifaceobjcurr.update_config_with_status(attrname, a, 1)
3b01ed76
JF
1384 running_addresses = set(running_addresses).difference(
1385 set(addresses))
223ba5af 1386 [ifaceobjcurr.update_config_with_status(attrname, a, 1) for a in running_addresses]
d486dd0d
JF
1387
1388 def _query_check(self, ifaceobj, ifaceobjcurr):
223ba5af
JF
1389 ifname = ifaceobj.name
1390
1391 if not self.cache.link_exists(ifname):
d486dd0d 1392 return
d486dd0d 1393
223ba5af
JF
1394 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
1395
1396 if not cached_vxlan_ifla_info_data:
1397 ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1)
1398 return
1399
1400 for vxlan_attr_str, vxlan_attr_nl, callable_type in (
1401 ('vxlan-id', Link.IFLA_VXLAN_ID, int),
1402 ('vxlan-ttl', Link.IFLA_VXLAN_TTL, int),
e521508b 1403 ('vxlan-tos', Link.IFLA_VXLAN_TOS, int),
223ba5af
JF
1404 ('vxlan-port', Link.IFLA_VXLAN_PORT, int),
1405 ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, int),
0e936c3f 1406 ('vxlan-mcastgrp', Link.IFLA_VXLAN_GROUP, ipnetwork.IPv4Address),
40658337 1407 ('vxlan-mcastgrp6', Link.IFLA_VXLAN_GROUP6, ipnetwork.IPv6Address),
0e936c3f 1408 ('vxlan-svcnodeip', Link.IFLA_VXLAN_GROUP, ipnetwork.IPv4Address),
40658337 1409 ('vxlan-svcnodeip6', Link.IFLA_VXLAN_GROUP6, ipnetwork.IPv6Address),
223ba5af
JF
1410 ('vxlan-physdev', Link.IFLA_VXLAN_LINK, lambda x: self.cache.get_ifindex(x)),
1411 ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda boolean_str: utils.get_boolean_from_string(boolean_str)),
e521508b 1412 ('vxlan-udp-csum', Link.IFLA_VXLAN_UDP_CSUM, lambda boolean_str: utils.get_boolean_from_string(boolean_str)),
223ba5af
JF
1413 ):
1414 vxlan_attr_value = ifaceobj.get_attr_value_first(vxlan_attr_str)
d486dd0d 1415
223ba5af
JF
1416 if not vxlan_attr_value:
1417 continue
1418
1419 cached_vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl)
1420
1421 try:
1422 vxlan_attr_value_nl = callable_type(vxlan_attr_value)
1423 except Exception as e:
1424 self.logger.warning('%s: %s: %s' % (ifname, vxlan_attr_str, str(e)))
1425 ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
1426 continue
1427
1428 if vxlan_attr_value_nl == cached_vxlan_attr_value:
1429 ifaceobjcurr.update_config_with_status(vxlan_attr_str, vxlan_attr_value, 0)
1430 else:
1431 ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
1432
1433 #
1434 # vxlan-local-tunnelip
1435 #
1436 running_attrval = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
d486dd0d
JF
1437 attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
1438 if not attrval:
223ba5af 1439 attrval = self._vxlan_local_tunnelip
0e936c3f 1440 # TODO: vxlan._vxlan_local_tunnelip should be a ipnetwork.IPNetwork obj
d486dd0d
JF
1441 ifaceobj.update_config('vxlan-local-tunnelip', attrval)
1442
223ba5af 1443 if str(running_attrval) == self._clagd_vxlan_anycast_ip:
d486dd0d
JF
1444 # if local ip is anycast_ip, then let query_check to go through
1445 attrval = self._clagd_vxlan_anycast_ip
d486dd0d 1446
223ba5af
JF
1447 self._query_check_n_update(
1448 ifaceobj,
1449 ifaceobjcurr,
1450 'vxlan-local-tunnelip',
1451 str(attrval),
0e936c3f 1452 str(running_attrval.ip) if running_attrval else None
223ba5af 1453 )
d486dd0d 1454
223ba5af
JF
1455 #
1456 # vxlan-remoteip
1457 #
1458 purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
d486dd0d
JF
1459 if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'):
1460 # If purge remotes or if vxlan-remoteip's are set
1461 # in the config file, we are owners of the installed
1462 # remote-ip's, lets check and report any remote ips we don't
1463 # understand
223ba5af 1464 cached_svcnode = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
d486dd0d 1465
223ba5af
JF
1466 self._query_check_n_update_addresses(
1467 ifaceobjcurr,
1468 'vxlan-remoteip',
1469 ifaceobj.get_attr_value('vxlan-remoteip'),
0e936c3f 1470 self.iproute2.get_vxlan_peers(ifaceobj.name, str(cached_svcnode.ip) if cached_svcnode else None)
223ba5af 1471 )
a382b488 1472
e6edcd21 1473 #
66eb9ce3 1474 # vxlan-mcastgrp-map & vxlan-remoteip-map
e6edcd21 1475 #
66eb9ce3
JF
1476 fdb_mcast = {}
1477 fdb_remote = {}
e6edcd21 1478
66eb9ce3
JF
1479 for _, src_vni, dst in self.get_svd_running_fdb(ifname):
1480 ip = ipnetwork.IPv4Address(dst)
e6edcd21 1481
66eb9ce3
JF
1482 if ip.is_multicast:
1483 # we need to reconvert back to ipaddress.IPv4Address because
1484 # the existing code uses this type of obj (namely: __get_vxlan_mcastgrp_map)
1485 fdb_mcast[int(src_vni)] = IPv4Address(ip.ip)
1486 else:
1487 fdb_remote.setdefault(int(src_vni), []).append(ip)
e6edcd21 1488
66eb9ce3
JF
1489 #
1490 # vxlan-mcastgrp-map
1491 #
1492 user_mcastgrp_map = self.__get_vxlan_mcastgrp_map(ifaceobj)
e6edcd21 1493
66eb9ce3
JF
1494 if not user_mcastgrp_map and fdb_mcast:
1495 ifaceobjcurr.update_config_with_status(
1496 "vxlan-mcastgrp-map",
1497 " ".join(["%s=%s" % (vni, ip) for vni, ip in fdb_mcast.items()]),
1498 1
1499 )
1500 elif user_mcastgrp_map and not fdb_mcast:
1501 ifaceobjcurr.update_config_with_status("vxlan-mcastgrp-map", "", 1)
1502 elif user_mcastgrp_map or fdb_mcast:
1503 ifaceobjcurr.update_config_with_status(
1504 "vxlan-mcastgrp-map",
1505 " ".join(["%s=%s" % (vni, ip) for vni, ip in fdb_mcast.items()]),
1506 user_mcastgrp_map != fdb_mcast
1507 )
e6edcd21 1508
66eb9ce3
JF
1509 #
1510 # vxlan-remoteip-map
1511 #
1512 user_remote_ip_map = self.__get_vxlan_remote_ip_map(ifaceobj)
e6edcd21 1513
66eb9ce3 1514 if not user_remote_ip_map and fdb_remote:
e6edcd21 1515 ifaceobjcurr.update_config_with_status(
66eb9ce3
JF
1516 "vxlan-remoteip-map",
1517 " ".join(["%s=%s" % (vni, ",".join(map(str, ips))) for vni, ips in fdb_remote.items()]),
1518 1
e6edcd21 1519 )
66eb9ce3
JF
1520 elif user_remote_ip_map and not fdb_remote:
1521 ifaceobjcurr.update_config_with_status("vxlan-remoteip-map", "", 1)
1522 elif user_remote_ip_map or fdb_remote:
1523
1524 if user_remote_ip_map == fdb_remote:
1525 # display the user config with "pass"
1526 for config in ifaceobj.get_attr_value("vxlan-remoteip-map"):
1527 ifaceobjcurr.update_config_with_status(
1528 "vxlan-remoteip-map",
1529 config,
1530 0
1531 )
1532 else:
1533 # display current running state with ip ranges (but no vni ranges yet)
1534 ifaceobjcurr.update_config_with_status(
1535 "vxlan-remoteip-map",
1536 " ".join(["%s=%s" % (vni, ",".join(utils.compress_into_ip_ranges(ips))) for vni, ips in fdb_remote.items()]),
1537 1
1538 )
e6edcd21 1539
223ba5af
JF
1540 def _query_running(self, ifaceobjrunning):
1541 ifname = ifaceobjrunning.name
a382b488 1542
223ba5af
JF
1543 if not self.cache.link_exists(ifname):
1544 return
a382b488 1545
223ba5af 1546 if not self.cache.get_link_kind(ifname) == 'vxlan':
d486dd0d 1547 return
223ba5af
JF
1548
1549 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
1550
1551 if not cached_vxlan_ifla_info_data:
d486dd0d
JF
1552 return
1553
223ba5af
JF
1554 #
1555 # vxlan-id
1556 #
1557 vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID)
1558
1559 if not vxlan_id:
1560 # no vxlan id, meaning this not a vxlan
1561 return
1562
1563 ifaceobjrunning.update_config('vxlan-id', str(vxlan_id))
1564
1565 #
1566 # vxlan-port
1567 #
1568 vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT)
1569
1570 if vxlan_port:
1571 ifaceobjrunning.update_config('vxlan-port', vxlan_port)
1572
1573 #
1574 # vxlan-svcnode
1575 #
1576 vxlan_svcnode_value = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
1577
1578 if vxlan_svcnode_value:
1579 vxlan_svcnode_value = str(vxlan_svcnode_value)
1580 ifaceobjrunning.update_config('vxlan-svcnode', vxlan_svcnode_value)
d486dd0d 1581
223ba5af
JF
1582 #
1583 # vxlan-remoteip
1584 #
1585 purge_remotes = self.__get_vlxan_purge_remotes(None)
d486dd0d
JF
1586 if purge_remotes:
1587 # if purge_remotes is on, it means we own the
1588 # remote ips. Query them and add it to the running config
223ba5af 1589 attrval = self.iproute2.get_vxlan_peers(ifname, vxlan_svcnode_value)
d486dd0d 1590 if attrval:
223ba5af
JF
1591 [ifaceobjrunning.update_config('vxlan-remoteip', a) for a in attrval]
1592
1593 #
1594 # vxlan-link
1595 # vxlan-ageing
1596 # vxlan-learning
1597 # vxlan-local-tunnelip
1598 #
1599 for vxlan_attr_name, vxlan_attr_nl, callable_netlink_value_to_string in (
1600 ('vxlan-physdev', Link.IFLA_VXLAN_LINK, self._get_ifname_for_ifindex),
1601 ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, str),
1602 ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda value: 'on' if value else 'off'),
e521508b 1603 ('vxlan-udp-csum', Link.IFLA_VXLAN_UDP_CSUM, lambda value: 'on' if value else 'off'),
223ba5af
JF
1604 ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL, str),
1605 ):
1606 vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl)
1607
1608 if vxlan_attr_value is not None:
dc74ceda 1609 vxlan_attr_value_str = callable_netlink_value_to_string(vxlan_attr_value)
223ba5af
JF
1610
1611 if vxlan_attr_value:
1612 ifaceobjrunning.update_config(vxlan_attr_name, vxlan_attr_value_str)
1613
1614 def _get_ifname_for_ifindex(self, ifindex):
1615 """
1616 we need this middle-man function to query the cache
1617 cache.get_ifname can raise KeyError, we need to catch
1618 it and return None
1619 """
1620 try:
1621 return self.cache.get_ifname(ifindex)
1622 except KeyError:
1623 return None
a382b488 1624
223ba5af
JF
1625 _run_ops = {
1626 "pre-up": _up,
1627 "post-down": _down,
1628 "query-running": _query_running,
1629 "query-checkcurr": _query_check
1630 }
d486dd0d
JF
1631
1632 def get_ops(self):
3b01ed76 1633 return list(self._run_ops.keys())
d486dd0d 1634
d486dd0d
JF
1635 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
1636 op_handler = self._run_ops.get(operation)
1637 if not op_handler:
1638 return
223ba5af 1639
66eb9ce3
JF
1640 if not self._is_vxlan_device(ifaceobj):
1641 return
223ba5af 1642
66eb9ce3 1643 if "query" not in operation:
223ba5af
JF
1644 if not self.vxlan_mcastgrp_ref \
1645 and self.vxlan_physdev_mcast \
1646 and self.cache.link_exists(self.vxlan_physdev_mcast):
1647 self.netlink.link_del(self.vxlan_physdev_mcast)
1648 self.reset()
1649
d486dd0d
JF
1650 if operation == 'query-checkcurr':
1651 op_handler(self, ifaceobj, query_ifaceobj)
1652 else:
1653 op_handler(self, ifaceobj)