1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2016 BISDN GmbH. All rights reserved.
6 #include <netinet/in.h>
7 #include <linux/if_bridge.h>
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"
20 static bool is_bit_set(unsigned bit
, uint32_t scope
) {
21 assert(bit
< sizeof(scope
)*8);
22 return scope
& (UINT32_C(1) << bit
);
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));
30 static int find_next_bit(int i
, uint32_t x
) {
38 return BUILTIN_FFS_U32(x
);
40 /* mask off prior finds to get next */
41 j
= __builtin_ffs(x
>> i
);
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;
53 assert(br_vid_bitmap
);
54 assert(br_untagged_bitmap
);
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
];
69 int j
= find_next_bit(i
, vid_map
);
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
);
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
) {
86 if (begin
!= UINT16_MAX
) {
88 if (done
&& k
< BRIDGE_VLAN_BITMAP_LEN
- 1)
93 br_vlan
.flags
|= BRIDGE_VLAN_INFO_UNTAGGED
;
99 br_vlan
.flags
|= BRIDGE_VLAN_INFO_PVID
;
101 r
= sd_netlink_message_append_data(req
, IFLA_BRIDGE_VLAN_INFO
, &br_vlan
, sizeof(br_vlan
));
103 return log_link_error_errno(link
, r
, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
106 br_vlan
.flags
|= BRIDGE_VLAN_INFO_RANGE_BEGIN
;
108 r
= sd_netlink_message_append_data(req
, IFLA_BRIDGE_VLAN_INFO
, &br_vlan
, sizeof(br_vlan
));
110 return log_link_error_errno(link
, r
, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
113 br_vlan
.flags
&= ~BRIDGE_VLAN_INFO_RANGE_BEGIN
;
114 br_vlan
.flags
|= BRIDGE_VLAN_INFO_RANGE_END
;
116 r
= sd_netlink_message_append_data(req
, IFLA_BRIDGE_VLAN_INFO
, &br_vlan
, sizeof(br_vlan
));
118 return log_link_error_errno(link
, r
, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m");
125 begin
= end
= j
- 1 + base_bit
;
126 untagged
= is_bit_set(j
- 1, untagged_map
);
138 static int set_brvlan_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
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");
150 int link_set_bridge_vlan(Link
*link
) {
151 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
155 assert(link
->manager
);
156 assert(link
->network
);
158 if (!link
->network
->use_br_vlan
)
161 if (!link
->network
->bridge
&& !streq_ptr(link
->kind
, "bridge"))
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
);
168 /* create new RTM message */
169 r
= sd_rtnl_message_new_link(link
->manager
->rtnl
, &req
, RTM_SETLINK
, link
->ifindex
);
171 return log_link_error_errno(link
, r
, "Could not allocate RTM_SETLINK message: %m");
173 r
= sd_rtnl_message_link_set_family(req
, AF_BRIDGE
);
175 return log_link_error_errno(link
, r
, "Could not set message family: %m");
177 r
= sd_netlink_message_open_container(req
, IFLA_AF_SPEC
);
179 return log_link_error_errno(link
, r
, "Could not open IFLA_AF_SPEC container: %m");
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
));
186 return log_link_error_errno(link
, r
, "Could not open IFLA_BRIDGE_FLAGS: %m");
190 r
= append_vlan_info_data(link
, req
, link
->network
->pvid
, link
->network
->br_vid_bitmap
, link
->network
->br_untagged_bitmap
);
192 return log_link_error_errno(link
, r
, "Could not append VLANs: %m");
194 r
= sd_netlink_message_close_container(req
);
196 return log_link_error_errno(link
, r
, "Could not close IFLA_AF_SPEC container: %m");
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
);
202 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
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
,
214 Network
*network
= userdata
;
218 r
= parse_vlanid(rvalue
, &pvid
);
222 network
->pvid
= pvid
;
223 network
->use_br_vlan
= true;
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
,
233 Network
*network
= userdata
;
234 uint16_t vid
, vid_end
;
243 r
= parse_vid_range(rvalue
, &vid
, &vid_end
);
245 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse VLAN, ignoring: %s", rvalue
);
249 for (; vid
<= vid_end
; vid
++)
250 set_bit(vid
, network
->br_vid_bitmap
);
252 network
->use_br_vlan
= true;
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
,
261 Network
*network
= userdata
;
263 uint16_t vid
, vid_end
;
271 r
= parse_vid_range(rvalue
, &vid
, &vid_end
);
273 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Could not parse VLAN: %s", rvalue
);
277 for (; vid
<= vid_end
; vid
++) {
278 set_bit(vid
, network
->br_vid_bitmap
);
279 set_bit(vid
, network
->br_untagged_bitmap
);
282 network
->use_br_vlan
= true;