public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH ifupdown2 16/16] d/patches: add support for VXLAN-GBP flag
Date: Tue,  9 Jun 2026 15:25:22 +0200	[thread overview]
Message-ID: <20260609132522.235917-17-h.laimer@proxmox.com> (raw)
In-Reply-To: <20260609132522.235917-1-h.laimer@proxmox.com>

Add a vxlan-gbp attribute, threading IFLA_VXLAN_GBP through netlink and
the iproute2 create paths, to enable VXLAN Group Based Policy (the group
policy extension) 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>
---
 ...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..6c3680a
--- /dev/null
+++ b/debian/patches/pve/0016-addons-vxlan-add-vxlan-gbp-attribute.patch
@@ -0,0 +1,228 @@
+From 653270c53d5091a5b3d02d1692cb4dd59d1a0d5f Mon Sep 17 00:00:00 2001
+From: Hannes Laimer <h.laimer@proxmox.com>
+Date: Wed, 20 May 2026 17:56:00 +0200
+Subject: [PATCH] addons: vxlan: add vxlan-gbp attribute
+
+Add a vxlan-gbp attribute, threading IFLA_VXLAN_GBP through netlink and
+the 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       | 57 +++++++++++++++++++++++++++++++--
+ ifupdown2/lib/iproute2.py       | 14 ++++++--
+ ifupdown2/nlmanager/nlpacket.py | 20 ++++++++++++
+ 3 files changed, 86 insertions(+), 5 deletions(-)
+
+diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py
+index bb1a6ca..93200fb 100644
+--- a/ifupdown2/addons/vxlan.py
++++ b/ifupdown2/addons/vxlan.py
+@@ -145,7 +145,15 @@ 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 (group policy extension);"
++                        " can only be set at interface creation time and cannot be"
++                        " toggled on a running vxlan device",
++                "validvals": ["yes", "no", "on", "off"],
++                "default": "no",
++                "example": ["vxlan-gbp yes"],
++            },
+         }
+     }
+ 
+@@ -908,6 +916,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 = ifaceobj.get_attr_value_first('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 +1141,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 +1213,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 +1225,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 +1547,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: the attribute is present when on and
++        # absent when off, so a missing cached value must compare equal to
++        # "off". The generic loop above assumes a stored value and can't
++        # express that, hence the dedicated check here.
++        #
++        vxlan_gbp_str = ifaceobj.get_attr_value_first('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 +1772,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..dab36d2 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 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: presence of the attribute encodes truth, zero payload.
++        if not info_data_value:
++            return
++        sub_attr_pack_layout.append("HH")
++        sub_attr_payload.append(4)  # length: header only
++        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





      parent reply	other threads:[~2026-06-09 13:27 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-09 13:25 [RFC cluster/docs/ifupdown2/manager/network/proxmox{-ebpf,-ve-rs,-perl-rs} 00/16] sdn: add microsegmentation support Hannes Laimer
2026-06-09 13:25 ` [PATCH proxmox-ebpf 01/16] agent: add userspace coordinator and stateless policy subsystem Hannes Laimer
2026-06-09 13:25 ` [PATCH proxmox-ebpf 02/16] bpf: add bridge subsystem Hannes Laimer
2026-06-09 13:25 ` [PATCH proxmox-ebpf 03/16] debian: add packaging and boot-time oneshot unit Hannes Laimer
2026-06-09 13:25 ` [PATCH proxmox-ve-rs 04/16] ve-config: sdn: add microseg config types Hannes Laimer
2026-06-09 13:25 ` [PATCH proxmox-perl-rs 05/16] sdn: add microseg config binding Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-cluster 06/16] cfs: add 'sdn/microseg.cfg' to observed files Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-network 07/16] sdn: microseg: add config and API Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-network 08/16] sdn: zones: trigger microseg apply on tap_plug Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-network 09/16] sdn: zones: add vxlan-gbp option to vxlan and evpn zones Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-network 10/16] evpn: disable vxlan-learning on create if GBP is enabled Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-manager 11/16] ui: sdn: add microsegmentation Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-manager 12/16] network: apply microseg state on reload Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-manager 13/16] ui: sdn: zones: add vxlan-gbp checkbox to vxlan and evpn Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-docs 14/16] sdn: add microsegmentation section Hannes Laimer
2026-06-09 13:25 ` [PATCH pve-docs 15/16] sdn: add VXLAN-GBP flag to evpn/vxlan zone sections Hannes Laimer
2026-06-09 13:25 ` Hannes Laimer [this message]

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=20260609132522.235917-17-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal