diff options
| author | Michael Biebl <biebl@debian.org> | 2022-01-13 22:30:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2022-01-13 22:30:39 +0100 |
| commit | 88c227d90a6b7b388c5c85d72802a0ca8f05ed5c (patch) | |
| tree | 71f32df6617802270e8a78574bd8e1637dc532f4 /src/core/ndisc | |
| parent | e74c568b07b50b97873fb4ee1d776dedefbd54d6 (diff) | |
New upstream version 1.34.0 upstream/1.34.0
Diffstat (limited to 'src/core/ndisc')
| -rw-r--r-- | src/core/ndisc/nm-lndp-ndisc.c | 83 | ||||
| -rw-r--r-- | src/core/ndisc/nm-lndp-ndisc.h | 7 | ||||
| -rw-r--r-- | src/core/ndisc/nm-ndisc.c | 238 | ||||
| -rw-r--r-- | src/core/ndisc/nm-ndisc.h | 15 | ||||
| -rw-r--r-- | src/core/ndisc/tests/test-ndisc-linux.c | 12 |
5 files changed, 213 insertions, 142 deletions
diff --git a/src/core/ndisc/nm-lndp-ndisc.c b/src/core/ndisc/nm-lndp-ndisc.c index f11178f8..552b0e4b 100644 --- a/src/core/ndisc/nm-lndp-ndisc.c +++ b/src/core/ndisc/nm-lndp-ndisc.c @@ -85,14 +85,14 @@ send_rs(NMNDisc *ndisc, GError **error) static NMIcmpv6RouterPref _route_preference_coerce(enum ndp_route_preference pref) { -#define _ASSERT_ENUM(v1, v2) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT((NMIcmpv6RouterPref) (v1) == (v2)); \ - G_STATIC_ASSERT((enum ndp_route_preference) (v2) == (v1)); \ - G_STATIC_ASSERT((gint64) (v1) == (v2)); \ - G_STATIC_ASSERT((gint64) (v2) == (v1)); \ - } \ +#define _ASSERT_ENUM(v1, v2) \ + G_STMT_START \ + { \ + G_STATIC_ASSERT((NMIcmpv6RouterPref) (v1) == (v2)); \ + G_STATIC_ASSERT((enum ndp_route_preference)(v2) == (v1)); \ + G_STATIC_ASSERT((gint64) (v1) == (v2)); \ + G_STATIC_ASSERT((gint64) (v2) == (v1)); \ + } \ G_STMT_END switch (pref) { @@ -599,9 +599,7 @@ start(NMNDisc *ndisc) fd = ndp_get_eventfd(priv->ndp); - priv->event_source = - nm_g_unix_fd_source_new(fd, G_IO_IN, G_PRIORITY_DEFAULT, event_ready, ndisc, NULL); - g_source_attach(priv->event_source, NULL); + priv->event_source = nm_g_unix_fd_add_source(fd, G_IO_IN, event_ready, ndisc); /* Flush any pending messages to avoid using obsolete information */ event_ready(fd, 0, ndisc); @@ -666,69 +664,6 @@ stop(NMNDisc *ndisc) /*****************************************************************************/ -static int -ipv6_sysctl_get(NMPlatform *platform, - const char *ifname, - const char *property, - int min, - int max, - int defval) -{ - return nm_platform_sysctl_ip_conf_get_int_checked(platform, - AF_INET6, - ifname, - property, - 10, - min, - max, - defval); -} - -void -nm_lndp_ndisc_get_sysctl(NMPlatform *platform, - const char *ifname, - int * out_max_addresses, - int * out_router_solicitations, - int * out_router_solicitation_interval, - guint32 * out_default_ra_timeout) -{ - int router_solicitation_interval = 0; - int router_solicitations = 0; - - if (out_max_addresses) { - *out_max_addresses = ipv6_sysctl_get(platform, - ifname, - "max_addresses", - 0, - G_MAXINT32, - NM_NDISC_MAX_ADDRESSES_DEFAULT); - } - if (out_router_solicitations || out_default_ra_timeout) { - router_solicitations = ipv6_sysctl_get(platform, - ifname, - "router_solicitations", - 1, - G_MAXINT32, - NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT); - NM_SET_OUT(out_router_solicitations, router_solicitations); - } - if (out_router_solicitation_interval || out_default_ra_timeout) { - router_solicitation_interval = ipv6_sysctl_get(platform, - ifname, - "router_solicitation_interval", - 1, - G_MAXINT32, - NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL); - NM_SET_OUT(out_router_solicitation_interval, router_solicitation_interval); - } - if (out_default_ra_timeout) { - *out_default_ra_timeout = - NM_MAX((((gint64) router_solicitations) * router_solicitation_interval) + 1, 30); - } -} - -/*****************************************************************************/ - static void nm_lndp_ndisc_init(NMLndpNDisc *lndp_ndisc) {} diff --git a/src/core/ndisc/nm-lndp-ndisc.h b/src/core/ndisc/nm-lndp-ndisc.h index 96fc9737..151f4bf7 100644 --- a/src/core/ndisc/nm-lndp-ndisc.h +++ b/src/core/ndisc/nm-lndp-ndisc.h @@ -36,11 +36,4 @@ NMNDisc *nm_lndp_ndisc_new(NMPlatform * platform, guint32 ra_timeout, GError ** error); -void nm_lndp_ndisc_get_sysctl(NMPlatform *platform, - const char *ifname, - int * out_max_addresses, - int * out_router_solicitations, - int * out_router_solicitation_interval, - guint32 * out_default_ra_timeout); - #endif /* __NETWORKMANAGER_LNDP_NDISC_H__ */ diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c index cf1b58ec..d7b1a2e1 100644 --- a/src/core/ndisc/nm-ndisc.c +++ b/src/core/ndisc/nm-ndisc.c @@ -23,6 +23,9 @@ #define RFC7559_IRT ((gint32) 4) /* RFC7559, Initial Retransmission Time, in seconds */ #define RFC7559_MRT ((gint32) 3600) /* RFC7559, Maximum Retransmission Time, in seconds */ +#define NM_NDISC_PRE_EXPIRY_TIME_MSEC 60000 +#define NM_NDISC_PRE_EXPIRY_MIN_LIFETIME_MSEC 120000 + #define _SIZE_MAX_GATEWAYS 100u #define _SIZE_MAX_ADDRESSES 100u #define _SIZE_MAX_ROUTES 1000u @@ -44,6 +47,7 @@ struct _NMNDiscPrivate { gint32 last_ra; gint32 solicit_retransmit_time_msec; + gint64 last_rs_msec; GSource *solicit_timer_source; @@ -96,6 +100,16 @@ static gboolean timeout_expire_cb(gpointer user_data); /*****************************************************************************/ +NM_UTILS_LOOKUP_STR_DEFINE(nm_ndisc_dhcp_level_to_string, + NMNDiscDHCPLevel, + NM_UTILS_LOOKUP_DEFAULT("INVALID"), + NM_UTILS_LOOKUP_STR_ITEM(NM_NDISC_DHCP_LEVEL_UNKNOWN, "unknown"), + NM_UTILS_LOOKUP_STR_ITEM(NM_NDISC_DHCP_LEVEL_NONE, "none"), + NM_UTILS_LOOKUP_STR_ITEM(NM_NDISC_DHCP_LEVEL_OTHERCONF, "otherconf"), + NM_UTILS_LOOKUP_STR_ITEM(NM_NDISC_DHCP_LEVEL_MANAGED, "managed"), ); + +/*****************************************************************************/ + NML3ConfigData * nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, int ifindex, @@ -112,9 +126,7 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, guint i; const gint32 now_sec = nm_utils_get_monotonic_timestamp_sec(); - l3cd = nm_l3_config_data_new(multi_idx, ifindex); - - nm_l3_config_data_set_source(l3cd, NM_IP_CONFIG_SOURCE_NDISC); + l3cd = nm_l3_config_data_new(multi_idx, ifindex, NM_IP_CONFIG_SOURCE_NDISC); nm_l3_config_data_set_ip6_privacy(l3cd, ip6_privacy); @@ -468,12 +480,12 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr) priv = NM_NDISC_GET_PRIVATE(ndisc); if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { - if (!nm_utils_ipv6_addr_set_stable_privacy(priv->stable_type, - &addr->address, - priv->ifname, - priv->network_id, - addr->dad_counter++, - &error)) { + if (!nm_utils_ipv6_addr_set_stable_privacy_may_fail(priv->stable_type, + &addr->address, + priv->ifname, + priv->network_id, + addr->dad_counter++, + &error)) { _LOGW("complete-address: failed to generate an stable-privacy address: %s", error->message); g_clear_error(&error); @@ -490,7 +502,7 @@ complete_address(NMNDisc *ndisc, NMNDiscAddress *addr) if (addr->address.s6_addr32[2] == 0x0 && addr->address.s6_addr32[3] == 0x0) { _LOGD("complete-address: adding an EUI-64 address"); - nm_utils_ipv6_addr_set_interface_identifier(&addr->address, priv->iid); + nm_utils_ipv6_addr_set_interface_identifier(&addr->address, &priv->iid); return TRUE; } @@ -832,6 +844,8 @@ solicit_timer_cb(gpointer user_data) else { _LOGT("solicit: router solicitation sent"); nm_clear_g_free(&priv->last_error); + + priv->last_rs_msec = nm_utils_get_monotonic_timestamp_msec(); } /* https://tools.ietf.org/html/rfc4861#section-6.3.7 describes how to send solicitations: @@ -1213,21 +1227,6 @@ config_map_to_string(NMNDiscConfigMap map, char *p) *p = '\0'; } -static const char * -dhcp_level_to_string(NMNDiscDHCPLevel dhcp_level) -{ - switch (dhcp_level) { - case NM_NDISC_DHCP_LEVEL_NONE: - return "none"; - case NM_NDISC_DHCP_LEVEL_OTHERCONF: - return "otherconf"; - case NM_NDISC_DHCP_LEVEL_MANAGED: - return "managed"; - default: - return "INVALID"; - } -} - static void _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed) { @@ -1250,7 +1249,7 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed) config_map_to_string(changed, changedstr); _LOGD("neighbor discovery configuration changed [%s]:", changedstr); - _LOGD(" dhcp-level %s", dhcp_level_to_string(priv->rdata.public.dhcp_level)); + _LOGD(" dhcp-level %s", nm_ndisc_dhcp_level_to_string(priv->rdata.public.dhcp_level)); if (rdata->public.hop_limit) _LOGD(" hop limit : %d", rdata->public.hop_limit); @@ -1508,33 +1507,6 @@ check_timestamps(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed) ndisc); } - /* When we receive an RA, we don't disable solicitations entirely. Instead, - * we set the interval the maximum (RFC7559_MRT). - * - * This contradicts https://tools.ietf.org/html/rfc7559#section-2.1, which says - * that we SHOULD stop sending RS if we receive an RA -- but only on a multicast - * capable link and if the RA has a valid router lifetime. - * - * But we really want to recover from a dead router on the network, so we - * don't want to cease sending RS entirely. - * - * But we only re-schedule the timer if the current interval is not already - * "RFC7559_MRT * 1000". Otherwise, we already have a slow interval counter - * pending. */ - if (priv->solicit_retransmit_time_msec != RFC7559_MRT * 1000) { - gint32 timeout_msec; - - priv->solicit_retransmit_time_msec = RFC7559_MRT * 1000; - timeout_msec = solicit_retransmit_time_jitter(priv->solicit_retransmit_time_msec); - - _LOGD("solicit: schedule sending next (slow) solicitation in about %.3f seconds", - ((double) timeout_msec) / 1000); - - nm_clear_g_source_inst(&priv->solicit_timer_source); - priv->solicit_timer_source = - nm_g_timeout_add_source_approx(timeout_msec, 0, solicit_timer_cb, ndisc); - } - if (changed != NM_NDISC_CONFIG_NONE) nm_ndisc_emit_config_change(ndisc, changed); } @@ -1546,14 +1518,111 @@ timeout_expire_cb(gpointer user_data) return G_SOURCE_CONTINUE; } +/* Calculate the earliest time where some part of the advertised data is about + * to expire. + * + * Entities are considered about to expire NM_NDISC_PRE_EXPIRY_TIME_MSEC before + * their expiration time. + * + * However, data which has a lifetime (as calculated from the time the last + * RS has been sent) shorter than NM_NDISC_PRE_EXPIRY_MIN_LIFETIME_MSEC, is + * ignored. This is because when we send out RSs because some data is about + * to expire, and the received RAs neither extend the lifetime nor remove + * the offending data, the data would be considered about to expire again, + * triggering more RS in an endless loop until it expired for good. + */ + +static void +_calc_pre_expiry_rs_msec_worker(gint64 *earliest_expiry_msec, + gint64 last_rs_msec, + gint64 expiry_msec) +{ + if (expiry_msec == NM_NDISC_EXPIRY_INFINITY) + return; + + if (expiry_msec < last_rs_msec + NM_NDISC_PRE_EXPIRY_MIN_LIFETIME_MSEC) + return; + + *earliest_expiry_msec = NM_MIN(*earliest_expiry_msec, expiry_msec); +} + +static gint64 +calc_pre_expiry_rs_msec(NMNDisc *ndisc) +{ + NMNDiscPrivate * priv = NM_NDISC_GET_PRIVATE(ndisc); + NMNDiscDataInternal *rdata = &priv->rdata; + gint64 expiry_msec = NM_NDISC_EXPIRY_INFINITY; + guint i; + + for (i = 0; i < rdata->gateways->len; i++) { + _calc_pre_expiry_rs_msec_worker( + &expiry_msec, + priv->last_rs_msec, + g_array_index(rdata->gateways, NMNDiscGateway, i).expiry_msec); + } + + for (i = 0; i < rdata->addresses->len; i++) { + _calc_pre_expiry_rs_msec_worker( + &expiry_msec, + priv->last_rs_msec, + g_array_index(rdata->addresses, NMNDiscAddress, 0).expiry_msec); + } + + for (i = 0; i < rdata->routes->len; i++) { + _calc_pre_expiry_rs_msec_worker(&expiry_msec, + priv->last_rs_msec, + g_array_index(rdata->routes, NMNDiscRoute, 0).expiry_msec); + } + + for (i = 0; i < rdata->dns_servers->len; i++) { + _calc_pre_expiry_rs_msec_worker( + &expiry_msec, + priv->last_rs_msec, + g_array_index(rdata->dns_servers, NMNDiscDNSServer, 0).expiry_msec); + } + + for (i = 0; i < rdata->dns_domains->len; i++) { + _calc_pre_expiry_rs_msec_worker( + &expiry_msec, + priv->last_rs_msec, + g_array_index(rdata->dns_domains, NMNDiscDNSDomain, 0).expiry_msec); + } + + return expiry_msec - solicit_retransmit_time_jitter(NM_NDISC_PRE_EXPIRY_TIME_MSEC); +} + void nm_ndisc_ra_received(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed) { NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE(ndisc); + gint64 pre_expiry_msec; + gint32 timeout_msec; nm_clear_g_source_inst(&priv->ra_timeout_source); nm_clear_g_free(&priv->last_error); check_timestamps(ndisc, now_msec, changed); + + /* When we receive an RA, we don't disable solicitations. + * + * This contradicts https://tools.ietf.org/html/rfc7559#section-2.1, which + * says that we SHOULD stop sending RS if we receive an RA -- but only on + * a multicast capable link and if the RA has a valid router lifetime. + * + * But there are routers out in the wild that won't send unsolicited RAs. + * So we begin sending out RS again when entities are about to expire. + */ + pre_expiry_msec = NM_CLAMP(calc_pre_expiry_rs_msec(ndisc), + priv->last_rs_msec + RFC7559_IRT * 1000, + priv->last_rs_msec + RFC7559_MRT * 1000); + timeout_msec = NM_CLAMP(pre_expiry_msec - now_msec, (gint64) 0, (gint64) G_MAXINT32); + + _LOGD("solicit: schedule sending next (slow) solicitation in about %.3f seconds", + ((double) timeout_msec) / 1000); + + priv->solicit_retransmit_time_msec = 0; + nm_clear_g_source_inst(&priv->solicit_timer_source); + priv->solicit_timer_source = + nm_g_timeout_add_source_approx(timeout_msec, 0, solicit_timer_cb, ndisc); } void @@ -1567,6 +1636,69 @@ nm_ndisc_rs_received(NMNDisc *ndisc) /*****************************************************************************/ +static int +ipv6_sysctl_get(NMPlatform *platform, + const char *ifname, + const char *property, + int min, + int max, + int defval) +{ + return nm_platform_sysctl_ip_conf_get_int_checked(platform, + AF_INET6, + ifname, + property, + 10, + min, + max, + defval); +} + +void +nm_ndisc_get_sysctl(NMPlatform *platform, + const char *ifname, + int * out_max_addresses, + int * out_router_solicitations, + int * out_router_solicitation_interval, + guint32 * out_default_ra_timeout) +{ + int router_solicitation_interval = 0; + int router_solicitations = 0; + + if (out_max_addresses) { + *out_max_addresses = ipv6_sysctl_get(platform, + ifname, + "max_addresses", + 0, + G_MAXINT32, + NM_NDISC_MAX_ADDRESSES_DEFAULT); + } + if (out_router_solicitations || out_default_ra_timeout) { + router_solicitations = ipv6_sysctl_get(platform, + ifname, + "router_solicitations", + 1, + G_MAXINT32, + NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT); + NM_SET_OUT(out_router_solicitations, router_solicitations); + } + if (out_router_solicitation_interval || out_default_ra_timeout) { + router_solicitation_interval = ipv6_sysctl_get(platform, + ifname, + "router_solicitation_interval", + 1, + G_MAXINT32, + NM_NDISC_RFC4861_RTR_SOLICITATION_INTERVAL); + NM_SET_OUT(out_router_solicitation_interval, router_solicitation_interval); + } + if (out_default_ra_timeout) { + *out_default_ra_timeout = + NM_MAX((((gint64) router_solicitations) * router_solicitation_interval) + 1, 30); + } +} + +/*****************************************************************************/ + static void dns_domain_free(gpointer data) { diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h index 15f95d20..5b827528 100644 --- a/src/core/ndisc/nm-ndisc.h +++ b/src/core/ndisc/nm-ndisc.h @@ -48,6 +48,8 @@ typedef enum { NM_NDISC_DHCP_LEVEL_MANAGED } NMNDiscDHCPLevel; +const char *nm_ndisc_dhcp_level_to_string(NMNDiscDHCPLevel level); + #define NM_NDISC_INFINITY_U32 ((uint32_t) -1) /* It's important that this is G_MAXINT64, so that we can meaningfully do @@ -239,7 +241,7 @@ static inline gboolean nm_ndisc_dad_addr_is_fail_candidate_event(NMPlatformSignalChangeType change_type, const NMPlatformIP6Address *addr) { - return !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TEMPORARY) + return !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_SECONDARY) && ((change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED) || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE)); @@ -253,7 +255,7 @@ nm_ndisc_dad_addr_is_fail_candidate(NMPlatform *platform, const NMPObject *obj) addr = NMP_OBJECT_CAST_IP6_ADDRESS( nm_platform_lookup_obj(platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj)); if (addr - && (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TEMPORARY) + && (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_SECONDARY) || !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_DADFAILED))) { /* the address still/again exists and is not in DADFAILED state. Skip it. */ return FALSE; @@ -264,6 +266,15 @@ nm_ndisc_dad_addr_is_fail_candidate(NMPlatform *platform, const NMPObject *obj) /*****************************************************************************/ +void nm_ndisc_get_sysctl(NMPlatform *platform, + const char *ifname, + int * out_max_addresses, + int * out_router_solicitations, + int * out_router_solicitation_interval, + guint32 * out_default_ra_timeout); + +/*****************************************************************************/ + struct _NML3ConfigData; struct _NML3ConfigData *nm_ndisc_data_to_l3cd(NMDedupMultiIndex * multi_idx, diff --git a/src/core/ndisc/tests/test-ndisc-linux.c b/src/core/ndisc/tests/test-ndisc-linux.c index c84ee142..57021606 100644 --- a/src/core/ndisc/tests/test-ndisc-linux.c +++ b/src/core/ndisc/tests/test-ndisc-linux.c @@ -49,12 +49,12 @@ main(int argc, char **argv) return EXIT_FAILURE; } - nm_lndp_ndisc_get_sysctl(NM_PLATFORM_GET, - ifname, - &max_addresses, - &router_solicitations, - &router_solicitation_interval, - &ra_timeout); + nm_ndisc_get_sysctl(NM_PLATFORM_GET, + ifname, + &max_addresses, + &router_solicitations, + &router_solicitation_interval, + &ra_timeout); ndisc = nm_lndp_ndisc_new(NM_PLATFORM_GET, ifindex, |