From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH ifupdown2] d/patches: add support for VXLAN-GBP flag
Date: Fri, 28 Aug 2026 15:47:08 +0200 [thread overview]
Message-ID: <20260828134708.692354-1-h.laimer@proxmox.com> (raw)
Add a vxlan-gbp attribute, threading the GBP netlink flag through the
netlink and iproute2 create paths, to enable VXLAN-GBP on a VXLAN
interface.
The flag is create-only, so it is only set on create, with a warning
when a running device diverges. As it is encoded by presence, ifquery
matches a configured "off" against an absent flag.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
this was originally part of the microseg series[1], but this also makes
sense standalone, and since that series is large enough as is, I decided
to send this one on its own
changes since the original:
- use `self.__get_vxlan_attribute`, so policy.d default are respected
- minor wording changes
[1] https://lore.proxmox.com/pve-devel/20260709091852.538885-12-h.laimer@proxmox.com/
...addons-vxlan-add-vxlan-gbp-attribute.patch | 228 ++++++++++++++++++
debian/patches/series | 1 +
2 files changed, 229 insertions(+)
create mode 100644 debian/patches/pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch
diff --git a/debian/patches/pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch b/debian/patches/pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch
new file mode 100644
index 0000000..e671fac
--- /dev/null
+++ b/debian/patches/pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch
@@ -0,0 +1,228 @@
+From 48fbd1d9d7e9d1e4c9682468ca2dac90d137e33f Mon Sep 17 00:00:00 2001
+From: Hannes Laimer <h.laimer@proxmox.com>
+Date: Fri, 28 Aug 2026 14:42:05 +0200
+Subject: [PATCH] addons: vxlan: add vxlan-gbp attribute
+
+Add a vxlan-gbp attribute, threading the GBP netlink flag through the
+netlink and iproute2 create paths. The flag is create-only, so it is only
+set on create, with a warning when a running device diverges. Being
+encoded by presence, ifquery matches a configured "off" against an absent
+flag.
+
+Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
+---
+ ifupdown2/addons/vxlan.py | 56 +++++++++++++++++++++++++++++++--
+ ifupdown2/lib/iproute2.py | 14 +++++++--
+ ifupdown2/nlmanager/nlpacket.py | 20 ++++++++++++
+ 3 files changed, 85 insertions(+), 5 deletions(-)
+
+diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py
+index bb1a6ca..1e1fda3 100644
+--- a/ifupdown2/addons/vxlan.py
++++ b/ifupdown2/addons/vxlan.py
+@@ -145,7 +145,14 @@ class vxlan(Vxlan, moduleBase):
+ "help": "L3 VxLAN interface (vni list and range are supported)",
+ "validvals": ["<number>"],
+ "example": ["vxlan-vni 42"]
+- }
++ },
++ "vxlan-gbp": {
++ "help": "enable VXLAN Group Based Policy, create-only since the"
++ " kernel cannot toggle it on a running vxlan device",
++ "validvals": ["yes", "no", "on", "off"],
++ "default": "no",
++ "example": ["vxlan-gbp yes"],
++ },
+ }
+ }
+
+@@ -908,6 +915,28 @@ class vxlan(Vxlan, moduleBase):
+ self.logger.info("%s: set vxlan-learning %s" % (ifaceobj.name, "on" if vxlan_learning else "off"))
+ user_request_vxlan_info_data[Link.IFLA_VXLAN_LEARNING] = vxlan_learning
+
++ def __config_vxlan_gbp(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
++ vxlan_gbp_str = self.__get_vxlan_attribute(ifaceobj, "vxlan-gbp")
++ if not vxlan_gbp_str:
++ return
++
++ vxlan_gbp = utils.get_boolean_from_string(vxlan_gbp_str)
++ cached_gbp = bool(cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GBP))
++
++ if link_exists and vxlan_gbp != cached_gbp:
++ # IFLA_VXLAN_GBP is a NLA_FLAG and the kernel only honors it at
++ # device creation, changing it on a running vxlan is rejected.
++ self.logger.warning(
++ "%s: vxlan-gbp can only be set at vxlan device creation,"
++ " recreate the interface to change it (current %s, requested %s)"
++ % (ifaceobj.name, "on" if cached_gbp else "off", "on" if vxlan_gbp else "off")
++ )
++ return
++
++ if vxlan_gbp and not link_exists:
++ self.logger.info("%s: set vxlan-gbp on" % ifaceobj.name)
++ user_request_vxlan_info_data[Link.IFLA_VXLAN_GBP] = True
++
+ def __config_vxlan_udp_csum(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
+ vxlan_udp_csum = ifaceobj.get_attr_value_first('vxlan-udp-csum')
+
+@@ -1111,6 +1140,7 @@ class vxlan(Vxlan, moduleBase):
+ self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+
+ self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
++ self.__config_vxlan_gbp(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+ self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+ self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+ vxlan_ttl = self.__config_vxlan_ttl(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
+@@ -1182,7 +1212,8 @@ class vxlan(Vxlan, moduleBase):
+ user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT),
+ vxlan_vnifilter,
+ vxlan_ttl,
+- local.version if local else 4
++ local.version if local else 4,
++ gbp=bool(user_request_vxlan_info_data.get(Link.IFLA_VXLAN_GBP))
+ )
+ elif ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI:
+ self.iproute2.link_add_l3vxi(
+@@ -1193,7 +1224,8 @@ class vxlan(Vxlan, moduleBase):
+ vxlan_physdev,
+ user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT),
+ vxlan_ttl,
+- local.version if local else 4
++ local.version if local else 4,
++ gbp=bool(user_request_vxlan_info_data.get(Link.IFLA_VXLAN_GBP))
+ )
+ else:
+ try:
+@@ -1514,6 +1546,23 @@ class vxlan(Vxlan, moduleBase):
+ else:
+ ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
+
++ #
++ # vxlan-gbp
++ #
++ # IFLA_VXLAN_GBP is a NLA_FLAG, so the attribute is present when on
++ # and absent when off. A missing cached value must compare equal to
++ # "off", which the generic string compare above cannot express,
++ # hence the dedicated check here.
++ #
++ vxlan_gbp_str = self.__get_vxlan_attribute(ifaceobj, "vxlan-gbp")
++ if vxlan_gbp_str:
++ vxlan_gbp = utils.get_boolean_from_string(vxlan_gbp_str)
++ cached_gbp = bool(cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GBP))
++ if vxlan_gbp == cached_gbp:
++ ifaceobjcurr.update_config_with_status('vxlan-gbp', vxlan_gbp_str, 0)
++ else:
++ ifaceobjcurr.update_config_with_status('vxlan-gbp', 'on' if cached_gbp else 'off', 1)
++
+ #
+ # vxlan-local-tunnelip
+ #
+@@ -1722,6 +1771,7 @@ class vxlan(Vxlan, moduleBase):
+ ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, str),
+ ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda value: 'on' if value else 'off'),
+ ('vxlan-udp-csum', Link.IFLA_VXLAN_UDP_CSUM, lambda value: 'on' if value else 'off'),
++ ('vxlan-gbp', Link.IFLA_VXLAN_GBP, lambda value: 'on' if value else 'off'),
+ ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL, str),
+ ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL6, str),
+ ):
+diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py
+index 15b581e..c5268ed 100644
+--- a/ifupdown2/lib/iproute2.py
++++ b/ifupdown2/lib/iproute2.py
+@@ -280,7 +280,7 @@ class IPRoute2(Cache, Requirements):
+
+ ###
+
+- def link_add_single_vxlan(self, link_exists, ifname, ip, group, physdev, port, vnifilter="off", ttl=None, ipversion=4):
++ def link_add_single_vxlan(self, link_exists, ifname, ip, group, physdev, port, vnifilter="off", ttl=None, ipversion=4, gbp=False):
+ cmd = []
+
+ if ipversion == 6:
+@@ -303,6 +303,11 @@ class IPRoute2(Cache, Requirements):
+ if vnifilter and utils.get_boolean_from_string(vnifilter):
+ cmd.append("vnifilter")
+
++ # GBP, like vnifilter, is a create-only flag and cannot be
++ # toggled on a running device.
++ if gbp:
++ cmd.append("gbp")
++
+ if ip:
+ cmd.append("local %s" % ip)
+
+@@ -321,7 +326,7 @@ class IPRoute2(Cache, Requirements):
+ 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, ipversion=4):
++ def link_add_l3vxi(self, link_exists, ifname, ip, group, physdev, port, ttl=None, ipversion=4, gbp=False):
+ self.logger.info("creating l3vxi device: %s" % ifname)
+
+ cmd = []
+@@ -343,6 +348,11 @@ class IPRoute2(Cache, Requirements):
+ # So we are only setting this attribute on vxlan creation
+ cmd.append("link add dev %s type vxlan external vnifilter" % ifname)
+
++ # GBP, like vnifilter, is a create-only flag and cannot be
++ # toggled on a running device.
++ if gbp:
++ cmd.append("gbp")
++
+ if ip:
+ cmd.append("local %s" % ip)
+
+diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py
+index c3b0b67..298c641 100644
+--- a/ifupdown2/nlmanager/nlpacket.py
++++ b/ifupdown2/nlmanager/nlpacket.py
+@@ -1043,6 +1043,11 @@ class Attribute(object):
+ return obj(self.value)
+ return self.value
+
++ @staticmethod
++ def decode_flag_attribute(data, _=None):
++ # NLA_FLAG, presence alone means True
++ return True
++
+ @staticmethod
+ def decode_one_byte_attribute(data, _=None):
+ # we don't need to use the unpack function because bytes are a list of ints
+@@ -1146,6 +1151,15 @@ class Attribute(object):
+ sub_attr_pack_layout.append("Bxxx")
+ sub_attr_payload.append(info_data_value)
+
++ @staticmethod
++ def encode_flag_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
++ # NLA_FLAG carries no payload, presence alone encodes True.
++ if not info_data_value:
++ return
++ sub_attr_pack_layout.append("HH")
++ sub_attr_payload.append(4) # header-only length
++ sub_attr_payload.append(info_data_type)
++
+ @staticmethod
+ def encode_bond_xmit_hash_policy_attribute(sub_attr_pack_layout, sub_attr_payload, info_data_type, info_data_value):
+ return Attribute.encode_one_byte_attribute(
+@@ -2337,6 +2351,9 @@ class AttributeIFLA_LINKINFO(Attribute):
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_RX: Attribute.decode_one_byte_attribute,
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REPLICATION_TYPE: Attribute.decode_one_byte_attribute,
+
++ # flag attributes (zero-length) ################################
++ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_GBP: Attribute.decode_flag_attribute,
++
+ # 2 bytes network byte order attributes ########################
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_PORT: Attribute.decode_two_bytes_network_byte_order_attribute,
+
+@@ -2678,6 +2695,9 @@ class AttributeIFLA_LINKINFO(Attribute):
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REMCSUM_RX: Attribute.encode_one_byte_attribute,
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_REPLICATION_TYPE: Attribute.encode_one_byte_attribute,
+
++ # flag attributes (zero-length) ################################
++ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_GBP: Attribute.encode_flag_attribute,
++
+ # 4 bytes attributes ###########################################
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_ID: Attribute.encode_four_bytes_attribute,
+ NetlinkPacket_IFLA_LINKINFO_Attributes.IFLA_VXLAN_LINK: Attribute.encode_four_bytes_attribute,
+--
+2.47.3
+
diff --git a/debian/patches/series b/debian/patches/series
index 2865533..ee2cb46 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -16,3 +16,4 @@ upstream/0001-use-raw-strings-for-regex-to-fix-backslash-interpret.patch
upstream/0002-vxlan-add-support-for-IPv6-vxlan-local-tunnelip.patch
pve/0014-nlmanager-read-ipv6-devconf-disable_ipv6-attribute-t.patch
pve/0015-revert-addons-bond-warn-if-sub-interface-is-detected-on-bond-slave.patch
+pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch
--
2.47.3
reply other threads:[~2026-08-28 13:47 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260828134708.692354-1-h.laimer@proxmox.com \
--to=h.laimer@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.