"help": "whether to perform checksumming or not",
"validvals": ["yes", "no"],
"example": ["vxlan-udp-csum no"]
+ },
+ "vxlan-vni": {
+ "help": "L3 VxLAN interface (vni list and range are supported)",
+ "validvals": ["<number>"],
+ "example": ["vxlan-vni 42"]
}
}
}
ifaceobj.link_kind |= ifaceLinkKind.VXLAN
self._set_global_local_ip(ifaceobj)
+ self.__check_and_tag_l3vxi(ifaceobj)
+
if not old_ifaceobjs and not self.tvd_svd_mix_support:
# mixing TVD and SVD is not supported - we need to warn the user
# we use a dictionary to make sure to only warn once and prevent each
"%s: mixing single-vxlan-device with tradional %s is not supported (TVD: %s)"
% (ifaceobj.name, "vxlans" if len(self.traditional_vxlan_configured) > 1 else "vxlan", ", ".join(self.traditional_vxlan_configured))
)
+ elif ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI:
+ pass
else:
self.traditional_vxlan_configured.add(ifaceobj.name)
return None
+ def __check_and_tag_l3vxi(self, ifaceobj):
+ if ifaceobj.get_attr_value_first("vxlan-vni"):
+ # to validate the l3vxi interface we need to see the vrf attribute
+ if ifaceobj.get_attr_value_first("vrf"):
+ ifaceobj.link_privflags |= ifaceLinkPrivFlags.L3VXI
+ else:
+ self.logger.warning("%s: l3vxi misconfiguration? missing `vrf` attribute" % ifaceobj.name)
+
def _set_global_local_ip(self, ifaceobj):
vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
if vxlan_local_tunnel_ip and not self._vxlan_local_tunnelip:
def _is_vxlan_device(ifaceobj):
return ifaceobj.link_kind & ifaceLinkKind.VXLAN \
or ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN \
+ or ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI \
or ifaceobj.get_attr_value_first("vxlan-id") \
+ or ifaceobj.get_attr_value_first("vxlan-vni") \
or ifaceobj.get_attr_value_first("bridge-vlan-vni-map")
def __get_vlxan_purge_remotes(self, ifaceobj):
if err:
self.log_error(err, ifaceobj)
+ def __get_vxlan_vni_list(self, ifaceobj, string=True):
+ vxlan_vni_str = self.__get_vxlan_attribute(ifaceobj, "vxlan-vni")
+
+ if vxlan_vni_str:
+ # validate range but return string to be used in bridge vni add cmd
+ vxlan_vni_range = utils.ranges_to_ints(vxlan_vni_str.split())
+ return vxlan_vni_str if string else vxlan_vni_range
+
+ return None
def _up(self, ifaceobj):
self.check_and_raise_svd_tvd_errors(ifaceobj)
vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id")
- if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and not vxlan_id_str:
+ if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and not ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI and not vxlan_id_str:
self.logger.warning("%s: missing vxlan-id attribute on vxlan device" % ifaceobj.name)
return
self.__config_vxlan_udp_csum(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
local = self.__config_vxlan_local_tunnelip(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+ vxlan_vni = self.__get_vxlan_vni_list(ifaceobj)
+
vxlan_mcast_grp = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp")
vxlan_svcnodeip = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip")
vxlan_vnifilter,
vxlan_ttl
)
+ elif ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI:
+ self.iproute2.link_add_l3vxi(
+ link_exists,
+ ifname,
+ local.ip if local else None,
+ group.ip if group else None,
+ vxlan_physdev,
+ user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT),
+ vxlan_ttl
+ )
+ try:
+ self.iproute2.bridge_vni_add(ifname, vxlan_vni)
+ except Exception as e:
+ self.logger.warning("%s: l3 vxlan vni failure: %s" % (ifname, e))
else:
try:
if flap_vxlan_device:
self.iproute2.get_vxlan_peers(ifaceobj.name, str(cached_svcnode.ip) if cached_svcnode else None)
)
+ # not ideal but will work for now, l3vxi dev:
+ if ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI:
+ user_config_vni_list = set(self.__get_vxlan_vni_list(ifaceobj, string=False))
+ vxlan_vni_list = set()
+
+ for obj in json.loads(utils.exec_command("bridge -j -p vni show dev %s" % ifname) or "[]"):
+ for vni_obj in obj.get("vnis", []):
+ start = vni_obj.get("vni")
+ end = vni_obj.get("vniEnd")
+
+ for vni in utils.ranges_to_ints(["%s-%s" % (start, end if end else start)]):
+ vxlan_vni_list.add(vni)
+
+ ifaceobjcurr.update_config_with_status(
+ "vxlan-vni",
+ " ".join(utils.compress_into_ranges(vxlan_vni_list)),
+ vxlan_vni_list != user_config_vni_list
+ )
+
#
# vxlan-mcastgrp-map & vxlan-remoteip-map
# fdb entries can be added by FRR, so we won't be checking the running
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
self.__update_cache_after_link_creation(ifname, "vxlan")
+ def link_add_l3vxi(self, link_exists, ifname, ip, group, physdev, port, ttl=None):
+ self.logger.info("creating l3vxi device: %s" % ifname)
+
+ if link_exists:
+ # When updating an SVD we need to use `ip link set` and we have to
+ # drop the external keyword:
+ # $ ip link set dev vxlan0 type vxlan external local 27.0.0.242 dev ipmr-lo
+ # Error: vxlan: cannot change COLLECT_METADATA flag.
+ cmd = ["link set dev %s type vxlan" % ifname]
+ else:
+ cmd = ["link add dev %s type vxlan external vnifilter" % ifname]
+ # when changing local ip, if we specify vnifilter we get:
+ # Error: vxlan: cannot change flag.
+ # So we are only setting this attribute on vxlan creation
+
+ if ip:
+ cmd.append("local %s" % ip)
+
+ if physdev:
+ cmd.append("dev %s" % physdev)
+
+ if group:
+ cmd.append("group %s" % group)
+
+ if port:
+ cmd.append("dstport %s" % port)
+
+ if ttl:
+ cmd.append("ttl %s" % ttl)
+
+ self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
+ self.__update_cache_after_link_creation(ifname, "vxlan")
+
def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None,
remoteips=None, learning='on', ageing=None, ttl=None, physdev=None, udp_csum='on', tos = None):
if svcnodeip and remoteips:
cmd_args += ' group %s' %(g)
self.__execute_or_batch(utils.bridge_cmd, cmd_args)
+ def bridge_vni_add(self, vxlan_device, vni):
+ # bridge vni add understands ranges:
+ # bridge vni add dev vx0 vni 10,11,20-30
+ self.__execute_or_batch(
+ utils.bridge_cmd,
+ "vni add dev %s vni %s" % (vxlan_device, ','.join(vni.split()))
+ )
+
def bridge_vni_del_list(self, vxlandev, vnis):
cmd_args = "vni del dev %s vni %s" % (vxlandev, ','.join(vnis))
self.__execute_or_batch(utils.bridge_cmd, cmd_args)