1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2016 BISDN GmbH. All rights reserved.
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <netinet/in.h>
22 #include <linux/if_bridge.h>
25 #include "alloc-util.h"
26 #include "conf-parser.h"
27 #include "netlink-util.h"
28 #include "networkd-brvlan.h"
29 #include "networkd-link.h"
30 #include "networkd-manager.h"
31 #include "networkd-network.h"
32 #include "parse-util.h"
33 #include "vlan-util.h"
35 static bool is_bit_set(unsigned bit
, uint32_t scope
) {
36 assert(bit
< sizeof(scope
)*8);
37 return scope
& (1 << bit
);
40 static inline void set_bit(unsigned nr
, uint32_t *addr
) {
41 if (nr
< BRIDGE_VLAN_BITMAP_MAX
)
42 addr
[nr
/ 32] |= (((uint32_t) 1) << (nr
% 32));
45 static int find_next_bit(int i
, uint32_t x
) {
53 return BUILTIN_FFS_U32(x
);
55 /* mask off prior finds to get next */
56 j
= __builtin_ffs(x
>> i
);
60 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
) {
61 struct bridge_vlan_info br_vlan
;
62 int i
, j
, k
, r
, done
, cnt
;
64 bool untagged
= false;
68 assert(br_vid_bitmap
);
69 assert(br_untagged_bitmap
);
73 begin
= end
= UINT16_MAX
;
74 for (k
= 0; k
< BRIDGE_VLAN_BITMAP_LEN
; k
++) {
76 uint32_t vid_map
= br_vid_bitmap
[k
];
77 uint32_t untagged_map
= br_untagged_bitmap
[k
];
83 j
= find_next_bit(i
, vid_map
);
85 /* first hit of any bit */
86 if (begin
== UINT16_MAX
&& end
== UINT16_MAX
) {
87 begin
= end
= j
- 1 + base_bit
;
88 untagged
= is_bit_set(j
- 1, untagged_map
);
92 /* this bit is a continuation of prior bits */
93 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
) {
100 if (begin
!= UINT16_MAX
) {
102 if (done
&& k
< BRIDGE_VLAN_BITMAP_LEN
- 1)
107 br_vlan
.flags
|= BRIDGE_VLAN_INFO_UNTAGGED
;
113 br_vlan
.flags
|= BRIDGE_VLAN_INFO_PVID
;
115 r
= sd_netlink_message_append_data(req
, IFLA_BRIDGE_VLAN_INFO
, &br_vlan
, sizeof(br_vlan
));
117 return log_link_error_errno(link
, r
, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
120 br_vlan
.flags
|= BRIDGE_VLAN_INFO_RANGE_BEGIN
;
122 r
= sd_netlink_message_append_data(req
, IFLA_BRIDGE_VLAN_INFO
, &br_vlan
, sizeof(br_vlan
));
124 return log_link_error_errno(link
, r
, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
127 br_vlan
.flags
&= ~BRIDGE_VLAN_INFO_RANGE_BEGIN
;
128 br_vlan
.flags
|= BRIDGE_VLAN_INFO_RANGE_END
;
130 r
= sd_netlink_message_append_data(req
, IFLA_BRIDGE_VLAN_INFO
, &br_vlan
, sizeof(br_vlan
));
132 return log_link_error_errno(link
, r
, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
139 begin
= end
= j
- 1 + base_bit
;
140 untagged
= is_bit_set(j
- 1, untagged_map
);
153 static int set_brvlan_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
154 Link
*link
= userdata
;
159 r
= sd_netlink_message_get_errno(m
);
160 if (r
< 0 && r
!= -EEXIST
)
161 log_link_error_errno(link
, r
, "Could not add VLAN to bridge port: %m");
166 int br_vlan_configure(Link
*link
, uint16_t pvid
, uint32_t *br_vid_bitmap
, uint32_t *br_untagged_bitmap
) {
167 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
173 assert(link
->manager
);
174 assert(br_vid_bitmap
);
175 assert(br_untagged_bitmap
);
176 assert(link
->network
);
178 /* pvid might not be in br_vid_bitmap yet */
180 set_bit(pvid
, br_vid_bitmap
);
182 rtnl
= link
->manager
->rtnl
;
184 /* create new RTM message */
185 r
= sd_rtnl_message_new_link(rtnl
, &req
, RTM_SETLINK
, link
->ifindex
);
187 return log_link_error_errno(link
, r
, "Could not allocate RTM_SETLINK message: %m");
189 r
= sd_rtnl_message_link_set_family(req
, PF_BRIDGE
);
191 return log_link_error_errno(link
, r
, "Could not set message family: %m");
193 r
= sd_netlink_message_open_container(req
, IFLA_AF_SPEC
);
195 return log_link_error_errno(link
, r
, "Could not open IFLA_AF_SPEC container: %m");
197 /* master needs flag self */
198 if (!link
->network
->bridge
) {
199 flags
= BRIDGE_FLAGS_SELF
;
200 sd_netlink_message_append_data(req
, IFLA_BRIDGE_FLAGS
, &flags
, sizeof(uint16_t));
204 r
= append_vlan_info_data(link
, req
, pvid
, br_vid_bitmap
, br_untagged_bitmap
);
206 return log_link_error_errno(link
, r
, "Could not append VLANs: %m");
208 r
= sd_netlink_message_close_container(req
);
210 return log_link_error_errno(link
, r
, "Could not close IFLA_AF_SPEC container: %m");
212 /* send message to the kernel */
213 r
= sd_netlink_call_async(rtnl
, req
, set_brvlan_handler
, link
, 0, NULL
);
215 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
220 static int parse_vid_range(const char *rvalue
, uint16_t *vid
, uint16_t *vid_end
) {
223 char *_rvalue
= NULL
;
224 uint16_t _vid
= UINT16_MAX
;
225 uint16_t _vid_end
= UINT16_MAX
;
231 _rvalue
= strdupa(rvalue
);
232 p
= strchr(_rvalue
, '-');
236 r
= parse_vlanid(_rvalue
, &_vid
);
243 r
= parse_vlanid(p
, &_vid_end
);
250 r
= parse_vlanid(_rvalue
, &_vid
);
263 int config_parse_brvlan_pvid(const char *unit
, const char *filename
,
264 unsigned line
, const char *section
,
265 unsigned section_line
, const char *lvalue
,
266 int ltype
, const char *rvalue
, void *data
,
268 Network
*network
= userdata
;
271 r
= parse_vlanid(rvalue
, &pvid
);
275 network
->pvid
= pvid
;
276 network
->use_br_vlan
= true;
281 int config_parse_brvlan_vlan(const char *unit
, const char *filename
,
282 unsigned line
, const char *section
,
283 unsigned section_line
, const char *lvalue
,
284 int ltype
, const char *rvalue
, void *data
,
286 Network
*network
= userdata
;
288 uint16_t vid
, vid_end
;
296 r
= parse_vid_range(rvalue
, &vid
, &vid_end
);
298 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse VLAN, ignoring: %s", rvalue
);
302 if (UINT16_MAX
== vid_end
)
303 set_bit(vid
++, network
->br_vid_bitmap
);
305 if (vid
>= vid_end
) {
306 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid VLAN range, ignoring %s", rvalue
);
309 for (; vid
<= vid_end
; vid
++)
310 set_bit(vid
, network
->br_vid_bitmap
);
312 network
->use_br_vlan
= true;
316 int config_parse_brvlan_untagged(const char *unit
, const char *filename
,
317 unsigned line
, const char *section
,
318 unsigned section_line
, const char *lvalue
,
319 int ltype
, const char *rvalue
, void *data
,
321 Network
*network
= userdata
;
323 uint16_t vid
, vid_end
;
331 r
= parse_vid_range(rvalue
, &vid
, &vid_end
);
333 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Could not parse VLAN: %s", rvalue
);
337 if (UINT16_MAX
== vid_end
) {
338 set_bit(vid
, network
->br_vid_bitmap
);
339 set_bit(vid
, network
->br_untagged_bitmap
);
341 if (vid
>= vid_end
) {
342 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid VLAN range, ignoring %s", rvalue
);
345 for (; vid
<= vid_end
; vid
++) {
346 set_bit(vid
, network
->br_vid_bitmap
);
347 set_bit(vid
, network
->br_untagged_bitmap
);
350 network
->use_br_vlan
= true;