]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-brvlan.c
network: bridgeFDB: rename FdbEntry -> BridgeFDB
[thirdparty/systemd.git] / src / network / networkd-brvlan.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2016 BISDN GmbH. All rights reserved.
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"
14 #include "networkd-link.h"
15 #include "networkd-manager.h"
16 #include "networkd-network.h"
17 #include "parse-util.h"
18 #include "vlan-util.h"
19
20 static bool is_bit_set(unsigned bit, uint32_t scope) {
21 assert(bit < sizeof(scope)*8);
22 return scope & (UINT32_C(1) << bit);
23 }
24
25 static void set_bit(unsigned nr, uint32_t *addr) {
26 if (nr < BRIDGE_VLAN_BITMAP_MAX)
27 addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
28 }
29
30 static 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
45 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) {
46 struct bridge_vlan_info br_vlan;
47 bool done, untagged = false;
48 uint16_t begin, end;
49 int r, cnt;
50
51 assert(link);
52 assert(req);
53 assert(br_vid_bitmap);
54 assert(br_untagged_bitmap);
55
56 cnt = 0;
57
58 begin = end = UINT16_MAX;
59 for (int k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
60 uint32_t untagged_map = br_untagged_bitmap[k];
61 uint32_t vid_map = br_vid_bitmap[k];
62 unsigned base_bit;
63 int i;
64
65 base_bit = k * 32;
66 i = -1;
67 done = false;
68 do {
69 int 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 = true;
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;
131 } while (!done);
132 }
133
134 assert(cnt > 0);
135 return cnt;
136 }
137
138 static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
139 int r;
140
141 assert(link);
142
143 r = sd_netlink_message_get_errno(m);
144 if (r < 0 && r != -EEXIST)
145 log_link_message_warning_errno(link, m, r, "Could not add VLAN to bridge port");
146
147 return 1;
148 }
149
150 int link_set_bridge_vlan(Link *link) {
151 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
152 int r;
153
154 assert(link);
155 assert(link->manager);
156 assert(link->network);
157
158 if (!link->network->use_br_vlan)
159 return 0;
160
161 if (!link->network->bridge && !streq_ptr(link->kind, "bridge"))
162 return 0;
163
164 /* pvid might not be in br_vid_bitmap yet */
165 if (link->network->pvid)
166 set_bit(link->network->pvid, link->network->br_vid_bitmap);
167
168 /* create new RTM message */
169 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
170 if (r < 0)
171 return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
172
173 r = sd_rtnl_message_link_set_family(req, AF_BRIDGE);
174 if (r < 0)
175 return log_link_error_errno(link, r, "Could not set message family: %m");
176
177 r = sd_netlink_message_open_container(req, IFLA_AF_SPEC);
178 if (r < 0)
179 return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
180
181 /* master needs flag self */
182 if (!link->network->bridge) {
183 uint16_t flags = BRIDGE_FLAGS_SELF;
184 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(flags));
185 if (r < 0)
186 return log_link_error_errno(link, r, "Could not open IFLA_BRIDGE_FLAGS: %m");
187 }
188
189 /* add vlan info */
190 r = append_vlan_info_data(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap);
191 if (r < 0)
192 return log_link_error_errno(link, r, "Could not append VLANs: %m");
193
194 r = sd_netlink_message_close_container(req);
195 if (r < 0)
196 return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
197
198 /* send message to the kernel */
199 r = netlink_call_async(link->manager->rtnl, NULL, req, set_brvlan_handler,
200 link_netlink_destroy_callback, link);
201 if (r < 0)
202 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
203
204 link_ref(link);
205
206 return 0;
207 }
208
209 int config_parse_brvlan_pvid(const char *unit, const char *filename,
210 unsigned line, const char *section,
211 unsigned section_line, const char *lvalue,
212 int ltype, const char *rvalue, void *data,
213 void *userdata) {
214 Network *network = userdata;
215 uint16_t pvid;
216 int r;
217
218 r = parse_vlanid(rvalue, &pvid);
219 if (r < 0)
220 return r;
221
222 network->pvid = pvid;
223 network->use_br_vlan = true;
224
225 return 0;
226 }
227
228 int config_parse_brvlan_vlan(const char *unit, const char *filename,
229 unsigned line, const char *section,
230 unsigned section_line, const char *lvalue,
231 int ltype, const char *rvalue, void *data,
232 void *userdata) {
233 Network *network = userdata;
234 uint16_t vid, vid_end;
235 int r;
236
237 assert(filename);
238 assert(section);
239 assert(lvalue);
240 assert(rvalue);
241 assert(data);
242
243 r = parse_vid_range(rvalue, &vid, &vid_end);
244 if (r < 0) {
245 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
246 return 0;
247 }
248
249 for (; vid <= vid_end; vid++)
250 set_bit(vid, network->br_vid_bitmap);
251
252 network->use_br_vlan = true;
253 return 0;
254 }
255
256 int config_parse_brvlan_untagged(const char *unit, const char *filename,
257 unsigned line, const char *section,
258 unsigned section_line, const char *lvalue,
259 int ltype, const char *rvalue, void *data,
260 void *userdata) {
261 Network *network = userdata;
262 int r;
263 uint16_t vid, vid_end;
264
265 assert(filename);
266 assert(section);
267 assert(lvalue);
268 assert(rvalue);
269 assert(data);
270
271 r = parse_vid_range(rvalue, &vid, &vid_end);
272 if (r < 0) {
273 log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue);
274 return 0;
275 }
276
277 for (; vid <= vid_end; vid++) {
278 set_bit(vid, network->br_vid_bitmap);
279 set_bit(vid, network->br_untagged_bitmap);
280 }
281
282 network->use_br_vlan = true;
283 return 0;
284 }