all lists on 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 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.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal