]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-bridge-vlan.c
resolved: add transaction result for upstream failures
[thirdparty/systemd.git] / src / network / networkd-bridge-vlan.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2016 BISDN GmbH. All rights reserved.
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"
13 #include "networkd-bridge-vlan.h"
14 #include "networkd-link.h"
15 #include "networkd-manager.h"
16 #include "networkd-network.h"
17 #include "parse-util.h"
18 #include "vlan-util.h"
19
20 static 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));
23 }
24
25 static void set_bit(unsigned nr, uint32_t *addr) {
26 assert(nr < BRIDGE_VLAN_BITMAP_MAX);
27 addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
28 }
29
30 static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid, char **str) {
31 assert(m);
32 assert(id < BRIDGE_VLAN_BITMAP_MAX);
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
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 }
50
51 static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged, char **str) {
52 int r;
53
54 assert(m);
55 assert(begin <= end);
56 assert(end < BRIDGE_VLAN_BITMAP_MAX);
57
58 if (begin == end)
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)" : "");
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;
73
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 }
86
87 static 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
109 static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
110 _cleanup_free_ char *str = NULL;
111 uint16_t pvid, begin = UINT16_MAX;
112 bool untagged, pvid_is_untagged;
113 int r;
114
115 assert(link);
116 assert(link->network);
117 assert(m);
118
119 pvid = link_get_pvid(link, &pvid_is_untagged);
120
121 for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++) {
122
123 if (k == pvid) {
124 /* PVID needs to be sent alone. Finish previous bits. */
125 if (begin != UINT16_MAX) {
126 assert(begin < k);
127
128 r = add_range(m, begin, k - 1, untagged, &str);
129 if (r < 0)
130 return r;
131
132 begin = UINT16_MAX;
133 }
134
135 r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true, &str);
136 if (r < 0)
137 return r;
138
139 continue;
140 }
141
142 if (!is_bit_set(k, link->network->bridge_vlan_bitmap)) {
143 /* This bit is not set. Finish previous bits. */
144 if (begin != UINT16_MAX) {
145 assert(begin < k);
146
147 r = add_range(m, begin, k - 1, untagged, &str);
148 if (r < 0)
149 return r;
150
151 begin = UINT16_MAX;
152 }
153
154 continue;
155 }
156
157 if (begin != UINT16_MAX) {
158 bool u;
159
160 assert(begin < k);
161
162 u = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
163 if (untagged == u)
164 continue;
165
166 /* Tagging flag is changed from the previous bits. Finish them. */
167 r = add_range(m, begin, k - 1, untagged, &str);
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;
178 untagged = is_bit_set(k, link->network->bridge_vlan_untagged_bitmap);
179 }
180
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);
186
187 log_link_debug(link, "Setting Bridge VLAN IDs: %s", strna(str));
188 return 0;
189 }
190
191 static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
192 _cleanup_free_ char *str = NULL;
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
211 r = add_range(m, begin, k - 1, /* untagged = */ false, &str);
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);
230
231 log_link_debug(link, "Removing Bridge VLAN IDs: %s", strna(str));
232 return 0;
233 }
234
235 int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set) {
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
256 if (is_set)
257 r = bridge_vlan_append_set_info(link, m);
258 else
259 r = bridge_vlan_append_del_info(link, m);
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;
268 }
269
270 #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
271
272 int link_update_bridge_vlan(Link *link, sd_netlink_message *m) {
273 _cleanup_free_ void *data = NULL;
274 size_t len;
275 uint16_t begin = UINT16_MAX;
276 int r, family;
277
278 assert(link);
279 assert(m);
280
281 r = sd_rtnl_message_get_family(m, &family);
282 if (r < 0)
283 return r;
284
285 if (family != AF_BRIDGE)
286 return 0;
287
288 r = sd_netlink_message_read_data(m, IFLA_AF_SPEC, &len, &data);
289 if (r == -ENODATA)
290 return 0;
291 if (r < 0)
292 return r;
293
294 memzero(link->bridge_vlan_bitmap, sizeof(link->bridge_vlan_bitmap));
295
296 for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
297 struct bridge_vlan_info *p;
298
299 if (RTA_TYPE(rta) != IFLA_BRIDGE_VLAN_INFO)
300 continue;
301 if (RTA_PAYLOAD(rta) != sizeof(struct bridge_vlan_info))
302 continue;
303
304 p = RTA_DATA(rta);
305
306 if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_BEGIN)) {
307 begin = p->vid;
308 continue;
309 }
310
311 if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_RANGE_END)) {
312 for (uint16_t k = begin; k <= p->vid; k++)
313 set_bit(k, link->bridge_vlan_bitmap);
314
315 begin = UINT16_MAX;
316 continue;
317 }
318
319 if (FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_PVID)) {
320 link->bridge_vlan_pvid = p->vid;
321 link->bridge_vlan_pvid_is_untagged = FLAGS_SET(p->flags, BRIDGE_VLAN_INFO_UNTAGGED);
322 }
323
324 set_bit(p->vid, link->bridge_vlan_bitmap);
325 begin = UINT16_MAX;
326 }
327
328 return 0;
329 }
330
331 void network_adjust_bridge_vlan(Network *network) {
332 assert(network);
333
334 for (uint16_t k = 0; k < BRIDGE_VLAN_BITMAP_MAX; k++)
335 if (is_bit_set(k, network->bridge_vlan_untagged_bitmap))
336 set_bit(k, network->bridge_vlan_bitmap);
337
338 if (vlanid_is_valid(network->bridge_vlan_pvid))
339 set_bit(network->bridge_vlan_pvid, network->bridge_vlan_bitmap);
340 }
341
342 int config_parse_bridge_vlan_id(
343 const char *unit,
344 const char *filename,
345 unsigned line,
346 const char *section,
347 unsigned section_line,
348 const char *lvalue,
349 int ltype,
350 const char *rvalue,
351 void *data,
352 void *userdata) {
353
354 uint16_t v, *id = ASSERT_PTR(data);
355 int r;
356
357 assert(filename);
358 assert(section);
359 assert(lvalue);
360 assert(rvalue);
361
362 if (isempty(rvalue)) {
363 *id = BRIDGE_VLAN_KEEP_PVID;
364 return 0;
365 }
366
367 if (parse_boolean(rvalue) == 0) {
368 *id = BRIDGE_VLAN_REMOVE_PVID;
369 return 0;
370 }
371
372 r = parse_vlanid(rvalue, &v);
373 if (r < 0) {
374 log_syntax(unit, LOG_WARNING, filename, line, r,
375 "Failed to parse %s=, ignoring: %s",
376 lvalue, rvalue);
377 return 0;
378 }
379
380 *id = v;
381 return 0;
382 }
383
384 int config_parse_bridge_vlan_id_range(
385 const char *unit,
386 const char *filename,
387 unsigned line,
388 const char *section,
389 unsigned section_line,
390 const char *lvalue,
391 int ltype,
392 const char *rvalue,
393 void *data,
394 void *userdata) {
395
396 uint32_t *bitmap = ASSERT_PTR(data);
397 uint16_t vid, vid_end;
398 int r;
399
400 assert(filename);
401 assert(section);
402 assert(lvalue);
403 assert(rvalue);
404
405 if (isempty(rvalue)) {
406 memzero(bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t));
407 return 0;
408 }
409
410 r = parse_vid_range(rvalue, &vid, &vid_end);
411 if (r < 0) {
412 log_syntax(unit, LOG_WARNING, filename, line, r,
413 "Failed to parse %s=, ignoring: %s",
414 lvalue, rvalue);
415 return 0;
416 }
417
418 for (; vid <= vid_end; vid++)
419 set_bit(vid, bitmap);
420
421 return 0;
422 }