Merge pull request #29987 from yuwata/network-bridge-vlan

network: remove unnecessary bridge vlan IDs
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2023-12-06 22:25:42 +01:00 committed by GitHub
commit bfa95c369a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 591 additions and 229 deletions

View file

@ -5866,8 +5866,9 @@ ServerAddress=192.168.0.1/24</programlisting>
<varlistentry>
<term><varname>VLAN=</varname></term>
<listitem>
<para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes
an integer in the range 1…4094.</para>
<para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an
integer in the range 1…4094. This setting can be specified multiple times. If an empty string is
assigned, then the all previous assignments are cleared.</para>
<xi:include href="version-info.xml" xpointer="v231"/>
</listitem>
@ -5876,8 +5877,10 @@ ServerAddress=192.168.0.1/24</programlisting>
<term><varname>EgressUntagged=</varname></term>
<listitem>
<para>The VLAN ID specified here will be used to untag frames on egress. Configuring
<varname>EgressUntagged=</varname> implicates the use of <varname>VLAN=</varname> above and will enable the
VLAN ID for ingress as well. This can be either a single ID or a range M-N.</para>
<varname>EgressUntagged=</varname> implicates the use of <varname>VLAN=</varname> above and will
enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This
setting can be specified multiple times. If an empty string is assigned, then the all previous
assignments are cleared.</para>
<xi:include href="version-info.xml" xpointer="v231"/>
</listitem>
@ -5885,9 +5888,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>

View file

