]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
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 | ||
20 | static 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 | 25 | static 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 |
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; | |
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 | 137 | static 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 | ||
149 | int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) { | |
150 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
13b498f9 | 151 | sd_netlink *rtnl; |
7bbb43a7 YW |
152 | uint16_t flags; |
153 | int r; | |
13b498f9 TJ |
154 | |
155 | assert(link); | |
156 | assert(link->manager); | |
157 | assert(br_vid_bitmap); | |
158 | assert(br_untagged_bitmap); | |
159 | assert(link->network); | |
160 | ||
161 | /* pvid might not be in br_vid_bitmap yet */ | |
162 | if (pvid) | |
163 | set_bit(pvid, br_vid_bitmap); | |
164 | ||
165 | rtnl = link->manager->rtnl; | |
166 | ||
167 | /* create new RTM message */ | |
168 | r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex); | |
169 | if (r < 0) | |
170 | return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); | |
171 | ||
172 | r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); | |
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) { | |
182 | flags = BRIDGE_FLAGS_SELF; | |
14bb274d ZJS |
183 | r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t)); |
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 */ | |
189 | r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap); | |
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 */ | |
302a796f YW |
198 | r = netlink_call_async(rtnl, NULL, req, set_brvlan_handler, |
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 |
208 | int 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 |
227 | int 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) { | |
244 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue); | |
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 | ||
255 | int 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) { | |
272 | log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue); | |
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 | } |