diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-11-12 08:04:19 +0100 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-11-12 19:58:46 +0100 |
commit | 228693af47ffb43098b5cee41f5624b8351d9efa (patch) | |
tree | 908363dce0e6d5769421cb4fba4668864eeea1d6 | |
parent | network/brvlan: read bridge vlan IDs through netlink and save them (diff) | |
download | systemd-228693af47ffb43098b5cee41f5624b8351d9efa.tar.xz systemd-228693af47ffb43098b5cee41f5624b8351d9efa.zip |
network/brvlan: remove unnecessary bridge vlan IDs
When an interface is being reconfigured with different bridge vlan
settings, this makes old vlan IDs on the interface removed.
This also makes the PVID= setting support negative boolean value, e.g. "no",
in which case, the currently assigned PVID (typically, assigned by the
kernel when an interface is joined to a bridge) is dropped.
This feature is requested by #15291.
Note, if a .network file has no settings about bridge vlan, networkd
keeps the currently assigned vlan IDs. That's intended, to make not
break existing setups.
When a .network file has only PVID=no line in [BridgeVLAN] section, then
all assigned vlan IDs are removed.
Fixes #29975.
Closes #15291.
-rw-r--r-- | man/systemd.network.xml | 8 | ||||
-rw-r--r-- | src/network/networkd-bridge-vlan.c | 88 | ||||
-rw-r--r-- | src/network/networkd-bridge-vlan.h | 8 | ||||
-rw-r--r-- | src/network/networkd-link.h | 1 | ||||
-rw-r--r-- | src/network/networkd-network.c | 2 | ||||
-rw-r--r-- | src/network/networkd-queue.c | 3 | ||||
-rw-r--r-- | src/network/networkd-queue.h | 1 | ||||
-rw-r--r-- | src/network/networkd-setlink.c | 49 |
8 files changed, 140 insertions, 20 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 886258fbd0..045ad271f8 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -5880,9 +5880,11 @@ ServerAddress=192.168.0.1/24</programlisting> <varlistentry> <term><varname>PVID=</varname></term> <listitem> - <para>The Port VLAN ID specified here is assigned to all untagged frames at ingress. - <varname>PVID=</varname> can be used only once. Configuring <varname>PVID=</varname> implicates the use of - <varname>VLAN=</varname> above and will enable the VLAN ID for ingress as well.</para> + <para>The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an + VLAN ID or negative boolean value (e.g. <literal>no</literal>). When false, the currently + assigned port VLAN ID will be dropped. Configuring <varname>PVID=</varname> implicates the use of + <varname>VLAN=</varname> setting in the above and will enable the VLAN ID for ingress as well. + Defaults to unset, and will keep the assigned port VLAN ID if exists.</para> <xi:include href="version-info.xml" xpointer="v231"/> </listitem> diff --git a/src/network/networkd-bridge-vlan.c b/src/network/networkd-bridge-vlan.c index c38ae16a47..d84b6b34f6 100644 --- a/src/network/networkd-bridge-vlan.c +++ b/src/network/networkd-bridge-vlan.c @@ -72,18 +72,42 @@ static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool u return 0; } +static uint16_t link_get_pvid(Link *link, bool *ret_untagged) { + assert(link); + assert(link->network); + + if (vlanid_is_valid(link->network->bridge_vlan_pvid)) { + if (ret_untagged) + *ret_untagged = is_bit_set(link->network->bridge_vlan_pvid, + link->network->bridge_vlan_untagged_bitmap); + return link->network->bridge_vlan_pvid; + } + + if (link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) { + if (ret_untagged) + *ret_untagged = link->bridge_vlan_pvid_is_untagged; + return link->bridge_vlan_pvid; + } + + if (ret_untagged) + *ret_untagged = false; + return UINT16_MAX; +} + static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { - uint16_t begin = UINT16_MAX; - bool untagged; + uint16_t pvid, begin = UINT16_MAX; + bool untagged, pvid_is_untagged; int r; assert(link); assert(link->network); assert(m); + pvid = link_get_pvid(link, &pvid_is_untagged); + for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) { - if (k == link->network->bridge_vlan_pvid) { + if (k == pvid) { /* PVID needs to be sent alone. Finish previous bits. */ if (begin != UINT16_MAX) { assert(begin < k); @@ -95,8 +119,7 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { begin = UINT16_MAX; } - untagged = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap); - r = add_single(m, k, untagged, /* is_pvid = */ true); + r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true); if (r < 0) return r; @@ -150,7 +173,48 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { return 0; } -int bridge_vlan_set_message(Link *link, sd_netlink_message *m) { +static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) { + uint16_t pvid, begin = UINT16_MAX; + int r; + + assert(link); + assert(link->network); + assert(m); + + pvid = link_get_pvid(link, NULL); + + for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) { + + if (k == pvid || + !is_bit_set(k, link->bridge_vlan_bitmap) || + is_bit_set(k, link->network->bridge_vlan_bitmap)) { + /* This bit is not necessary to be removed. Finish previous bits. */ + if (begin != UINT16_MAX) { + assert(begin < k); + + r = add_range(m, begin, k - 1, /* untagged = */ false); + if (r < 0) + return r; + + begin = UINT16_MAX; + } + + continue; + } + + if (begin != UINT16_MAX) + continue; + + /* This is the starting point of a new bit sequence. Save the position. */ + begin = k; + } + + /* No pending bit sequence. */ + assert(begin == UINT16_MAX); + return 0; +} + +int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set) { int r; assert(link); @@ -171,7 +235,10 @@ int bridge_vlan_set_message(Link *link, sd_netlink_message *m) { return r; } - r = bridge_vlan_append_set_info(link, m); + if (is_set) + r = bridge_vlan_append_set_info(link, m); + else + r = bridge_vlan_append_del_info(link, m); if (r < 0) return r; @@ -275,7 +342,12 @@ int config_parse_bridge_vlan_id( assert(rvalue); if (isempty(rvalue)) { - *id = UINT16_MAX; + *id = BRIDGE_VLAN_KEEP_PVID; + return 0; + } + + if (parse_boolean(rvalue) == 0) { + *id = BRIDGE_VLAN_REMOVE_PVID; return 0; } diff --git a/src/network/networkd-bridge-vlan.h b/src/network/networkd-bridge-vlan.h index 43d365e745..0366cc6fee 100644 --- a/src/network/networkd-bridge-vlan.h +++ b/src/network/networkd-bridge-vlan.h @@ -6,20 +6,26 @@ ***/ #include <inttypes.h> +#include <stdbool.h> #include "sd-netlink.h" #include "conf-parser.h" +#include "vlan-util.h" #define BRIDGE_VLAN_BITMAP_MAX 4096 #define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32) +#define BRIDGE_VLAN_KEEP_PVID UINT16_MAX +#define BRIDGE_VLAN_REMOVE_PVID (UINT16_MAX - 1) +assert_cc(BRIDGE_VLAN_REMOVE_PVID > VLANID_MAX); + typedef struct Link Link; typedef struct Network Network; void network_adjust_bridge_vlan(Network *network); -int bridge_vlan_set_message(Link *link, sd_netlink_message *m); +int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set); int link_update_bridge_vlan(Link *link, sd_netlink_message *m); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index a6c2f2d720..7458ea93bd 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -155,6 +155,7 @@ typedef struct Link { bool activated:1; bool master_set:1; bool stacked_netdevs_created:1; + bool bridge_vlan_set:1; sd_dhcp_server *dhcp_server; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 654fd7bb9e..d4401757bd 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -450,7 +450,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID, .multicast_router = _MULTICAST_ROUTER_INVALID, - .bridge_vlan_pvid = UINT16_MAX, + .bridge_vlan_pvid = BRIDGE_VLAN_KEEP_PVID, .lldp_mode = LLDP_MODE_ROUTERS_ONLY, .lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID, diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 90caefbc72..cc439a74a8 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -303,7 +303,8 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = { [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode", [REQUEST_TYPE_SET_LINK_BOND] = "bond configurations", [REQUEST_TYPE_SET_LINK_BRIDGE] = "bridge configurations", - [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations", + [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations (step 1)", + [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN] = "bridge VLAN configurations (step 2)", [REQUEST_TYPE_SET_LINK_CAN] = "CAN interface configurations", [REQUEST_TYPE_SET_LINK_FLAGS] = "link flags", [REQUEST_TYPE_SET_LINK_GROUP] = "interface group", diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index e58d1be66f..d6f5de421e 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -37,6 +37,7 @@ typedef enum RequestType { REQUEST_TYPE_SET_LINK_BOND, /* Setting bond configs. */ REQUEST_TYPE_SET_LINK_BRIDGE, /* Setting bridge configs. */ REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, /* Setting bridge VLAN configs. */ + REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN, /* Removing bridge VLAN configs. */ REQUEST_TYPE_SET_LINK_CAN, /* Setting CAN interface configs. */ REQUEST_TYPE_SET_LINK_FLAGS, /* Setting IFF_NOARP or friends. */ REQUEST_TYPE_SET_LINK_GROUP, /* Setting interface group. */ diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 3e1d904836..2b37c86d23 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -103,6 +103,19 @@ static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Requ } static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + int r; + + assert(link); + + r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); + if (r <= 0) + return r; + + link->bridge_vlan_set = true; + return 0; +} + +static int link_del_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); } @@ -326,7 +339,12 @@ static int link_configure_fill_message( return r; break; case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN: - r = bridge_vlan_set_message(link, req); + r = bridge_vlan_set_message(link, req, /* is_set = */ true); + if (r < 0) + return r; + break; + case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN: + r = bridge_vlan_set_message(link, req, /* is_set = */ false); if (r < 0) return r; break; @@ -410,6 +428,8 @@ static int link_configure(Link *link, Request *req) { r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->master_ifindex); else if (IN_SET(req->type, REQUEST_TYPE_SET_LINK_CAN, REQUEST_TYPE_SET_LINK_IPOIB)) r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); + else if (req->type == REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN) + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_DELLINK, link->ifindex); else r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); if (r < 0) @@ -460,9 +480,11 @@ static int link_is_ready_to_set_link(Link *link, Request *req) { if (link->network->keep_master && link->master_ifindex <= 0 && !streq_ptr(link->kind, "bridge")) return false; - break; + case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN: + return link->bridge_vlan_set; + case REQUEST_TYPE_SET_LINK_CAN: /* Do not check link->set_flgas_messages here, as it is ok even if link->flags * is outdated, and checking the counter causes a deadlock. */ @@ -684,11 +706,14 @@ int link_request_to_set_bridge(Link *link) { } int link_request_to_set_bridge_vlan(Link *link) { + int r; + assert(link); assert(link->network); /* If nothing configured, use the default vlan ID. */ - if (memeqzero(link->network->bridge_vlan_bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t))) + if (memeqzero(link->network->bridge_vlan_bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t)) && + link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) return 0; if (!link->network->bridge && !streq_ptr(link->kind, "bridge")) { @@ -704,9 +729,21 @@ int link_request_to_set_bridge_vlan(Link *link) { return 0; } - return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, - link_set_bridge_vlan_handler, - NULL); + link->bridge_vlan_set = false; + + r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, + link_set_bridge_vlan_handler, + NULL); + if (r < 0) + return r; + + r = link_request_set_link(link, REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN, + link_del_bridge_vlan_handler, + NULL); + if (r < 0) + return r; + + return 0; } int link_request_to_set_can(Link *link) { |