]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-brvlan.c
eb35684ac9e0907b2d93c2378136176c2ca3246d
[thirdparty/systemd.git] / src / network / networkd-brvlan.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2016 BISDN GmbH. All rights reserved.
6 ***/
7
8 #include <netinet/in.h>
9 #include <linux/if_bridge.h>
10 #include <stdbool.h>
11
12 #include "alloc-util.h"
13 #include "conf-parser.h"
14 #include "netlink-util.h"
15 #include "networkd-brvlan.h"
16 #include "networkd-link.h"
17 #include "networkd-manager.h"
18 #include "networkd-network.h"
19 #include "parse-util.h"
20 #include "vlan-util.h"
21
22 static bool is_bit_set(unsigned bit, uint32_t scope) {
23 assert(bit < sizeof(scope)*8);
24 return scope & (1 << bit);
25 }
26
27 static inline void set_bit(unsigned nr, uint32_t *addr) {
28 if (nr < BRIDGE_VLAN_BITMAP_MAX)
29 addr[nr / 32] |= (((uint32_t) 1) << (nr % 32));
30 }
31
32 static int find_next_bit(int i, uint32_t x) {
33 int j;
34
35 if (i >= 32)
36 return -1;
37
38 /* find first bit */
39 if (i < 0)
40 return BUILTIN_FFS_U32(x);
41
42 /* mask off prior finds to get next */
43 j = __builtin_ffs(x >> i);
44 return j ? j + i : 0;
45 }
46
47 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) {
48 struct bridge_vlan_info br_vlan;
49 int i, j, k, r, done, cnt;
50 uint16_t begin, end;
51 bool untagged = false;
52
53 assert(link);
54 assert(req);
55 assert(br_vid_bitmap);
56 assert(br_untagged_bitmap);
57
58 i = cnt = -1;
59
60 begin = end = UINT16_MAX;
61 for (k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
62 unsigned base_bit;
63 uint32_t vid_map = br_vid_bitmap[k];
64 uint32_t untagged_map = br_untagged_bitmap[k];
65
66 base_bit = k * 32;
67 i = -1;
68 done = 0;
69 do {
70 j = find_next_bit(i, vid_map);
71 if (j > 0) {
72 /* first hit of any bit */
73 if (begin == UINT16_MAX && end == UINT16_MAX) {
74 begin = end = j - 1 + base_bit;
75 untagged = is_bit_set(j - 1, untagged_map);
76 goto next;
77 }
78
79 /* this bit is a continuation of prior bits */
80 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) {
81 end++;
82 goto next;
83 }
84 } else
85 done = 1;
86
87 if (begin != UINT16_MAX) {
88 cnt++;
89 if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1)
90 break;
91
92 br_vlan.flags = 0;
93 if (untagged)
94 br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
95
96 if (begin == end) {
97 br_vlan.vid = begin;
98
99 if (begin == pvid)
100 br_vlan.flags |= BRIDGE_VLAN_INFO_PVID;
101
102 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
103 if (r < 0)
104 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
105 } else {
106 br_vlan.vid = begin;
107 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
108
109 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
110 if (r < 0)
111 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
112
113 br_vlan.vid = end;
114 br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
115 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END;
116
117 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
118 if (r < 0)
119 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
120 }
121
122 if (done)
123 break;
124 }
125 if (j > 0) {
126 begin = end = j - 1 + base_bit;
127 untagged = is_bit_set(j - 1, untagged_map);
128 }
129
130 next:
131 i = j;
132 } while (!done);
133 }
134 if (!cnt)
135 return -EINVAL;
136
137 return cnt;
138 }
139
140 static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
141 Link *link = userdata;
142 int r;
143
144 assert(link);
145
146 r = sd_netlink_message_get_errno(m);
147 if (r < 0 && r != -EEXIST)
148 log_link_error_errno(link, r, "Could not add VLAN to bridge port: %m");
149
150 return 1;
151 }
152
153 int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) {
154 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
155 int r;
156 uint16_t flags;
157 sd_netlink *rtnl;
158
159 assert(link);
160 assert(link->manager);
161 assert(br_vid_bitmap);
162 assert(br_untagged_bitmap);
163 assert(link->network);
164
165 /* pvid might not be in br_vid_bitmap yet */
166 if (pvid)
167 set_bit(pvid, br_vid_bitmap);
168
169 rtnl = link->manager->rtnl;
170
171 /* create new RTM message */
172 r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex);
173 if (r < 0)
174 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
175
176 r = sd_rtnl_message_link_set_family(req, PF_BRIDGE);
177 if (r < 0)
178 return log_link_error_errno(link, r, "Could not set message family: %m");
179
180 r = sd_netlink_message_open_container(req, IFLA_AF_SPEC);
181 if (r < 0)
182 return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
183
184 /* master needs flag self */
185 if (!link->network->bridge) {
186 flags = BRIDGE_FLAGS_SELF;
187 sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t));
188 }
189
190 /* add vlan info */
191 r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap);
192 if (r < 0)
193 return log_link_error_errno(link, r, "Could not append VLANs: %m");
194
195 r = sd_netlink_message_close_container(req);
196 if (r < 0)
197 return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
198
199 /* send message to the kernel */
200 r = sd_netlink_call_async(rtnl, req, set_brvlan_handler, link, 0, NULL);
201 if (r < 0)
202 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
203
204 return 0;
205 }
206
207 static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) {
208 int r;
209 char *p;
210 char *_rvalue = NULL;
211 uint16_t _vid = UINT16_MAX;
212 uint16_t _vid_end = UINT16_MAX;
213
214 assert(rvalue);
215 assert(vid);
216 assert(vid_end);
217
218 _rvalue = strdupa(rvalue);
219 p = strchr(_rvalue, '-');
220 if (p) {
221 *p = '\0';
222 p++;
223 r = parse_vlanid(_rvalue, &_vid);
224 if (r < 0)
225 return r;
226
227 if (_vid == 0)
228 return -ERANGE;
229
230 r = parse_vlanid(p, &_vid_end);
231 if (r < 0)
232 return r;
233
234 if (_vid_end == 0)
235 return -ERANGE;
236 } else {
237 r = parse_vlanid(_rvalue, &_vid);
238 if (r < 0)
239 return r;
240
241 if (_vid == 0)
242 return -ERANGE;
243 }
244
245 *vid = _vid;
246 *vid_end = _vid_end;
247 return r;
248 }
249
250 int config_parse_brvlan_pvid(const char *unit, const char *filename,
251 unsigned line, const char *section,
252 unsigned section_line, const char *lvalue,
253 int ltype, const char *rvalue, void *data,
254 void *userdata) {
255 Network *network = userdata;
256 int r;
257 uint16_t pvid;
258 r = parse_vlanid(rvalue, &pvid);
259 if (r < 0)
260 return r;
261
262 network->pvid = pvid;
263 network->use_br_vlan = true;
264
265 return 0;
266 }
267
268 int config_parse_brvlan_vlan(const char *unit, const char *filename,
269 unsigned line, const char *section,
270 unsigned section_line, const char *lvalue,
271 int ltype, const char *rvalue, void *data,
272 void *userdata) {
273 Network *network = userdata;
274 int r;
275 uint16_t vid, vid_end;
276
277 assert(filename);
278 assert(section);
279 assert(lvalue);
280 assert(rvalue);
281 assert(data);
282
283 r = parse_vid_range(rvalue, &vid, &vid_end);
284 if (r < 0) {
285 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
286 return 0;
287 }
288
289 if (UINT16_MAX == vid_end)
290 set_bit(vid++, network->br_vid_bitmap);
291 else {
292 if (vid >= vid_end) {
293 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue);
294 return 0;
295 }
296 for (; vid <= vid_end; vid++)
297 set_bit(vid, network->br_vid_bitmap);
298 }
299 network->use_br_vlan = true;
300 return 0;
301 }
302
303 int config_parse_brvlan_untagged(const char *unit, const char *filename,
304 unsigned line, const char *section,
305 unsigned section_line, const char *lvalue,
306 int ltype, const char *rvalue, void *data,
307 void *userdata) {
308 Network *network = userdata;
309 int r;
310 uint16_t vid, vid_end;
311
312 assert(filename);
313 assert(section);
314 assert(lvalue);
315 assert(rvalue);
316 assert(data);
317
318 r = parse_vid_range(rvalue, &vid, &vid_end);
319 if (r < 0) {
320 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue);
321 return 0;
322 }
323
324 if (UINT16_MAX == vid_end) {
325 set_bit(vid, network->br_vid_bitmap);
326 set_bit(vid, network->br_untagged_bitmap);
327 } else {
328 if (vid >= vid_end) {
329 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue);
330 return 0;
331 }
332 for (; vid <= vid_end; vid++) {
333 set_bit(vid, network->br_vid_bitmap);
334 set_bit(vid, network->br_untagged_bitmap);
335 }
336 }
337 network->use_br_vlan = true;
338 return 0;
339 }