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-bridge-vlan.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
is_bit_set(unsigned bit,uint32_t scope)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
set_bit(unsigned nr,uint32_t * addr)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
find_next_bit(int i,uint32_t x)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
bridge_vlan_append_info(const Link * link,sd_netlink_message * req,uint16_t pvid,const uint32_t * br_vid_bitmap,const uint32_t * br_untagged_bitmap)45 int bridge_vlan_append_info(
46 const Link *link,
47 sd_netlink_message *req,
48 uint16_t pvid,
49 const uint32_t *br_vid_bitmap,
50 const uint32_t *br_untagged_bitmap) {
51
52 struct bridge_vlan_info br_vlan;
53 bool done, untagged = false;
54 uint16_t begin, end;
55 int r, cnt;
56
57 assert(link);
58 assert(req);
59 assert(br_vid_bitmap);
60 assert(br_untagged_bitmap);
61
62 cnt = 0;
63
64 begin = end = UINT16_MAX;
65 for (int k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
66 uint32_t untagged_map = br_untagged_bitmap[k];
67 uint32_t vid_map = br_vid_bitmap[k];
68 unsigned base_bit = k * 32;
69 int i = -1;
70
71 done = false;
72 do {
73 int j = find_next_bit(i, vid_map);
74 if (j > 0) {
75 /* first hit of any bit */
76 if (begin == UINT16_MAX && end == UINT16_MAX) {
77 begin = end = j - 1 + base_bit;
78 untagged = is_bit_set(j - 1, untagged_map);
79 goto next;
80 }
81
82 /* this bit is a continuation of prior bits */
83 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) {
84 end++;
85 goto next;
86 }
87 } else
88 done = true;
89
90 if (begin != UINT16_MAX) {
91 cnt++;
92 if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1)
93 break;
94
95 br_vlan.flags = 0;
96 if (untagged)
97 br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
98
99 if (begin == end) {
100 br_vlan.vid = begin;
101
102 if (begin == pvid)
103 br_vlan.flags |= BRIDGE_VLAN_INFO_PVID;
104
105 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
106 if (r < 0)
107 return r;
108 } else {
109 br_vlan.vid = begin;
110 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
111
112 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
113 if (r < 0)
114 return r;
115
116 br_vlan.vid = end;
117 br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
118 br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END;
119
120 r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan));
121 if (r < 0)
122 return r;
123 }
124
125 if (done)
126 break;
127 }
128 if (j > 0) {
129 begin = end = j - 1 + base_bit;
130 untagged = is_bit_set(j - 1, untagged_map);
131 }
132
133 next:
134 i = j;
135 } while (!done);
136 }
137
138 assert(cnt > 0);
139 return cnt;
140 }
141
network_adjust_bridge_vlan(Network * network)142 void network_adjust_bridge_vlan(Network *network) {
143 assert(network);
144
145 if (!network->use_br_vlan)
146 return;
147
148 /* pvid might not be in br_vid_bitmap yet */
149 if (network->pvid)
150 set_bit(network->pvid, network->br_vid_bitmap);
151 }
152
config_parse_brvlan_pvid(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)153 int config_parse_brvlan_pvid(
154 const char *unit,
155 const char *filename,
156 unsigned line,
157 const char *section,
158 unsigned section_line,
159 const char *lvalue,
160 int ltype,
161 const char *rvalue,
162 void *data,
163 void *userdata) {
164
165 Network *network = userdata;
166 uint16_t pvid;
167 int r;
168
169 r = parse_vlanid(rvalue, &pvid);
170 if (r < 0)
171 return r;
172
173 network->pvid = pvid;
174 network->use_br_vlan = true;
175
176 return 0;
177 }
178
config_parse_brvlan_vlan(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)179 int config_parse_brvlan_vlan(
180 const char *unit,
181 const char *filename,
182 unsigned line,
183 const char *section,
184 unsigned section_line,
185 const char *lvalue,
186 int ltype,
187 const char *rvalue,
188 void *data,
189 void *userdata) {
190
191 Network *network = userdata;
192 uint16_t vid, vid_end;
193 int r;
194
195 assert(filename);
196 assert(section);
197 assert(lvalue);
198 assert(rvalue);
199 assert(data);
200
201 r = parse_vid_range(rvalue, &vid, &vid_end);
202 if (r < 0) {
203 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
204 return 0;
205 }
206
207 for (; vid <= vid_end; vid++)
208 set_bit(vid, network->br_vid_bitmap);
209
210 network->use_br_vlan = true;
211 return 0;
212 }
213
config_parse_brvlan_untagged(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)214 int config_parse_brvlan_untagged(
215 const char *unit,
216 const char *filename,
217 unsigned line,
218 const char *section,
219 unsigned section_line,
220 const char *lvalue,
221 int ltype,
222 const char *rvalue,
223 void *data,
224 void *userdata) {
225
226 Network *network = userdata;
227 uint16_t vid, vid_end;
228 int r;
229
230 assert(filename);
231 assert(section);
232 assert(lvalue);
233 assert(rvalue);
234 assert(data);
235
236 r = parse_vid_range(rvalue, &vid, &vid_end);
237 if (r < 0) {
238 log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue);
239 return 0;
240 }
241
242 for (; vid <= vid_end; vid++) {
243 set_bit(vid, network->br_vid_bitmap);
244 set_bit(vid, network->br_untagged_bitmap);
245 }
246
247 network->use_br_vlan = true;
248 return 0;
249 }
250