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