]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-bridge-vlan.c
network/link: shorten code a bit
[thirdparty/systemd.git] / src / network / networkd-bridge-vlan.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
13b498f9 2/***
810adae9 3 Copyright © 2016 BISDN GmbH. All rights reserved.
13b498f9
TJ
4***/
5
6#include <netinet/in.h>
7#include <linux/if_bridge.h>
8#include <stdbool.h>
9
10#include "alloc-util.h"
11#include "conf-parser.h"
12#include "netlink-util.h"
9670e45a 13#include "networkd-bridge-vlan.h"
23f53b99
TG
14#include "networkd-link.h"
15#include "networkd-manager.h"
16#include "networkd-network.h"
13b498f9
TJ
17#include "parse-util.h"
18#include "vlan-util.h"
19
26ba67e5
YW
20static bool is_bit_set(unsigned nr, const uint32_t *addr) {
21 assert(nr < BRIDGE_VLAN_BITMAP_MAX);
22 return addr[nr / 32] & (UINT32_C(1) << (nr % 32));
13b498f9
TJ
23}
24
a1e92eee 25static void set_bit(unsigned nr, uint32_t *addr) {
f0f93d9c
YW
26 assert(nr < BRIDGE_VLAN_BITMAP_MAX);
27 addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
13b498f9
TJ
28}
29
1e67047f 30static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid, char **str) {
26ba67e5
YW
31 assert(m);
32 assert(id < BRIDGE_VLAN_BITMAP_MAX);
1e67047f
YW
33
34 if (DEBUG_LOGGING)
35 (void) strextendf_with_separator(str, ",", "%u%s%s%s%s%s", id,
36 (untagged || is_pvid) ? "(" : "",
37 untagged ? "untagged" : "",
38 (untagged && is_pvid) ? "," : "",
39 is_pvid ? "pvid" : "",
40 (untagged || is_pvid) ? ")" : "");
41
26ba67e5
YW
42 return sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
43 &(struct bridge_vlan_info) {
44 .vid = id,
45 .flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) |
46 (is_pvid ? BRIDGE_VLAN_INFO_PVID : 0),
47 },
48 sizeof(struct bridge_vlan_info));
49}
13b498f9 50
1e67047f 51static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged, char **str) {
26ba67e5 52 int r;
13b498f9 53
26ba67e5
YW
54 assert(m);
55 assert(begin <= end);
56 assert(end < BRIDGE_VLAN_BITMAP_MAX);
13b498f9 57
26ba67e5 58 if (begin == end)
1e67047f
YW
59 return add_single(m, begin, untagged, /* is_pvid = */ false, str);
60
61 if (DEBUG_LOGGING)
62 (void) strextendf_with_separator(str, ",", "%u-%u%s", begin, end, untagged ? "(untagged)" : "");
26ba67e5
YW
63
64 r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
65 &(struct bridge_vlan_info) {
66 .vid = begin,
67 .flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) |
68 BRIDGE_VLAN_INFO_RANGE_BEGIN,
69 },
70 sizeof(struct bridge_vlan_info));
71 if (r < 0)
72 return r;
13b498f9 73
26ba67e5
YW
74 r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
75 &(struct bridge_vlan_info) {
76 .vid = end,
77 .flags = (untagged ? BRIDGE_VLAN_INFO_UNTAGGED : 0) |
78 BRIDGE_VLAN_INFO_RANGE_END,
79 },
80 sizeof(struct bridge_vlan_info));
81 if (r < 0)
82 return r;
83
84 return 0;
85}
5106ad00 86
228693af
YW
87static uint16_t link_get_pvid(Link *link, bool *ret_untagged) {
88 assert(link);
89 assert(link->network);
90
91 if (vlanid_is_valid(link->network->bridge_vlan_pvid)) {
92 if (ret_untagged)
93 *ret_untagged = is_bit_set(link->network->bridge_vlan_pvid,
94 link->network->bridge_vlan_untagged_bitmap);
95 return link->network->bridge_vlan_pvid;
96 }
97
98 if (link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) {
99 if (ret_untagged)
100 *ret_untagged = link->bridge_vlan_pvid_is_untagged;
101 return link->bridge_vlan_pvid;
102 }
103
104 if (ret_untagged)
105 *ret_untagged = false;
106 return UINT16_MAX;
107}
108
26ba67e5 109static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
1e67047f 110 _cleanup_free_ char *str = NULL;
228693af
YW
111 uint16_t pvid, begin = UINT16_MAX;
112 bool untagged, pvid_is_untagged;
26ba67e5 113 int r;
13b498f9
TJ
114
115 assert(link);
26ba67e5
YW
116 assert(link->network);
117 assert(m);
118
228693af
YW
119 pvid = link_get_pvid(link, &pvid_is_untagged);
120
26ba67e5 121 for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) {
13b498f9 122
228693af 123 if (k == pvid) {
26ba67e5 124 /* PVID needs to be sent alone. Finish previous bits. */
13b498f9 125 if (begin != UINT16_MAX) {
26ba67e5
YW
126 assert(begin < k);
127
1e67047f 128 r = add_range(m, begin, k - 1, untagged, &str);
26ba67e5
YW
129 if (r < 0)
130 return r;
131
132 begin = UINT16_MAX;
13b498f9 133 }
26ba67e5 134
1e67047f 135 r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true, &str);
26ba67e5
YW
136 if (r < 0)
137 return r;
138
139 continue;
140 }
141
f269016c 142 if (!is_bit_set(k, link->network->bridge_vlan_bitmap)) {
26ba67e5
YW
143 /* This bit is not set. Finish previous bits. */
144 if (begin != UINT16_MAX) {
145 assert(begin < k);
146
1e67047f 147 r = add_range(m, begin, k - 1, untagged, &str);
26ba67e5
YW
148 if (r < 0)
149 return r;
150
151 begin = UINT16_MAX;
13b498f9
TJ
152 }
153
26ba67e5
YW
154 continue;
155 }
156
157 if (begin != UINT16_MAX) {
158 bool u;
159
160 assert(begin < k);
161
f269016c 162 u = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
26ba67e5
YW
163 if (untagged == u)
164 continue;
165
166 /* Tagging flag is changed from the previous bits. Finish them. */
1e67047f 167 r = add_range(m, begin, k - 1, untagged, &str);
26ba67e5
YW
168 if (r < 0)
169 return r;
170
171 begin = k;
172 untagged = u;
173 continue;
174 }
175
176 /* This is the starting point of a new bit sequence. Save the position and the tagging flag. */
177 begin = k;
f269016c 178 untagged = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
13b498f9 179 }
13b498f9 180
26ba67e5
YW
181 /* No pending bit sequence.
182 * Why? There is a trick. The conf parsers below only accepts vlan ID in the range 0…4094, but in
183 * the above loop, we run 0…4095. */
184 assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX);
185 assert(begin == UINT16_MAX);
1e67047f
YW
186
187 log_link_debug(link, "Setting Bridge VLAN IDs: %s", strna(str));
26ba67e5
YW
188 return 0;
189}
190
228693af 191static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
1e67047f 192 _cleanup_free_ char *str = NULL;
228693af
YW
193 uint16_t pvid, begin = UINT16_MAX;
194 int r;
195
196 assert(link);
197 assert(link->network);
198 assert(m);
199
200 pvid = link_get_pvid(link, NULL);
201
202 for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) {
203
204 if (k == pvid ||
205 !is_bit_set(k, link->bridge_vlan_bitmap) ||
206 is_bit_set(k, link->network->bridge_vlan_bitmap)) {
207 /* This bit is not necessary to be removed. Finish previous bits. */
208 if (begin != UINT16_MAX) {
209 assert(begin < k);
210
1e67047f 211 r = add_range(m, begin, k - 1, /* untagged = */ false, &str);
228693af
YW
212 if (r < 0)
213 return r;
214
215 begin = UINT16_MAX;
216 }
217
218 continue;
219 }
220
221 if (begin != UINT16_MAX)
222 continue;
223
224 /* This is the starting point of a new bit sequence. Save the position. */
225 begin = k;
226 }
227
228 /* No pending bit sequence. */
229 assert(begin == UINT16_MAX);
1e67047f
YW
230
231 log_link_debug(link, "Removing Bridge VLAN IDs: %s", strna(str));
228693af
YW
232 return 0;
233}
234
235int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set) {
26ba67e5
YW
236 int r;
237
238 assert(link);
239 assert(m);
240
241 r = sd_rtnl_message_link_set_family(m, AF_BRIDGE);
242 if (r < 0)
243 return r;
244
245 r = sd_netlink_message_open_container(m, IFLA_AF_SPEC);
246 if (r < 0)
247 return r;
248
249 if (link->master_ifindex <= 0) {
250 /* master needs BRIDGE_FLAGS_SELF flag */
251 r = sd_netlink_message_append_u16(m, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF);
252 if (r < 0)
253 return r;
254 }
255
228693af
YW
256 if (is_set)
257 r = bridge_vlan_append_set_info(link, m);
258 else
259 r = bridge_vlan_append_del_info(link, m);
26ba67e5
YW
260 if (r < 0)
261 return r;
262
263 r = sd_netlink_message_close_container(m);
264 if (r < 0)
265 return r;
266
267 return 0;
13b498f9
TJ
268}
269
11cee6ef
YW
270int link_update_bridge_vlan(Link *link, sd_netlink_message *m) {
271 _cleanup_free_ void *data = NULL;
272 size_t len;
273 uint16_t begin = UINT16_MAX;
274 int r, family;
275
276 assert(link);
277 assert(m);
278
279 r = sd_rtnl_message_get_family(m, &family);
280 if (r < 0)
281 return r;
282
283 if (family != AF_BRIDGE)
284 return 0;
285
286 r = sd_netlink_message_read_data(m, IFLA_AF_SPEC, &len, &data);
287 if (r == -ENODATA)
288 return 0;
289 if (r < 0)
290 return r;
291
292 memzero(link->bridge_vlan_bitmap, sizeof(link->bridge_vlan_bitmap));
293
294 for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
295 struct bridge_vlan_info *p;
296
297 if (RTA_TYPE(rta) != IFLA_BRIDGE_VLAN_INFO)
298 continue;
299 if (RTA_PAYLOAD(rta) != sizeof(struct bridge_vlan_info))
300 continue;
301
302 p = RTA_DATA(rta);
303
304 if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_BEGIN)) {
305 begin = p->vid;
306 continue;
307 }
308
309 if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_END)) {
310 for (uint16_t k = begin; k <= p->vid; k++)
311 set_bit(k, link->bridge_vlan_bitmap);
312
313 begin = UINT16_MAX;
314 continue;
315 }
316
317 if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_PVID)) {
318 link->bridge_vlan_pvid = p->vid;
319 link->bridge_vlan_pvid_is_untagged = FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_UNTAGGED);
320 }
321
322 set_bit(p->vid, link->bridge_vlan_bitmap);
323 begin = UINT16_MAX;
324 }
325
326 return 0;
327}
328
5546870e
YW
329void network_adjust_bridge_vlan(Network *network) {
330 assert(network);
331
f269016c
YW
332 for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++)
333 if (is_bit_set(k, network->bridge_vlan_untagged_bitmap))
334 set_bit(k, network->bridge_vlan_bitmap);
5546870e 335
f269016c
YW
336 if (vlanid_is_valid(network->bridge_vlan_pvid))
337 set_bit(network->bridge_vlan_pvid, network->bridge_vlan_bitmap);
5546870e
YW
338}
339
f269016c 340int config_parse_bridge_vlan_id(
1a0e5ca2
YW
341 const char *unit,
342 const char *filename,
343 unsigned line,
344 const char *section,
345 unsigned section_line,
346 const char *lvalue,
347 int ltype,
348 const char *rvalue,
349 void *data,
350 void *userdata) {
351
f269016c 352 uint16_t v, *id = ASSERT_PTR(data);
7bbb43a7 353 int r;
13b498f9
TJ
354
355 assert(filename);
356 assert(section);
357 assert(lvalue);
358 assert(rvalue);
13b498f9 359
f269016c 360 if (isempty(rvalue)) {
228693af
YW
361 *id = BRIDGE_VLAN_KEEP_PVID;
362 return 0;
363 }
364
365 if (parse_boolean(rvalue) == 0) {
366 *id = BRIDGE_VLAN_REMOVE_PVID;
13b498f9
TJ
367 return 0;
368 }
369
f269016c
YW
370 r = parse_vlanid(rvalue, &v);
371 if (r < 0) {
372 log_syntax(unit, LOG_WARNING, filename, line, r,
373 "Failed to parse %s=, ignoring: %s",
374 lvalue, rvalue);
375 return 0;
376 }
fb721f08 377
f269016c 378 *id = v;
13b498f9
TJ
379 return 0;
380}
381
f269016c 382int config_parse_bridge_vlan_id_range(
1a0e5ca2
YW
383 const char *unit,
384 const char *filename,
385 unsigned line,
386 const char *section,
387 unsigned section_line,
388 const char *lvalue,
389 int ltype,
390 const char *rvalue,
391 void *data,
392 void *userdata) {
393
f269016c 394 uint32_t *bitmap = ASSERT_PTR(data);
13b498f9 395 uint16_t vid, vid_end;
1a0e5ca2 396 int r;
13b498f9
TJ
397
398 assert(filename);
399 assert(section);
400 assert(lvalue);
401 assert(rvalue);
f269016c
YW
402
403 if (isempty(rvalue)) {
404 memzero(bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t));
405 return 0;
406 }
13b498f9
TJ
407
408 r = parse_vid_range(rvalue, &vid, &vid_end);
409 if (r < 0) {
f269016c
YW
410 log_syntax(unit, LOG_WARNING, filename, line, r,
411 "Failed to parse %s=, ignoring: %s",
412 lvalue, rvalue);
13b498f9
TJ
413 return 0;
414 }
415
f269016c
YW
416 for (; vid <= vid_end; vid++)
417 set_bit(vid, bitmap);
fb721f08 418
13b498f9
TJ
419 return 0;
420}