@ -17,140 +17,311 @@
#include "parse-util.h"
#include "vlan-util.h"
static bool is_bit_set(unsigned bit, uint32_t scope) {
assert(bit < sizeof(scope)*8);
return scope & (UINT32_C(1) << bit);
static bool is_bit_set(unsigned nr, const uint32_t *addr) {
assert(nr < BRIDGE_VLAN_BITMAP_MAX);
return addr[nr / 32] & (UINT32_C(1) << (nr % 32));
}
static void set_bit(unsigned nr, uint32_t *addr) {
if (nr < BRIDGE_VLAN_BITMAP_MAX)
addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
assert(nr < BRIDGE_VLAN_BITMAP_MAX);
addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
}
static int find_next_bit(int i, uint32_t x) {
int j;
if (i >= 32)
return -1;
/* find first bit */
if (i < 0)
return BUILTIN_FFS_U32(x);
/* mask off prior finds to get next */
j = __builtin_ffs(x >> i);
return j ? j + i : 0;
static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid) {
assert(m);
assert(id < BRIDGE_VLAN_BITMAP_MAX);
return sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
&(struct bridge_vlan_info) {
.vid = id,
.flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) |
(is_pvid ? BRIDGE_VLAN_INFO_PVID : 0),
},
sizeof(struct bridge_vlan_info));
}
int bridge_vlan_append_info(
const Link *link,
sd_netlink_message *req,
uint16_t pvid,
const uint32_t *br_vid_bitmap,
const uint32_t *br_untagged_bitmap) {
static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged) {
int r;
struct bridge_vlan_info br_vlan;
bool done, untagged = false;
uint16_t begin, end;
int r, cnt;
assert(m);
assert(begin <= end);
assert(end < BRIDGE_VLAN_BITMAP_MAX);
if (begin == end)
return add_single(m, begin, untagged, /* is_pvid = */ false);
r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
&(struct bridge_vlan_info) {
.vid = begin,
.flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) |
BRIDGE_VLAN_INFO_RANGE_BEGIN,
},
sizeof(struct bridge_vlan_info));
if (r < 0)
return r;
r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
&(struct bridge_vlan_info) {
.vid = end,
.flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) |
BRIDGE_VLAN_INFO_RANGE_END,
},
sizeof(struct bridge_vlan_info));
if (r < 0)
return r;
return 0;
}
static uint16_t link_get_pvid(Link *link, bool *ret_untagged) {
assert(link);
assert(req);
assert(br_vid_bitmap);
assert(br_untagged_bitmap);
assert(link->network);
cnt = 0;
begin = end = UINT16_MAX;
for (int k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
uint32_t untagged_map = br_untagged_bitmap[k];
uint32_t vid_map = br_vid_bitmap[k];
unsigned base_bit = k * 32;
int i = -1;
done = false;
do {
int j = find_next_bit(i, vid_map);
if (j > 0) {
/* first hit of any bit */
if (begin == UINT16_MAX && end == UINT16_MAX) {
begin = end = j - 1 + base_bit;
untagged = is_bit_set(j - 1, untagged_map);
goto next;
}
/* this bit is a continuation of prior bits */
if (j - 2 + base_bit == end && untagged == is_bit_set(j - 1, untagged_map) && (uint16_t)j - 1 + base_bit != pvid && (uint16_t)begin != pvid) {
end++;
goto next;
}
} else
done = true;
if (begin != UINT16_MAX) {
cnt++;
if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1)
break;
br_vlan.flags = 0;
if (untagged)
br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
if (begin == end) {
br_vlan.vid = begin;
if (begin == pvid)
br_vlan.flags |= BRIDGE_VLAN_INFO_PVID;
r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
if (r < 0)
return r;
} else {
br_vlan.vid = begin;
br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
if (r < 0)
return r;
br_vlan.vid = end;
br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END;
r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
if (r < 0)
return r;
}
if (done)
break;
}
if (j > 0) {
begin = end = j - 1 + base_bit;
untagged = is_bit_set(j - 1, untagged_map);
}
next:
i = j;
} while (!done);
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;
}
assert(cnt > 0);
return cnt;
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 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 == pvid) {
/* PVID needs to be sent alone. Finish previous bits. */
if (begin != UINT16_MAX) {
assert(begin < k);
r = add_range(m, begin, k - 1, untagged);
if (r < 0)
return r;
begin = UINT16_MAX;
}
r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true);
if (r < 0)
return r;
continue;
}
if (!is_bit_set(k, link->network->bridge_vlan_bitmap)) {
/* This bit is not set. Finish previous bits. */
if (begin != UINT16_MAX) {
assert(begin < k);
r = add_range(m, begin, k - 1, untagged);
if (r < 0)
return r;
begin = UINT16_MAX;
}
continue;
}
if (begin != UINT16_MAX) {
bool u;
assert(begin < k);
u = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
if (untagged == u)
continue;
/* Tagging flag is changed from the previous bits. Finish them. */
r = add_range(m, begin, k - 1, untagged);
if (r < 0)
return r;
begin = k;
untagged = u;
continue;
}
/* This is the starting point of a new bit sequence. Save the position and the tagging flag. */
begin = k;
untagged = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
}
/* No pending bit sequence.
* Why? There is a trick. The conf parsers below only accepts vlan ID in the range 04094, but in
* the above loop, we run 04095. */
assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX);
assert(begin == UINT16_MAX);
return 0;
}
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);
assert(m);
r = sd_rtnl_message_link_set_family(m, AF_BRIDGE);
if (r < 0)
return r;
r = sd_netlink_message_open_container(m, IFLA_AF_SPEC);
if (r < 0)
return r;
if (link->master_ifindex <= 0) {
/* master needs BRIDGE_FLAGS_SELF flag */
r = sd_netlink_message_append_u16(m, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF);
if (r < 0)
return r;
}
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;
r = sd_netlink_message_close_container(m);
if (r < 0)
return r;
return 0;
}
#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
int link_update_bridge_vlan(Link *link, sd_netlink_message *m) {
_cleanup_free_ void *data = NULL;
size_t len;
uint16_t begin = UINT16_MAX;
int r, family;
assert(link);
assert(m);
r = sd_rtnl_message_get_family(m, &family);
if (r < 0)
return r;
if (family != AF_BRIDGE)
return 0;
r = sd_netlink_message_read_data(m, IFLA_AF_SPEC, &len, &data);
if (r == -ENODATA)
return 0;
if (r < 0)
return r;
memzero(link->bridge_vlan_bitmap, sizeof(link->bridge_vlan_bitmap));
for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
struct bridge_vlan_info *p;
if (RTA_TYPE(rta) != IFLA_BRIDGE_VLAN_INFO)
continue;
if (RTA_PAYLOAD(rta) != sizeof(struct bridge_vlan_info))
continue;
p = RTA_DATA(rta);
if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_BEGIN)) {
begin = p->vid;
continue;
}
if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_END)) {
for (uint16_t k = begin; k <= p->vid; k++)
set_bit(k, link->bridge_vlan_bitmap);
begin = UINT16_MAX;
continue;
}
if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_PVID)) {
link->bridge_vlan_pvid = p->vid;
link->bridge_vlan_pvid_is_untagged = FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_UNTAGGED);
}
set_bit(p->vid, link->bridge_vlan_bitmap);
begin = UINT16_MAX;
}
return 0;
}
void network_adjust_bridge_vlan(Network *network) {
assert(network);
if (!network->use_br_vlan)
return;
for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++)
if (is_bit_set(k, network->bridge_vlan_untagged_bitmap))
set_bit(k, network->bridge_vlan_bitmap);
/* pvid might not be in br_vid_bitmap yet */
if (network->pvid)
set_bit(network->pvid, network->br_vid_bitmap);
if (vlanid_is_valid(network->bridge_vlan_pvid))
set_bit(network->bridge_vlan_pvid, network->bridge_vlan_bitmap);
}
int config_parse_brvlan_pvid(
int config_parse_bridge_vlan_id(
const char *unit,
const char *filename,
unsigned line,
@ -162,21 +333,37 @@ int config_parse_brvlan_pvid(
void *data,
void *userdata) {
Network *network = userdata;
uint16_t pvid;
uint16_t v, *id = ASSERT_PTR(data);
int r;
r = parse_vlanid(rvalue, &pvid);
if (r < 0)
return r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
network->pvid = pvid;
network->use_br_vlan = true;
if (isempty(rvalue)) {
*id = BRIDGE_VLAN_KEEP_PVID;
return 0;
}
if (parse_boolean(rvalue) == 0) {
*id = BRIDGE_VLAN_REMOVE_PVID;
return 0;
}
r = parse_vlanid(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring: %s",
lvalue, rvalue);
return 0;
}
*id = v;
return 0;
}
int config_parse_brvlan_vlan(
int config_parse_bridge_vlan_id_range(
const char *unit,
const char *filename,
unsigned line,
@ -188,7 +375,7 @@ int config_parse_brvlan_vlan(
void *data,
void *userdata) {
Network *network = userdata;
uint32_t *bitmap = ASSERT_PTR(data);
uint16_t vid, vid_end;
int r;
@ -196,54 +383,22 @@ int config_parse_brvlan_vlan(
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
memzero(bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t));
return 0;
}
r = parse_vid_range(rvalue, &vid, &vid_end);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring: %s",
lvalue, rvalue);
return 0;
}
for (; vid <= vid_end; vid++)
set_bit(vid, network->br_vid_bitmap);
set_bit(vid, bitmap);
network->use_br_vlan = true;
return 0;
}
int config_parse_brvlan_untagged(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *network = userdata;
uint16_t vid, vid_end;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = parse_vid_range(rvalue, &vid, &vid_end);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue);
return 0;
}
for (; vid <= vid_end; vid++) {
set_bit(vid, network->br_vid_bitmap);
set_bit(vid, network->br_untagged_bitmap);
}
network->use_br_vlan = true;
return 0;
}

