Coverage Report

Created: 2026-04-29 07:06

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