bonding: add support to ns_ip6_target option

This is the IPv6 equivalent of arp_ip_target option. It requires
arp_interval set and allow the user to specify up to 16 IPv6 addresses
as targets. By default, the list is empty.
This commit is contained in:
Fernando Fernandez Mancera 2023-03-02 15:07:07 +01:00
parent 426658b422
commit c6487c240c
11 changed files with 157 additions and 50 deletions

View file

@ -57,7 +57,7 @@
#define OPTIONS_REAPPLY_FULL \
OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \
NM_SETTING_BOND_OPTION_ARP_IP_TARGET
NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_NS_IP6_TARGET
/*****************************************************************************/
@ -270,7 +270,7 @@ set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new
cur_strv =
nm_strsplit_set_full(cur_arp_ip_target, NM_ASCII_SPACES, NM_STRSPLIT_SET_FLAGS_STRSTRIP);
new_strv = nm_utils_bond_option_arp_ip_targets_split(new_arp_ip_target);
new_strv = nm_utils_bond_option_ip_split(new_arp_ip_target);
cur_len = NM_PTRARRAY_LEN(cur_strv);
new_len = NM_PTRARRAY_LEN(new_strv);
@ -367,7 +367,7 @@ _bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_
int i;
int added = 0;
ip = nm_utils_bond_option_arp_ip_targets_split(value);
ip = nm_utils_bond_option_ip_split(value);
if (!ip)
return added;
@ -383,6 +383,31 @@ _bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_
return added;
}
static guint8
_bond_ns_ip6_target_to_platform(const char *value,
struct in6_addr out[static NM_BOND_MAX_ARP_TARGETS])
{
gs_free const char **ip = NULL;
struct in6_addr in6_a;
int i;
int added = 0;
ip = nm_utils_bond_option_ip_split(value);
if (!ip)
return added;
for (i = 0; ip[i]; i++) {
if (added > NM_BOND_MAX_ARP_TARGETS - 1)
break;
if (!nm_inet_parse_bin(AF_INET6, ip[i], NULL, &in6_a))
nm_assert_not_reached(); /* verify() already validated the IP addresses */
out[added++] = in6_a;
}
return added;
}
static int
_setting_bond_primary_opt_as_ifindex(NMSettingBond *s_bond)
{
@ -462,6 +487,11 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p
props->arp_ip_targets_num =
_bond_arp_ip_target_to_platform(opt_value, props->arp_ip_target);
opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_NS_IP6_TARGET);
if (opt_value != NULL)
props->ns_ip6_targets_num =
_bond_ns_ip6_target_to_platform(opt_value, props->ns_ip6_target);
props->miimon_has = !props->arp_interval && !props->arp_validate;
props->updelay_has = props->miimon_has && props->miimon;
props->downdelay_has = props->miimon_has && props->miimon;

View file

@ -13,7 +13,7 @@
/*****************************************************************************/
const char **
nm_utils_bond_option_arp_ip_targets_split(const char *arp_ip_target)
nm_utils_bond_option_ip_split(const char *arp_ip_target)
{
return nm_strsplit_set_full(arp_ip_target, ",", NM_STRSPLIT_SET_FLAGS_STRSTRIP);
}
@ -36,6 +36,7 @@ _nm_setting_bond_remove_options_arp_interval(NMSettingBond *s_bond)
nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
nm_setting_bond_remove_option(s_bond, NM_SETTING_BOND_OPTION_NS_IP6_TARGET);
}
/*****************************************************************************/

View file