View file

@ -6,26 +6,28 @@
***/
#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_append_info(
const Link * link,
sd_netlink_message *req,
uint16_t pvid,
const uint32_t *br_vid_bitmap,
const uint32_t *br_untagged_bitmap);
int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set);
CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_pvid);
CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_vlan);
CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_untagged);
int link_update_bridge_vlan(Link *link, sd_netlink_message *m);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_vlan_id);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_vlan_id_range);

View file

@ -35,6 +35,7 @@
#include "networkd-address.h"
#include "networkd-bridge-fdb.h"
#include "networkd-bridge-mdb.h"
#include "networkd-bridge-vlan.h"
#include "networkd-can.h"
#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp-server.h"
@ -2435,6 +2436,10 @@ static int link_update(Link *link, sd_netlink_message *message) {
if (r < 0)
return r;
r = link_update_bridge_vlan(link, message);
if (r < 0)
return r;
return needs_reconfigure;
}
@ -2508,6 +2513,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
.ifname = TAKE_PTR(ifname),
.kind = TAKE_PTR(kind),
.bridge_vlan_pvid = UINT16_MAX,
.ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
.state_file = TAKE_PTR(state_file),

View file

@ -21,6 +21,7 @@
#include "log-link.h"
#include "netif-util.h"
#include "network-util.h"
#include "networkd-bridge-vlan.h"
#include "networkd-ipv6ll.h"
#include "networkd-util.h"
#include "ordered-set.h"
@ -72,6 +73,11 @@ typedef struct Link {
sd_device *dev;
char *driver;
/* bridge vlan */
uint16_t bridge_vlan_pvid;
bool bridge_vlan_pvid_is_untagged;
uint32_t bridge_vlan_bitmap[BRIDGE_VLAN_BITMAP_LEN];
/* to prevent multiple ethtool calls */
bool ethtool_driver_read;
bool ethtool_permanent_hw_addr_read;
@ -149,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;

View file

@ -753,6 +753,20 @@ static int manager_enumerate_links(Manager *m) {
if (r < 0)
return r;
r = manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link);
if (r < 0)
return r;
req = sd_netlink_message_unref(req);
r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return r;
r = sd_rtnl_message_link_set_family(req, AF_BRIDGE);
if (r < 0)
return r;
return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link);
}

