diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-11-11 10:15:55 +0100 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-11-12 19:58:46 +0100 |
commit | 26ba67e5eca0fe7c924ab2b870544299313ad8df (patch) | |
tree | 96c92e8c6cce61a4caa720abeed0b2651f38c91a /src/network/networkd-bridge-vlan.c | |
parent | network/neighbor: suppress noisy debugging logs (diff) | |
download | systemd-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.c | 241 |
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) { |