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