View file

@ -510,10 +510,9 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
if (r < 0) {
log_warning_errno(r, "rtnl: received neighbor message with invalid state, ignoring: %m");
return 0;
} else if (!FLAGS_SET(state, NUD_PERMANENT)) {
log_debug("rtnl: received non-static neighbor, ignoring.");
} else if (!FLAGS_SET(state, NUD_PERMANENT))
/* Currently, we are interested in only static neighbors. */
return 0;
}
r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex);
if (r < 0) {
@ -525,12 +524,10 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
}
r = link_get_by_index(m, ifindex, &link);
if (r < 0) {
if (r < 0)
/* when enumerating we might be out of sync, but we will get the neighbor again. Also,
* kernel sends messages about neighbors after a link is removed. So, just ignore it. */
log_debug("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex);
return 0;
}
tmp = new0(Neighbor, 1);
@ -539,7 +536,10 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
if (r < 0) {
log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
return 0;
} else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
}
if (tmp->family == AF_BRIDGE) /* Currently, we do not support it. */
return 0;
if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family);
return 0;
}

View file

@ -368,9 +368,9 @@ BridgeFDB.AssociatedWith, config_parse_fdb_ntf_flags,
BridgeFDB.OutgoingInterface, config_parse_fdb_interface, 0, 0
BridgeMDB.MulticastGroupAddress, config_parse_mdb_group_address, 0, 0
BridgeMDB.VLANId, config_parse_mdb_vlan_id, 0, 0
BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
BridgeVLAN.PVID, config_parse_bridge_vlan_id, 0, offsetof(Network, bridge_vlan_pvid)
BridgeVLAN.VLAN, config_parse_bridge_vlan_id_range, 0, offsetof(Network, bridge_vlan_bitmap)
BridgeVLAN.EgressUntagged, config_parse_bridge_vlan_id_range, 0, offsetof(Network, bridge_vlan_untagged_bitmap)
DHCPPrefixDelegation.UplinkInterface, config_parse_uplink, 0, 0
DHCPPrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id)
DHCPPrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce)