@ -52,7 +52,7 @@ NM_AUTO_DEFINE_FCN0(NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_
/****************************************************************************/
const char **nm_utils_bond_option_arp_ip_targets_split(const char *arp_ip_target);
const char **nm_utils_bond_option_ip_split(const char *arp_ip_target);
void _nm_setting_bond_remove_options_miimon(NMSettingBond *s_bond);
void _nm_setting_bond_remove_options_arp_interval(NMSettingBond *s_bond);

View file

@ -94,6 +94,7 @@ static const char *const valid_options_lst[] = {
NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY,
NM_SETTING_BOND_OPTION_ARP_MISSED_MAX,
NM_SETTING_BOND_OPTION_LACP_ACTIVE,
NM_SETTING_BOND_OPTION_NS_IP6_TARGET,
NULL,
};
@ -135,6 +136,7 @@ _nm_assert_bond_meta(const OptionMeta *option_meta)
}));
return TRUE;
case NM_BOND_OPTION_TYPE_IP:
case NM_BOND_OPTION_TYPE_IP6:
nm_assert(option_meta->val);
/* fall-through */
case NM_BOND_OPTION_TYPE_IFNAME:
@ -213,6 +215,7 @@ static NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE(
{NM_SETTING_BOND_OPTION_MIN_LINKS, {"0", NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT}},
{NM_SETTING_BOND_OPTION_MODE,
{"balance-rr", NM_BOND_OPTION_TYPE_BOTH, 0, 6, _option_default_strv_mode}},
{NM_SETTING_BOND_OPTION_NS_IP6_TARGET, {"", NM_BOND_OPTION_TYPE_IP6}},
{NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, {"1", NM_BOND_OPTION_TYPE_INT, 0, 255}},
{NM_SETTING_BOND_OPTION_NUM_UNSOL_NA, {"1", NM_BOND_OPTION_TYPE_INT, 0, 255}},
{NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, {"1", NM_BOND_OPTION_TYPE_INT, 0, 65535}},
@ -510,12 +513,12 @@ validate_list(const char *name, const char *value, const OptionMeta *option_meta
}
static gboolean
validate_ip(const char *name, const char *value, GError **error)
validate_ip(int addr_family, const char *name, const char *value, GError **error)
{
gs_free const char **addrs = NULL;
gsize i;
addrs = nm_utils_bond_option_arp_ip_targets_split(value);
addrs = nm_utils_bond_option_ip_split(value);
if (!addrs) {
g_set_error(error,
NM_CONNECTION_ERROR,
@ -524,13 +527,18 @@ validate_ip(const char *name, const char *value, GError **error)
name);
return FALSE;
}
/* An empty list is invalid. */
nm_assert(addrs[0]);
for (i = 0; addrs[i]; i++) {
if (!nm_inet_parse_bin(AF_INET, addrs[i], NULL, NULL)) {
if (!nm_inet_parse_bin(addr_family, addrs[i], NULL, NULL)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid IPv4 address for '%s' option"),
_("'%s' is not a valid %s address for '%s' option"),
addrs[i],
addr_family == AF_INET ? "IPv4" : "IPv6",
name);
return FALSE;
}
@ -580,7 +588,10 @@ _nm_setting_bond_validate_option(const char *name, const char *value, GError **e
goto handle_error;
case NM_BOND_OPTION_TYPE_IP:
nm_assert(nm_streq0(name, NM_SETTING_BOND_OPTION_ARP_IP_TARGET));
return validate_ip(name, value, error);
return validate_ip(AF_INET, name, value, error);
case NM_BOND_OPTION_TYPE_IP6:
nm_assert(nm_streq0(name, NM_SETTING_BOND_OPTION_NS_IP6_TARGET));
return validate_ip(AF_INET6, name, value, error);
case NM_BOND_OPTION_TYPE_MAC:
success = nm_utils_hwaddr_valid(value, ETH_ALEN);
goto handle_error;
@ -858,6 +869,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
int peer_notif_delay;
const char *mode_str;
const char *arp_ip_target = NULL;
const char *ns_ip6_target;
const char *lacp_rate;
const char *lacp_active;
const char *primary;
@ -1078,6 +1090,21 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
}
}
/* ns_ip6_target can only be used with arp_interval, and must
* contain a comma-separated list of IPv6 addresses.
*/
ns_ip6_target = _bond_get_option(self, NM_SETTING_BOND_OPTION_NS_IP6_TARGET);
if (ns_ip6_target && arp_interval == 0) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' option requires '%s' option to be set"),
NM_SETTING_BOND_OPTION_NS_IP6_TARGET,
NM_SETTING_BOND_OPTION_ARP_INTERVAL);
g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
return FALSE;
}
lacp_rate = _bond_get_option(self, NM_SETTING_BOND_OPTION_LACP_RATE);
if (lacp_rate && bond_mode != NM_BOND_MODE_8023AD && !NM_IN_STRSET(lacp_rate, "0", "slow")) {
g_set_error(error,

View file

@ -471,6 +471,7 @@ typedef enum {
NM_BOND_OPTION_TYPE_INT,
NM_BOND_OPTION_TYPE_BOTH,
NM_BOND_OPTION_TYPE_IP,
NM_BOND_OPTION_TYPE_IP6,
NM_BOND_OPTION_TYPE_MAC,
NM_BOND_OPTION_TYPE_IFNAME,
} NMBondOptionType;

View file

@ -60,6 +60,7 @@ G_BEGIN_DECLS
#define NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY "peer_notif_delay"
#define NM_SETTING_BOND_OPTION_ARP_MISSED_MAX "arp_missed_max"
#define NM_SETTING_BOND_OPTION_LACP_ACTIVE "lacp_active"
#define NM_SETTING_BOND_OPTION_NS_IP6_TARGET "ns_ip6_target"
typedef struct _NMSettingBondClass NMSettingBondClass;

View file

@ -182,6 +182,7 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1));
#define IFLA_BOND_PEER_NOTIF_DELAY 28
#define IFLA_BOND_AD_LACP_ACTIVE 29
#define IFLA_BOND_MISSED_MAX 30
#define IFLA_BOND_NS_IP6_TARGET 31
#undef IFLA_BOND_MAX
@ -1610,6 +1611,7 @@ _parse_lnk_bond(const char *kind, struct nlattr *info_data)
[IFLA_BOND_PEER_NOTIF_DELAY] = {.type = NLA_U32},
[IFLA_BOND_MISSED_MAX] = {.type = NLA_U8},
[IFLA_BOND_AD_LACP_ACTIVE] = {.type = NLA_U8},
[IFLA_BOND_NS_IP6_TARGET] = {.type = NLA_NESTED},
};
NMPlatformLnkBond *props;
struct nlattr *tb[G_N_ELEMENTS(policy)];
@ -1664,6 +1666,19 @@ _parse_lnk_bond(const char *kind, struct nlattr *info_data)
props->arp_ip_target[props->arp_ip_targets_num++] = nla_get_u32(attr);
}
}
if (tb[IFLA_BOND_NS_IP6_TARGET]) {
struct nlattr *attr;
int rem;
nla_for_each_nested (attr, tb[IFLA_BOND_NS_IP6_TARGET], rem) {
if (props->ns_ip6_targets_num > NM_BOND_MAX_ARP_TARGETS - 1)
break;
if (nla_len(attr) < sizeof(struct in6_addr))
break;
props->ns_ip6_target[props->ns_ip6_targets_num++] = nla_get_in6_addr(attr);
}
}
if (tb[IFLA_BOND_ARP_VALIDATE])
props->arp_validate = nla_get_u32(tb[IFLA_BOND_ARP_VALIDATE]);
if (tb[IFLA_BOND_ARP_ALL_TARGETS])
@ -4709,6 +4724,17 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
nla_nest_end(msg, targets);
}
if (props->ns_ip6_targets_num > 0) {
targets = nla_nest_start(msg, IFLA_BOND_NS_IP6_TARGET);
if (!targets)
goto nla_put_failure;
for (i = 0; i < props->ns_ip6_targets_num; i++)
NLA_PUT(msg, i, sizeof(struct in6_addr), &props->ns_ip6_target[i]);
nla_nest_end(msg, targets);
}
if (props->arp_all_targets)
NLA_PUT_U32(msg, IFLA_BOND_ARP_ALL_TARGETS, props->arp_all_targets);
if (props->arp_interval)

