mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-25 08:20:43 +01:00
merge: branch 'bg/nexthop'
Draft: Generate IPv6 default routes using nexthop objects Closes #1789 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2329
This commit is contained in:
commit
97f80b82a4
19 changed files with 1659 additions and 71 deletions
|
|
@ -13447,6 +13447,9 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family)
|
|||
* addresses and routes on activation.
|
||||
*/
|
||||
if (ip_ifindex > 0) {
|
||||
nm_platform_ip_nexthop_flush(nm_device_get_platform(self),
|
||||
AF_INET6,
|
||||
ip_ifindex);
|
||||
nm_platform_ip_route_flush(nm_device_get_platform(self),
|
||||
AF_INET6,
|
||||
ip_ifindex);
|
||||
|
|
|
|||
|
|
@ -1237,6 +1237,7 @@ deactivate_cleanup(NMModem *self, NMDevice *device, gboolean stop_ppp_manager)
|
|||
if (ifindex > 0) {
|
||||
NMPlatform *platform = nm_device_get_platform(device);
|
||||
|
||||
nm_platform_ip_nexthop_flush(platform, AF_UNSPEC, ifindex);
|
||||
nm_platform_ip_route_flush(platform, AF_UNSPEC, ifindex);
|
||||
nm_platform_ip_address_flush(platform, AF_UNSPEC, ifindex);
|
||||
nm_platform_link_change_flags(platform, ifindex, IFF_UP, FALSE);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "nm-l3-config-data.h"
|
||||
#include "nm-l3cfg.h"
|
||||
#include "nm-ndisc-private.h"
|
||||
#include "nm-netns.h"
|
||||
#include "nm-setting-ip6-config.h"
|
||||
#include "nm-utils.h"
|
||||
|
||||
|
|
@ -105,6 +106,77 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_ndisc_dhcp_level_to_string,
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define NEXTHOP_ID_RETRIES 400
|
||||
|
||||
static guint32
|
||||
nexthop_id_alloc(NMNDisc *ndisc,
|
||||
const struct in6_addr *dest,
|
||||
guint plen,
|
||||
const struct in6_addr *gateway)
|
||||
{
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(ndisc);
|
||||
NMNetns *netns = nm_l3cfg_get_netns(priv->config.l3cfg);
|
||||
const char *ifname = nm_l3cfg_get_ifname(priv->config.l3cfg, FALSE);
|
||||
NMPlatform *platform = nm_l3cfg_get_platform(priv->config.l3cfg);
|
||||
int ifindex = nm_l3cfg_get_ifindex(priv->config.l3cfg);
|
||||
nm_auto_nmpobj NMPObject *obj = NULL;
|
||||
CSipHash state;
|
||||
guint64 id64;
|
||||
guint32 id;
|
||||
guint i;
|
||||
|
||||
/* Determine a stable nexthop ID by hashing the interface name, the destination
|
||||
* and the gateway. We set the high bit to decrease the chance of collisions with
|
||||
* external (manually added) nexthops. */
|
||||
c_siphash_init(&state, NM_HASH_SEED_16_U64(725697701u));
|
||||
c_siphash_append(&state, (const uint8_t *) ifname, strlen(ifname) + 1);
|
||||
c_siphash_append(&state, (const uint8_t *) dest, sizeof(struct in6_addr));
|
||||
c_siphash_append(&state, (const uint8_t *) &plen, sizeof(guint));
|
||||
c_siphash_append(&state, (const uint8_t *) gateway, sizeof(struct in6_addr));
|
||||
id64 = c_siphash_finalize(&state);
|
||||
id = ((guint32) (id64 >> 32u)) | (1u << 31);
|
||||
|
||||
for (i = 0; i < NEXTHOP_ID_RETRIES; i++, id++) {
|
||||
if (i < NEXTHOP_ID_RETRIES * 3 / 4) {
|
||||
id |= (1u << 31);
|
||||
} else {
|
||||
/* After many collisions, start probing random ids */
|
||||
id = (guint32) nm_random_u64_range(1u << 31, G_MAXUINT32);
|
||||
}
|
||||
|
||||
if (nm_netns_nexthop_id_is_reserved(netns, id))
|
||||
continue;
|
||||
|
||||
if (nm_platform_ip_nexthop_get(platform, id, &obj)) {
|
||||
/* The id already exists in platform. We can reuse it only
|
||||
* if it's an IPv6 RA nexthop on the same interface. */
|
||||
if (NMP_OBJECT_GET_TYPE(obj) != NMP_OBJECT_TYPE_IP6_NEXTHOP
|
||||
|| obj->ip6_nexthop.nh_source != NM_IP_CONFIG_SOURCE_RTPROT_RA
|
||||
|| obj->ip6_nexthop.ifindex != ifindex)
|
||||
continue;
|
||||
}
|
||||
|
||||
nm_netns_nexthop_id_reserve(netns, id, ndisc);
|
||||
return id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_nexthop_id_release_one(NMNDisc *ndisc, guint32 nexthop_id)
|
||||
{
|
||||
NMNDiscPrivate *priv;
|
||||
|
||||
if (nexthop_id == 0)
|
||||
return;
|
||||
|
||||
priv = NM_NDISC_GET_PRIVATE(ndisc);
|
||||
nm_netns_nexthop_id_release(nm_l3cfg_get_netns(priv->config.l3cfg), nexthop_id);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NML3ConfigData *
|
||||
nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
|
||||
int ifindex,
|
||||
|
|
@ -172,9 +244,6 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
|
|||
}
|
||||
|
||||
if (rdata->gateways_n > 0) {
|
||||
guint metric_offset = 0;
|
||||
NMIcmpv6RouterPref prev_pref = NM_ICMPV6_ROUTER_PREF_INVALID;
|
||||
|
||||
NMPlatformIP6Route r = {
|
||||
.rt_source = NM_IP_CONFIG_SOURCE_NDISC,
|
||||
.ifindex = ifindex,
|
||||
|
|
@ -185,24 +254,38 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
|
|||
};
|
||||
|
||||
for (i = 0; i < rdata->gateways_n; i++) {
|
||||
/* If we add multiple default routes with the same metric and
|
||||
* different preferences, kernel merges them into a single ECMP
|
||||
* route, with overall preference equal to the preference of the
|
||||
* first route added. Therefore, the preference of individual routes
|
||||
* is not respected.
|
||||
* To avoid that, add routes with different metrics if they have
|
||||
* different preferences, so that they are not merged together. Here
|
||||
* the gateways are already ordered by increasing preference. */
|
||||
if (i != 0 && rdata->gateways[i].preference != prev_pref) {
|
||||
metric_offset++;
|
||||
}
|
||||
NMPlatformIP6NextHop nh;
|
||||
|
||||
prev_pref = rdata->gateways[i].preference;
|
||||
r.metric = metric_offset;
|
||||
r.gateway = rdata->gateways[i].address;
|
||||
r.rt_pref = rdata->gateways[i].preference;
|
||||
nm_assert((NMIcmpv6RouterPref) r.rt_pref == rdata->gateways[i].preference);
|
||||
|
||||
/* If we add multiple routes with the same destination (in this case, the
|
||||
* default route) and the same metric, the kernel merges them into a single
|
||||
* ECMP route, which is forbidden by RFCs as it breaks NUD and other use cases.
|
||||
* Use nexthop objects to avoid this merging behavior.
|
||||
*
|
||||
* We could use nexthops only when there are multiple default routes on this
|
||||
* interface. But that is not enough, because there can be multiple profiles
|
||||
* with the same ipv6.route-metric value, and their default routes would still
|
||||
* be merged. We need to always use nexthops.
|
||||
*/
|
||||
|
||||
if (rdata->gateways[i].nexthop_id == 0) {
|
||||
/* The nexthop id could not be reserved and we already emitted a warning */
|
||||
continue;
|
||||
}
|
||||
|
||||
nh = (NMPlatformIP6NextHop) {
|
||||
.ifindex = ifindex,
|
||||
.nh_source = NM_IP_CONFIG_SOURCE_NDISC,
|
||||
.gateway = rdata->gateways[i].address,
|
||||
.id = rdata->gateways[i].nexthop_id,
|
||||
};
|
||||
|
||||
r.nhid = nh.id;
|
||||
r.gateway = nh.gateway;
|
||||
nm_l3_config_data_add_route_6(l3cd, &r);
|
||||
nm_l3_config_data_add_nexthop(l3cd, AF_INET6, NULL, (const NMPlatformIPNextHop *) &nh);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -465,23 +548,38 @@ nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gint64 now_
|
|||
{
|
||||
NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
|
||||
guint i;
|
||||
guint insert_idx = G_MAXUINT;
|
||||
guint insert_idx = G_MAXUINT;
|
||||
guint32 old_nexthop_id = 0;
|
||||
NMNDiscGateway gw;
|
||||
|
||||
for (i = 0; i < rdata->gateways->len;) {
|
||||
NMNDiscGateway *item = &nm_g_array_index(rdata->gateways, NMNDiscGateway, i);
|
||||
|
||||
if (IN6_ARE_ADDR_EQUAL(&item->address, &new_item->address)) {
|
||||
if (new_item->expiry_msec <= now_msec) {
|
||||
_nexthop_id_release_one(ndisc, item->nexthop_id);
|
||||
g_array_remove_index(rdata->gateways, i);
|
||||
_ASSERT_data_gateways(rdata);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (item->preference != new_item->preference) {
|
||||
/* Preference changed: save the nexthop ID so that we can
|
||||
* reuse it when re-inserting at the correct position. */
|
||||
old_nexthop_id = item->nexthop_id;
|
||||
g_array_remove_index(rdata->gateways, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->nexthop_id == 0) {
|
||||
/* We failed to allocate the nexthop id previously; retry. */
|
||||
item->nexthop_id = nexthop_id_alloc(ndisc, &in6addr_any, 0, &new_item->address);
|
||||
if (item->nexthop_id > 0) {
|
||||
item->expiry_msec = new_item->expiry_msec;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->expiry_msec == new_item->expiry_msec)
|
||||
return FALSE;
|
||||
|
||||
|
|
@ -505,9 +603,26 @@ nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gint64 now_
|
|||
if (new_item->expiry_msec <= now_msec)
|
||||
return FALSE;
|
||||
|
||||
/* Make a copy of the gateway and assign a nexthop id, reusing the existing
|
||||
* one if possible */
|
||||
gw = *new_item;
|
||||
if (old_nexthop_id != 0) {
|
||||
gw.nexthop_id = old_nexthop_id;
|
||||
} else {
|
||||
gw.nexthop_id = nexthop_id_alloc(ndisc, &in6addr_any, 0, &new_item->address);
|
||||
if (gw.nexthop_id == 0) {
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
|
||||
_LOGW("failed to find a free nexthop id for gateway %s",
|
||||
nm_inet6_ntop(&new_item->address, buf));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
g_array_insert_val(rdata->gateways,
|
||||
insert_idx == G_MAXUINT ? rdata->gateways->len : insert_idx,
|
||||
*new_item);
|
||||
gw);
|
||||
|
||||
_ASSERT_data_gateways(rdata);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -1330,6 +1445,9 @@ nm_ndisc_stop(NMNDisc *ndisc)
|
|||
|
||||
NM_NDISC_GET_CLASS(ndisc)->stop(ndisc);
|
||||
|
||||
/* Release all nexthop IDs reserved by this ndisc instance. */
|
||||
nm_netns_nexthop_id_release_all(nm_l3cfg_get_netns(priv->config.l3cfg), ndisc);
|
||||
|
||||
rdata = &priv->rdata;
|
||||
|
||||
g_array_set_size(rdata->gateways, 0);
|
||||
|
|
@ -1442,9 +1560,10 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed)
|
|||
for (i = 0; i < rdata->gateways->len; i++) {
|
||||
const NMNDiscGateway *gateway = &nm_g_array_index(rdata->gateways, NMNDiscGateway, i);
|
||||
|
||||
_LOGD(" gateway %s pref %s exp %s",
|
||||
_LOGD(" gateway %s pref %s nhid %u exp %s",
|
||||
nm_inet6_ntop(&gateway->address, addrstr),
|
||||
nm_icmpv6_router_pref_to_string(gateway->preference, str_pref, sizeof(str_pref)),
|
||||
gateway->nexthop_id,
|
||||
get_exp(str_exp, now_msec, gateway));
|
||||
}
|
||||
for (i = 0; i < rdata->addresses->len; i++) {
|
||||
|
|
@ -1521,8 +1640,11 @@ clean_gateways(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint6
|
|||
arr = &nm_g_array_first(rdata->gateways, NMNDiscGateway);
|
||||
|
||||
for (i = 0, j = 0; i < rdata->gateways->len; i++) {
|
||||
if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec))
|
||||
if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)) {
|
||||
/* Gateway expired. Release its nexthop ID. */
|
||||
_nexthop_id_release_one(ndisc, arr[i].nexthop_id);
|
||||
continue;
|
||||
}
|
||||
if (i != j)
|
||||
arr[j] = arr[i];
|
||||
j++;
|
||||
|
|
@ -1533,8 +1655,14 @@ clean_gateways(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint6
|
|||
g_array_set_size(rdata->gateways, j);
|
||||
}
|
||||
|
||||
if (_array_set_size_max(rdata->gateways, _SIZE_MAX_GATEWAYS))
|
||||
if (rdata->gateways->len > _SIZE_MAX_GATEWAYS) {
|
||||
for (i = _SIZE_MAX_GATEWAYS; i < rdata->gateways->len; i++)
|
||||
_nexthop_id_release_one(
|
||||
ndisc,
|
||||
nm_g_array_index(rdata->gateways, NMNDiscGateway, i).nexthop_id);
|
||||
g_array_set_size(rdata->gateways, _SIZE_MAX_GATEWAYS);
|
||||
*changed |= NM_NDISC_CONFIG_GATEWAYS;
|
||||
}
|
||||
|
||||
_ASSERT_data_gateways(rdata);
|
||||
}
|
||||
|
|
@ -1596,7 +1724,7 @@ clean_routes(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64
|
|||
g_array_set_size(rdata->routes, j);
|
||||
}
|
||||
|
||||
if (_array_set_size_max(rdata->gateways, _SIZE_MAX_ROUTES))
|
||||
if (_array_set_size_max(rdata->routes, _SIZE_MAX_ROUTES))
|
||||
*changed |= NM_NDISC_CONFIG_ROUTES;
|
||||
}
|
||||
|
||||
|
|
@ -1665,7 +1793,7 @@ clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi
|
|||
g_array_set_size(rdata->dns_servers, j);
|
||||
}
|
||||
|
||||
if (_array_set_size_max(rdata->gateways, _SIZE_MAX_DNS_SERVERS))
|
||||
if (_array_set_size_max(rdata->dns_servers, _SIZE_MAX_DNS_SERVERS))
|
||||
*changed |= NM_NDISC_CONFIG_DNS_SERVERS;
|
||||
}
|
||||
|
||||
|
|
@ -1700,7 +1828,7 @@ clean_dns_domains(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gi
|
|||
g_array_set_size(rdata->dns_domains, j);
|
||||
}
|
||||
|
||||
if (_array_set_size_max(rdata->gateways, _SIZE_MAX_DNS_DOMAINS))
|
||||
if (_array_set_size_max(rdata->dns_domains, _SIZE_MAX_DNS_DOMAINS))
|
||||
*changed |= NM_NDISC_CONFIG_DNS_DOMAINS;
|
||||
}
|
||||
|
||||
|
|
@ -2065,6 +2193,8 @@ finalize(GObject *object)
|
|||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(ndisc);
|
||||
NMNDiscDataInternal *rdata = &priv->rdata;
|
||||
|
||||
nm_netns_nexthop_id_release_all(nm_l3cfg_get_netns(priv->config.l3cfg), ndisc);
|
||||
|
||||
g_array_unref(rdata->gateways);
|
||||
g_array_unref(rdata->addresses);
|
||||
g_array_unref(rdata->routes);
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ typedef struct _NMNDiscGateway {
|
|||
struct in6_addr address;
|
||||
gint64 expiry_msec;
|
||||
NMIcmpv6RouterPref preference;
|
||||
guint32 nexthop_id;
|
||||
} NMNDiscGateway;
|
||||
|
||||
typedef struct _NMNDiscAddress {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,14 @@ struct _NML3ConfigData {
|
|||
DedupMultiIdxType idx_routes_x[2];
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
DedupMultiIdxType idx_nexthops_6;
|
||||
DedupMultiIdxType idx_nexthops_4;
|
||||
};
|
||||
DedupMultiIdxType idx_nexthops_x[2];
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
const NMPObject *best_default_route_6;
|
||||
|
|
@ -455,6 +463,17 @@ nm_l3_config_data_log(const NML3ConfigData *self,
|
|||
i++;
|
||||
}
|
||||
|
||||
nm_l3_config_data_iter_obj_for_each (&iter,
|
||||
self,
|
||||
&obj,
|
||||
NMP_OBJECT_TYPE_IP_NEXTHOP(IS_IPv4)) {
|
||||
_L("nexthop%c[%u]: %s",
|
||||
nm_utils_addr_family_to_char(addr_family),
|
||||
i,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (self->route_table_sync_x[IS_IPv4] != NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) {
|
||||
_L("route-table-sync-mode%c: %d",
|
||||
nm_utils_addr_family_to_char(addr_family),
|
||||
|
|
@ -777,6 +796,8 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc
|
|||
_idx_type_init(&self->idx_addresses_6, NMP_OBJECT_TYPE_IP6_ADDRESS);
|
||||
_idx_type_init(&self->idx_routes_4, NMP_OBJECT_TYPE_IP4_ROUTE);
|
||||
_idx_type_init(&self->idx_routes_6, NMP_OBJECT_TYPE_IP6_ROUTE);
|
||||
_idx_type_init(&self->idx_nexthops_4, NMP_OBJECT_TYPE_IP4_NEXTHOP);
|
||||
_idx_type_init(&self->idx_nexthops_6, NMP_OBJECT_TYPE_IP6_NEXTHOP);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
|
@ -844,6 +865,8 @@ nm_l3_config_data_unref(const NML3ConfigData *self)
|
|||
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_addresses_6.parent);
|
||||
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_routes_4.parent);
|
||||
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_routes_6.parent);
|
||||
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_nexthops_4.parent);
|
||||
nm_dedup_multi_index_remove_idx(mutable->multi_idx, &mutable->idx_nexthops_6.parent);
|
||||
|
||||
nmp_object_unref(mutable->best_default_route_4);
|
||||
nmp_object_unref(mutable->best_default_route_6);
|
||||
|
|
@ -938,6 +961,40 @@ nm_l3_config_data_lookup_route(const NML3ConfigData *self,
|
|||
nmp_object_stackinit(&obj_stack, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), needle));
|
||||
}
|
||||
|
||||
const NMPlatformIP4NextHop *
|
||||
nm_l3_config_data_lookup_nexthop4(const NML3ConfigData *self, guint32 id)
|
||||
{
|
||||
const NMDedupMultiEntry *head;
|
||||
NMPObject obj_stack;
|
||||
|
||||
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
||||
|
||||
nmp_object_stackinit_id_ip4_nexthop(&obj_stack, id);
|
||||
|
||||
head = nm_l3_config_data_lookup_obj(self, &obj_stack);
|
||||
if (!head)
|
||||
return NULL;
|
||||
|
||||
return NMP_OBJECT_CAST_IP4_NEXTHOP(head->obj);
|
||||
}
|
||||
|
||||
const NMPlatformIP6NextHop *
|
||||
nm_l3_config_data_lookup_nexthop6(const NML3ConfigData *self, guint32 id)
|
||||
{
|
||||
const NMDedupMultiEntry *head;
|
||||
NMPObject obj_stack;
|
||||
|
||||
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
|
||||
|
||||
nmp_object_stackinit_id_ip6_nexthop(&obj_stack, id);
|
||||
|
||||
head = nm_l3_config_data_lookup_obj(self, &obj_stack);
|
||||
if (!head)
|
||||
return NULL;
|
||||
|
||||
return NMP_OBJECT_CAST_IP6_NEXTHOP(head->obj);
|
||||
}
|
||||
|
||||
const NMDedupMultiIdxType *
|
||||
nm_l3_config_data_lookup_index(const NML3ConfigData *self, NMPObjectType obj_type)
|
||||
{
|
||||
|
|
@ -952,6 +1009,10 @@ nm_l3_config_data_lookup_index(const NML3ConfigData *self, NMPObjectType obj_typ
|
|||
return &self->idx_routes_4.parent;
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
return &self->idx_routes_6.parent;
|
||||
case NMP_OBJECT_TYPE_IP4_NEXTHOP:
|
||||
return &self->idx_nexthops_4.parent;
|
||||
case NMP_OBJECT_TYPE_IP6_NEXTHOP:
|
||||
return &self->idx_nexthops_6.parent;
|
||||
default:
|
||||
return nm_assert_unreachable_val(NULL);
|
||||
}
|
||||
|
|
@ -1168,7 +1229,9 @@ _l3_config_data_add_obj(NMDedupMultiIndex *multi_idx,
|
|||
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE));
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP));
|
||||
nm_assert((!!obj_new) != (!!pl_new));
|
||||
|
||||
if (NM_IN_SET(idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) {
|
||||
|
|
@ -1257,6 +1320,9 @@ _l3_config_data_add_obj(NMDedupMultiIndex *multi_idx,
|
|||
modified = TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_IP4_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_IP6_NEXTHOP:
|
||||
break;
|
||||
default:
|
||||
nm_assert_not_reached();
|
||||
|
|
@ -1448,6 +1514,29 @@ nm_l3_config_data_add_route_full(NML3ConfigData *self,
|
|||
return changed;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_l3_config_data_add_nexthop(NML3ConfigData *self,
|
||||
int addr_family,
|
||||
const NMPObject *obj_new,
|
||||
const NMPlatformIPNextHop *pl_new)
|
||||
{
|
||||
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
||||
|
||||
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
||||
nm_assert_addr_family(addr_family);
|
||||
nm_assert((!pl_new) != (!obj_new));
|
||||
nm_assert(!obj_new || (NMP_OBJECT_GET_ADDR_FAMILY(obj_new) == addr_family));
|
||||
|
||||
return _l3_config_data_add_obj(self->multi_idx,
|
||||
&self->idx_nexthops_x[IS_IPv4],
|
||||
self->ifindex,
|
||||
obj_new,
|
||||
(const NMPlatformObject *) pl_new,
|
||||
NM_L3_CONFIG_ADD_FLAGS_MERGE,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
const NMPObject *
|
||||
nm_l3_config_data_get_best_default_route(const NML3ConfigData *self, int addr_family)
|
||||
{
|
||||
|
|
@ -3673,6 +3762,41 @@ nm_l3_config_data_merge(NML3ConfigData *self,
|
|||
#undef _ensure_r
|
||||
}
|
||||
|
||||
nm_l3_config_data_iter_obj_for_each (&iter,
|
||||
src,
|
||||
&obj,
|
||||
NMP_OBJECT_TYPE_IP_NEXTHOP(IS_IPv4)) {
|
||||
const NMPlatformIPNextHop *nh_src = NMP_OBJECT_CAST_IP_NEXTHOP(obj);
|
||||
NMPlatformIPXNextHop nh;
|
||||
#define _ensure_a() \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
if (nh_src != &nh.nhx) { \
|
||||
if (IS_IPv4) \
|
||||
nh.nh4 = *NMP_OBJECT_CAST_IP4_NEXTHOP(obj); \
|
||||
else \
|
||||
nh.nh6 = *NMP_OBJECT_CAST_IP6_NEXTHOP(obj); \
|
||||
nh_src = &nh.nhx; \
|
||||
} \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
if (nh_src->ifindex != self->ifindex) {
|
||||
_ensure_a();
|
||||
nh.nhx.ifindex = self->ifindex;
|
||||
}
|
||||
|
||||
_l3_config_data_add_obj(self->multi_idx,
|
||||
&self->idx_nexthops_x[IS_IPv4],
|
||||
self->ifindex,
|
||||
nh_src == &nh.nhx ? NULL : obj,
|
||||
(const NMPlatformObject *) (nh_src == &nh.nhx ? nh_src : NULL),
|
||||
NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE,
|
||||
NULL,
|
||||
NULL);
|
||||
#undef _ensure_r
|
||||
}
|
||||
|
||||
if (!NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS))
|
||||
_strv_ptrarray_merge(&self->nameservers_x[IS_IPv4], src->nameservers_x[IS_IPv4]);
|
||||
|
||||
|
|
|
|||
|
|
@ -251,6 +251,12 @@ const NMDedupMultiEntry *nm_l3_config_data_lookup_route(const NML3ConfigData
|
|||
int addr_family,
|
||||
const NMPlatformIPRoute *needle);
|
||||
|
||||
const NMPlatformIP4NextHop *nm_l3_config_data_lookup_nexthop4(const NML3ConfigData *self,
|
||||
guint32 id);
|
||||
|
||||
const NMPlatformIP6NextHop *nm_l3_config_data_lookup_nexthop6(const NML3ConfigData *self,
|
||||
guint32 id);
|
||||
|
||||
const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_objs(const NML3ConfigData *self,
|
||||
NMPObjectType obj_type);
|
||||
|
||||
|
|
@ -266,6 +272,12 @@ nm_l3_config_data_lookup_routes(const NML3ConfigData *self, int addr_family)
|
|||
return nm_l3_config_data_lookup_objs(self, NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)));
|
||||
}
|
||||
|
||||
static inline const NMDedupMultiHeadEntry *
|
||||
nm_l3_config_data_lookup_nexthops(const NML3ConfigData *self, int addr_family)
|
||||
{
|
||||
return nm_l3_config_data_lookup_objs(self, NMP_OBJECT_TYPE_IP_NEXTHOP(NM_IS_IPv4(addr_family)));
|
||||
}
|
||||
|
||||
#define nm_l3_config_data_iter_obj_for_each(iter, self, obj, type) \
|
||||
for (nm_dedup_multi_iter_init((iter), nm_l3_config_data_lookup_objs((self), (type))); \
|
||||
nm_platform_dedup_multi_iter_next_obj((iter), (obj), (type));)
|
||||
|
|
@ -435,6 +447,11 @@ nm_l3_config_data_add_route(NML3ConfigData *self,
|
|||
NULL);
|
||||
}
|
||||
|
||||
gboolean nm_l3_config_data_add_nexthop(NML3ConfigData *self,
|
||||
int addr_family,
|
||||
const NMPObject *obj_new,
|
||||
const NMPlatformIPNextHop *pl_new);
|
||||
|
||||
static inline gboolean
|
||||
nm_l3_config_data_add_route_4(NML3ConfigData *self, const NMPlatformIP4Route *rt)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -319,6 +319,17 @@ typedef struct _NML3CfgPrivate {
|
|||
int clat_socket;
|
||||
#endif /* HAVE_CLAT */
|
||||
|
||||
/* Tracks nexthop IDs that NM has configured, per address family.
|
||||
* Used to build the nexthop prune list without relying on the
|
||||
* platform cache or the zombie mechanism. */
|
||||
union {
|
||||
struct {
|
||||
GHashTable *nexthop_configured_ids_6;
|
||||
GHashTable *nexthop_configured_ids_4;
|
||||
};
|
||||
GHashTable *nexthop_configured_ids_x[2];
|
||||
};
|
||||
|
||||
/* Whether we earlier configured MPTCP endpoints for the interface. */
|
||||
union {
|
||||
struct {
|
||||
|
|
@ -826,7 +837,9 @@ _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gp
|
|||
NMP_OBJECT_TYPE_IP4_ADDRESS, \
|
||||
NMP_OBJECT_TYPE_IP6_ADDRESS, \
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE, \
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE)); \
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE, \
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP, \
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP)); \
|
||||
nm_assert(!_obj_state->os_plobj || _obj_state->os_was_in_platform); \
|
||||
nm_assert(_obj_state->os_failedobj_expiry_msec != 0 \
|
||||
|| _obj_state->os_failedobj_prioq_idx == NM_PRIOQ_IDX_NULL); \
|
||||
|
|
@ -1189,6 +1202,8 @@ _obj_states_update_all(NML3Cfg *self)
|
|||
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP,
|
||||
};
|
||||
ObjStateData *obj_state;
|
||||
int i;
|
||||
|
|
@ -1321,6 +1336,23 @@ _commit_collect_addresses(NML3Cfg *self, int addr_family, NML3CfgCommitType comm
|
|||
(gpointer) &sync_filter_data);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
_commit_collect_nexthops(NML3Cfg *self, int addr_family, NML3CfgCommitType commit_type)
|
||||
{
|
||||
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
||||
const NMDedupMultiHeadEntry *head_entry;
|
||||
const ObjStatesSyncFilterData sync_filter_data = {
|
||||
.self = self,
|
||||
.commit_type = commit_type,
|
||||
};
|
||||
|
||||
head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
|
||||
NMP_OBJECT_TYPE_IP_NEXTHOP(IS_IPv4));
|
||||
return nm_dedup_multi_objs_to_ptr_array_head(head_entry,
|
||||
_obj_states_sync_filter_predicate,
|
||||
(gpointer) &sync_filter_data);
|
||||
}
|
||||
|
||||
static void
|
||||
_commit_collect_routes(NML3Cfg *self,
|
||||
int addr_family,
|
||||
|
|
@ -5486,13 +5518,16 @@ _l3_commit_one(NML3Cfg *self,
|
|||
NML3CfgCommitType commit_type,
|
||||
const NML3ConfigData *l3cd_old)
|
||||
{
|
||||
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
||||
gs_unref_ptrarray GPtrArray *addresses = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes_nodev = NULL;
|
||||
gs_unref_ptrarray GPtrArray *addresses_prune = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes_prune = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes_failed = NULL;
|
||||
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
||||
gs_unref_ptrarray GPtrArray *addresses = NULL;
|
||||
gs_unref_ptrarray GPtrArray *nexthops = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes_nodev = NULL;
|
||||
gs_unref_ptrarray GPtrArray *addresses_prune = NULL;
|
||||
gs_unref_ptrarray GPtrArray *nexthops_prune = NULL;
|
||||
gs_unref_ptrarray GPtrArray *nexthops_platform = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes_prune = NULL;
|
||||
gs_unref_ptrarray GPtrArray *routes_failed = NULL;
|
||||
NMIPRouteTableSyncMode route_table_sync;
|
||||
char sbuf_commit_type[50];
|
||||
guint i;
|
||||
|
|
@ -5513,6 +5548,7 @@ _l3_commit_one(NML3Cfg *self,
|
|||
any_dirty = _obj_states_track_mark_dirty(self, TRUE);
|
||||
|
||||
addresses = _commit_collect_addresses(self, addr_family, commit_type);
|
||||
nexthops = _commit_collect_nexthops(self, addr_family, commit_type);
|
||||
|
||||
_commit_collect_routes(self,
|
||||
addr_family,
|
||||
|
|
@ -5594,17 +5630,46 @@ _l3_commit_one(NML3Cfg *self,
|
|||
route_table_sync,
|
||||
routes_old);
|
||||
|
||||
nexthops_platform =
|
||||
nm_platform_ip_nexthop_dump(self->priv.platform, addr_family, self->priv.ifindex);
|
||||
nexthops_prune = nm_g_ptr_array_ref(nexthops_platform);
|
||||
|
||||
_obj_state_zombie_lst_prune_all(self, addr_family);
|
||||
}
|
||||
} else {
|
||||
if (c_list_is_empty(&self->priv.p->blocked_lst_head_x[IS_IPv4])) {
|
||||
GHashTable *ht = self->priv.p->nexthop_configured_ids_x[IS_IPv4];
|
||||
GHashTableIter h_iter;
|
||||
gpointer key;
|
||||
|
||||
_obj_state_zombie_lst_get_prune_lists(self,
|
||||
addr_family,
|
||||
&addresses_prune,
|
||||
&routes_prune);
|
||||
|
||||
/* Unlike addresses and routes, nexthop pruning doesn't use the
|
||||
* zombie mechanism. On update, build the nexthop prune list from the internally
|
||||
* tracked set of IDs that NM has configured. */
|
||||
g_hash_table_iter_init(&h_iter, ht);
|
||||
while (g_hash_table_iter_next(&h_iter, &key, NULL)) {
|
||||
NMPObject *obj;
|
||||
|
||||
obj = nmp_object_new(NMP_OBJECT_TYPE_IP_NEXTHOP(IS_IPv4), NULL);
|
||||
obj->ip_nexthop.id = GPOINTER_TO_UINT(key);
|
||||
|
||||
if (!nexthops_prune)
|
||||
nexthops_prune =
|
||||
g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
||||
g_ptr_array_add(nexthops_prune, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nexthops_platform) {
|
||||
nexthops_platform =
|
||||
nm_platform_ip_nexthop_dump(self->priv.platform, addr_family, self->priv.ifindex);
|
||||
}
|
||||
|
||||
if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) {
|
||||
if (!addresses) {
|
||||
NMPlatformIPXAddress ax;
|
||||
|
|
@ -5639,6 +5704,21 @@ _l3_commit_one(NML3Cfg *self,
|
|||
? NMP_IP_ADDRESS_SYNC_FLAGS_NONE
|
||||
: NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE);
|
||||
|
||||
nm_platform_ip_nexthop_sync(self->priv.platform,
|
||||
addr_family,
|
||||
self->priv.ifindex,
|
||||
nexthops,
|
||||
nexthops_prune,
|
||||
nexthops_platform);
|
||||
|
||||
/* Update the set of nexthop IDs we have configured. */
|
||||
g_hash_table_remove_all(self->priv.p->nexthop_configured_ids_x[IS_IPv4]);
|
||||
for (i = 0; i < nm_g_ptr_array_len(nexthops); i++) {
|
||||
guint32 id = NMP_OBJECT_CAST_IP_NEXTHOP(nexthops->pdata[i])->id;
|
||||
|
||||
g_hash_table_add(self->priv.p->nexthop_configured_ids_x[IS_IPv4], GUINT_TO_POINTER(id));
|
||||
}
|
||||
|
||||
self->priv.p->commit_reentrant_count_ip_address_sync_x[IS_IPv4]--;
|
||||
|
||||
_nodev_routes_sync(self, addr_family, commit_type, routes_nodev);
|
||||
|
|
@ -6385,6 +6465,9 @@ nm_l3cfg_init(NML3Cfg *self)
|
|||
_obj_state_data_free,
|
||||
NULL);
|
||||
|
||||
self->priv.p->nexthop_configured_ids_4 = g_hash_table_new(nm_direct_hash, NULL);
|
||||
self->priv.p->nexthop_configured_ids_6 = g_hash_table_new(nm_direct_hash, NULL);
|
||||
|
||||
nm_prioq_init(&self->priv.p->failedobj_prioq, _failedobj_prioq_cmp);
|
||||
}
|
||||
|
||||
|
|
@ -6464,6 +6547,9 @@ finalize(GObject *object)
|
|||
nm_assert(c_list_is_empty(&self->priv.p->obj_state_lst_head));
|
||||
nm_assert(c_list_is_empty(&self->priv.p->obj_state_zombie_lst_head));
|
||||
|
||||
nm_clear_pointer(&self->priv.p->nexthop_configured_ids_4, g_hash_table_destroy);
|
||||
nm_clear_pointer(&self->priv.p->nexthop_configured_ids_6, g_hash_table_destroy);
|
||||
|
||||
if (_nodev_routes_untrack(self, AF_INET))
|
||||
nmp_global_tracker_sync(self->priv.global_tracker, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE);
|
||||
if (_nodev_routes_untrack(self, AF_INET6))
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ typedef struct {
|
|||
* by IP address. */
|
||||
GHashTable *watcher_ip_data_idx;
|
||||
|
||||
/* Tracks reserved nexthop IDs. Maps guint32 id -> gconstpointer tag.
|
||||
* This is used to avoid nexthop ID collisions across different
|
||||
* interfaces/ndisc instances within the same network namespace. */
|
||||
GHashTable *nexthop_id_reserved;
|
||||
|
||||
CList l3cfg_signal_pending_lst_head;
|
||||
GSource *signal_pending_idle_source;
|
||||
} NMNetnsPrivate;
|
||||
|
|
@ -1521,6 +1526,76 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
nm_netns_nexthop_id_is_reserved(NMNetns *self, guint32 id)
|
||||
{
|
||||
NMNetnsPrivate *priv;
|
||||
|
||||
g_return_val_if_fail(NM_IS_NETNS(self), FALSE);
|
||||
nm_assert(id != 0);
|
||||
|
||||
priv = NM_NETNS_GET_PRIVATE(self);
|
||||
return g_hash_table_contains(priv->nexthop_id_reserved, GUINT_TO_POINTER(id));
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_netns_nexthop_id_reserve(NMNetns *self, guint32 id, gconstpointer tag)
|
||||
{
|
||||
NMNetnsPrivate *priv;
|
||||
gpointer existing_tag;
|
||||
|
||||
g_return_val_if_fail(NM_IS_NETNS(self), FALSE);
|
||||
nm_assert(id != 0);
|
||||
nm_assert(tag);
|
||||
|
||||
priv = NM_NETNS_GET_PRIVATE(self);
|
||||
|
||||
if (g_hash_table_lookup_extended(priv->nexthop_id_reserved,
|
||||
GUINT_TO_POINTER(id),
|
||||
NULL,
|
||||
&existing_tag)) {
|
||||
/* The ID is already reserved. If it's by the same tag, it's
|
||||
* idempotent; otherwise it's a collision. */
|
||||
return existing_tag == tag;
|
||||
}
|
||||
|
||||
g_hash_table_insert(priv->nexthop_id_reserved, GUINT_TO_POINTER(id), (gpointer) tag);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nm_netns_nexthop_id_release(NMNetns *self, guint32 id)
|
||||
{
|
||||
NMNetnsPrivate *priv;
|
||||
|
||||
g_return_if_fail(NM_IS_NETNS(self));
|
||||
nm_assert(id != 0);
|
||||
|
||||
priv = NM_NETNS_GET_PRIVATE(self);
|
||||
g_hash_table_remove(priv->nexthop_id_reserved, GUINT_TO_POINTER(id));
|
||||
}
|
||||
|
||||
void
|
||||
nm_netns_nexthop_id_release_all(NMNetns *self, gconstpointer tag)
|
||||
{
|
||||
NMNetnsPrivate *priv;
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
|
||||
g_return_if_fail(NM_IS_NETNS(self));
|
||||
nm_assert(tag);
|
||||
|
||||
priv = NM_NETNS_GET_PRIVATE(self);
|
||||
|
||||
g_hash_table_iter_init(&iter, priv->nexthop_id_reserved);
|
||||
while (g_hash_table_iter_next(&iter, NULL, &value)) {
|
||||
if (value == tag)
|
||||
g_hash_table_iter_remove(&iter);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
nm_netns_init(NMNetns *self)
|
||||
{
|
||||
|
|
@ -1545,6 +1620,8 @@ nm_netns_init(NMNetns *self)
|
|||
(GDestroyNotify) _watcher_by_tag_destroy,
|
||||
NULL);
|
||||
priv->watcher_ip_data_idx = g_hash_table_new(_watcher_ip_data_hash, _watcher_ip_data_equal);
|
||||
|
||||
priv->nexthop_id_reserved = g_hash_table_new(nm_direct_hash, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1637,6 +1714,8 @@ dispose(GObject *object)
|
|||
nm_clear_pointer(&priv->watcher_by_tag_idx, g_hash_table_destroy);
|
||||
nm_clear_pointer(&priv->watcher_ip_data_idx, g_hash_table_destroy);
|
||||
|
||||
nm_clear_pointer(&priv->nexthop_id_reserved, g_hash_table_destroy);
|
||||
|
||||
nm_clear_g_source_inst(&priv->signal_pending_idle_source);
|
||||
|
||||
if (priv->platform)
|
||||
|
|
|
|||
|
|
@ -109,4 +109,11 @@ void nm_netns_watcher_add(NMNetns *self,
|
|||
void nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag);
|
||||
void nm_netns_watcher_remove_dirty(NMNetns *self, gconstpointer tag);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean nm_netns_nexthop_id_is_reserved(NMNetns *self, guint32 id);
|
||||
gboolean nm_netns_nexthop_id_reserve(NMNetns *self, guint32 id, gconstpointer tag);
|
||||
void nm_netns_nexthop_id_release(NMNetns *self, guint32 id);
|
||||
void nm_netns_nexthop_id_release_all(NMNetns *self, gconstpointer tag);
|
||||
|
||||
#endif /* __NM_NETNS_H__ */
|
||||
|
|
|
|||
|
|
@ -2239,6 +2239,251 @@ test_mptcp(gconstpointer test_data)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
test_nexthop_dump(void)
|
||||
{
|
||||
const int ifindex1 = NMTSTP_ENV1_IFINDEXES[0];
|
||||
const int ifindex2 = NMTSTP_ENV1_IFINDEXES[1];
|
||||
const char *ifname1 = NMTSTP_ENV1_DEVICE_NAME[0];
|
||||
const char *ifname2 = NMTSTP_ENV1_DEVICE_NAME[1];
|
||||
GPtrArray *result;
|
||||
const NMPObject *obj;
|
||||
|
||||
nmtstp_run_command_check("ip addr add 1.2.3.0/24 dev %s", ifname1);
|
||||
nmtstp_run_command_check("ip addr add fe80::1/64 dev %s", ifname1);
|
||||
nmtstp_run_command_check("ip addr add fe80::2/64 dev %s", ifname2);
|
||||
|
||||
nmtstp_run_command_check("ip nexthop add id 4 dev %s", ifname1);
|
||||
nmtstp_run_command_check("ip nexthop add id 5 dev %s via 1.2.3.4", ifname1);
|
||||
nmtstp_run_command_check("ip nexthop add id 6 dev %s", ifname2);
|
||||
|
||||
nmtstp_run_command_check("ip -6 nexthop add id 12345670 dev %s", ifname1);
|
||||
nmtstp_run_command_check("ip -6 nexthop add id 12345671 dev %s via fe80::11", ifname1);
|
||||
nmtstp_run_command_check("ip -6 nexthop add id 12345672 dev %s via fe80::12", ifname1);
|
||||
nmtstp_run_command_check("ip -6 nexthop add id 12345673 dev %s via fe80::11", ifname2);
|
||||
|
||||
/* interface 1, IPv4 */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET, ifindex1);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 2);
|
||||
obj = result->pdata[0];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->id, ==, 4);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->ifindex, ==, ifindex1);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->gateway, ==, 0);
|
||||
obj = result->pdata[1];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->id, ==, 5);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->ifindex, ==, ifindex1);
|
||||
nmtst_assert_ip4_address(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->gateway, "1.2.3.4");
|
||||
g_ptr_array_unref(result);
|
||||
|
||||
/* interface 1, IPv6 */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET6, ifindex1);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 3);
|
||||
obj = result->pdata[0];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->id, ==, 12345670);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->ifindex, ==, ifindex1);
|
||||
nmtst_assert_ip6_address(&NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->gateway, "::");
|
||||
obj = result->pdata[1];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->id, ==, 12345671);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->ifindex, ==, ifindex1);
|
||||
nmtst_assert_ip6_address(&NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->gateway, "fe80::11");
|
||||
obj = result->pdata[2];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->id, ==, 12345672);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->ifindex, ==, ifindex1);
|
||||
nmtst_assert_ip6_address(&NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->gateway, "fe80::12");
|
||||
g_ptr_array_unref(result);
|
||||
|
||||
/* interface 2, IPv4 */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET, ifindex2);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 1);
|
||||
obj = result->pdata[0];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->id, ==, 6);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->ifindex, ==, ifindex2);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(obj)->gateway, ==, 0);
|
||||
g_ptr_array_unref(result);
|
||||
|
||||
/* interface 2, IPv6 */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET6, ifindex2);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 1);
|
||||
obj = result->pdata[0];
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->id, ==, 12345673);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->ifindex, ==, ifindex2);
|
||||
nmtst_assert_ip6_address(&NMP_OBJECT_CAST_IP6_NEXTHOP(obj)->gateway, "fe80::11");
|
||||
g_ptr_array_unref(result);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
test_nexthop_add(void)
|
||||
{
|
||||
const int ifindex = NMTSTP_ENV1_IFINDEXES[0];
|
||||
const char *ifname = NMTSTP_ENV1_DEVICE_NAME[0];
|
||||
NMPObject obj;
|
||||
GPtrArray *result;
|
||||
int r;
|
||||
|
||||
nmtstp_run_command_check("ip addr add 1.2.3.0/24 dev %s", ifname);
|
||||
nmtstp_run_command_check("ip addr add fe80::1/64 dev %s", ifname);
|
||||
|
||||
/* Add IPv4 nexthop without gateway */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_NEXTHOP, NULL);
|
||||
obj.ip4_nexthop.id = 100;
|
||||
obj.ip4_nexthop.ifindex = ifindex;
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add IPv4 nexthop with gateway */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_NEXTHOP, NULL);
|
||||
obj.ip4_nexthop.id = 101;
|
||||
obj.ip4_nexthop.ifindex = ifindex;
|
||||
obj.ip4_nexthop.gateway = nmtst_inet4_from_string("1.2.3.4");
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add IPv6 nexthop without gateway */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_NEXTHOP, NULL);
|
||||
obj.ip6_nexthop.id = 200;
|
||||
obj.ip6_nexthop.ifindex = ifindex;
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add IPv6 nexthop with gateway */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_NEXTHOP, NULL);
|
||||
obj.ip6_nexthop.id = 201;
|
||||
obj.ip6_nexthop.ifindex = ifindex;
|
||||
obj.ip6_nexthop.gateway = nmtst_inet6_from_string("fe80::99");
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Verify IPv4 nexthops via dump */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET, ifindex);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 2);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(result->pdata[0])->id, ==, 100);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(result->pdata[0])->ifindex, ==, ifindex);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(result->pdata[0])->gateway, ==, 0);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(result->pdata[1])->id, ==, 101);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP4_NEXTHOP(result->pdata[1])->ifindex, ==, ifindex);
|
||||
nmtst_assert_ip4_address(NMP_OBJECT_CAST_IP4_NEXTHOP(result->pdata[1])->gateway, "1.2.3.4");
|
||||
g_ptr_array_unref(result);
|
||||
|
||||
/* Verify IPv6 nexthops via dump */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET6, ifindex);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 2);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(result->pdata[0])->id, ==, 200);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(result->pdata[0])->ifindex, ==, ifindex);
|
||||
nmtst_assert_ip6_address(&NMP_OBJECT_CAST_IP6_NEXTHOP(result->pdata[0])->gateway, "::");
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(result->pdata[1])->id, ==, 201);
|
||||
g_assert_cmpint(NMP_OBJECT_CAST_IP6_NEXTHOP(result->pdata[1])->ifindex, ==, ifindex);
|
||||
nmtst_assert_ip6_address(&NMP_OBJECT_CAST_IP6_NEXTHOP(result->pdata[1])->gateway, "fe80::99");
|
||||
g_ptr_array_unref(result);
|
||||
|
||||
/* Delete all nexthops */
|
||||
g_assert(
|
||||
nm_platform_object_delete(NM_PLATFORM_GET, nmp_object_stackinit_id_ip4_nexthop(&obj, 100)));
|
||||
g_assert(
|
||||
nm_platform_object_delete(NM_PLATFORM_GET, nmp_object_stackinit_id_ip4_nexthop(&obj, 101)));
|
||||
g_assert(
|
||||
nm_platform_object_delete(NM_PLATFORM_GET, nmp_object_stackinit_id_ip6_nexthop(&obj, 200)));
|
||||
g_assert(
|
||||
nm_platform_object_delete(NM_PLATFORM_GET, nmp_object_stackinit_id_ip6_nexthop(&obj, 201)));
|
||||
|
||||
/* Verify IPv4 nexthops are gone */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET, ifindex);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 0);
|
||||
g_ptr_array_unref(result);
|
||||
|
||||
/* Verify IPv6 nexthops are gone */
|
||||
result = nm_platform_ip_nexthop_dump(NM_PLATFORM_GET, AF_INET6, ifindex);
|
||||
g_assert(result);
|
||||
g_assert_cmpint(result->len, ==, 0);
|
||||
g_ptr_array_unref(result);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
test_ip6_route_with_nexthop(void)
|
||||
{
|
||||
const int ifindex = NMTSTP_ENV1_IFINDEXES[0];
|
||||
NMPObject obj;
|
||||
int r;
|
||||
const NMPlatformIP6Route *r6;
|
||||
struct in6_addr network;
|
||||
const guint32 metric = 22987;
|
||||
|
||||
g_assert(nm_platform_ip6_address_add(NM_PLATFORM_GET,
|
||||
ifindex,
|
||||
nmtst_inet6_from_string("fe80::1"),
|
||||
64,
|
||||
in6addr_any,
|
||||
NM_PLATFORM_LIFETIME_PERMANENT,
|
||||
NM_PLATFORM_LIFETIME_PERMANENT,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
/* Add IPv6 nexthop without gateway (id 300) */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_NEXTHOP, NULL);
|
||||
obj.ip6_nexthop.id = 300;
|
||||
obj.ip6_nexthop.ifindex = ifindex;
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add IPv6 nexthop with gateway (id 301) */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_NEXTHOP, NULL);
|
||||
obj.ip6_nexthop.id = 301;
|
||||
obj.ip6_nexthop.ifindex = ifindex;
|
||||
obj.ip6_nexthop.gateway = nmtst_inet6_from_string("fe80::99");
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add route using nexthop without gateway */
|
||||
inet_pton(AF_INET6, "a:b:c:1::", &network);
|
||||
r = nm_platform_ip6_route_add(NM_PLATFORM_GET,
|
||||
NMP_NLM_FLAG_REPLACE,
|
||||
&((NMPlatformIP6Route) {
|
||||
.ifindex = ifindex,
|
||||
.network = network,
|
||||
.plen = 64,
|
||||
.metric = metric,
|
||||
.nhid = 300,
|
||||
}));
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
r6 = nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, 64, metric, NULL, 0);
|
||||
g_assert(r6);
|
||||
g_assert_cmpint(r6->ifindex, ==, ifindex);
|
||||
g_assert_cmpint(r6->nhid, ==, 300);
|
||||
nmtst_assert_ip6_address(&r6->gateway, "::");
|
||||
|
||||
/* Add route using nexthop with gateway */
|
||||
inet_pton(AF_INET6, "a:b:c:2::", &network);
|
||||
r = nm_platform_ip6_route_add(NM_PLATFORM_GET,
|
||||
NMP_NLM_FLAG_REPLACE,
|
||||
&((NMPlatformIP6Route) {
|
||||
.ifindex = ifindex,
|
||||
.network = network,
|
||||
.plen = 64,
|
||||
.metric = metric,
|
||||
.nhid = 301,
|
||||
}));
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
r6 = nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, 64, metric, NULL, 0);
|
||||
g_assert(r6);
|
||||
g_assert_cmpint(r6->ifindex, ==, ifindex);
|
||||
g_assert_cmpint(r6->nhid, ==, 301);
|
||||
nmtst_assert_ip6_address(&r6->gateway, "fe80::99");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
_ensure_onlink_routes(void)
|
||||
{
|
||||
|
|
@ -2474,6 +2719,8 @@ void
|
|||
_nmtstp_setup_tests(void)
|
||||
{
|
||||
#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, TRUE)
|
||||
#define add_test_func_with_if2(testpath, test_func) \
|
||||
nmtstp_env1_add_test_func(testpath, test_func, 2, TRUE)
|
||||
#define add_test_func_data(testpath, test_func, arg) \
|
||||
nmtstp_env1_add_test_func_data(testpath, test_func, arg, 1, TRUE)
|
||||
#define add_test_func_data_with_if2(testpath, test_func, arg) \
|
||||
|
|
@ -2511,6 +2758,13 @@ _nmtstp_setup_tests(void)
|
|||
add_test_func_data("/route/mptcp/1", test_mptcp, GINT_TO_POINTER(1));
|
||||
add_test_func_data("/route/mptcp/2", test_mptcp, GINT_TO_POINTER(2));
|
||||
}
|
||||
|
||||
if (nmtstp_is_root_test()) {
|
||||
add_test_func_with_if2("/route/nexthop/dump", test_nexthop_dump);
|
||||
add_test_func("/route/nexthop/add", test_nexthop_add);
|
||||
add_test_func("/route/ip6_with_nexthop", test_ip6_route_with_nexthop);
|
||||
}
|
||||
|
||||
if (nmtstp_is_root_test()) {
|
||||
add_test_func_data_with_if2("/route/test_cache_consistency_routes/1",
|
||||
test_cache_consistency_routes,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/if_tunnel.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/ip6_tunnel.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <linux/tc_act/tc_mirred.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
|
|
@ -4071,6 +4072,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
|
|||
[RTA_PRIORITY] = {.type = NLA_U32},
|
||||
[RTA_PREF] = {.type = NLA_U8},
|
||||
[RTA_FLOW] = {.type = NLA_U32},
|
||||
[RTA_NH_ID] = {.type = NLA_U32},
|
||||
[RTA_CACHEINFO] = {.minlen = nm_offsetofend(struct rta_cacheinfo, rta_tsage)},
|
||||
[RTA_VIA] = {.minlen = nm_offsetofend(struct rtvia, rtvia_family)},
|
||||
[RTA_METRICS] = {.type = NLA_NESTED},
|
||||
|
|
@ -4108,6 +4110,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
|
|||
guint32 mtu = 0;
|
||||
guint32 rto_min = 0;
|
||||
guint32 lock = 0;
|
||||
guint32 nhid = 0;
|
||||
gboolean quickack = FALSE;
|
||||
gboolean rto_min_set = FALSE;
|
||||
|
||||
|
|
@ -4153,7 +4156,12 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
|
|||
if (rtm->rtm_dst_len > (IS_IPv4 ? 32 : 128))
|
||||
return NULL;
|
||||
|
||||
if (tb[RTA_MULTIPATH]) {
|
||||
if (tb[RTA_NH_ID] && !IS_IPv4) {
|
||||
/* we only support NHID for IPv6 at the moment */
|
||||
nhid = nla_get_u32(tb[RTA_NH_ID]);
|
||||
}
|
||||
|
||||
if (nhid == 0 && tb[RTA_MULTIPATH]) {
|
||||
size_t tlen;
|
||||
struct rtnexthop *rtnh;
|
||||
guint idx;
|
||||
|
|
@ -4262,7 +4270,7 @@ rta_multipath_done:
|
|||
return nm_assert_unreachable_val(NULL);
|
||||
}
|
||||
|
||||
if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA]) {
|
||||
if (nhid == 0 && (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA])) {
|
||||
int ifindex = 0;
|
||||
NMIPAddr gateway = {};
|
||||
|
||||
|
|
@ -4308,6 +4316,15 @@ rta_multipath_done:
|
|||
}
|
||||
}
|
||||
|
||||
if (nhid != 0) {
|
||||
if (tb[RTA_OIF]) {
|
||||
nh.ifindex = nla_get_u32(tb[RTA_OIF]);
|
||||
nh.found = TRUE;
|
||||
}
|
||||
if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
|
||||
memcpy(&nh.gateway, nla_data(tb[RTA_GATEWAY]), addr_len);
|
||||
}
|
||||
|
||||
if (nm_platform_route_type_is_nodev(rtm->rtm_type)) {
|
||||
/* These routes are special. They don't have an device/ifindex.
|
||||
*
|
||||
|
|
@ -4444,6 +4461,9 @@ rta_multipath_done:
|
|||
obj->ip6_route.src_plen = rtm->rtm_src_len;
|
||||
}
|
||||
|
||||
if (!IS_IPv4)
|
||||
obj->ip6_route.nhid = nhid;
|
||||
|
||||
obj->ip_route.mss = mss;
|
||||
obj->ip_route.window = window;
|
||||
obj->ip_route.cwnd = cwnd;
|
||||
|
|
@ -4477,6 +4497,62 @@ rta_multipath_done:
|
|||
return g_steal_pointer(&obj);
|
||||
}
|
||||
|
||||
static NMPObject *
|
||||
_new_from_nl_nexthop(const struct nlmsghdr *nlh, gboolean id_only, gboolean tracked_protos_only)
|
||||
{
|
||||
static const struct nla_policy policy[] = {
|
||||
[NHA_ID] = {.type = NLA_U32},
|
||||
[NHA_OIF] = {.type = NLA_U32},
|
||||
[NHA_GATEWAY] = {.maxlen = sizeof(struct in6_addr)},
|
||||
};
|
||||
const struct nhmsg *nhm;
|
||||
gboolean IS_IPv4;
|
||||
struct nlattr *tb[G_N_ELEMENTS(policy)];
|
||||
nm_auto_nmpobj NMPObject *obj = NULL;
|
||||
|
||||
if (!nlmsg_valid_hdr(nlh, sizeof(*nhm)))
|
||||
return NULL;
|
||||
|
||||
nhm = nlmsg_data(nlh);
|
||||
|
||||
if (tracked_protos_only && !NM_IN_SET(nhm->nh_protocol, IP_ROUTE_TRACKED_PROTOCOLS)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nhm->nh_family == AF_INET)
|
||||
IS_IPv4 = TRUE;
|
||||
else if (nhm->nh_family == AF_INET6)
|
||||
IS_IPv4 = FALSE;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
if (nlmsg_parse_arr(nlh, sizeof(*nhm), tb, policy) < 0)
|
||||
return NULL;
|
||||
|
||||
if (!tb[NHA_ID])
|
||||
return NULL;
|
||||
|
||||
obj = nmp_object_new(NMP_OBJECT_TYPE_IP_NEXTHOP(IS_IPv4), NULL);
|
||||
obj->ip_nexthop.id = nla_get_u32(tb[NHA_ID]);
|
||||
|
||||
if (id_only)
|
||||
return g_steal_pointer(&obj);
|
||||
|
||||
obj->ip_nexthop.nh_source = nmp_utils_ip_config_source_from_rtprot(nhm->nh_protocol);
|
||||
if (tb[NHA_OIF])
|
||||
obj->ip_nexthop.ifindex = nla_get_u32(tb[NHA_OIF]);
|
||||
|
||||
if (IS_IPv4) {
|
||||
if (tb[NHA_GATEWAY] && nla_len(tb[NHA_GATEWAY]) == sizeof(in_addr_t))
|
||||
obj->ip4_nexthop.gateway = nla_get_u32(tb[NHA_GATEWAY]);
|
||||
return g_steal_pointer(&obj);
|
||||
} else {
|
||||
if (tb[NHA_GATEWAY] && nla_len(tb[NHA_GATEWAY]) == sizeof(struct in6_addr))
|
||||
nla_memcpy(&obj->ip6_nexthop.gateway, tb[NHA_GATEWAY], sizeof(struct in6_addr));
|
||||
return g_steal_pointer(&obj);
|
||||
}
|
||||
}
|
||||
|
||||
static NMPObject *
|
||||
_new_from_nl_routing_rule(const struct nlmsghdr *nlh, gboolean id_only)
|
||||
{
|
||||
|
|
@ -4942,6 +5018,10 @@ nmp_object_new_from_nl(NMPlatform *platform,
|
|||
case RTM_DELRULE:
|
||||
case RTM_GETRULE:
|
||||
return _new_from_nl_routing_rule(msghdr, id_only);
|
||||
case RTM_NEWNEXTHOP:
|
||||
case RTM_DELNEXTHOP:
|
||||
case RTM_GETNEXTHOP:
|
||||
return _new_from_nl_nexthop(msghdr, id_only, FALSE);
|
||||
case RTM_NEWQDISC:
|
||||
case RTM_DELQDISC:
|
||||
case RTM_GETQDISC:
|
||||
|
|
@ -5793,6 +5873,54 @@ ip_route_is_alive(const NMPlatformIPRoute *route)
|
|||
return ip_route_is_tracked(proto, type);
|
||||
}
|
||||
|
||||
static struct nl_msg *
|
||||
_nl_msg_new_nexthop(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *obj)
|
||||
{
|
||||
nm_auto_nlmsg struct nl_msg *msg = NULL;
|
||||
const NMPClass *klass = NMP_OBJECT_GET_CLASS(obj);
|
||||
const gboolean IS_IPv4 = NM_IS_IPv4(klass->addr_family);
|
||||
struct nhmsg nhm = {
|
||||
.nh_family = klass->addr_family,
|
||||
};
|
||||
|
||||
nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj),
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP));
|
||||
nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP));
|
||||
|
||||
msg = nlmsg_alloc_new(0, nlmsg_type, nlmsg_flags);
|
||||
|
||||
if (nlmsg_type == RTM_NEWNEXTHOP) {
|
||||
/* the protocol must be zero for deletion messages */
|
||||
nhm.nh_protocol = nmp_utils_ip_config_source_coerce_to_rtprot(obj->ip_nexthop.nh_source);
|
||||
}
|
||||
|
||||
if (nlmsg_append_struct(msg, &nhm) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
NLA_PUT_U32(msg, NHA_ID, obj->ip_nexthop.id);
|
||||
|
||||
if (nlmsg_type == RTM_DELNEXTHOP)
|
||||
goto end;
|
||||
|
||||
if (obj->ip_nexthop.ifindex > 0)
|
||||
NLA_PUT_U32(msg, NHA_OIF, obj->ip_nexthop.ifindex);
|
||||
|
||||
if (IS_IPv4) {
|
||||
if (obj->ip4_nexthop.gateway != INADDR_ANY)
|
||||
NLA_PUT(msg, NHA_GATEWAY, sizeof(in_addr_t), &obj->ip4_nexthop.gateway);
|
||||
} else {
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_nexthop.gateway))
|
||||
NLA_PUT(msg, NHA_GATEWAY, sizeof(struct in6_addr), &obj->ip6_nexthop.gateway);
|
||||
}
|
||||
|
||||
end:
|
||||
return g_steal_pointer(&msg);
|
||||
|
||||
nla_put_failure:
|
||||
g_return_val_if_reached(NULL);
|
||||
}
|
||||
|
||||
/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */
|
||||
static struct nl_msg *
|
||||
_nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *obj)
|
||||
|
|
@ -5968,10 +6096,14 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob
|
|||
NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway);
|
||||
}
|
||||
} else {
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway))
|
||||
if (obj->ip6_route.nhid != 0) {
|
||||
NLA_PUT_U32(msg, RTA_NH_ID, obj->ip6_route.nhid);
|
||||
} else if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway))
|
||||
NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway);
|
||||
}
|
||||
NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex);
|
||||
|
||||
if (IS_IPv4 || obj->ip6_route.nhid == 0)
|
||||
NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex);
|
||||
|
||||
if (!IS_IPv4 && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM)
|
||||
NLA_PUT_U8(msg, RTA_PREF, obj->ip6_route.rt_pref);
|
||||
|
|
@ -8634,7 +8766,9 @@ do_add_addrroute(NMPlatform *platform,
|
|||
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE));
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP));
|
||||
|
||||
event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE);
|
||||
|
||||
|
|
@ -10809,6 +10943,177 @@ ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guin
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int
|
||||
ip_nexthop_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack, char **out_extack_msg)
|
||||
{
|
||||
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
|
||||
|
||||
nlmsg = _nl_msg_new_nexthop(RTM_NEWNEXTHOP, flags & NMP_NLM_FLAG_FMASK, obj_stack);
|
||||
if (!nlmsg)
|
||||
g_return_val_if_reached(-NME_BUG);
|
||||
return do_add_addrroute(platform,
|
||||
obj_stack,
|
||||
nlmsg,
|
||||
NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE),
|
||||
out_extack_msg);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GPtrArray *result;
|
||||
int addr_family;
|
||||
int ifindex;
|
||||
} NexthopDumpData;
|
||||
|
||||
static int
|
||||
_ip_nexthop_dump_parse_cb(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
NexthopDumpData *data = arg;
|
||||
nm_auto_nmpobj NMPObject *obj = NULL;
|
||||
|
||||
obj = _new_from_nl_nexthop(nlmsg_hdr(msg), FALSE, TRUE);
|
||||
if (!obj)
|
||||
return NL_SKIP;
|
||||
|
||||
g_ptr_array_add(data->result, g_steal_pointer(&obj));
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
ip_nexthop_dump(NMPlatform *platform, int addr_family, int ifindex)
|
||||
{
|
||||
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
|
||||
struct nl_sock *sk = NULL;
|
||||
struct nhmsg nhm = {
|
||||
.nh_family = addr_family,
|
||||
};
|
||||
NexthopDumpData data;
|
||||
int nle;
|
||||
|
||||
nlmsg = nlmsg_alloc_new(0, RTM_GETNEXTHOP, NLM_F_DUMP);
|
||||
if (!nlmsg)
|
||||
g_return_val_if_reached(NULL);
|
||||
|
||||
if (nlmsg_append_struct(nlmsg, &nhm) < 0)
|
||||
g_return_val_if_reached(NULL);
|
||||
|
||||
NLA_PUT_U32(nlmsg, NHA_OIF, ifindex);
|
||||
|
||||
nle = nl_socket_new(&sk, NETLINK_ROUTE, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK, 0, 0);
|
||||
if (nle < 0) {
|
||||
_LOGD("nexthop-dump: error opening socket: %s (%d)", nm_strerror(nle), nle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nle = nl_send_auto(sk, nlmsg);
|
||||
if (nle < 0) {
|
||||
_LOGD("nexthop-dump: failed sending request: %s (%d)", nm_strerror(nle), nle);
|
||||
nl_socket_free(sk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = (NexthopDumpData) {
|
||||
.result = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref),
|
||||
.addr_family = addr_family,
|
||||
.ifindex = ifindex,
|
||||
};
|
||||
|
||||
do {
|
||||
nle = nl_recvmsgs(sk,
|
||||
&((const struct nl_cb) {
|
||||
.valid_cb = _ip_nexthop_dump_parse_cb,
|
||||
.valid_arg = &data,
|
||||
}));
|
||||
} while (nle == -EAGAIN);
|
||||
|
||||
nl_socket_free(sk);
|
||||
|
||||
if (nle < 0) {
|
||||
_LOGD("nexthop-dump: recv failed: %s (%d)", nm_strerror(nle), nle);
|
||||
g_ptr_array_unref(data.result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data.result;
|
||||
nla_put_failure:
|
||||
g_return_val_if_reached(NULL);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
NMPObject *obj;
|
||||
} NexthopGetData;
|
||||
|
||||
static int
|
||||
_ip_nexthop_get_parse_cb(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
NexthopGetData *data = arg;
|
||||
|
||||
nm_assert(!data->obj);
|
||||
|
||||
data->obj = _new_from_nl_nexthop(nlmsg_hdr(msg), FALSE, FALSE);
|
||||
if (!data->obj)
|
||||
return NL_SKIP;
|
||||
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip_nexthop_get(NMPlatform *platform, guint32 nh_id, NMPObject **out_obj)
|
||||
{
|
||||
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
|
||||
struct nl_sock *sk = NULL;
|
||||
struct nhmsg nhm = {
|
||||
.nh_family = AF_UNSPEC,
|
||||
};
|
||||
NexthopGetData data = {
|
||||
.obj = NULL,
|
||||
};
|
||||
int nle;
|
||||
|
||||
nlmsg = nlmsg_alloc_new(0, RTM_GETNEXTHOP, 0);
|
||||
if (!nlmsg)
|
||||
g_return_val_if_reached(FALSE);
|
||||
|
||||
if (nlmsg_append_struct(nlmsg, &nhm) < 0)
|
||||
g_return_val_if_reached(FALSE);
|
||||
|
||||
NLA_PUT_U32(nlmsg, NHA_ID, nh_id);
|
||||
|
||||
nle = nl_socket_new(&sk, NETLINK_ROUTE, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK, 0, 0);
|
||||
if (nle < 0) {
|
||||
_LOGD("nexthop-get: error opening socket: %s (%d)", nm_strerror(nle), nle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nle = nl_send_auto(sk, nlmsg);
|
||||
if (nle < 0) {
|
||||
_LOGD("nexthop-get: failed sending request: %s (%d)", nm_strerror(nle), nle);
|
||||
nl_socket_free(sk);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nle = nl_recvmsgs(sk,
|
||||
&((const struct nl_cb) {
|
||||
.valid_cb = _ip_nexthop_get_parse_cb,
|
||||
.valid_arg = &data,
|
||||
}));
|
||||
|
||||
nl_socket_free(sk);
|
||||
|
||||
if (nle < 0) {
|
||||
nm_clear_pointer(&data.obj, nmp_object_unref);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!data.obj)
|
||||
return FALSE;
|
||||
|
||||
NM_SET_OUT(out_obj, data.obj);
|
||||
return TRUE;
|
||||
|
||||
nla_put_failure:
|
||||
g_return_val_if_reached(FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack, char **out_extack_msg)
|
||||
{
|
||||
|
|
@ -10838,6 +11143,10 @@ object_delete(NMPlatform *platform, const NMPObject *obj)
|
|||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
nlmsg = _nl_msg_new_route(RTM_DELROUTE, 0, obj);
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_IP4_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_IP6_NEXTHOP:
|
||||
nlmsg = _nl_msg_new_nexthop(RTM_DELNEXTHOP, 0, obj);
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
||||
nlmsg = _nl_msg_new_routing_rule(RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE(obj));
|
||||
break;
|
||||
|
|
@ -12524,6 +12833,10 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
|
|||
platform_class->ip_route_add = ip_route_add;
|
||||
platform_class->ip_route_get = ip_route_get;
|
||||
|
||||
platform_class->ip_nexthop_add = ip_nexthop_add;
|
||||
platform_class->ip_nexthop_dump = ip_nexthop_dump;
|
||||
platform_class->ip_nexthop_get = ip_nexthop_get;
|
||||
|
||||
platform_class->routing_rule_add = routing_rule_add;
|
||||
|
||||
platform_class->qdisc_add = qdisc_add;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ static NM_UTILS_LOOKUP_STR_DEFINE(_rtnl_type_to_str,
|
|||
NM_UTILS_LOOKUP_STR_ITEM(RTM_GETRULE, "RTM_GETRULE"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWRULE, "RTM_NEWRULE"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_DELRULE, "RTM_DELRULE"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_GETNEXTHOP, "RTM_GETNEXTHOP"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_DELNEXTHOP, "RTM_DELNEXTHOP"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_GETQDISC, "RTM_GETQDISC"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWQDISC, "RTM_NEWQDISC"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(RTM_DELQDISC, "RTM_DELQDISC"),
|
||||
|
|
|
|||
|
|
@ -4782,6 +4782,84 @@ next_plat:;
|
|||
return success;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_platform_ip_nexthop_sync(NMPlatform *self,
|
||||
int addr_family,
|
||||
int ifindex,
|
||||
GPtrArray *known_nexthops,
|
||||
GPtrArray *nexthops_prune,
|
||||
GPtrArray *nexthops_platform)
|
||||
{
|
||||
gs_unref_hashtable GHashTable *known_nexthops_idx = NULL;
|
||||
gs_unref_hashtable GHashTable *nexthops_platform_idx = NULL;
|
||||
int IS_IPv4 = NM_IS_IPv4(addr_family);
|
||||
guint i;
|
||||
gboolean success = TRUE;
|
||||
|
||||
if (known_nexthops && known_nexthops->len > 0) {
|
||||
known_nexthops_idx = g_hash_table_new(nm_direct_hash, NULL);
|
||||
for (i = 0; i < known_nexthops->len; i++) {
|
||||
const NMPObject *nh = known_nexthops->pdata[i];
|
||||
|
||||
g_hash_table_add(known_nexthops_idx, GUINT_TO_POINTER(nh->ipx_nexthop.nhx.id));
|
||||
}
|
||||
}
|
||||
|
||||
if (nexthops_platform && nexthops_platform->len > 0) {
|
||||
nexthops_platform_idx = g_hash_table_new(nm_direct_hash, NULL);
|
||||
for (i = 0; i < nexthops_platform->len; i++) {
|
||||
const NMPObject *nh = nexthops_platform->pdata[i];
|
||||
|
||||
g_hash_table_insert(nexthops_platform_idx,
|
||||
GUINT_TO_POINTER(nh->ipx_nexthop.nhx.id),
|
||||
(gpointer) nh);
|
||||
}
|
||||
}
|
||||
|
||||
if (nexthops_prune) {
|
||||
for (i = 0; i < nexthops_prune->len; i++) {
|
||||
const NMPObject *prune_o = nexthops_prune->pdata[i];
|
||||
|
||||
nm_assert((IS_IPv4 && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP4_NEXTHOP)
|
||||
|| (!IS_IPv4 && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP6_NEXTHOP));
|
||||
|
||||
if (nm_g_hash_table_lookup(known_nexthops_idx,
|
||||
GUINT_TO_POINTER(NMP_OBJECT_CAST_IP_NEXTHOP(prune_o)->id)))
|
||||
continue;
|
||||
|
||||
if (!nm_platform_object_delete(self, prune_o)) {
|
||||
/* ignore error... */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (known_nexthops) {
|
||||
for (i = 0; i < known_nexthops->len; i++) {
|
||||
const NMPObject *nh = known_nexthops->pdata[i];
|
||||
const NMPObject *candidate;
|
||||
|
||||
candidate =
|
||||
nm_g_hash_table_lookup(nexthops_platform_idx, GUINT_TO_POINTER(nh->ip_nexthop.id));
|
||||
if (candidate
|
||||
&& (IS_IPv4
|
||||
? nm_platform_ip4_nexthop_cmp(&nh->ip4_nexthop,
|
||||
&candidate->ip4_nexthop,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY)
|
||||
: nm_platform_ip6_nexthop_cmp(&nh->ip6_nexthop,
|
||||
&candidate->ip6_nexthop,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY))
|
||||
== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nm_platform_ip_nexthop_add(self, NMP_NLM_FLAG_REPLACE, nh, NULL) != 0)
|
||||
success = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex)
|
||||
{
|
||||
|
|
@ -5376,6 +5454,69 @@ nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex)
|
|||
return success;
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
nm_platform_ip_nexthop_dump(NMPlatform *self, int addr_family, int ifindex)
|
||||
{
|
||||
_CHECK_SELF(self, klass, NULL);
|
||||
|
||||
nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
|
||||
|
||||
return klass->ip_nexthop_dump(self, addr_family, ifindex);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_platform_ip_nexthop_get(NMPlatform *self, guint32 nh_id, NMPObject **out_obj)
|
||||
{
|
||||
nm_auto_nmpobj NMPObject *obj = NULL;
|
||||
gboolean result;
|
||||
|
||||
_CHECK_SELF(self, klass, FALSE);
|
||||
|
||||
nm_assert(nh_id > 0);
|
||||
|
||||
if (!klass->ip_nexthop_get) {
|
||||
NM_SET_OUT(out_obj, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
result = klass->ip_nexthop_get(self, nh_id, &obj);
|
||||
|
||||
if (result) {
|
||||
nm_assert(obj);
|
||||
_LOGD("nexthop: get nexthop %u succeeded", nh_id);
|
||||
NM_SET_OUT(out_obj, g_steal_pointer(&obj));
|
||||
} else {
|
||||
_LOGD("nexthop: get nexthop %u failed", nh_id);
|
||||
NM_SET_OUT(out_obj, NULL);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_platform_ip_nexthop_flush(NMPlatform *self, int addr_family, int ifindex)
|
||||
{
|
||||
gboolean success = TRUE;
|
||||
|
||||
_CHECK_SELF(self, klass, FALSE);
|
||||
|
||||
nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
|
||||
|
||||
if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) {
|
||||
gs_unref_ptrarray GPtrArray *nexthops_prune = NULL;
|
||||
|
||||
nexthops_prune = nm_platform_ip_nexthop_dump(self, AF_INET, ifindex);
|
||||
success &= nm_platform_ip_nexthop_sync(self, AF_INET, ifindex, NULL, nexthops_prune, NULL);
|
||||
}
|
||||
if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) {
|
||||
gs_unref_ptrarray GPtrArray *nexthops_prune = NULL;
|
||||
|
||||
nexthops_prune = nm_platform_ip_nexthop_dump(self, AF_INET6, ifindex);
|
||||
success &= nm_platform_ip_nexthop_sync(self, AF_INET6, ifindex, NULL, nexthops_prune, NULL);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static guint8
|
||||
|
|
@ -5629,6 +5770,34 @@ nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformI
|
|||
return _ip_route_add(self, flags, &obj, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
nm_platform_ip_nexthop_add(NMPlatform *self,
|
||||
NMPNlmFlags flags,
|
||||
const NMPObject *obj,
|
||||
char **out_extack_msg)
|
||||
{
|
||||
NMPObject obj_stack;
|
||||
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
||||
int ifindex;
|
||||
|
||||
_CHECK_SELF(self, klass, -NME_BUG);
|
||||
|
||||
nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj),
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP));
|
||||
|
||||
nmp_object_stackinit(&obj_stack, NMP_OBJECT_GET_TYPE(obj), &obj->ip_nexthop);
|
||||
|
||||
ifindex = obj_stack.ip_nexthop.ifindex;
|
||||
|
||||
_LOG3D("nexthop: %-10s IPv%c nexthop: %s",
|
||||
_nmp_nlm_flag_to_string(flags & NMP_NLM_FLAG_FMASK),
|
||||
nm_utils_addr_family_to_char(NMP_OBJECT_GET_ADDR_FAMILY(&obj_stack)),
|
||||
nmp_object_to_string(&obj_stack, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
|
||||
return klass->ip_nexthop_add(self, flags, &obj_stack, out_extack_msg);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_platform_object_delete(NMPlatform *self, const NMPObject *obj)
|
||||
{
|
||||
|
|
@ -5647,6 +5816,8 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj)
|
|||
break;
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP4_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_IP6_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_QDISC:
|
||||
case NMP_OBJECT_TYPE_TFILTER:
|
||||
ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex;
|
||||
|
|
@ -7438,6 +7609,52 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
|
|||
return buf0;
|
||||
}
|
||||
|
||||
const char *
|
||||
nm_platform_ip4_nexthop_to_string(const NMPlatformIP4NextHop *nexthop, char *buf, gsize len)
|
||||
{
|
||||
char s_gateway[INET_ADDRSTRLEN];
|
||||
char s_source[50];
|
||||
|
||||
if (!nm_utils_to_string_buffer_init_null(nexthop, &buf, &len))
|
||||
return buf;
|
||||
|
||||
inet_ntop(AF_INET, &nexthop->gateway, s_gateway, sizeof(s_gateway));
|
||||
nmp_utils_ip_config_source_to_string(nexthop->nh_source, s_source, sizeof(s_source));
|
||||
|
||||
g_snprintf(buf,
|
||||
len,
|
||||
"id %u dev %d gw %s rt-src %s",
|
||||
nexthop->id,
|
||||
nexthop->ifindex,
|
||||
s_gateway,
|
||||
s_source);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *
|
||||
nm_platform_ip6_nexthop_to_string(const NMPlatformIP6NextHop *nexthop, char *buf, gsize len)
|
||||
{
|
||||
char s_gateway[INET6_ADDRSTRLEN];
|
||||
char s_source[50];
|
||||
|
||||
if (!nm_utils_to_string_buffer_init_null(nexthop, &buf, &len))
|
||||
return buf;
|
||||
|
||||
inet_ntop(AF_INET6, &nexthop->gateway, s_gateway, sizeof(s_gateway));
|
||||
nmp_utils_ip_config_source_to_string(nexthop->nh_source, s_source, sizeof(s_source));
|
||||
|
||||
g_snprintf(buf,
|
||||
len,
|
||||
"id %u dev %d gw %s rt-src %s",
|
||||
nexthop->id,
|
||||
nexthop->ifindex,
|
||||
s_gateway,
|
||||
s_source);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_ip6_route_to_string:
|
||||
* @route: pointer to NMPlatformIP6Route route structure
|
||||
|
|
@ -7453,33 +7670,37 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
|
|||
const char *
|
||||
nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len)
|
||||
{
|
||||
char s_network[INET6_ADDRSTRLEN];
|
||||
char s_gateway[INET6_ADDRSTRLEN];
|
||||
char s_pref_src[INET6_ADDRSTRLEN];
|
||||
char s_src_all[INET6_ADDRSTRLEN + 40];
|
||||
char s_src[INET6_ADDRSTRLEN];
|
||||
char str_type[30];
|
||||
char str_table[30];
|
||||
char str_pref[40];
|
||||
char str_pref2[30];
|
||||
char str_dev[30];
|
||||
char str_mss[32];
|
||||
char s_source[50];
|
||||
char str_window[32];
|
||||
char str_cwnd[32];
|
||||
char str_initcwnd[32];
|
||||
char str_initrwnd[32];
|
||||
char str_rto_min[32];
|
||||
char str_mtu[32];
|
||||
char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN];
|
||||
char str_metric[30];
|
||||
char s_network[INET6_ADDRSTRLEN];
|
||||
char s_gateway[INET6_ADDRSTRLEN];
|
||||
char s_pref_src[INET6_ADDRSTRLEN];
|
||||
char s_src_all[INET6_ADDRSTRLEN + 40];
|
||||
char s_src[INET6_ADDRSTRLEN];
|
||||
char str_type[30];
|
||||
char str_table[30];
|
||||
char str_pref[40];
|
||||
char str_pref2[30];
|
||||
char str_dev[30];
|
||||
char str_mss[32];
|
||||
char s_source[50];
|
||||
char str_window[32];
|
||||
char str_cwnd[32];
|
||||
char str_initcwnd[32];
|
||||
char str_initrwnd[32];
|
||||
char str_rto_min[32];
|
||||
char str_mtu[32];
|
||||
char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN];
|
||||
char str_metric[30];
|
||||
gboolean has_nhid = FALSE;
|
||||
|
||||
if (!nm_utils_to_string_buffer_init_null(route, &buf, &len))
|
||||
return buf;
|
||||
|
||||
inet_ntop(AF_INET6, &route->network, s_network, sizeof(s_network));
|
||||
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&route->gateway))
|
||||
if (route->nhid) {
|
||||
g_snprintf(s_gateway, sizeof(s_gateway), " nhid %u", route->nhid);
|
||||
has_nhid = TRUE;
|
||||
} else if (IN6_IS_ADDR_UNSPECIFIED(&route->gateway))
|
||||
s_gateway[0] = '\0';
|
||||
else
|
||||
inet_ntop(AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway));
|
||||
|
|
@ -7523,7 +7744,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz
|
|||
: ""),
|
||||
s_network,
|
||||
route->plen,
|
||||
s_gateway[0] ? " via " : "",
|
||||
!has_nhid && s_gateway[0] ? " via " : "",
|
||||
s_gateway,
|
||||
_to_string_dev(str_dev, route->ifindex),
|
||||
route->metric_any
|
||||
|
|
@ -9321,23 +9542,25 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
|
|||
*nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen),
|
||||
obj->plen,
|
||||
obj->metric,
|
||||
obj->nhid,
|
||||
*nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen),
|
||||
obj->src_plen,
|
||||
NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any),
|
||||
/* on top of WEAK_ID: */
|
||||
obj->ifindex,
|
||||
obj->gateway);
|
||||
obj->nhid == 0 ? obj->ifindex : 0,
|
||||
obj->nhid == 0 ? obj->gateway : in6addr_any);
|
||||
break;
|
||||
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
||||
nm_hash_update_vals(
|
||||
h,
|
||||
obj->type_coerced,
|
||||
nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
|
||||
obj->ifindex,
|
||||
*nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen),
|
||||
obj->plen,
|
||||
obj->metric,
|
||||
obj->gateway,
|
||||
obj->nhid,
|
||||
obj->nhid == 0 ? obj->ifindex : 0,
|
||||
obj->nhid == 0 ? obj->gateway : in6addr_any,
|
||||
obj->pref_src,
|
||||
*nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen),
|
||||
obj->src_plen,
|
||||
|
|
@ -9366,10 +9589,11 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
|
|||
nm_hash_update_vals(h,
|
||||
obj->type_coerced,
|
||||
obj->table_coerced,
|
||||
obj->ifindex,
|
||||
obj->network,
|
||||
obj->metric,
|
||||
obj->gateway,
|
||||
obj->nhid,
|
||||
obj->nhid == 0 ? obj->ifindex : 0,
|
||||
obj->nhid == 0 ? obj->gateway : in6addr_any,
|
||||
obj->pref_src,
|
||||
obj->src,
|
||||
obj->src_plen,
|
||||
|
|
@ -9419,9 +9643,12 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
|
|||
NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, NM_MIN(a->src_plen, b->src_plen));
|
||||
NM_CMP_FIELD(a, b, src_plen);
|
||||
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD(a, b, type_coerced);
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
NM_CMP_FIELD(a, b, nhid);
|
||||
if (a->nhid == 0) {
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
||||
|
|
@ -9433,7 +9660,6 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
|
|||
nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b)));
|
||||
} else
|
||||
NM_CMP_FIELD(a, b, table_coerced);
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
||||
NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->network, &b->network, NM_MIN(a->plen, b->plen));
|
||||
else
|
||||
|
|
@ -9441,7 +9667,11 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
|
|||
NM_CMP_FIELD(a, b, plen);
|
||||
NM_CMP_FIELD_UNSAFE(a, b, metric_any);
|
||||
NM_CMP_FIELD(a, b, metric);
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
NM_CMP_FIELD(a, b, nhid);
|
||||
if (a->nhid == 0) {
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
}
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, pref_src);
|
||||
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
|
||||
NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, NM_MIN(a->src_plen, b->src_plen));
|
||||
|
|
|
|||
|
|
@ -531,6 +531,22 @@ struct _NMPlatformIP6Route {
|
|||
* The type is guint8 to keep the struct size small. But the values are compatible with
|
||||
* the NMIcmpv6RouterPref enum. */
|
||||
guint8 rt_pref;
|
||||
|
||||
/* RTA_NH_ID. The unique id of the nexthop object.
|
||||
*
|
||||
* When sending a route with a nexthop to the kernel, the ifindex
|
||||
* and gateway must be unset, otherwise the route will be
|
||||
* rejected. When the kernel sends notifications to userspace it
|
||||
* copies the ifindex and the gateway from the nexthop into the
|
||||
* route.
|
||||
*
|
||||
* In a route platform object, the ifindex and gateway are ignored
|
||||
* if the routes has a nexthop. However, in the platform cache, routes
|
||||
* with nexthops always have also the ifindex and the gateway set. In
|
||||
* particular, the ifindex must be set when creating new synthetic
|
||||
* route because the platform code needs it to properly track the object.
|
||||
*/
|
||||
guint32 nhid;
|
||||
} _nm_alignas(NMPlatformObject);
|
||||
|
||||
typedef union {
|
||||
|
|
@ -1349,6 +1365,15 @@ typedef struct {
|
|||
int oif_ifindex,
|
||||
NMPObject **out_route);
|
||||
|
||||
int (*ip_nexthop_add)(NMPlatform *self,
|
||||
NMPNlmFlags flags,
|
||||
NMPObject *obj_stack,
|
||||
char **out_extack_msg);
|
||||
|
||||
GPtrArray *(*ip_nexthop_dump)(NMPlatform *self, int addr_family, int ifindex);
|
||||
|
||||
gboolean (*ip_nexthop_get)(NMPlatform *self, guint32 nh_id, NMPObject **out_obj);
|
||||
|
||||
int (*routing_rule_add)(NMPlatform *self,
|
||||
NMPNlmFlags flags,
|
||||
const NMPlatformRoutingRule *routing_rule);
|
||||
|
|
@ -2465,6 +2490,11 @@ int nm_platform_ip4_route_add(NMPlatform *self,
|
|||
const NMPlatformIP4RtNextHop *extra_nexthops);
|
||||
int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route);
|
||||
|
||||
int nm_platform_ip_nexthop_add(NMPlatform *self,
|
||||
NMPNlmFlags flags,
|
||||
const NMPObject *nexthop,
|
||||
char **out_extack_msg);
|
||||
|
||||
GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform *self,
|
||||
int addr_family,
|
||||
int ifindex,
|
||||
|
|
@ -2479,6 +2509,7 @@ gboolean nm_platform_ip_route_sync(NMPlatform *self,
|
|||
GPtrArray **out_routes_failed);
|
||||
|
||||
gboolean nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex);
|
||||
gboolean nm_platform_ip_nexthop_flush(NMPlatform *self, int addr_family, int ifindex);
|
||||
|
||||
int nm_platform_ip_route_get(NMPlatform *self,
|
||||
int addr_family,
|
||||
|
|
@ -2487,6 +2518,17 @@ int nm_platform_ip_route_get(NMPlatform *self,
|
|||
int oif_ifindex,
|
||||
NMPObject **out_route);
|
||||
|
||||
GPtrArray *nm_platform_ip_nexthop_dump(NMPlatform *self, int addr_family, int ifindex);
|
||||
|
||||
gboolean nm_platform_ip_nexthop_get(NMPlatform *self, guint32 nh_id, NMPObject **out_obj);
|
||||
|
||||
gboolean nm_platform_ip_nexthop_sync(NMPlatform *self,
|
||||
int addr_family,
|
||||
int ifindex,
|
||||
GPtrArray *known_nexthops,
|
||||
GPtrArray *nexthops_prune,
|
||||
GPtrArray *nexthops_platform);
|
||||
|
||||
int nm_platform_routing_rule_add(NMPlatform *self,
|
||||
NMPNlmFlags flags,
|
||||
const NMPlatformRoutingRule *routing_rule);
|
||||
|
|
@ -2536,6 +2578,11 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz
|
|||
}
|
||||
|
||||
const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len);
|
||||
const char *
|
||||
nm_platform_ip4_nexthop_to_string(const NMPlatformIP4NextHop *nexthop, char *buf, gsize len);
|
||||
const char *
|
||||
nm_platform_ip6_nexthop_to_string(const NMPlatformIP6NextHop *nexthop, char *buf, gsize len);
|
||||
|
||||
const char *
|
||||
nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len);
|
||||
const char *nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len);
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@ typedef struct _NMPlatformIP4Address NMPlatformIP4Address;
|
|||
typedef struct _NMPlatformIP4Route NMPlatformIP4Route;
|
||||
typedef struct _NMPlatformIP6Address NMPlatformIP6Address;
|
||||
typedef struct _NMPlatformIP6Route NMPlatformIP6Route;
|
||||
typedef struct _NMPlatformIP4NextHop NMPlatformIP4NextHop;
|
||||
typedef struct _NMPlatformIP6NextHop NMPlatformIP6NextHop;
|
||||
typedef struct _NMPlatformLink NMPlatformLink;
|
||||
typedef struct _NMPObject NMPObject;
|
||||
|
||||
|
|
@ -159,6 +161,11 @@ typedef enum _nm_packed {
|
|||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
|
||||
#define NMP_OBJECT_TYPE_IP_NEXTHOP(is_ipv4) \
|
||||
((is_ipv4) ? NMP_OBJECT_TYPE_IP4_NEXTHOP : NMP_OBJECT_TYPE_IP6_NEXTHOP)
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP,
|
||||
|
||||
NMP_OBJECT_TYPE_ROUTING_RULE,
|
||||
|
||||
NMP_OBJECT_TYPE_QDISC,
|
||||
|
|
|
|||
|
|
@ -381,6 +381,8 @@ _idx_obj_part(const DedupMultiIdxType *idx_type,
|
|||
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_QDISC,
|
||||
NMP_OBJECT_TYPE_TFILTER,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR)
|
||||
|
|
@ -899,6 +901,22 @@ nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in
|
|||
return obj;
|
||||
}
|
||||
|
||||
const NMPObject *
|
||||
nmp_object_stackinit_id_ip4_nexthop(NMPObject *obj, guint32 id)
|
||||
{
|
||||
_nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP4_NEXTHOP);
|
||||
obj->ip4_nexthop.id = id;
|
||||
return obj;
|
||||
}
|
||||
|
||||
const NMPObject *
|
||||
nmp_object_stackinit_id_ip6_nexthop(NMPObject *obj, guint32 id)
|
||||
{
|
||||
_nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP6_NEXTHOP);
|
||||
obj->ip6_nexthop.id = id;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
const char *
|
||||
|
|
@ -1668,6 +1686,22 @@ _vt_cmd_plobj_id_cmp_ip6_address(const NMPlatformObject *obj1, const NMPlatformO
|
|||
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID);
|
||||
}
|
||||
|
||||
static int
|
||||
_vt_cmd_plobj_id_cmp_ip4_nexthop(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
||||
{
|
||||
return nm_platform_ip4_nexthop_cmp((const NMPlatformIP4NextHop *) obj1,
|
||||
(const NMPlatformIP4NextHop *) obj2,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID);
|
||||
}
|
||||
|
||||
static int
|
||||
_vt_cmd_plobj_id_cmp_ip6_nexthop(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
||||
{
|
||||
return nm_platform_ip6_nexthop_cmp((const NMPlatformIP6NextHop *) obj1,
|
||||
(const NMPlatformIP6NextHop *) obj2,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID);
|
||||
}
|
||||
|
||||
_vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, {
|
||||
NM_CMP_FIELD(obj1, obj2, ifindex);
|
||||
NM_CMP_FIELD(obj1, obj2, parent);
|
||||
|
|
@ -1770,6 +1804,14 @@ _vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, {
|
|||
nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h);
|
||||
});
|
||||
|
||||
_vt_cmd_plobj_id_hash_update(ip4_nexthop, NMPlatformIP4NextHop, {
|
||||
nm_platform_ip4_nexthop_hash_update(obj, NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID, h);
|
||||
});
|
||||
|
||||
_vt_cmd_plobj_id_hash_update(ip6_nexthop, NMPlatformIP6NextHop, {
|
||||
nm_platform_ip6_nexthop_hash_update(obj, NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID, h);
|
||||
});
|
||||
|
||||
_vt_cmd_plobj_id_hash_update(routing_rule, NMPlatformRoutingRule, {
|
||||
nm_platform_routing_rule_hash_update(obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h);
|
||||
});
|
||||
|
|
@ -1804,6 +1846,38 @@ _vt_cmd_plobj_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject
|
|||
NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
|
||||
}
|
||||
|
||||
static int
|
||||
_vt_cmd_plobj_cmp_ip4_nexthop(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
||||
{
|
||||
return nm_platform_ip4_nexthop_cmp((const NMPlatformIP4NextHop *) obj1,
|
||||
(const NMPlatformIP4NextHop *) obj2,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_vt_cmd_plobj_hash_update_ip4_nexthop(const NMPlatformObject *obj, NMHashState *h)
|
||||
{
|
||||
return nm_platform_ip4_nexthop_hash_update((const NMPlatformIP4NextHop *) obj,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL,
|
||||
h);
|
||||
}
|
||||
|
||||
static int
|
||||
_vt_cmd_plobj_cmp_ip6_nexthop(const NMPlatformObject *obj1, const NMPlatformObject *obj2)
|
||||
{
|
||||
return nm_platform_ip6_nexthop_cmp((const NMPlatformIP6NextHop *) obj1,
|
||||
(const NMPlatformIP6NextHop *) obj2,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_vt_cmd_plobj_hash_update_ip6_nexthop(const NMPlatformObject *obj, NMHashState *h)
|
||||
{
|
||||
return nm_platform_ip6_nexthop_hash_update((const NMPlatformIP6NextHop *) obj,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL,
|
||||
h);
|
||||
}
|
||||
|
||||
static void
|
||||
_vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState *h)
|
||||
{
|
||||
|
|
@ -2263,6 +2337,8 @@ nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type)
|
|||
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP4_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_IP6_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
||||
case NMP_OBJECT_TYPE_QDISC:
|
||||
case NMP_OBJECT_TYPE_TFILTER:
|
||||
|
|
@ -2300,6 +2376,8 @@ nmp_lookup_init_object_by_ifindex(NMPLookup *lookup, NMPObjectType obj_type, int
|
|||
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_IP6_NEXTHOP,
|
||||
NMP_OBJECT_TYPE_QDISC,
|
||||
NMP_OBJECT_TYPE_TFILTER,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR));
|
||||
|
|
@ -3400,6 +3478,36 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
|
|||
.cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route,
|
||||
.cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip6_route,
|
||||
},
|
||||
[NMP_OBJECT_TYPE_IP4_NEXTHOP - 1] =
|
||||
{
|
||||
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
||||
.obj_type = NMP_OBJECT_TYPE_IP4_NEXTHOP,
|
||||
.sizeof_data = sizeof(NMPObjectIP4NextHop),
|
||||
.sizeof_public = sizeof(NMPlatformIP4NextHop),
|
||||
.obj_type_name = "ip4-nexthop",
|
||||
.addr_family = AF_INET,
|
||||
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_nexthop,
|
||||
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_nexthop,
|
||||
.cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip4_nexthop_to_string,
|
||||
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_nexthop_to_string,
|
||||
.cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_nexthop,
|
||||
.cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip4_nexthop,
|
||||
},
|
||||
[NMP_OBJECT_TYPE_IP6_NEXTHOP - 1] =
|
||||
{
|
||||
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
||||
.obj_type = NMP_OBJECT_TYPE_IP6_NEXTHOP,
|
||||
.sizeof_data = sizeof(NMPObjectIP6NextHop),
|
||||
.sizeof_public = sizeof(NMPlatformIP6NextHop),
|
||||
.obj_type_name = "ip6-nexthop",
|
||||
.addr_family = AF_INET6,
|
||||
.cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_nexthop,
|
||||
.cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_nexthop,
|
||||
.cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_nexthop_to_string,
|
||||
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_nexthop_to_string,
|
||||
.cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_nexthop,
|
||||
.cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip6_nexthop,
|
||||
},
|
||||
[NMP_OBJECT_TYPE_ROUTING_RULE - 1] =
|
||||
{
|
||||
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
|
||||
|
|
|
|||
|
|
@ -352,6 +352,14 @@ typedef struct {
|
|||
NMPlatformIP6Route _public;
|
||||
} NMPObjectIP6Route;
|
||||
|
||||
typedef struct {
|
||||
NMPlatformIP4NextHop _public;
|
||||
} NMPObjectIP4NextHop;
|
||||
|
||||
typedef struct {
|
||||
NMPlatformIP6NextHop _public;
|
||||
} NMPObjectIP6NextHop;
|
||||
|
||||
typedef struct {
|
||||
NMPlatformRoutingRule _public;
|
||||
} NMPObjectRoutingRule;
|
||||
|
|
@ -452,6 +460,13 @@ struct _NMPObject {
|
|||
NMPObjectIP4Route _ip4_route;
|
||||
NMPObjectIP6Route _ip6_route;
|
||||
|
||||
NMPlatformIPNextHop ip_nexthop;
|
||||
NMPlatformIPXNextHop ipx_nexthop;
|
||||
NMPlatformIP4NextHop ip4_nexthop;
|
||||
NMPlatformIP6NextHop ip6_nexthop;
|
||||
NMPObjectIP4NextHop _ip4_nexthop;
|
||||
NMPObjectIP6NextHop _ip6_nexthop;
|
||||
|
||||
NMPlatformRoutingRule routing_rule;
|
||||
NMPObjectRoutingRule _routing_rule;
|
||||
|
||||
|
|
@ -543,6 +558,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
|
|||
case NMP_OBJECT_TYPE_IP6_ADDRESS:
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP4_NEXTHOP:
|
||||
case NMP_OBJECT_TYPE_IP6_NEXTHOP:
|
||||
|
||||
case NMP_OBJECT_TYPE_QDISC:
|
||||
|
||||
|
|
@ -628,6 +645,12 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
|
|||
_NMP_OBJECT_CAST(obj, ipx_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)
|
||||
#define NMP_OBJECT_CAST_IP4_ROUTE(obj) _NMP_OBJECT_CAST(obj, ip4_route, NMP_OBJECT_TYPE_IP4_ROUTE)
|
||||
#define NMP_OBJECT_CAST_IP6_ROUTE(obj) _NMP_OBJECT_CAST(obj, ip6_route, NMP_OBJECT_TYPE_IP6_ROUTE)
|
||||
#define NMP_OBJECT_CAST_IP4_NEXTHOP(obj) \
|
||||
_NMP_OBJECT_CAST(obj, ip4_nexthop, NMP_OBJECT_TYPE_IP4_NEXTHOP)
|
||||
#define NMP_OBJECT_CAST_IP6_NEXTHOP(obj) \
|
||||
_NMP_OBJECT_CAST(obj, ip6_nexthop, NMP_OBJECT_TYPE_IP6_NEXTHOP)
|
||||
#define NMP_OBJECT_CAST_IP_NEXTHOP(obj) \
|
||||
_NMP_OBJECT_CAST(obj, ip_nexthop, NMP_OBJECT_TYPE_IP4_NEXTHOP, NMP_OBJECT_TYPE_IP6_NEXTHOP)
|
||||
#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \
|
||||
_NMP_OBJECT_CAST(obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE)
|
||||
#define NMP_OBJECT_CAST_QDISC(obj) _NMP_OBJECT_CAST(obj, qdisc, NMP_OBJECT_TYPE_QDISC)
|
||||
|
|
@ -767,6 +790,9 @@ const NMPObject *nmp_object_stackinit_id_ip4_address(NMPObject *obj,
|
|||
const NMPObject *
|
||||
nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address);
|
||||
|
||||
const NMPObject *nmp_object_stackinit_id_ip4_nexthop(NMPObject *obj, guint32 id);
|
||||
const NMPObject *nmp_object_stackinit_id_ip6_nexthop(NMPObject *obj, guint32 id);
|
||||
|
||||
const char *nmp_object_to_string(const NMPObject *obj,
|
||||
NMPObjectToStringMode to_string_mode,
|
||||
char *buf,
|
||||
|
|
|
|||
|
|
@ -170,6 +170,106 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a,
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
nm_platform_ip4_nexthop_hash_update(const NMPlatformIP4NextHop *obj,
|
||||
NMPlatformIPNextHopCmpType cmp_type,
|
||||
NMHashState *h)
|
||||
{
|
||||
switch (cmp_type) {
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID:
|
||||
nm_hash_update_vals(h, obj->id);
|
||||
break;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY:
|
||||
nm_hash_update_vals(h,
|
||||
obj->id,
|
||||
obj->ifindex,
|
||||
nmp_utils_ip_config_source_round_trip_rtprot(obj->nh_source),
|
||||
obj->gateway);
|
||||
break;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL:
|
||||
nm_hash_update_vals(h, obj->id, obj->ifindex, obj->nh_source, obj->gateway);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nm_platform_ip4_nexthop_cmp(const NMPlatformIP4NextHop *a,
|
||||
const NMPlatformIP4NextHop *b,
|
||||
NMPlatformIPNextHopCmpType cmp_type)
|
||||
{
|
||||
NM_CMP_SELF(a, b);
|
||||
|
||||
NM_CMP_FIELD(a, b, id);
|
||||
|
||||
switch (cmp_type) {
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID:
|
||||
return 0;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY:
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->nh_source),
|
||||
nmp_utils_ip_config_source_round_trip_rtprot(b->nh_source));
|
||||
NM_CMP_FIELD(a, b, gateway);
|
||||
return 0;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL:
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD(a, b, nh_source);
|
||||
NM_CMP_FIELD(a, b, gateway);
|
||||
return 0;
|
||||
}
|
||||
return nm_assert_unreachable_val(0);
|
||||
}
|
||||
|
||||
void
|
||||
nm_platform_ip6_nexthop_hash_update(const NMPlatformIP6NextHop *obj,
|
||||
NMPlatformIPNextHopCmpType cmp_type,
|
||||
NMHashState *h)
|
||||
{
|
||||
switch (cmp_type) {
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID:
|
||||
nm_hash_update_vals(h, obj->id);
|
||||
break;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY:
|
||||
nm_hash_update_vals(h,
|
||||
obj->id,
|
||||
obj->ifindex,
|
||||
nmp_utils_ip_config_source_round_trip_rtprot(obj->nh_source),
|
||||
obj->gateway);
|
||||
break;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL:
|
||||
nm_hash_update_vals(h, obj->id, obj->ifindex, obj->nh_source, obj->gateway);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nm_platform_ip6_nexthop_cmp(const NMPlatformIP6NextHop *a,
|
||||
const NMPlatformIP6NextHop *b,
|
||||
NMPlatformIPNextHopCmpType cmp_type)
|
||||
{
|
||||
NM_CMP_SELF(a, b);
|
||||
|
||||
NM_CMP_FIELD(a, b, id);
|
||||
|
||||
switch (cmp_type) {
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID:
|
||||
return 0;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY:
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->nh_source),
|
||||
nmp_utils_ip_config_source_round_trip_rtprot(b->nh_source));
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
return 0;
|
||||
case NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL:
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD(a, b, nh_source);
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
return 0;
|
||||
}
|
||||
return nm_assert_unreachable_val(0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -141,6 +141,34 @@ typedef union {
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
__NMPlatformObjWithIfindex_COMMON;
|
||||
guint32 id;
|
||||
NMIPConfigSource nh_source;
|
||||
} _nm_alignas(NMPlatformObject) NMPlatformIPNextHop;
|
||||
|
||||
struct _NMPlatformIP4NextHop {
|
||||
__NMPlatformObjWithIfindex_COMMON;
|
||||
guint32 id;
|
||||
NMIPConfigSource nh_source;
|
||||
in_addr_t gateway;
|
||||
} _nm_alignas(NMPlatformObject);
|
||||
|
||||
struct _NMPlatformIP6NextHop {
|
||||
__NMPlatformObjWithIfindex_COMMON;
|
||||
guint32 id;
|
||||
NMIPConfigSource nh_source;
|
||||
struct in6_addr gateway;
|
||||
} _nm_alignas(NMPlatformObject);
|
||||
|
||||
typedef union {
|
||||
NMPlatformIPNextHop nhx;
|
||||
NMPlatformIP4NextHop nh4;
|
||||
NMPlatformIP6NextHop nh6;
|
||||
} NMPlatformIPXNextHop;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID,
|
||||
|
||||
|
|
@ -162,6 +190,14 @@ typedef enum {
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_ID,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_SEMANTICALLY,
|
||||
NM_PLATFORM_IP_NEXTHOP_CMP_TYPE_FULL,
|
||||
} NMPlatformIPNextHopCmpType;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
bool is_ip4;
|
||||
NMPObjectType obj_type;
|
||||
|
|
@ -299,6 +335,22 @@ gboolean nm_platform_ip_address_match(int addr_family,
|
|||
const NMPlatformIPAddress *addr,
|
||||
NMPlatformMatchFlags match_flag);
|
||||
|
||||
void nm_platform_ip4_nexthop_hash_update(const NMPlatformIP4NextHop *obj,
|
||||
NMPlatformIPNextHopCmpType cmp_type,
|
||||
NMHashState *h);
|
||||
|
||||
int nm_platform_ip4_nexthop_cmp(const NMPlatformIP4NextHop *a,
|
||||
const NMPlatformIP4NextHop *b,
|
||||
NMPlatformIPNextHopCmpType cmp_type);
|
||||
|
||||
void nm_platform_ip6_nexthop_hash_update(const NMPlatformIP6NextHop *obj,
|
||||
NMPlatformIPNextHopCmpType cmp_type,
|
||||
NMHashState *h);
|
||||
|
||||
int nm_platform_ip6_nexthop_cmp(const NMPlatformIP6NextHop *a,
|
||||
const NMPlatformIP6NextHop *b,
|
||||
NMPlatformIPNextHopCmpType cmp_type);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif /* __NMP_PLOBJ_H__ */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue