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