]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-brvlan.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / network / networkd-brvlan.c
CommitLineData
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"
13#include "networkd-brvlan.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
20static bool is_bit_set(unsigned bit, uint32_t scope) {
21 assert(bit < sizeof(scope)*8);
3a1df8c4 22 return scope & (UINT32_C(1) << bit);
13b498f9
TJ
23}
24
a1e92eee 25static void set_bit(unsigned nr, uint32_t *addr) {
13b498f9 26 if (nr < BRIDGE_VLAN_BITMAP_MAX)
3a1df8c4 27 addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
13b498f9
TJ
28}
29
13b498f9
TJ
30static int find_next_bit(int i, uint32_t x) {
31 int j;
32
33 if (i >= 32)
34 return -1;
35
36 /* find first bit */
37 if (i < 0)
38 return BUILTIN_FFS_U32(x);
39
40 /* mask off prior finds to get next */
41 j = __builtin_ffs(x >> i);
42 return j ? j + i : 0;
43}
44
45static 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) {
46 struct bridge_vlan_info br_vlan;
7bbb43a7 47 int i, j, k, r, cnt;
13b498f9 48 uint16_t begin, end;
7bbb43a7 49 bool done, untagged = false;
13b498f9
TJ
50
51 assert(link);
52 assert(req);
53 assert(br_vid_bitmap);
54 assert(br_untagged_bitmap);
55
412ac780 56 cnt = 0;
13b498f9
TJ
57
58 begin = end = UINT16_MAX;
59 for (k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
60 unsigned base_bit;
61 uint32_t vid_map = br_vid_bitmap[k];
62 uint32_t untagged_map = br_untagged_bitmap[k];
63
64 base_bit = k * 32;
65 i = -1;
7bbb43a7 66 done = false;
13b498f9
TJ
67 do {
68 j = find_next_bit(i, vid_map);
69 if (j > 0) {
70 /* first hit of any bit */
71 if (begin == UINT16_MAX && end == UINT16_MAX) {
72 begin = end = j - 1 + base_bit;
73 untagged = is_bit_set(j - 1, untagged_map);
74 goto next;
75 }
76
77 /* this bit is a continuation of prior bits */
78 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) {
79 end++;
80 goto next;
81 }
82 } else
7bbb43a7 83 done = true;
13b498f9
TJ
84
85 if (begin != UINT16_MAX) {
86 cnt++;
87 if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1)
88 break;
89
90 br_vlan.flags = 0;
91 if (untagged)
92 br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
93
94 if (begin == end) {
95 br_vlan.vid = begin;
96
97 if (begin == pvid)
98 br_vlan.flags |= BRIDGE_VLAN_INFO_PVID;
99
100 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
101 if (r < 0)
102 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
103 } else {
104 br_vlan.vid = begin;
105 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
106
107 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
108 if (r < 0)
109 return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
110
111 br_vlan.vid = end;
112 br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
113 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END;
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 }
119
120 if (done)
121 break;
122 }
123 if (j > 0) {
124 begin = end = j - 1 + base_bit;
125 untagged = is_bit_set(j - 1, untagged_map);
126 }
127
128 next:
129 i = j;
508f63b4 130 } while (!done);
13b498f9 131 }
13b498f9 132
412ac780 133 assert(cnt > 0);
13b498f9
TJ
134 return cnt;
135}
136
302a796f 137static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
13b498f9
TJ
138 int r;
139
140 assert(link);
141
142 r = sd_netlink_message_get_errno(m);
143 if (r < 0 && r != -EEXIST)
5ecb131d 144 log_link_message_warning_errno(link, m, r, "Could not add VLAN to bridge port");
13b498f9
TJ
145
146 return 1;
147}
148
34d7f2c9 149int link_set_bridge_vlan(Link *link) {
13b498f9 150 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
7bbb43a7 151 int r;
13b498f9
TJ
152
153 assert(link);
154 assert(link->manager);
13b498f9
TJ
155 assert(link->network);
156
34d7f2c9
YW
157 if (!link->network->use_br_vlan)
158 return 0;
13b498f9 159
34d7f2c9
YW
160 if (!link->network->bridge && !streq_ptr(link->kind, "bridge"))
161 return 0;
162
163 /* pvid might not be in br_vid_bitmap yet */
164 if (link->network->pvid)
165 set_bit(link->network->pvid, link->network->br_vid_bitmap);
13b498f9
TJ
166
167 /* create new RTM message */
34d7f2c9 168 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
13b498f9
TJ
169 if (r < 0)
170 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
171
40eb1b0a 172 r = sd_rtnl_message_link_set_family(req, AF_BRIDGE);
13b498f9
TJ
173 if (r < 0)
174 return log_link_error_errno(link, r, "Could not set message family: %m");
175
176 r = sd_netlink_message_open_container(req, IFLA_AF_SPEC);
177 if (r < 0)
178 return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
179
180 /* master needs flag self */
181 if (!link->network->bridge) {
34d7f2c9
YW
182 uint16_t flags = BRIDGE_FLAGS_SELF;
183 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(flags));
14bb274d
ZJS
184 if (r < 0)
185 return log_link_error_errno(link, r, "Could not open IFLA_BRIDGE_FLAGS: %m");
13b498f9
TJ
186 }
187
188 /* add vlan info */
34d7f2c9 189 r = append_vlan_info_data(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap);
13b498f9
TJ
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 */
34d7f2c9 198 r = netlink_call_async(link->manager->rtnl, NULL, req, set_brvlan_handler,
302a796f 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
ffff9abe
TJ
208int config_parse_brvlan_pvid(const char *unit, const char *filename,
209 unsigned line, const char *section,
210 unsigned section_line, const char *lvalue,
211 int ltype, const char *rvalue, void *data,
212 void *userdata) {
213 Network *network = userdata;
ffff9abe 214 uint16_t pvid;
7bbb43a7
YW
215 int r;
216
ffff9abe
TJ
217 r = parse_vlanid(rvalue, &pvid);
218 if (r < 0)
219 return r;
220
221 network->pvid = pvid;
222 network->use_br_vlan = true;
223
224 return 0;
225}
226
13b498f9
TJ
227int config_parse_brvlan_vlan(const char *unit, const char *filename,
228 unsigned line, const char *section,
229 unsigned section_line, const char *lvalue,
230 int ltype, const char *rvalue, void *data,
231 void *userdata) {
232 Network *network = userdata;
13b498f9 233 uint16_t vid, vid_end;
7bbb43a7 234 int r;
13b498f9
TJ
235
236 assert(filename);
237 assert(section);
238 assert(lvalue);
239 assert(rvalue);
240 assert(data);
241
242 r = parse_vid_range(rvalue, &vid, &vid_end);
243 if (r < 0) {
d96edb2c 244 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
13b498f9
TJ
245 return 0;
246 }
247
fb721f08
YW
248 for (; vid <= vid_end; vid++)
249 set_bit(vid, network->br_vid_bitmap);
250
ffff9abe 251 network->use_br_vlan = true;
13b498f9
TJ
252 return 0;
253}
254
255int config_parse_brvlan_untagged(const char *unit, const char *filename,
256 unsigned line, const char *section,
257 unsigned section_line, const char *lvalue,
258 int ltype, const char *rvalue, void *data,
259 void *userdata) {
260 Network *network = userdata;
261 int r;
262 uint16_t vid, vid_end;
263
264 assert(filename);
265 assert(section);
266 assert(lvalue);
267 assert(rvalue);
268 assert(data);
269
270 r = parse_vid_range(rvalue, &vid, &vid_end);
271 if (r < 0) {
d96edb2c 272 log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue);
13b498f9
TJ
273 return 0;
274 }
275
fb721f08 276 for (; vid <= vid_end; vid++) {
13b498f9
TJ
277 set_bit(vid, network->br_vid_bitmap);
278 set_bit(vid, network->br_untagged_bitmap);
13b498f9 279 }
fb721f08 280
ffff9abe 281 network->use_br_vlan = true;
13b498f9
TJ
282 return 0;
283}