]>
Commit | Line | Data |
---|---|---|
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 |
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)); | |
13b498f9 TJ |
23 | } |
24 | ||
a1e92eee | 25 | static 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 | 30 | static 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 | 51 | static 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 |
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 | ||
26ba67e5 | 109 | static 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 | 191 | static 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 | ||
235 | int 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 |
270 | int 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 |
329 | void 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 | 340 | int 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 | 382 | int 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 | } |