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