summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-bridge-vlan.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2023-11-11 10:15:55 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2023-11-12 19:58:46 +0100
commit26ba67e5eca0fe7c924ab2b870544299313ad8df (patch)
tree96c92e8c6cce61a4caa720abeed0b2651f38c91a /src/network/networkd-bridge-vlan.c
parentnetwork/neighbor: suppress noisy debugging logs (diff)
downloadsystemd-26ba67e5eca0fe7c924ab2b870544299313ad8df.tar.xz
systemd-26ba67e5eca0fe7c924ab2b870544299313ad8df.zip
network/brvlan: rework bridge vlan ID assignment
No functional change, just refactoring.
Diffstat (limited to 'src/network/networkd-bridge-vlan.c')
-rw-r--r--src/network/networkd-bridge-vlan.c241
1 files changed, 142 insertions, 99 deletions
diff --git a/src/network/networkd-bridge-vlan.c b/src/network/networkd-bridge-vlan.c
index 36e3610a8f..bfc404f154 100644
--- a/src/network/networkd-bridge-vlan.c
+++ b/src/network/networkd-bridge-vlan.c
@@ -17,9 +17,9 @@
#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) {
@@ -27,116 +27,159 @@ static void set_bit(unsigned nr, uint32_t *addr) {
addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
}
-static int find_next_bit(int i, uint32_t x) {
- int j;
+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));
+}
- if (i >= 32)
- return -1;
+static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged) {
+ int r;
- /* find first bit */
- if (i < 0)
- return BUILTIN_FFS_U32(x);
+ assert(m);
+ assert(begin <= end);
+ assert(end < BRIDGE_VLAN_BITMAP_MAX);
- /* mask off prior finds to get next */
- j = __builtin_ffs(x >> i);
- return j ? j + i : 0;
-}
+ if (begin == end)
+ return add_single(m, begin, untagged, /* is_pvid = */ false);
-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) {
+ 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;
- struct bridge_vlan_info br_vlan;
- bool done, untagged = false;
- uint16_t begin, end;
- int r, cnt;
+ 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 int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
+ uint16_t begin = UINT16_MAX;
+ bool untagged;
+ int r;
assert(link);
- assert(req);
- assert(br_vid_bitmap);
- assert(br_untagged_bitmap);
-
- 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;
+ assert(link->network);
+ assert(m);
+
+ for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) {
+ if (k > 0 && k == link->network->pvid) {
+ /* PVID needs to be sent alone. Finish previous bits. */
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;
+ assert(begin < k);
+
+ r = add_range(m, begin, k - 1, untagged);
+ if (r < 0)
+ return r;
+
+ begin = UINT16_MAX;
}
- if (j > 0) {
- begin = end = j - 1 + base_bit;
- untagged = is_bit_set(j - 1, untagged_map);
+
+ untagged = is_bit_set(k, link->network->br_untagged_bitmap);
+ r = add_single(m, k, untagged, /* is_pvid = */ true);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ if (!is_bit_set(k, link->network->br_vid_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;
}
- next:
- i = j;
- } while (!done);
+ continue;
+ }
+
+ if (begin != UINT16_MAX) {
+ bool u;
+
+ assert(begin < k);
+
+ u = is_bit_set(k, link->network->br_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->br_untagged_bitmap);
+ }
+
+ /* No pending bit sequence.
+ * Why? There is a trick. The conf parsers below only accepts vlan ID in the range 0…4094, but in
+ * the above loop, we run 0…4095. */
+ assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX);
+ assert(begin == UINT16_MAX);
+ return 0;
+}
+
+int bridge_vlan_set_message(Link *link, sd_netlink_message *m) {
+ 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;
}
- assert(cnt > 0);
- return cnt;
+ r = bridge_vlan_append_set_info(link, m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
}
void network_adjust_bridge_vlan(Network *network) {