2 This file is part of elogind.
4 Copyright (C) 2016 BISDN GmbH. All rights reserved.
6 elogind is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 elogind is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with elogind; If not, see <http://www.gnu.org/licenses/>.
20 #include <netinet/in.h>
21 #include <linux/if_bridge.h>
24 #include "alloc-util.h"
25 #include "conf-parser.h"
26 #include "netlink-util.h"
27 #include "networkd-brvlan.h"
29 #include "parse-util.h"
30 #include "vlan-util.h"
32 static bool is_bit_set(unsigned bit, uint32_t scope) {
33 assert(bit < sizeof(scope)*8);
34 return scope & (1 << bit);
37 static inline void set_bit(unsigned nr, uint32_t *addr) {
38 if (nr < BRIDGE_VLAN_BITMAP_MAX)
39 addr[nr / 32] |= (((uint32_t) 1) << (nr % 32));
42 static inline int is_vid_valid(unsigned vid) {
43 if (vid > VLANID_MAX || vid == 0)
48 static int find_next_bit(int i, uint32_t x) {
56 return BUILTIN_FFS_U32(x);
58 /* mask off prior finds to get next */
59 j = __builtin_ffs(x >> i);
63 static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint16_t pvid, const uint32_t *br_vid_bitmap, const uint32_t *br_untagged_bitmap) {
64 struct bridge_vlan_info br_vlan;
65 int i, j, k, r, done, cnt;
71 assert(br_vid_bitmap);
72 assert(br_untagged_bitmap);
76 begin = end = UINT16_MAX;
77 for (k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
79 uint32_t vid_map = br_vid_bitmap[k];
80 uint32_t untagged_map = br_untagged_bitmap[k];
86 j = find_next_bit(i, vid_map);
88 /* first hit of any bit */
89 if (begin == UINT16_MAX && end == UINT16_MAX) {
90 begin = end = j - 1 + base_bit;
91 untagged = is_bit_set(j - 1, untagged_map);
95 /* this bit is a continuation of prior bits */
96 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) {
103 if (begin != UINT16_MAX) {
105 if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1)
110 br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
116 br_vlan.flags |= BRIDGE_VLAN_INFO_PVID;
118 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
120 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
123 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
125 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
127 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
130 br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
131 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END;
133 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
135 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
142 begin = end = j - 1 + base_bit;
143 untagged = is_bit_set(j - 1, untagged_map);
156 static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
157 Link *link = userdata;
162 r = sd_netlink_message_get_errno(m);
163 if (r < 0 && r != -EEXIST)
164 log_link_error_errno(link, r, "Could not add VLAN to bridge port: %m");
169 int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) {
170 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
176 assert(link->manager);
177 assert(br_vid_bitmap);
178 assert(br_untagged_bitmap);
179 assert(link->network);
181 /* pvid might not be in br_vid_bitmap yet */
183 set_bit(pvid, br_vid_bitmap);
185 rtnl = link->manager->rtnl;
187 /* create new RTM message */
188 r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex);
190 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
192 r = sd_rtnl_message_link_set_family(req, PF_BRIDGE);
194 return log_link_error_errno(link, r, "Could not set message family: %m");
196 r = sd_netlink_message_open_container(req, IFLA_AF_SPEC);
198 return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
200 /* master needs flag self */
201 if (!link->network->bridge) {
202 flags = BRIDGE_FLAGS_SELF;
203 sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t));
207 r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap);
209 return log_link_error_errno(link, r, "Could not append VLANs: %m");
211 r = sd_netlink_message_close_container(req);
213 return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
215 /* send message to the kernel */
216 r = sd_netlink_call_async(rtnl, req, set_brvlan_handler, link, 0, NULL);
218 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
223 static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) {
226 char *_rvalue = NULL;
227 uint16_t _vid = UINT16_MAX;
228 uint16_t _vid_end = UINT16_MAX;
234 _rvalue = strdupa(rvalue);
235 p = strchr(_rvalue, '-');
239 r = parse_vlanid(_rvalue, &_vid);
246 r = parse_vlanid(p, &_vid_end);
253 r = parse_vlanid(_rvalue, &_vid);
266 int config_parse_brvlan_vlan(const char *unit, const char *filename,
267 unsigned line, const char *section,
268 unsigned section_line, const char *lvalue,
269 int ltype, const char *rvalue, void *data,
271 Network *network = userdata;
273 uint16_t vid, vid_end;
281 r = parse_vid_range(rvalue, &vid, &vid_end);
283 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
287 if (UINT16_MAX == vid_end)
288 set_bit(vid++, network->br_vid_bitmap);
290 if (vid >= vid_end) {
291 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue);
294 for (; vid <= vid_end; vid++)
295 set_bit(vid, network->br_vid_bitmap);
300 int config_parse_brvlan_untagged(const char *unit, const char *filename,
301 unsigned line, const char *section,
302 unsigned section_line, const char *lvalue,
303 int ltype, const char *rvalue, void *data,
305 Network *network = userdata;
307 uint16_t vid, vid_end;
315 r = parse_vid_range(rvalue, &vid, &vid_end);
317 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue);
321 if (UINT16_MAX == vid_end) {
322 set_bit(vid, network->br_vid_bitmap);
323 set_bit(vid, network->br_untagged_bitmap);
325 if (vid >= vid_end) {
326 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue);
329 for (; vid <= vid_end; vid++) {
330 set_bit(vid, network->br_vid_bitmap);
331 set_bit(vid, network->br_untagged_bitmap);