View file

@ -450,6 +450,8 @@ 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 = BRIDGE_VLAN_KEEP_PVID,
.lldp_mode = LLDP_MODE_ROUTERS_ONLY,
.lldp_multicast_mode = _SD_LLDP_MULTICAST_MODE_INVALID,

View file

@ -289,10 +289,9 @@ struct Network {
MulticastRouter multicast_router;
/* Bridge VLAN */
bool use_br_vlan;
uint16_t pvid;
uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN];
uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
uint16_t bridge_vlan_pvid;
uint32_t bridge_vlan_bitmap[BRIDGE_VLAN_BITMAP_LEN];
uint32_t bridge_vlan_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
/* CAN support */
uint32_t can_bitrate;

View file

@ -312,7 +312,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",

View file

@ -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. */

View file

@ -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,29 +339,14 @@ static int link_configure_fill_message(
return r;
break;
case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN:
r = sd_rtnl_message_link_set_family(req, AF_BRIDGE);
r = bridge_vlan_set_message(link, req, /* is_set = */ true);
if (r < 0)
return r;
r = sd_netlink_message_open_container(req, IFLA_AF_SPEC);
break;
case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN:
r = bridge_vlan_set_message(link, req, /* is_set = */ false);
if (r < 0)
return r;
if (link->master_ifindex <= 0) {
/* master needs BRIDGE_FLAGS_SELF flag */
r = sd_netlink_message_append_u16(req, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF);
if (r < 0)
return r;
}
r = bridge_vlan_append_info(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap);
if (r < 0)
return r;
r = sd_netlink_message_close_container(req);
if (r < 0)
return r;
break;
case REQUEST_TYPE_SET_LINK_CAN:
r = can_set_netlink_message(link, req);
@ -430,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)
@ -480,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. */
@ -704,10 +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 (!link->network->use_br_vlan)
/* If nothing configured, use the default vlan ID. */
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")) {
@ -723,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) {

View file

@ -6,4 +6,8 @@ Name=bridge99
IPv6AcceptRA=false
[BridgeVLAN]
VLAN=4060-4094
PVID=1020
VLAN=1018-1023
VLAN=1200-1210
EgressUntagged=1022-1025
EgressUntagged=1203-1208

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[BridgeVLAN]
PVID=
VLAN=
EgressUntagged=
PVID=2020
VLAN=2018-2023
VLAN=2200-2210
EgressUntagged=2022-2025
EgressUntagged=2203-2208

View file

@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[BridgeVLAN]
PVID=
VLAN=
EgressUntagged=
PVID=2020
VLAN=2018-2023
EgressUntagged=2022-2025

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[BridgeVLAN]
PVID=no
VLAN=
EgressUntagged=

View file

@ -7,4 +7,8 @@ IPv6AcceptRA=no
Bridge=bridge99
[BridgeVLAN]
VLAN=4064-4094
PVID=1010
VLAN=1008-1013
VLAN=1100-1110
EgressUntagged=1012-1015
EgressUntagged=1103-1108

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[BridgeVLAN]
PVID=
VLAN=
EgressUntagged=
PVID=2010
VLAN=2008-2013
VLAN=2100-2110
EgressUntagged=2012-2015
EgressUntagged=2103-2108

View file

@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[BridgeVLAN]
PVID=
VLAN=
EgressUntagged=
PVID=2010
VLAN=2008-2013
EgressUntagged=2012-2015

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[BridgeVLAN]
PVID=no
VLAN=
EgressUntagged=

View file

@ -4422,23 +4422,116 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
def test_bridge_vlan(self):
copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
'26-bridge.netdev', '26-bridge-vlan-master.network')
'26-bridge.netdev', '26-bridge-vlan-master.network',
copy_dropins=False)
start_networkd()
self.wait_online(['test1:enslaved', 'bridge99:degraded'])
output = check_output('bridge vlan show dev test1')
print(output)
self.assertNotRegex(output, '4063')
for i in range(4064, 4095):
self.assertRegex(output, f'{i}')
self.assertNotRegex(output, '4095')
# check if the default VID is removed
self.assertNotIn('1 Egress Untagged', output)
for i in range(1000, 3000):
if i == 1010:
self.assertIn(f'{i} PVID', output)
elif i in range(1012, 1016) or i in range(1103, 1109):
self.assertIn(f'{i} Egress Untagged', output)
elif i in range(1008, 1014) or i in range(1100, 1111):
self.assertIn(f'{i}', output)
else:
self.assertNotIn(f'{i}', output)
output = check_output('bridge vlan show dev bridge99')
print(output)
self.assertNotRegex(output, '4059')
for i in range(4060, 4095):
self.assertRegex(output, f'{i}')
self.assertNotRegex(output, '4095')
# check if the default VID is removed
self.assertNotIn('1 Egress Untagged', output)
for i in range(1000, 3000):
if i == 1020:
self.assertIn(f'{i} PVID', output)
elif i in range(1022, 1026) or i in range(1203, 1209):
self.assertIn(f'{i} Egress Untagged', output)
elif i in range(1018, 1024) or i in range(1200, 1211):
self.assertIn(f'{i}', output)
else:
self.assertNotIn(f'{i}', output)
# Change vlan IDs
copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
'26-bridge-vlan-master.network.d/10-override.conf')
networkctl_reload()
self.wait_online(['test1:enslaved', 'bridge99:degraded'])
output = check_output('bridge vlan show dev test1')
print(output)
for i in range(1000, 3000):
if i == 2010:
self.assertIn(f'{i} PVID', output)
elif i in range(2012, 2016) or i in range(2103, 2109):
self.assertIn(f'{i} Egress Untagged', output)
elif i in range(2008, 2014) or i in range(2100, 2111):
self.assertIn(f'{i}', output)
else:
self.assertNotIn(f'{i}', output)
output = check_output('bridge vlan show dev bridge99')
print(output)
for i in range(1000, 3000):
if i == 2020:
self.assertIn(f'{i} PVID', output)
elif i in range(2022, 2026) or i in range(2203, 2209):
self.assertIn(f'{i} Egress Untagged', output)
elif i in range(2018, 2024) or i in range(2200, 2211):
self.assertIn(f'{i}', output)
else:
self.assertNotIn(f'{i}', output)
# Remove several vlan IDs
copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
'26-bridge-vlan-master.network.d/20-override.conf')
networkctl_reload()
self.wait_online(['test1:enslaved', 'bridge99:degraded'])
output = check_output('bridge vlan show dev test1')
print(output)
for i in range(1000, 3000):
if i == 2010:
self.assertIn(f'{i} PVID', output)
elif i in range(2012, 2016):
self.assertIn(f'{i} Egress Untagged', output)
elif i in range(2008, 2014):
self.assertIn(f'{i}', output)
else:
self.assertNotIn(f'{i}', output)
output = check_output('bridge vlan show dev bridge99')
print(output)
for i in range(1000, 3000):
if i == 2020:
self.assertIn(f'{i} PVID', output)
elif i in range(2022, 2026):
self.assertIn(f'{i} Egress Untagged', output)
elif i in range(2018, 2024):
self.assertIn(f'{i}', output)
else:
self.assertNotIn(f'{i}', output)
# Remove all vlan IDs
copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
'26-bridge-vlan-master.network.d/30-override.conf')
networkctl_reload()
self.wait_online(['test1:enslaved', 'bridge99:degraded'])
output = check_output('bridge vlan show dev test1')
print(output)
self.assertNotIn('PVID', output)
for i in range(1000, 3000):
self.assertNotIn(f'{i}', output)
output = check_output('bridge vlan show dev bridge99')
print(output)
self.assertNotIn('PVID', output)
for i in range(1000, 3000):
self.assertNotIn(f'{i}', output)
def test_bridge_vlan_issue_20373(self):
copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',