View file

@ -6299,6 +6299,15 @@ nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize le
nm_strbuf_append_str(&buf, &len, nm_inet4_ntop(lnk->arp_ip_target[i], target));
}
}
if (lnk->ns_ip6_targets_num > 0) {
nm_strbuf_append_str(&buf, &len, " ns_ip6_target");
for (i = 0; i < lnk->ns_ip6_targets_num; i++) {
char target[INET6_ADDRSTRLEN];
nm_strbuf_append_c(&buf, &len, ' ');
nm_strbuf_append_str(&buf, &len, nm_inet6_ntop(&lnk->ns_ip6_target[i], target));
}
}
return buf;
}
@ -8052,6 +8061,7 @@ nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h)
obj->fail_over_mac,
obj->lacp_rate,
obj->lacp_active,
obj->ns_ip6_targets_num,
obj->num_grat_arp,
obj->mode,
obj->primary_reselect,
@ -8069,6 +8079,7 @@ nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h)
obj->use_carrier));
nm_hash_update(h, obj->arp_ip_target, obj->arp_ip_targets_num * sizeof(obj->arp_ip_target[0]));
nm_hash_update(h, obj->ns_ip6_target, obj->ns_ip6_targets_num * sizeof(obj->ns_ip6_target[0]));
}
int
@ -8076,6 +8087,11 @@ nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b)
{
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, arp_ip_targets_num);
NM_CMP_FIELD(a, b, ns_ip6_targets_num);
NM_CMP_FIELD_MEMCMP_LEN(a,
b,
ns_ip6_target,
a->ns_ip6_targets_num * sizeof(a->ns_ip6_target[0]));
NM_CMP_FIELD_MEMCMP_LEN(a,
b,
arp_ip_target,

View file

@ -764,43 +764,45 @@ extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default;
#define NM_BOND_MAX_ARP_TARGETS 16
typedef struct {
int primary;
in_addr_t arp_ip_target[NM_BOND_MAX_ARP_TARGETS];
guint32 arp_all_targets;
guint32 arp_interval;
guint32 arp_validate;
guint32 downdelay;
guint32 lp_interval;
guint32 miimon;
guint32 min_links;
guint32 packets_per_port;
guint32 peer_notif_delay;
guint32 resend_igmp;
guint32 updelay;
guint16 ad_actor_sys_prio;
guint16 ad_user_port_key;
NMEtherAddr ad_actor_system;
guint8 ad_select;
guint8 all_ports_active;
guint8 arp_missed_max;
guint8 arp_ip_targets_num;
guint8 fail_over_mac;
guint8 lacp_active;
guint8 lacp_rate;
guint8 num_grat_arp;
guint8 mode;
guint8 primary_reselect;
guint8 xmit_hash_policy;
bool downdelay_has : 1;
bool lacp_active_has : 1;
bool lp_interval_has : 1;
bool miimon_has : 1;
bool peer_notif_delay_has : 1;
bool resend_igmp_has : 1;
bool tlb_dynamic_lb : 1;
bool tlb_dynamic_lb_has : 1;
bool updelay_has : 1;
bool use_carrier : 1;
struct in6_addr ns_ip6_target[NM_BOND_MAX_ARP_TARGETS];
int primary;
in_addr_t arp_ip_target[NM_BOND_MAX_ARP_TARGETS];
guint32 arp_all_targets;
guint32 arp_interval;
guint32 arp_validate;
guint32 downdelay;
guint32 lp_interval;
guint32 miimon;
guint32 min_links;
guint32 packets_per_port;
guint32 peer_notif_delay;
guint32 resend_igmp;
guint32 updelay;
guint16 ad_actor_sys_prio;
guint16 ad_user_port_key;
NMEtherAddr ad_actor_system;
guint8 ad_select;
guint8 all_ports_active;
guint8 arp_missed_max;
guint8 arp_ip_targets_num;
guint8 fail_over_mac;
guint8 lacp_active;
guint8 lacp_rate;
guint8 ns_ip6_targets_num;
guint8 num_grat_arp;
guint8 mode;
guint8 primary_reselect;
guint8 xmit_hash_policy;
bool downdelay_has : 1;
bool lacp_active_has : 1;
bool lp_interval_has : 1;
bool miimon_has : 1;
bool peer_notif_delay_has : 1;
bool resend_igmp_has : 1;
bool tlb_dynamic_lb : 1;
bool tlb_dynamic_lb_has : 1;
bool updelay_has : 1;
bool use_carrier : 1;
} _nm_alignas(NMPlatformObject) NMPlatformLnkBond;
typedef struct {

View file

@ -2474,7 +2474,9 @@ _get_fcn_bond_options(ARGS_GET_FCN)
nm_setting_bond_get_option(s_bond, i, &key, &val);
if (nm_streq(key, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) {
if (NM_IN_STRSET(key,
NM_SETTING_BOND_OPTION_ARP_IP_TARGET,
NM_SETTING_BOND_OPTION_NS_IP6_TARGET)) {
val_tmp = g_strdup(val);
for (p = val_tmp; p && *p; p++) {
if (*p == ',')
@ -2516,7 +2518,8 @@ _nm_meta_setting_bond_add_option(NMSetting *setting,
value = nmc_bond_validate_mode(value, error);
if (!value)
return FALSE;
} else if (nm_streq(name, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) {
} else if (nm_streq(name, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)
|| nm_streq(name, NM_SETTING_BOND_OPTION_NS_IP6_TARGET)) {
value = tmp_value = g_strdup(value);
for (p = tmp_value; p && *p; p++)
if (*p == ' ')

View file

@ -141,7 +141,7 @@ bond_options_changed(GObject *object, GParamSpec *pspec, gpointer user_data)
nmt_newt_entry_set_text(priv->arp_interval, val ?: "0");
val = nm_setting_bond_get_option_by_name(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
ips = nm_utils_bond_option_arp_ip_targets_split(val);
ips = nm_utils_bond_option_ip_split(val);
g_object_set(G_OBJECT(priv->arp_ip_target),
"strings",
ips ?: NM_PTRARRAY_EMPTY(const char *),