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