1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
5
6 #include "alloc-util.h"
7 #include "bond.h"
8 #include "bond-util.h"
9 #include "conf-parser.h"
10 #include "ether-addr-util.h"
11 #include "extract-word.h"
12 #include "netlink-util.h"
13 #include "networkd-manager.h"
14 #include "string-table.h"
15
16 /*
17 * Number of seconds between instances where the bonding
18 * driver sends learning packets to each slaves peer switch
19 */
20 #define LEARNING_PACKETS_INTERVAL_MIN_SEC (1 * USEC_PER_SEC)
21 #define LEARNING_PACKETS_INTERVAL_MAX_SEC (0x7fffffff * USEC_PER_SEC)
22
23 /* Number of IGMP membership reports to be issued after
24 * a failover event.
25 */
26 #define RESEND_IGMP_MIN 0
27 #define RESEND_IGMP_MAX 255
28 #define RESEND_IGMP_DEFAULT 1
29
30 /*
31 * Number of packets to transmit through a slave before
32 * moving to the next one.
33 */
34 #define PACKETS_PER_SLAVE_MIN 0
35 #define PACKETS_PER_SLAVE_MAX 65535
36 #define PACKETS_PER_SLAVE_DEFAULT 1
37
38 /*
39 * Number of peer notifications (gratuitous ARPs and
40 * unsolicited IPv6 Neighbor Advertisements) to be issued after a
41 * failover event.
42 */
43 #define GRATUITOUS_ARP_MIN 0
44 #define GRATUITOUS_ARP_MAX 255
45 #define GRATUITOUS_ARP_DEFAULT 1
46
47 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_mode, bond_mode, BondMode, "Failed to parse bond mode");
48 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_xmit_hash_policy,
49 bond_xmit_hash_policy,
50 BondXmitHashPolicy,
51 "Failed to parse bond transmit hash policy");
52 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate");
53 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_ad_select, bond_ad_select, BondAdSelect, "Failed to parse bond AD select");
54 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_fail_over_mac, bond_fail_over_mac, BondFailOverMac, "Failed to parse bond fail over MAC");
55 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_validate, bond_arp_validate, BondArpValidate, "Failed to parse bond arp validate");
56 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets");
57 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_primary_reselect, bond_primary_reselect, BondPrimaryReselect, "Failed to parse bond primary reselect");
58
netdev_bond_fill_message_create(NetDev * netdev,Link * link,sd_netlink_message * m)59 static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
60 Bond *b;
61 int r;
62
63 assert(netdev);
64 assert(!link);
65 assert(m);
66
67 b = BOND(netdev);
68
69 assert(b);
70
71 if (b->mode != _NETDEV_BOND_MODE_INVALID) {
72 r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE, b->mode);
73 if (r < 0)
74 return r;
75 }
76
77 if (b->xmit_hash_policy != _NETDEV_BOND_XMIT_HASH_POLICY_INVALID) {
78 r = sd_netlink_message_append_u8(m, IFLA_BOND_XMIT_HASH_POLICY, b->xmit_hash_policy);
79 if (r < 0)
80 return r;
81 }
82
83 if (b->lacp_rate != _NETDEV_BOND_LACP_RATE_INVALID &&
84 b->mode == NETDEV_BOND_MODE_802_3AD) {
85 r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate);
86 if (r < 0)
87 return r;
88 }
89
90 if (b->miimon != 0) {
91 r = sd_netlink_message_append_u32(m, IFLA_BOND_MIIMON, b->miimon / USEC_PER_MSEC);
92 if (r < 0)
93 return r;
94 }
95
96 if (b->downdelay != 0) {
97 r = sd_netlink_message_append_u32(m, IFLA_BOND_DOWNDELAY, b->downdelay / USEC_PER_MSEC);
98 if (r < 0)
99 return r;
100 }
101
102 if (b->updelay != 0) {
103 r = sd_netlink_message_append_u32(m, IFLA_BOND_UPDELAY, b->updelay / USEC_PER_MSEC);
104 if (r < 0)
105 return r;
106 }
107
108 if (b->arp_interval != 0) {
109 r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_INTERVAL, b->arp_interval / USEC_PER_MSEC);
110 if (r < 0)
111 return r;
112
113 if (b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC &&
114 b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC) {
115 r = sd_netlink_message_append_u32(m, IFLA_BOND_LP_INTERVAL, b->lp_interval / USEC_PER_SEC);
116 if (r < 0)
117 return r;
118 }
119 }
120
121 if (b->ad_select != _NETDEV_BOND_AD_SELECT_INVALID &&
122 b->mode == NETDEV_BOND_MODE_802_3AD) {
123 r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_SELECT, b->ad_select);
124 if (r < 0)
125 return r;
126 }
127
128 if (b->fail_over_mac != _NETDEV_BOND_FAIL_OVER_MAC_INVALID &&
129 b->mode == NETDEV_BOND_MODE_ACTIVE_BACKUP) {
130 r = sd_netlink_message_append_u8(m, IFLA_BOND_FAIL_OVER_MAC, b->fail_over_mac);
131 if (r < 0)
132 return r;
133 }
134
135 if (b->arp_validate != _NETDEV_BOND_ARP_VALIDATE_INVALID) {
136 r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_VALIDATE, b->arp_validate);
137 if (r < 0)
138 return r;
139 }
140
141 if (b->arp_all_targets != _NETDEV_BOND_ARP_ALL_TARGETS_INVALID) {
142 r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->arp_all_targets);
143 if (r < 0)
144 return r;
145 }
146
147 if (b->primary_reselect != _NETDEV_BOND_PRIMARY_RESELECT_INVALID) {
148 r = sd_netlink_message_append_u8(m, IFLA_BOND_PRIMARY_RESELECT, b->primary_reselect);
149 if (r < 0)
150 return r;
151 }
152
153 if (b->resend_igmp <= RESEND_IGMP_MAX) {
154 r = sd_netlink_message_append_u32(m, IFLA_BOND_RESEND_IGMP, b->resend_igmp);
155 if (r < 0)
156 return r;
157 }
158
159 if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX &&
160 b->mode == NETDEV_BOND_MODE_BALANCE_RR) {
161 r = sd_netlink_message_append_u32(m, IFLA_BOND_PACKETS_PER_SLAVE, b->packets_per_slave);
162 if (r < 0)
163 return r;
164 }
165
166 if (b->num_grat_arp <= GRATUITOUS_ARP_MAX) {
167 r = sd_netlink_message_append_u8(m, IFLA_BOND_NUM_PEER_NOTIF, b->num_grat_arp);
168 if (r < 0)
169 return r;
170 }
171
172 if (b->min_links != 0) {
173 r = sd_netlink_message_append_u32(m, IFLA_BOND_MIN_LINKS, b->min_links);
174 if (r < 0)
175 return r;
176 }
177
178 if (b->ad_actor_sys_prio != 0) {
179 r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_ACTOR_SYS_PRIO, b->ad_actor_sys_prio);
180 if (r < 0)
181 return r;
182 }
183
184 if (b->ad_user_port_key != 0) {
185 r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_USER_PORT_KEY, b->ad_user_port_key);
186 if (r < 0)
187 return r;
188 }
189
190 if (!ether_addr_is_null(&b->ad_actor_system)) {
191 r = sd_netlink_message_append_ether_addr(m, IFLA_BOND_AD_ACTOR_SYSTEM, &b->ad_actor_system);
192 if (r < 0)
193 return r;
194 }
195
196 r = sd_netlink_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active);
197 if (r < 0)
198 return r;
199
200 if (b->tlb_dynamic_lb >= 0) {
201 r = sd_netlink_message_append_u8(m, IFLA_BOND_TLB_DYNAMIC_LB, b->tlb_dynamic_lb);
202 if (r < 0)
203 return r;
204 }
205
206 if (b->arp_interval > 0 && !ordered_set_isempty(b->arp_ip_targets)) {
207 void *val;
208 int n = 0;
209
210 r = sd_netlink_message_open_container(m, IFLA_BOND_ARP_IP_TARGET);
211 if (r < 0)
212 return r;
213
214 ORDERED_SET_FOREACH(val, b->arp_ip_targets) {
215 r = sd_netlink_message_append_u32(m, n++, PTR_TO_UINT32(val));
216 if (r < 0)
217 return r;
218 }
219
220 r = sd_netlink_message_close_container(m);
221 if (r < 0)
222 return r;
223 }
224
225 return 0;
226 }
227
config_parse_arp_ip_target_address(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)228 int config_parse_arp_ip_target_address(
229 const char *unit,
230 const char *filename,
231 unsigned line,
232 const char *section,
233 unsigned section_line,
234 const char *lvalue,
235 int ltype,
236 const char *rvalue,
237 void *data,
238 void *userdata) {
239
240 Bond *b = userdata;
241 int r;
242
243 assert(filename);
244 assert(lvalue);
245 assert(rvalue);
246 assert(data);
247
248 if (isempty(rvalue)) {
249 b->arp_ip_targets = ordered_set_free(b->arp_ip_targets);
250 return 0;
251 }
252
253 for (const char *p = rvalue;;) {
254 _cleanup_free_ char *n = NULL;
255 union in_addr_union ip;
256
257 r = extract_first_word(&p, &n, NULL, 0);
258 if (r == -ENOMEM)
259 return log_oom();
260 if (r < 0) {
261 log_syntax(unit, LOG_WARNING, filename, line, r,
262 "Failed to parse Bond ARP IP target address, ignoring assignment: %s",
263 rvalue);
264 return 0;
265 }
266 if (r == 0)
267 return 0;
268
269 r = in_addr_from_string(AF_INET, n, &ip);
270 if (r < 0) {
271 log_syntax(unit, LOG_WARNING, filename, line, r,
272 "Bond ARP IP target address is invalid, ignoring assignment: %s", n);
273 continue;
274 }
275
276 if (ordered_set_size(b->arp_ip_targets) >= NETDEV_BOND_ARP_TARGETS_MAX) {
277 log_syntax(unit, LOG_WARNING, filename, line, 0,
278 "Too many ARP IP targets are specified. The maximum number is %d. Ignoring assignment: %s",
279 NETDEV_BOND_ARP_TARGETS_MAX, n);
280 continue;
281 }
282
283 r = ordered_set_ensure_put(&b->arp_ip_targets, NULL, UINT32_TO_PTR(ip.in.s_addr));
284 if (r == -ENOMEM)
285 return log_oom();
286 if (r == -EEXIST)
287 log_syntax(unit, LOG_WARNING, filename, line, r,
288 "Bond ARP IP target address is duplicated, ignoring assignment: %s", n);
289 if (r < 0)
290 log_syntax(unit, LOG_WARNING, filename, line, r,
291 "Failed to store bond ARP IP target address '%s', ignoring assignment: %m", n);
292 }
293 }
294
config_parse_ad_actor_sys_prio(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)295 int config_parse_ad_actor_sys_prio(
296 const char *unit,
297 const char *filename,
298 unsigned line,
299 const char *section,
300 unsigned section_line,
301 const char *lvalue,
302 int ltype,
303 const char *rvalue,
304 void *data,
305 void *userdata) {
306 Bond *b = userdata;
307 uint16_t v;
308 int r;
309
310 assert(filename);
311 assert(lvalue);
312 assert(rvalue);
313 assert(data);
314
315 r = safe_atou16(rvalue, &v);
316 if (r < 0) {
317 log_syntax(unit, LOG_WARNING, filename, line, r,
318 "Failed to parse actor system priority '%s', ignoring: %m", rvalue);
319 return 0;
320 }
321
322 if (v == 0) {
323 log_syntax(unit, LOG_WARNING, filename, line, 0,
324 "Failed to parse actor system priority '%s'. Range is [1,65535], ignoring.",
325 rvalue);
326 return 0;
327 }
328
329 b->ad_actor_sys_prio = v;
330
331 return 0;
332 }
333
config_parse_ad_user_port_key(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)334 int config_parse_ad_user_port_key(
335 const char *unit,
336 const char *filename,
337 unsigned line,
338 const char *section,
339 unsigned section_line,
340 const char *lvalue,
341 int ltype,
342 const char *rvalue,
343 void *data,
344 void *userdata) {
345 Bond *b = userdata;
346 uint16_t v;
347 int r;
348
349 assert(filename);
350 assert(lvalue);
351 assert(rvalue);
352 assert(data);
353
354 r = safe_atou16(rvalue, &v);
355 if (r < 0) {
356 log_syntax(unit, LOG_WARNING, filename, line, r,
357 "Failed to parse user port key '%s', ignoring: %m", rvalue);
358 return 0;
359 }
360
361 if (v > 1023) {
362 log_syntax(unit, LOG_WARNING, filename, line, 0,
363 "Failed to parse user port key '%s'. Range is [0…1023], ignoring.", rvalue);
364 return 0;
365 }
366
367 b->ad_user_port_key = v;
368
369 return 0;
370 }
371
config_parse_ad_actor_system(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)372 int config_parse_ad_actor_system(
373 const char *unit,
374 const char *filename,
375 unsigned line,
376 const char *section,
377 unsigned section_line,
378 const char *lvalue,
379 int ltype,
380 const char *rvalue,
381 void *data,
382 void *userdata) {
383 Bond *b = userdata;
384 struct ether_addr n;
385 int r;
386
387 assert(filename);
388 assert(lvalue);
389 assert(rvalue);
390 assert(data);
391
392 r = parse_ether_addr(rvalue, &n);
393 if (r < 0) {
394 log_syntax(unit, LOG_WARNING, filename, line, r,
395 "Not a valid MAC address %s. Ignoring assignment: %m",
396 rvalue);
397 return 0;
398 }
399 if (ether_addr_is_null(&n) || (n.ether_addr_octet[0] & 0x01)) {
400 log_syntax(unit, LOG_WARNING, filename, line, 0,
401 "Not an appropriate MAC address %s, cannot be null or multicast. Ignoring assignment.",
402 rvalue);
403 return 0;
404 }
405
406 b->ad_actor_system = n;
407
408 return 0;
409 }
410
bond_done(NetDev * netdev)411 static void bond_done(NetDev *netdev) {
412 Bond *b;
413
414 assert(netdev);
415 b = BOND(netdev);
416 assert(b);
417
418 ordered_set_free(b->arp_ip_targets);
419 }
420
bond_init(NetDev * netdev)421 static void bond_init(NetDev *netdev) {
422 Bond *b;
423
424 assert(netdev);
425
426 b = BOND(netdev);
427
428 assert(b);
429
430 b->mode = _NETDEV_BOND_MODE_INVALID;
431 b->xmit_hash_policy = _NETDEV_BOND_XMIT_HASH_POLICY_INVALID;
432 b->lacp_rate = _NETDEV_BOND_LACP_RATE_INVALID;
433 b->ad_select = _NETDEV_BOND_AD_SELECT_INVALID;
434 b->fail_over_mac = _NETDEV_BOND_FAIL_OVER_MAC_INVALID;
435 b->arp_validate = _NETDEV_BOND_ARP_VALIDATE_INVALID;
436 b->arp_all_targets = _NETDEV_BOND_ARP_ALL_TARGETS_INVALID;
437 b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID;
438
439 b->all_slaves_active = false;
440 b->tlb_dynamic_lb = -1;
441
442 b->resend_igmp = RESEND_IGMP_DEFAULT;
443 b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT;
444 b->num_grat_arp = GRATUITOUS_ARP_DEFAULT;
445 b->lp_interval = LEARNING_PACKETS_INTERVAL_MIN_SEC;
446 }
447
448 const NetDevVTable bond_vtable = {
449 .object_size = sizeof(Bond),
450 .init = bond_init,
451 .done = bond_done,
452 .sections = NETDEV_COMMON_SECTIONS "Bond\0",
453 .fill_message_create = netdev_bond_fill_message_create,
454 .create_type = NETDEV_CREATE_INDEPENDENT,
455 .iftype = ARPHRD_ETHER,
456 .generate_mac = true,
457 };
458