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