1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <net/if.h>
4 #include <netinet/in.h>
5 #include <linux/if_arp.h>
6 #include <linux/if_bridge.h>
7 
8 #include "bridge.h"
9 #include "netlink-util.h"
10 #include "networkd-manager.h"
11 #include "string-table.h"
12 #include "vlan-util.h"
13 
14 assert_cc((int) MULTICAST_ROUTER_NONE            == (int) MDB_RTR_TYPE_DISABLED);
15 assert_cc((int) MULTICAST_ROUTER_TEMPORARY_QUERY == (int) MDB_RTR_TYPE_TEMP_QUERY);
16 assert_cc((int) MULTICAST_ROUTER_PERMANENT       == (int) MDB_RTR_TYPE_PERM);
17 assert_cc((int) MULTICAST_ROUTER_TEMPORARY       == (int) MDB_RTR_TYPE_TEMP);
18 
19 static const char* const multicast_router_table[_MULTICAST_ROUTER_MAX] = {
20         [MULTICAST_ROUTER_NONE]            = "no",
21         [MULTICAST_ROUTER_TEMPORARY_QUERY] = "query",
22         [MULTICAST_ROUTER_PERMANENT]       = "permanent",
23         [MULTICAST_ROUTER_TEMPORARY]       = "temporary",
24 };
25 
26 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(multicast_router, MulticastRouter, _MULTICAST_ROUTER_INVALID);
27 DEFINE_CONFIG_PARSE_ENUM(config_parse_multicast_router, multicast_router, MulticastRouter,
28                          "Failed to parse bridge multicast router setting");
29 
30 /* callback for bridge netdev's parameter set */
netdev_bridge_set_handler(sd_netlink * rtnl,sd_netlink_message * m,NetDev * netdev)31 static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
32         int r;
33 
34         assert(netdev);
35         assert(m);
36 
37         r = sd_netlink_message_get_errno(m);
38         if (r < 0) {
39                 log_netdev_warning_errno(netdev, r, "Bridge parameters could not be set: %m");
40                 return 1;
41         }
42 
43         log_netdev_debug(netdev, "Bridge parameters set success");
44 
45         return 1;
46 }
47 
netdev_bridge_post_create_message(NetDev * netdev,sd_netlink_message * req)48 static int netdev_bridge_post_create_message(NetDev *netdev, sd_netlink_message *req) {
49         Bridge *b;
50         int r;
51 
52         assert_se(b = BRIDGE(netdev));
53 
54         r = sd_netlink_message_open_container(req, IFLA_LINKINFO);
55         if (r < 0)
56                 return r;
57 
58         r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
59         if (r < 0)
60                 return r;
61 
62         /* convert to jiffes */
63         if (b->forward_delay != USEC_INFINITY) {
64                 r = sd_netlink_message_append_u32(req, IFLA_BR_FORWARD_DELAY, usec_to_jiffies(b->forward_delay));
65                 if (r < 0)
66                         return r;
67         }
68 
69         if (b->hello_time > 0) {
70                 r = sd_netlink_message_append_u32(req, IFLA_BR_HELLO_TIME, usec_to_jiffies(b->hello_time));
71                 if (r < 0)
72                         return r;
73         }
74 
75         if (b->max_age > 0) {
76                 r = sd_netlink_message_append_u32(req, IFLA_BR_MAX_AGE, usec_to_jiffies(b->max_age));
77                 if (r < 0)
78                         return r;
79         }
80 
81         if (b->ageing_time != USEC_INFINITY) {
82                 r = sd_netlink_message_append_u32(req, IFLA_BR_AGEING_TIME, usec_to_jiffies(b->ageing_time));
83                 if (r < 0)
84                         return r;
85         }
86 
87         if (b->priority > 0) {
88                 r = sd_netlink_message_append_u16(req, IFLA_BR_PRIORITY, b->priority);
89                 if (r < 0)
90                         return r;
91         }
92 
93         if (b->group_fwd_mask > 0) {
94                 r = sd_netlink_message_append_u16(req, IFLA_BR_GROUP_FWD_MASK, b->group_fwd_mask);
95                 if (r < 0)
96                         return r;
97         }
98 
99         if (b->default_pvid != VLANID_INVALID) {
100                 r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_DEFAULT_PVID, b->default_pvid);
101                 if (r < 0)
102                         return r;
103         }
104 
105         if (b->mcast_querier >= 0) {
106                 r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier);
107                 if (r < 0)
108                         return r;
109         }
110 
111         if (b->mcast_snooping >= 0) {
112                 r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping);
113                 if (r < 0)
114                         return r;
115         }
116 
117         if (b->vlan_filtering >= 0) {
118                 r = sd_netlink_message_append_u8(req, IFLA_BR_VLAN_FILTERING, b->vlan_filtering);
119                 if (r < 0)
120                         return r;
121         }
122 
123         if (b->vlan_protocol >= 0) {
124                 r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_PROTOCOL, htobe16(b->vlan_protocol));
125                 if (r < 0)
126                         return r;
127         }
128 
129         if (b->stp >= 0) {
130                 r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp);
131                 if (r < 0)
132                         return r;
133         }
134 
135         if (b->igmp_version > 0) {
136                 r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_IGMP_VERSION, b->igmp_version);
137                 if (r < 0)
138                         return r;
139         }
140 
141         r = sd_netlink_message_close_container(req);
142         if (r < 0)
143                 return r;
144 
145         r = sd_netlink_message_close_container(req);
146         if (r < 0)
147                 return r;
148 
149         return 0;
150 }
151 
netdev_bridge_post_create(NetDev * netdev,Link * link)152 static int netdev_bridge_post_create(NetDev *netdev, Link *link) {
153         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
154         int r;
155 
156         assert(netdev);
157 
158         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex);
159         if (r < 0)
160                 return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m");
161 
162         r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK);
163         if (r < 0)
164                 return log_link_error_errno(link, r, "Could not set netlink message flags: %m");
165 
166         r = netdev_bridge_post_create_message(netdev, req);
167         if (r < 0)
168                 return log_netdev_error_errno(netdev, r, "Could not create netlink message: %m");
169 
170         r = netlink_call_async(netdev->manager->rtnl, NULL, req, netdev_bridge_set_handler,
171                                netdev_destroy_callback, netdev);
172         if (r < 0)
173                 return log_netdev_error_errno(netdev, r, "Could not send netlink message: %m");
174 
175         netdev_ref(netdev);
176 
177         return r;
178 }
179 
config_parse_bridge_igmp_version(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)180 int config_parse_bridge_igmp_version(
181                 const char *unit,
182                 const char *filename,
183                 unsigned line,
184                 const char *section,
185                 unsigned section_line,
186                 const char *lvalue,
187                 int ltype,
188                 const char *rvalue,
189                 void *data,
190                 void *userdata) {
191 
192         Bridge *b = userdata;
193         uint8_t u;
194         int r;
195 
196         assert(filename);
197         assert(lvalue);
198         assert(rvalue);
199         assert(data);
200 
201         if (isempty(rvalue)) {
202                 b->igmp_version = 0; /* 0 means unset. */
203                 return 0;
204         }
205 
206         r = safe_atou8(rvalue, &u);
207         if (r < 0) {
208                 log_syntax(unit, LOG_WARNING, filename, line, r,
209                            "Failed to parse bridge's multicast IGMP version number '%s', ignoring assignment: %m",
210                            rvalue);
211                 return 0;
212         }
213         if (!IN_SET(u, 2, 3)) {
214                 log_syntax(unit, LOG_WARNING, filename, line, 0,
215                            "Invalid bridge's multicast IGMP version number '%s', ignoring assignment.", rvalue);
216                 return 0;
217         }
218 
219         b->igmp_version = u;
220 
221         return 0;
222 }
223 
config_parse_bridge_port_priority(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)224 int config_parse_bridge_port_priority(
225                 const char *unit,
226                 const char *filename,
227                 unsigned line,
228                 const char *section,
229                 unsigned section_line,
230                 const char *lvalue,
231                 int ltype,
232                 const char *rvalue,
233                 void *data,
234                 void *userdata) {
235 
236         uint16_t i;
237         int r;
238 
239         assert(filename);
240         assert(lvalue);
241         assert(rvalue);
242         assert(data);
243 
244         /* This is used in networkd-network-gperf.gperf. */
245 
246         r = safe_atou16(rvalue, &i);
247         if (r < 0) {
248                 log_syntax(unit, LOG_WARNING, filename, line, r,
249                            "Failed to parse bridge port priority, ignoring: %s", rvalue);
250                 return 0;
251         }
252 
253         if (i > LINK_BRIDGE_PORT_PRIORITY_MAX) {
254                 log_syntax(unit, LOG_WARNING, filename, line, 0,
255                            "Bridge port priority is larger than maximum %u, ignoring: %s",
256                            LINK_BRIDGE_PORT_PRIORITY_MAX, rvalue);
257                 return 0;
258         }
259 
260         *((uint16_t *)data) = i;
261 
262         return 0;
263 }
264 
bridge_init(NetDev * n)265 static void bridge_init(NetDev *n) {
266         Bridge *b;
267 
268         b = BRIDGE(n);
269 
270         assert(b);
271 
272         b->mcast_querier = -1;
273         b->mcast_snooping = -1;
274         b->vlan_filtering = -1;
275         b->vlan_protocol = -1;
276         b->stp = -1;
277         b->default_pvid = VLANID_INVALID;
278         b->forward_delay = USEC_INFINITY;
279         b->ageing_time = USEC_INFINITY;
280 }
281 
282 const NetDevVTable bridge_vtable = {
283         .object_size = sizeof(Bridge),
284         .init = bridge_init,
285         .sections = NETDEV_COMMON_SECTIONS "Bridge\0",
286         .post_create = netdev_bridge_post_create,
287         .create_type = NETDEV_CREATE_INDEPENDENT,
288         .iftype = ARPHRD_ETHER,
289         .generate_mac = true,
290 };
291