diff options
Diffstat (limited to 'src/libnm-platform')
22 files changed, 3191 insertions, 1851 deletions
diff --git a/src/libnm-platform/meson.build b/src/libnm-platform/meson.build index e273046f..696ca1a6 100644 --- a/src/libnm-platform/meson.build +++ b/src/libnm-platform/meson.build @@ -7,9 +7,11 @@ libnm_platform = static_library( 'nm-netlink.c', 'nm-platform-utils.c', 'nm-platform.c', + 'nmp-base.c', 'nmp-global-tracker.c', 'nmp-netns.c', 'nmp-object.c', + 'nmp-plobj.c', 'wifi/nm-wifi-utils-nl80211.c', 'wifi/nm-wifi-utils.c', 'wpan/nm-wpan-utils.c', diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index b798d12d..d4ab36f9 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -222,6 +222,16 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +#define IFLA_VTI_UNSPEC 0 +#define IFLA_VTI_LINK 1 +#define IFLA_VTI_IKEY 2 +#define IFLA_VTI_OKEY 3 +#define IFLA_VTI_LOCAL 4 +#define IFLA_VTI_REMOTE 5 +#define IFLA_VTI_FWMARK 6 + +/*****************************************************************************/ + #define WG_CMD_GET_DEVICE 0 #define WG_CMD_SET_DEVICE 1 @@ -580,25 +590,29 @@ NM_LINUX_PLATFORM_FROM_PRIVATE(NMLinuxPlatformPrivate *priv) #define _NMLOG2(level, ...) _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) #define _NMLOG2_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) -#define _LOG_print(__level, __domain, __errsv, self, ...) \ - G_STMT_START \ - { \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - NMPlatform *const __self = (self); \ - \ - if (__self && nm_platform_get_log_with_ptr(__self)) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log(__level, \ - __domain, \ - __errsv, \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ +#define _LOG_print(__level, __domain, __errsv, self, ...) \ + G_STMT_START \ + { \ + char __prefix[64]; \ + const char *__p_prefix = _NMLOG_PREFIX_NAME; \ + NMPlatform *const __self = (self); \ + \ + if (__self && nm_platform_get_log_with_ptr(__self)) { \ + g_snprintf(__prefix, \ + sizeof(__prefix), \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR(__self)); \ + __p_prefix = __prefix; \ + } \ + _nm_log(__level, \ + __domain, \ + __errsv, \ + NULL, \ + NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ G_STMT_END #define _LOG(level, domain, self, ...) \ @@ -811,6 +825,8 @@ static const LinkDesc link_descs[] = { [NM_LINK_TYPE_VETH] = {"veth", "veth", NULL}, [NM_LINK_TYPE_VLAN] = {"vlan", "vlan", "vlan"}, [NM_LINK_TYPE_VRF] = {"vrf", "vrf", "vrf"}, + [NM_LINK_TYPE_VTI] = {"vti", "vti", NULL}, + [NM_LINK_TYPE_VTI6] = {"vti6", "vti6", NULL}, [NM_LINK_TYPE_VXLAN] = {"vxlan", "vxlan", "vxlan"}, [NM_LINK_TYPE_WIREGUARD] = {"wireguard", "wireguard", "wireguard"}, @@ -853,6 +869,8 @@ _link_type_from_rtnl_type(const char *name) NM_LINK_TYPE_VETH, /* "veth" */ NM_LINK_TYPE_VLAN, /* "vlan" */ NM_LINK_TYPE_VRF, /* "vrf" */ + NM_LINK_TYPE_VTI, /* "vti" */ + NM_LINK_TYPE_VTI6, /* "vti6" */ NM_LINK_TYPE_VXLAN, /* "vxlan" */ NM_LINK_TYPE_WIMAX, /* "wimax" */ NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ @@ -1158,7 +1176,7 @@ _linktype_read_devtype(int dirfd) end = strpbrk(cont, "\r\n"); if (end) *end++ = '\0'; - if (strncmp(cont, DEVTYPE_PREFIX, NM_STRLEN(DEVTYPE_PREFIX)) == 0) { + if (NM_STR_HAS_PREFIX(cont, DEVTYPE_PREFIX)) { cont += NM_STRLEN(DEVTYPE_PREFIX); memmove(contents, cont, strlen(cont) + 1); return g_steal_pointer(&contents); @@ -1289,14 +1307,36 @@ _linktype_get_type(NMPlatform *platform, } if (arptype == ARPHRD_ETHER) { - /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary - * modem interface, ccmni is MediaTek's. FIXME: these drivers should - * really set devtype=WWAN. - */ - if (g_str_has_prefix(ifname, "rmnet") || g_str_has_prefix(ifname, "rev_rmnet") - || g_str_has_prefix(ifname, "ccmni")) + /* The USB gadget interfaces behave and look like ordinary ethernet devices + * aside from the DEVTYPE. */ + if (nm_streq0(devtype, "gadget")) + return NM_LINK_TYPE_ETHERNET; + + /* Distributed Switch Architecture switch chips */ + if (nm_streq0(devtype, "dsa")) + return NM_LINK_TYPE_ETHERNET; + } + + /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary + * modem interface, ccmni is MediaTek's. FIXME: these drivers should + * really set devtype=WWAN. + * + * Value "530" is the out-of-tree version of ARPHRD_RAWIP before it's + * merged in Linux 4.14. For the mainline version, this has the value + * of "519". + * + * [1] https://github.com/LineageOS/android_kernel_google_msm-4.9/commit/54948008c293fdf48552a5c39c91c09c3eb76ed2 + */ + if (NM_IN_SET(arptype, + ARPHRD_ETHER, + 519 /* ARPHRD_RAWIP */, + 530 /* out-of-tree ARPHRD_RAWIP */)) { + if (NM_STR_HAS_PREFIX(ifname, "rmnet") || NM_STR_HAS_PREFIX(ifname, "rev_rmnet") + || NM_STR_HAS_PREFIX(ifname, "ccmni")) return NM_LINK_TYPE_WWAN_NET; + } + if (arptype == ARPHRD_ETHER) { /* Standard wired ethernet interfaces don't report an rtnl_link_type, so * only allow fallback to Ethernet if no type is given. This should * prevent future virtual network drivers from being treated as Ethernet @@ -1304,15 +1344,6 @@ _linktype_get_type(NMPlatform *platform, */ if (!kind && !devtype) return NM_LINK_TYPE_ETHERNET; - - /* The USB gadget interfaces behave and look like ordinary ethernet devices - * aside from the DEVTYPE. */ - if (nm_streq0(devtype, "gadget")) - return NM_LINK_TYPE_ETHERNET; - - /* Distributed Switch Architecture switch chips */ - if (nm_streq0(devtype, "dsa")) - return NM_LINK_TYPE_ETHERNET; } } @@ -1338,7 +1369,8 @@ typedef struct { }; } ParseNlmsgIter; -#define NLMSG_TAIL(nmsg) ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#define NLMSG_TAIL(nmsg) \ + NM_CAST_ALIGN(struct rtattr, ((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)) /* copied from iproute2's addattr_l(). */ static gboolean @@ -2244,6 +2276,11 @@ _parse_lnk_vlan(const char *kind, struct nlattr *info_data) obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VLAN, NULL); obj->lnk_vlan.id = nla_get_u16(tb[IFLA_VLAN_ID]); + if (tb[IFLA_VLAN_PROTOCOL]) + obj->lnk_vlan.protocol = ntohs(nla_get_u16(tb[IFLA_VLAN_PROTOCOL])); + else + obj->lnk_vlan.protocol = ETH_P_8021Q; + if (tb[IFLA_VLAN_FLAGS]) { struct ifla_vlan_flags flags; @@ -2389,6 +2426,76 @@ _parse_lnk_vxlan(const char *kind, struct nlattr *info_data) } static NMPObject * +_parse_lnk_vti(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_VTI_LINK] = {.type = NLA_U32}, + [IFLA_VTI_LOCAL] = {.type = NLA_U32}, + [IFLA_VTI_REMOTE] = {.type = NLA_U32}, + [IFLA_VTI_IKEY] = {.type = NLA_U32}, + [IFLA_VTI_OKEY] = {.type = NLA_U32}, + [IFLA_VTI_FWMARK] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + NMPObject *obj; + NMPlatformLnkVti *props; + + if (!info_data || !nm_streq0(kind, "vti")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VTI, NULL); + props = &obj->lnk_vti; + + props->parent_ifindex = tb[IFLA_VTI_LINK] ? nla_get_u32(tb[IFLA_VTI_LINK]) : 0; + props->local = tb[IFLA_VTI_LOCAL] ? nla_get_u32(tb[IFLA_VTI_LOCAL]) : 0; + props->remote = tb[IFLA_VTI_REMOTE] ? nla_get_u32(tb[IFLA_VTI_REMOTE]) : 0; + props->ikey = tb[IFLA_VTI_IKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_IKEY])) : 0; + props->okey = tb[IFLA_VTI_OKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_OKEY])) : 0; + props->fwmark = tb[IFLA_VTI_FWMARK] ? nla_get_u32(tb[IFLA_VTI_FWMARK]) : 0; + + return obj; +} + +static NMPObject * +_parse_lnk_vti6(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_VTI_LINK] = {.type = NLA_U32}, + [IFLA_VTI_LOCAL] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_VTI_REMOTE] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_VTI_IKEY] = {.type = NLA_U32}, + [IFLA_VTI_OKEY] = {.type = NLA_U32}, + [IFLA_VTI_FWMARK] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + NMPObject *obj; + NMPlatformLnkVti6 *props; + + if (!info_data || !nm_streq0(kind, "vti6")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VTI6, NULL); + props = &obj->lnk_vti6; + + props->parent_ifindex = tb[IFLA_VTI_LINK] ? nla_get_u32(tb[IFLA_VTI_LINK]) : 0; + if (tb[IFLA_VTI_LOCAL]) + props->local = *nla_data_as(struct in6_addr, tb[IFLA_VTI_LOCAL]); + if (tb[IFLA_VTI_REMOTE]) + props->remote = *nla_data_as(struct in6_addr, tb[IFLA_VTI_REMOTE]); + props->ikey = tb[IFLA_VTI_IKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_IKEY])) : 0; + props->okey = tb[IFLA_VTI_OKEY] ? ntohl(nla_get_u32(tb[IFLA_VTI_OKEY])) : 0; + props->fwmark = tb[IFLA_VTI_FWMARK] ? nla_get_u32(tb[IFLA_VTI_FWMARK]) : 0; + + return obj; +} + +static NMPObject * _parse_lnk_vrf(const char *kind, struct nlattr *info_data) { static const struct nla_policy policy[] = { @@ -2540,16 +2647,15 @@ _wireguard_update_from_peers_nla(CList *peers, GArray **p_allowed_ips, struct nl GArray *allowed_ips = *p_allowed_ips; nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) { + NMPWireGuardAllowedIP *new; + if (!allowed_ips) { allowed_ips = g_array_new(FALSE, FALSE, sizeof(NMPWireGuardAllowedIP)); *p_allowed_ips = allowed_ips; - g_array_set_size(allowed_ips, 1); - } else - g_array_set_size(allowed_ips, allowed_ips->len + 1); + } - if (!_wireguard_update_from_allowed_ips_nla( - &g_array_index(allowed_ips, NMPWireGuardAllowedIP, allowed_ips->len - 1), - attr)) { + new = nm_g_array_append_new(allowed_ips, NMPWireGuardAllowedIP); + if (!_wireguard_update_from_allowed_ips_nla(new, attr)) { /* we ignore the error of parsing one allowed-ip. */ g_array_set_size(allowed_ips, allowed_ips->len - 1); continue; @@ -3379,6 +3485,12 @@ _new_from_nl_link(NMPlatform *platform, case NM_LINK_TYPE_VRF: lnk_data = _parse_lnk_vrf(nl_info_kind, nl_info_data); break; + case NM_LINK_TYPE_VTI: + lnk_data = _parse_lnk_vti(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VTI6: + lnk_data = _parse_lnk_vti6(nl_info_kind, nl_info_data); + break; case NM_LINK_TYPE_VXLAN: lnk_data = _parse_lnk_vxlan(nl_info_kind, nl_info_data); break; @@ -3631,21 +3743,27 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter struct { gboolean found; gboolean has_more; + guint8 weight; int ifindex; NMIPAddr gateway; } nh = { .found = FALSE, .has_more = FALSE, }; - guint32 mss; - guint32 window = 0; - guint32 cwnd = 0; - guint32 initcwnd = 0; - guint32 initrwnd = 0; - guint32 mtu = 0; - guint32 rto_min = 0; - guint32 lock = 0; - gboolean quickack = FALSE; + guint v4_n_nexthops = 0; + NMPlatformIP4RtNextHop v4_nh_extra_nexthops_stack[10]; + gs_free NMPlatformIP4RtNextHop *v4_nh_extra_nexthops_heap = NULL; + NMPlatformIP4RtNextHop *v4_nh_extra_nexthops = v4_nh_extra_nexthops_stack; + guint v4_nh_extra_alloc = G_N_ELEMENTS(v4_nh_extra_nexthops_stack); + guint32 mss; + guint32 window = 0; + guint32 cwnd = 0; + guint32 initcwnd = 0; + guint32 initrwnd = 0; + guint32 mtu = 0; + guint32 rto_min = 0; + guint32 lock = 0; + gboolean quickack = FALSE; nm_assert((parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop > 0) || (!parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop == 0)); @@ -3669,15 +3787,6 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter else return NULL; - if (!NM_IN_SET(rtm->rtm_type, - RTN_UNICAST, - RTN_LOCAL, - RTN_BLACKHOLE, - RTN_UNREACHABLE, - RTN_PROHIBIT, - RTN_THROW)) - return NULL; - if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0) return NULL; @@ -3703,9 +3812,57 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter idx = 0; while (TRUE) { - if (idx == multihop_idx) { + if (nh.found && IS_IPv4) { + NMPlatformIP4RtNextHop *new_nexthop; + + /* we parsed the first IPv4 nexthop in "nh", let's parse the following ones. + * + * At this point, v4_n_nexthops still counts how many hops we already added, + * now we are about to add the (v4_n_nexthops+1) hop. + * + * Note that the first hop (of then v4_n_nexthops) is tracked in "nh". + * v4_nh_extra_nexthops tracks the additional hops. + * + * v4_nh_extra_alloc is how many space is allocated for + * v4_nh_extra_nexthops (note that in the end we will only add (v4_n_nexthops-1) + * hops in this list). */ + nm_assert(v4_n_nexthops > 0u); + if (v4_n_nexthops - 1u >= v4_nh_extra_alloc) { + v4_nh_extra_alloc = NM_MAX(4, v4_nh_extra_alloc * 2u); + if (!v4_nh_extra_nexthops_heap) { + v4_nh_extra_nexthops_heap = + g_new(NMPlatformIP4RtNextHop, v4_nh_extra_alloc); + memcpy(v4_nh_extra_nexthops_heap, + v4_nh_extra_nexthops_stack, + G_N_ELEMENTS(v4_nh_extra_nexthops_stack)); + } else { + v4_nh_extra_nexthops_heap = g_renew(NMPlatformIP4RtNextHop, + v4_nh_extra_nexthops_heap, + v4_nh_extra_alloc); + } + v4_nh_extra_nexthops = v4_nh_extra_nexthops_heap; + } + nm_assert(v4_n_nexthops - 1u < v4_nh_extra_alloc); + new_nexthop = &v4_nh_extra_nexthops[v4_n_nexthops - 1u]; + new_nexthop->ifindex = rtnh->rtnh_ifindex; + new_nexthop->weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u); + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + if (nla_parse_arr(ntb, + (struct nlattr *) RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + NULL) + < 0) + return NULL; + + if (_check_addr_or_return_null(ntb, RTA_GATEWAY, addr_len)) + memcpy(&new_nexthop->gateway, nla_data(ntb[RTA_GATEWAY]), addr_len); + } + } else if (IS_IPv4 || idx == multihop_idx) { nh.found = TRUE; nh.ifindex = rtnh->rtnh_ifindex; + nh.weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct nlattr *ntb[RTA_MAX + 1]; @@ -3722,15 +3879,6 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter } else if (nh.found) { /* we just parsed a nexthop, but there is yet another hop afterwards. */ nm_assert(idx == multihop_idx + 1); - if (IS_IPv4) { - /* for IPv4, multihop routes are currently not supported. - * - * If we ever support them, then the next-hop list is part of the NMPlatformIPRoute, - * that is, for IPv4 we truly have multihop routes. Unlike for IPv6. - * - * For now, just error out. */ - return NULL; - } /* For IPv6 multihop routes, we need to remember to iterate again. * For each next-hop, we will create a distinct single-hop NMPlatformIP6Route. */ @@ -3738,6 +3886,9 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter break; } + if (IS_IPv4) + v4_n_nexthops++; + if (tlen < RTNH_ALIGN(rtnh->rtnh_len) + sizeof(*rtnh)) break; @@ -3771,6 +3922,9 @@ rta_multipath_done: nh.ifindex = ifindex; nh.gateway = gateway; nh.found = TRUE; + nm_assert(v4_n_nexthops == 0); + if (IS_IPv4) + v4_n_nexthops = 1; } else { /* Kernel supports new style nexthop configuration, * verify that it is a duplicate and ignore old-style nexthop. */ @@ -3861,6 +4015,23 @@ rta_multipath_done: obj->ip_route.ifindex = nh.ifindex; + if (IS_IPv4) { + nm_assert((!!nh.found) == (v4_n_nexthops > 0u)); + obj->ip4_route.n_nexthops = v4_n_nexthops; + if (v4_n_nexthops > 1) { + /* We only set the weight for multihop routes. I think that corresponds to what kernel + * does. The weight is mostly undefined for single-hop. */ + obj->ip4_route.weight = NM_MAX(nh.weight, 1u); + + obj->_ip4_route.extra_nexthops = + (v4_nh_extra_alloc == v4_n_nexthops - 1u + && v4_nh_extra_nexthops == v4_nh_extra_nexthops_heap) + ? g_steal_pointer(&v4_nh_extra_nexthops_heap) + : nm_memdup(v4_nh_extra_nexthops, + sizeof(v4_nh_extra_nexthops[0]) * (v4_n_nexthops - 1u)); + } + } + if (_check_addr_or_return_null(tb, RTA_DST, addr_len)) memcpy(obj->ip_route.network_ptr, nla_data(tb[RTA_DST]), addr_len); @@ -4580,11 +4751,13 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo const NMPlatformLnkVlan *props = extra_data; nm_assert(extra_data); + nm_assert(props->protocol != 0); if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) goto nla_put_failure; NLA_PUT_U16(msg, IFLA_VLAN_ID, props->id); + NLA_PUT_U16(msg, IFLA_VLAN_PROTOCOL, htons(props->protocol)); { struct ifla_vlan_flags flags = { @@ -4835,6 +5008,44 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0); break; } + case NM_LINK_TYPE_VTI: + { + const NMPlatformLnkVti *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex > 0) + NLA_PUT_U32(msg, IFLA_VTI_LINK, props->parent_ifindex); + NLA_PUT_U32(msg, IFLA_VTI_LOCAL, props->local); + NLA_PUT_U32(msg, IFLA_VTI_REMOTE, props->remote); + NLA_PUT_U32(msg, IFLA_VTI_IKEY, htonl(props->ikey)); + NLA_PUT_U32(msg, IFLA_VTI_OKEY, htonl(props->okey)); + NLA_PUT_U32(msg, IFLA_VTI_FWMARK, props->fwmark); + break; + } + case NM_LINK_TYPE_VTI6: + { + const NMPlatformLnkVti6 *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex > 0) + NLA_PUT_U32(msg, IFLA_VTI_LINK, props->parent_ifindex); + if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) + NLA_PUT(msg, IFLA_VTI_LOCAL, sizeof(props->local), &props->local); + if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote)) + NLA_PUT(msg, IFLA_VTI_REMOTE, sizeof(props->remote), &props->remote); + NLA_PUT_U32(msg, IFLA_VTI_IKEY, htonl(props->ikey)); + NLA_PUT_U32(msg, IFLA_VTI_OKEY, htonl(props->okey)); + NLA_PUT_U32(msg, IFLA_VTI_FWMARK, props->fwmark); + break; + } default: nm_assert(!extra_data); break; @@ -5088,7 +5299,7 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route) } static gboolean -ip_route_ignored_protocol(const NMPlatformIPRoute *route) +ip_route_is_alive(const NMPlatformIPRoute *route) { guint8 prot; @@ -5100,12 +5311,29 @@ ip_route_ignored_protocol(const NMPlatformIPRoute *route) nm_assert(nmp_utils_ip_config_source_from_rtprot(prot) == route->rt_source); - /* We ignore all routes outside a certain subest of rtm_protocol. NetworkManager - * itself wouldn't configure those, so they are always configured by somebody - * external. We thus ignore them to avoid the overhead that processing them brings. - * For example, the BGP daemon "bird" might configure a huge number of RTPROT_BIRD routes. */ + if (prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA)) { + /* We ignore certain rtm_protocol, because NetworkManager would only ever + * configure certain protocols. Other routes are not configured by NetworkManager + * and we don't track them in the platform cache. + * + * This is to help with the performance overhead of a huge number of + * routes, for example with the bird BGP software, that adds routes + * with RTPROT_BIRD protocol. */ + return FALSE; + } - return prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA); + if (!NM_IN_SET(nm_platform_route_type_uncoerce(route->type_coerced), + RTN_UNICAST, + RTN_LOCAL, + RTN_BLACKHOLE, + RTN_UNREACHABLE, + RTN_PROHIBIT, + RTN_THROW)) { + /* Certain route types are ignored and not placed into the cache. */ + return FALSE; + } + + return TRUE; } /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ @@ -5118,7 +5346,7 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob const guint32 lock = ip_route_get_lock_flag(NMP_OBJECT_CAST_IP_ROUTE(obj)); const guint32 table = nm_platform_route_table_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->table_coerced, TRUE); - const struct rtmsg rtmsg = { + struct rtmsg rtmsg = { .rtm_family = klass->addr_family, .rtm_tos = IS_IPv4 ? obj->ip4_route.tos : 0, .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, @@ -5126,7 +5354,7 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob .rtm_scope = IS_IPv4 ? nm_platform_route_scope_inv(obj->ip4_route.scope_inv) : RT_SCOPE_NOWHERE, .rtm_type = nm_platform_route_type_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced), - .rtm_flags = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)), + .rtm_flags = 0, .rtm_dst_len = obj->ip_route.plen, .rtm_src_len = IS_IPv4 ? 0 : NMP_OBJECT_CAST_IP6_ROUTE(obj)->src_plen, }; @@ -5137,6 +5365,17 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); + if (NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, ((unsigned) (RTNH_F_ONLINK)))) { + if (IS_IPv4 && obj->ip4_route.gateway == 0) { + /* Kernel does not allow setting the onlink flag, if there is no gateway. + * We silently don't configure the flag. + * + * For multi-hop routes, we will set the flag for each next-hop (which + * has a gateway set). */ + } else + rtmsg.rtm_flags |= ((unsigned) RTNH_F_ONLINK); + } + msg = nlmsg_alloc_new(0, nlmsg_type, nlmsg_flags); if (nlmsg_append_struct(msg, &rtmsg) < 0) @@ -5171,6 +5410,54 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); } + if (IS_IPv4 && obj->ip4_route.n_nexthops > 1u) { + struct nlattr *multipath; + guint i; + + if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) + goto nla_put_failure; + + for (i = 0u; i < obj->ip4_route.n_nexthops; i++) { + struct rtnexthop *rtnh; + in_addr_t gw; + + rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); + if (!rtnh) + goto nla_put_failure; + + /* For multihop routes, a valid weight must be in range 1-256 (on netlink's + * "rtnh_hops" this is 0-255). We allow that the caller leaves the value unset + * at zero. */ + if (i == 0u) { + rtnh->rtnh_hops = NM_MAX(obj->ip4_route.weight, 1u) - 1u; + rtnh->rtnh_ifindex = obj->ip4_route.ifindex; + gw = obj->ip4_route.gateway; + } else { + const NMPlatformIP4RtNextHop *n = &obj->_ip4_route.extra_nexthops[i - 1u]; + + rtnh->rtnh_hops = NM_MAX(n->weight, 1u) - 1u; + rtnh->rtnh_ifindex = n->ifindex; + gw = n->gateway; + } + NLA_PUT_U32(msg, RTA_GATEWAY, gw); + + rtnh->rtnh_flags = 0; + + if (obj->ip4_route.n_nexthops > 1 + && NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, (unsigned) (RTNH_F_ONLINK)) && gw != 0) { + /* Unlike kernel, we only track the onlink flag per NMPlatformIP4Address, and + * not per nexthop. That is fine for NetworkManager configuring addresses. + * It is not fine for tracking addresses from kernel in platform cache, + * because the rtnh_flags of the nexthops need to be part of nmp_object_id_cmp(). */ + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } + + rtnh->rtnh_len = (char *) nlmsg_tail(nlmsg_hdr(msg)) - (char *) rtnh; + } + + nla_nest_end(msg, multipath); + } + if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd || obj->ip_route.initrwnd || obj->ip_route.mtu || obj->ip_route.quickack || obj->ip_route.rto_min || lock) { @@ -5462,7 +5749,7 @@ _nl_msg_new_tfilter(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPlatformT if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { const NMPlatformActionSimple *simple = &action->simple; struct tc_defact sel = { - 0, + 0, }; if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) @@ -5476,7 +5763,7 @@ _nl_msg_new_tfilter(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPlatformT } else if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { const NMPlatformActionMirred *mirred = &action->mirred; struct tc_mirred sel = { - 0, + 0, }; if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) @@ -5512,25 +5799,25 @@ nla_put_failure: /*****************************************************************************/ -#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ - G_STMT_START \ - { \ - const char *const _pathid = (pathid); \ - const int _dirfd = (dirfd); \ - const char *const _path = (path); \ - \ - nm_assert(_path &&_path[0]); \ - g_assert(!strstr(_path, "/../")); \ - if (_dirfd < 0) { \ - nm_assert(!_pathid); \ - nm_assert(_path[0] == '/'); \ - nm_assert(g_str_has_prefix(_path, "/proc/sys/") || g_str_has_prefix(_path, "/sys/") \ - || g_str_has_prefix(_path, "/proc/net")); \ - } else { \ - nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/'); \ - nm_assert(_path[0] != '/'); \ - } \ - } \ +#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ + G_STMT_START \ + { \ + const char *const _pathid = (pathid); \ + const int _dirfd = (dirfd); \ + const char *const _path = (path); \ + \ + nm_assert(_path &&_path[0]); \ + g_assert(!strstr(_path, "/../")); \ + if (_dirfd < 0) { \ + nm_assert(!_pathid); \ + nm_assert(_path[0] == '/'); \ + nm_assert(NM_STR_HAS_PREFIX(_path, "/proc/sys/") || NM_STR_HAS_PREFIX(_path, "/sys/") \ + || NM_STR_HAS_PREFIX(_path, "/proc/net")); \ + } else { \ + nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/'); \ + nm_assert(_path[0] != '/'); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -5599,12 +5886,14 @@ sysctl_set_internal(NMPlatform *platform, const char *path, const char *value) { - int fd, tries; + int fd; + int tries; gssize nwrote; gssize len; char *actual; gs_free char *actual_free = NULL; int errsv; + int r; if (dirfd < 0) { pathid = path; @@ -5698,18 +5987,13 @@ sysctl_set_internal(NMPlatform *platform, _LOGE("sysctl: failed to set '%s' to '%s' after three attempts", path, value); } - if (nwrote < len - 1) { - if (nm_close(fd) != 0) { - if (errsv != 0) - errno = errsv; - } else if (errsv != 0) - errno = errsv; - else - errno = EIO; - return FALSE; - } - if (nm_close(fd) != 0) { - /* errno is already properly set. */ + r = nm_close_with_error(fd); + if (r < 0 || nwrote < len - 1) { + if (errsv == 0) { + /* propagate the error from nm_close_with_error(). */ + errsv = (r < 0) ? -r : EIO; + } + errno = errsv; return FALSE; } @@ -6260,11 +6544,11 @@ static NM_UTILS_LOOKUP_STR_DEFINE( NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE(__DELAYED_ACTION_TYPE_MAX), ); -#define delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, idx) \ - (&g_array_index((priv)->delayed_action.list_wait_for_response_x[nmp_netlink_protocol_check( \ - (netlink_protocol))], \ - DelayedActionWaitForNlResponseData, \ - (idx))) +#define delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, idx) \ + (&nm_g_array_index((priv)->delayed_action.list_wait_for_response_x[nmp_netlink_protocol_check( \ + (netlink_protocol))], \ + DelayedActionWaitForNlResponseData, \ + (idx))) static const char * delayed_action_to_string_full(DelayedActionType action_type, @@ -6753,6 +7037,14 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) obj = main_entry->obj; + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + const NMPlatformIP6Address *pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(obj); + + if (pladdr->n_ifa_flags & IFA_F_TENTATIVE) { + nm_platform_ip6_dadfailed_set(platform, pladdr->ifindex, &pladdr->address, TRUE); + } + } + _LOGt("cache-prune: prune %s", nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf))); @@ -7568,6 +7860,7 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) gboolean resync_required = FALSE; gboolean only_dirty = FALSE; gboolean is_ipv6; + gboolean route_is_alive; /* IPv4 routes that are a response to RTM_GETROUTE must have * the cloned flag while IPv6 routes don't have to. */ @@ -7597,24 +7890,13 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) } } - if (ip_route_ignored_protocol(NMP_OBJECT_CAST_IP_ROUTE(obj))) { - /* We ignore certain rtm_protocol, because NetworkManager would only ever - * configure certain protocols. Other routes were not added by NetworkManager - * and we don't need to track them in the platform cache. - * - * This is to help with the performance overhead of a huge number of - * routes, for example with the bird BGP software, that adds routes - * with RTPROT_BIRD protocol. - * - * Even if this is a IPv6 multipath route, we abort (parse_nlmsg_iter). There - * is nothing for us to do. */ - return; - } + route_is_alive = ip_route_is_alive(NMP_OBJECT_CAST_IP_ROUTE(obj)); cache_op = nmp_cache_update_netlink_route(cache, obj, is_dump, msghdr->nlmsg_flags, + route_is_alive, &obj_old, &obj_new, &obj_replace, @@ -7661,11 +7943,21 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) delayed_action_schedule(platform, delayed_action_refresh_from_needle_object(obj), NULL); + /* We are done here. */ + return; } break; } case RTM_DELADDR: + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + const NMPlatformIP6Address *ip6 = NMP_OBJECT_CAST_IP6_ADDRESS(obj); + + if (ip6->n_ifa_flags & IFA_F_DADFAILED) { + nm_platform_ip6_dadfailed_set(platform, ip6->ifindex, &ip6->address, TRUE); + } + } + /* fall-through */ case RTM_DELLINK: case RTM_DELQDISC: case RTM_DELROUTE: @@ -7948,7 +8240,8 @@ retry: } else if (NM_IN_SET(seq_result, -ESRCH, -ENOENT)) { log_detail = ", firmware not found"; result = -NME_PL_NO_FIRMWARE; - } else if (NM_IN_SET(seq_result, -ERANGE) && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { + } else if (NM_IN_SET(seq_result, -ERANGE, -EINVAL) + && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { log_detail = ", setting MTU to requested size is not possible"; result = -NME_PL_CANT_SET_MTU; } else if (NM_IN_SET(seq_result, -ENFILE) && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS @@ -8126,7 +8419,7 @@ static gboolean link_set_token(NMPlatform *platform, int ifindex, const NMUtilsIPv6IfaceId *iid) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; _LOGD("link: change %d: token: set IPv6 address generation token to %s", ifindex, @@ -8202,7 +8495,7 @@ link_set_address(NMPlatform *platform, int ifindex, gconstpointer address, size_ nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const ChangeLinkData d = { .set_address = - { + { .address = address, .length = length, }, @@ -9310,25 +9603,15 @@ ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guin /*****************************************************************************/ static int -ip_route_add(NMPlatform *platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) +ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj; - - nmp_object_stackinit(&obj, - NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), - (const NMPlatformObject *) route); - - nm_platform_ip_route_normalize(addr_family, NMP_OBJECT_CAST_IP_ROUTE(&obj)); - nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, obj_stack); if (!nlmsg) g_return_val_if_reached(-NME_BUG); return do_add_addrroute(platform, - &obj, + obj_stack, nlmsg, NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } @@ -9820,7 +10103,7 @@ continue_reading: goto stop; } - hdr = (struct nlmsghdr *) priv->netlink_recv_buf.buf; + hdr = NM_CAST_ALIGN(struct nlmsghdr, priv->netlink_recv_buf.buf); while (nlmsg_ok(hdr, n)) { WaitForNlResponseResult seq_result; gboolean process_valid_msg = FALSE; @@ -10588,8 +10871,8 @@ constructed(GObject *_object) : (!nmp_netns_get_current() ? "no netns support" : nm_sprintf_bufa(100, - "in netns[%p]%s", - nmp_netns_get_current(), + "in netns[" NM_HASH_OBFUSCATE_PTR_FMT "]%s", + NM_HASH_OBFUSCATE_PTR(nmp_netns_get_current()), nmp_netns_get_current() == nmp_netns_get_initial() ? "/main" : "")), nm_platform_get_use_udev(platform) ? "use" : "no", @@ -10732,7 +11015,10 @@ path_is_read_only_fs(const char *path) } NMPlatform * -nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean cache_tc) +nm_linux_platform_new(NMDedupMultiIndex *multi_idx, + gboolean log_with_ptr, + gboolean netns_support, + gboolean cache_tc) { gboolean use_udev = FALSE; @@ -10740,6 +11026,8 @@ nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean ca use_udev = TRUE; return g_object_new(NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_MULTI_IDX, + multi_idx, NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, NM_PLATFORM_USE_UDEV, diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h index 85e06934..08135a4a 100644 --- a/src/libnm-platform/nm-linux-platform.h +++ b/src/libnm-platform/nm-linux-platform.h @@ -10,7 +10,7 @@ #define NM_TYPE_LINUX_PLATFORM (nm_linux_platform_get_type()) #define NM_LINUX_PLATFORM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform)) #define NM_LINUX_PLATFORM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass)) #define NM_IS_LINUX_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LINUX_PLATFORM)) @@ -23,6 +23,11 @@ typedef struct _NMLinuxPlatformClass NMLinuxPlatformClass; GType nm_linux_platform_get_type(void); -NMPlatform *nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean cache_tc); +struct _NMDedupMultiIndex; + +NMPlatform *nm_linux_platform_new(struct _NMDedupMultiIndex *multi_idx, + gboolean log_with_ptr, + gboolean netns_support, + gboolean cache_tc); #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c index e08eee5e..fc704226 100644 --- a/src/libnm-platform/nm-netlink.c +++ b/src/libnm-platform/nm-netlink.c @@ -325,7 +325,8 @@ nlmsg_parse_error(const struct nlmsghdr *nlh, const char **out_extack_msg) struct nlattr *tb[G_N_ELEMENTS(policy)]; struct nlattr *tlvs; - tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len - NLMSG_HDRLEN); + tlvs = NM_CAST_ALIGN(struct nlattr, + (((char *) e) + sizeof(*e) + e->msg.nlmsg_len - NLMSG_HDRLEN)); if (nla_parse_arr(tb, tlvs, nlh->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, policy) >= 0) { if (tb[NLMSGERR_ATTR_MSG]) *out_extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]); @@ -842,7 +843,7 @@ genlmsg_len(const struct genlmsghdr *gnlh) { const struct nlmsghdr *nlh; - nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); + nlh = NM_CAST_ALIGN(const struct nlmsghdr, (((char *) gnlh) - NLMSG_HDRLEN)); return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); } @@ -1253,7 +1254,7 @@ continue_reading: if (n <= 0) return n; - hdr = (struct nlmsghdr *) buf; + hdr = NM_CAST_ALIGN(struct nlmsghdr, buf); while (nlmsg_ok(hdr, n)) { nm_auto_nlmsg struct nl_msg *msg = NULL; diff --git a/src/libnm-platform/nm-netlink.h b/src/libnm-platform/nm-netlink.h index 634be2b4..cdb8a81c 100644 --- a/src/libnm-platform/nm-netlink.h +++ b/src/libnm-platform/nm-netlink.h @@ -340,7 +340,7 @@ nla_next(const struct nlattr *nla, int *remaining) int totlen = NLA_ALIGN(nla->nla_len); *remaining -= totlen; - return (struct nlattr *) ((char *) nla + totlen); + return NM_CAST_ALIGN(struct nlattr, (((char *) nla) + totlen)); } #define nla_for_each_attr(pos, head, len, rem) \ @@ -434,7 +434,7 @@ nlmsg_next(struct nlmsghdr *nlh, int *remaining) *remaining -= totlen; - return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); + return NM_CAST_ALIGN(struct nlmsghdr, (((char *) nlh) + totlen)); } int nlmsg_get_proto(struct nl_msg *msg); @@ -493,8 +493,9 @@ nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) static inline struct nlattr * nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) { - unsigned char *data = nlmsg_data(nlh); - return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); + char *data = nlmsg_data(nlh); + + return NM_CAST_ALIGN(struct nlattr, (data + NLMSG_ALIGN(hdrlen))); } static inline struct nlattr * diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 198e5f0a..c80d9648 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -115,28 +115,32 @@ nmp_link_address_get_as_bytes(const NMPLinkAddress *addr) #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" -#define NMLOG_COMMON(level, name, ...) \ - G_STMT_START \ - { \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - const NMPlatform *const __self = (self); \ - const char *__name = name; \ - \ - if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log((level), \ - _NMLOG_DOMAIN, \ - 0, \ - __name, \ - NULL, \ - "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix, \ - NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ +#define NMLOG_COMMON(level, name, ...) \ + G_STMT_START \ + { \ + char __prefix[64]; \ + const char *__p_prefix = _NMLOG_PREFIX_NAME; \ + const NMPlatform *const __self = (self); \ + const char *__name = name; \ + \ + if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ + g_snprintf(__prefix, \ + sizeof(__prefix), \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR(__self)); \ + __p_prefix = __prefix; \ + } \ + _nm_log((level), \ + _NMLOG_DOMAIN, \ + 0, \ + __name, \ + NULL, \ + "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __p_prefix, \ + NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ G_STMT_END #define _NMLOG(level, ...) \ @@ -180,6 +184,7 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = {0}; enum { PROP_0, + PROP_MULTI_IDX, PROP_NETNS_SUPPORT, PROP_USE_UDEV, PROP_LOG_WITH_PTR, @@ -195,6 +200,7 @@ typedef struct _NMPlatformPrivate { guint ip4_dev_route_blacklist_check_id; guint ip4_dev_route_blacklist_gc_timeout_id; GHashTable *ip4_dev_route_blacklist_hash; + CList ip6_dadfailed_lst_head; NMDedupMultiIndex *multi_idx; NMPCache *cache; } NMPlatformPrivate; @@ -948,9 +954,9 @@ _link_get_all_presort(gconstpointer p_a, gconstpointer p_b, gpointer sort_by_nam const NMPlatformLink *b = NMP_OBJECT_CAST_LINK(*((const NMPObject **) p_b)); /* Loopback always first */ - if (a->ifindex == 1) + if (a->ifindex == NM_LOOPBACK_IFINDEX) return -1; - if (b->ifindex == 1) + if (b->ifindex == NM_LOOPBACK_IFINDEX) return 1; if (GPOINTER_TO_INT(sort_by_name)) { @@ -1369,6 +1375,18 @@ nm_platform_link_add(NMPlatform *self, buf_p, buf_len); break; + case NM_LINK_TYPE_VTI: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_vti_to_string((const NMPlatformLnkVti *) extra_data, + buf_p, + buf_len); + break; + case NM_LINK_TYPE_VTI6: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_vti6_to_string((const NMPlatformLnkVti6 *) extra_data, + buf_p, + buf_len); + break; case NM_LINK_TYPE_BOND: nm_strbuf_append_str(&buf_p, &buf_len, ", "); nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data, @@ -2417,6 +2435,18 @@ nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VRF, out_link); } +const NMPlatformLnkVti * +nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VTI, out_link); +} + +const NMPlatformLnkVti6 * +nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VTI6, out_link); +} + const NMPlatformLnkVxlan * nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) { @@ -3305,52 +3335,14 @@ nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 return klass->wpan_set_channel(self, ifindex, page, channel); } -#define TO_STRING_DEV_BUF_SIZE (5 + 15 + 1) -static const char * -_to_string_dev(NMPlatform *self, int ifindex, char *buf, size_t size) -{ - nm_assert(buf); - nm_assert(size >= TO_STRING_DEV_BUF_SIZE); - - if (ifindex) { - const char *name = ifindex > 0 && self ? nm_platform_link_get_name(self, ifindex) : NULL; - char *buf2; - - strcpy(buf, " dev "); - buf2 = buf + 5; - size -= 5; - - if (name) - g_strlcpy(buf2, name, size); - else - g_snprintf(buf2, size, "%d", ifindex); - } else - buf[0] = 0; - - return buf; -} - -#define TO_STRING_IFA_FLAGS_BUF_SIZE 256 - -static const char * -_to_string_ifa_flags(guint32 ifa_flags, char *buf, gsize size) -{ -#define S_FLAGS_PREFIX " flags " - nm_assert(buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN(S_FLAGS_PREFIX)); +/*****************************************************************************/ - if (!ifa_flags) - buf[0] = '\0'; - else { - nm_platform_addr_flags2str(ifa_flags, - &buf[NM_STRLEN(S_FLAGS_PREFIX)], - size - NM_STRLEN(S_FLAGS_PREFIX)); - if (buf[NM_STRLEN(S_FLAGS_PREFIX)] == '\0') - buf[0] = '\0'; - else - memcpy(buf, S_FLAGS_PREFIX, NM_STRLEN(S_FLAGS_PREFIX)); - } - return buf; -} +#define _to_string_dev(arr, ifindex) \ + ({ \ + const int _ifindex = (ifindex); \ + \ + _ifindex ? nm_sprintf_buf((arr), " dev %d", ifindex) : nm_str_truncate((arr)); \ + }) /*****************************************************************************/ @@ -3557,72 +3549,6 @@ nm_platform_lookup_clone(NMPlatform *self, user_data); } -void -nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) -{ - nm_assert(plen <= 32); - - addr->address = address; - addr->peer_address = address; - addr->plen = plen; -} - -const struct in6_addr * -nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr) -{ - if (IN6_IS_ADDR_UNSPECIFIED(&addr->peer_address) - || IN6_ARE_ADDR_EQUAL(&addr->peer_address, &addr->address)) - return &addr->address; - return &addr->peer_address; -} - -gboolean -nm_platform_ip_address_match(int addr_family, - const NMPlatformIPAddress *address, - NMPlatformMatchFlags match_flag) -{ - nm_assert(!NM_FLAGS_ANY( - match_flag, - ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); - nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY)); - nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)); - - if (addr_family == AF_INET) { - if (nm_utils_ip4_address_is_link_local(((NMPlatformIP4Address *) address)->address)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) - return FALSE; - } - } else { - if (IN6_IS_ADDR_LINKLOCAL(address->address_ptr)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) - return FALSE; - } - } - - if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DADFAILED)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED)) - return FALSE; - } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_TENTATIVE) - && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_OPTIMISTIC)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) - return FALSE; - } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DEPRECATED)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) - return FALSE; - } - - return TRUE; -} - gboolean nm_platform_ip4_address_add(NMPlatform *self, int ifindex, @@ -3711,6 +3637,9 @@ nm_platform_ip6_address_add(NMPlatform *self, _LOG3D("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string(&addr, sbuf, sizeof(sbuf))); } + + nm_platform_ip6_dadfailed_set(self, ifindex, &address, FALSE); + return klass ->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags); } @@ -3722,9 +3651,9 @@ nm_platform_ip4_address_delete(NMPlatform *self, guint8 plen, in_addr_t peer_address) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; + char str_dev[30]; + char b1[NM_INET_ADDRSTRLEN]; + char b2[NM_INET_ADDRSTRLEN]; char str_peer[INET_ADDRSTRLEN + 50]; _CHECK_SELF(self, klass, FALSE); @@ -3733,20 +3662,20 @@ nm_platform_ip4_address_delete(NMPlatform *self, g_return_val_if_fail(plen <= 32, FALSE); _LOG3D("address: deleting IPv4 address %s/%d, %s%s", - _nm_utils_inet4_ntop(address, b1), + nm_inet4_ntop(address, b1), plen, peer_address != address - ? nm_sprintf_buf(str_peer, "peer %s, ", _nm_utils_inet4_ntop(peer_address, b2)) + ? nm_sprintf_buf(str_peer, "peer %s, ", nm_inet4_ntop(peer_address, b2)) : "", - _to_string_dev(self, ifindex, str_dev, sizeof(str_dev))); + _to_string_dev(str_dev, ifindex)); return klass->ip4_address_delete(self, ifindex, address, plen, peer_address); } gboolean nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char str_dev[30]; + char sbuf[NM_INET_ADDRSTRLEN]; _CHECK_SELF(self, klass, FALSE); @@ -3754,9 +3683,9 @@ nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr ad g_return_val_if_fail(plen <= 128, FALSE); _LOG3D("address: deleting IPv6 address %s/%d, %s", - _nm_utils_inet6_ntop(&address, sbuf), + nm_inet6_ntop(&address, sbuf), plen, - _to_string_dev(self, ifindex, str_dev, sizeof(str_dev))); + _to_string_dev(str_dev, ifindex)); return klass->ip6_address_delete(self, ifindex, address, plen); } @@ -3940,13 +3869,10 @@ static guint _ip4_addr_subnets_hash(gconstpointer ptr) { const NMPlatformIP4Address *addr = NMP_OBJECT_CAST_IP4_ADDRESS(ptr); - NMHashState h; - nm_hash_init(&h, 3282159733); - nm_hash_update_vals(&h, + return nm_hash_vals(3282159733, addr->plen, - nm_utils_ip4_address_clear_host_address(addr->address, addr->plen)); - return nm_hash_complete(&h); + nm_ip4_addr_clear_host_address(addr->address, addr->plen)); } static gboolean @@ -3956,8 +3882,8 @@ _ip4_addr_subnets_equal(gconstpointer p_a, gconstpointer p_b) const NMPlatformIP4Address *b = NMP_OBJECT_CAST_IP4_ADDRESS(p_b); return a->plen == b->plen - && (nm_utils_ip4_address_clear_host_address(a->address, a->plen) - == nm_utils_ip4_address_clear_host_address(b->address, b->plen)); + && (nm_ip4_addr_clear_host_address(a->address, a->plen) + == nm_ip4_addr_clear_host_address(b->address, b->plen)); } static GHashTable * @@ -4107,6 +4033,9 @@ ip6_address_scope_cmp_descending(gconstpointer p_a, gconstpointer p_b, gpointer * by the function. * Addresses that are both contained in @known_addresses and @addresses_prune * will be configured. + * @flags: #NMPIPAddressSyncFlags to affect the sync. If "with-noprefixroute" + * flag is set, the method will automatically set IFA_F_NOPREFIXROUTE for + * all addresses. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -4115,11 +4044,12 @@ ip6_address_scope_cmp_descending(gconstpointer p_a, gconstpointer p_b, gpointer * Returns: %TRUE on success. */ gboolean -nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray *known_addresses, - GPtrArray *addresses_prune) +nm_platform_ip_address_sync(NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *known_addresses, + GPtrArray *addresses_prune, + NMPIPAddressSyncFlags flags) { gint32 now = 0; const int IS_IPv4 = NM_IS_IPv4(addr_family); @@ -4531,7 +4461,9 @@ next_plat:; nm_platform_ip4_broadcast_address_from_addr(&known_address->a4), lifetime, preferred, - known_address->ax.a_no_auto_noprefixroute ? 0 : IFA_F_NOPREFIXROUTE, + NM_FLAGS_HAS(flags, NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE) + ? IFA_F_NOPREFIXROUTE + : 0, known_address->a4.label)) success = FALSE; } else { @@ -4543,7 +4475,9 @@ next_plat:; known_address->a6.peer_address, lifetime, preferred, - (known_address->ax.a_no_auto_noprefixroute ? 0 : IFA_F_NOPREFIXROUTE) + (NM_FLAGS_HAS(flags, NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE) + ? IFA_F_NOPREFIXROUTE + : 0) | known_address->a6.n_ifa_flags)) success = FALSE; } @@ -4572,7 +4506,12 @@ nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex) addresses_prune = nm_platform_ip_address_get_prune_list(self, addr_family2, ifindex, NULL, 0); - if (!nm_platform_ip_address_sync(self, addr_family2, ifindex, NULL, addresses_prune)) + if (!nm_platform_ip_address_sync(self, + addr_family2, + ifindex, + NULL, + addresses_prune, + NMP_IP_ADDRESS_SYNC_FLAGS_NONE)) success = FALSE; } return success; @@ -4654,7 +4593,26 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; - if (!IS_IPv4) { + if (IS_IPv4) { + const NMPlatformIP4Address *a4 = NMP_OBJECT_CAST_IP4_ADDRESS(obj); + + if (a4->address == NM_IPV4LO_ADDR1 && a4->plen == NM_IPV4LO_PREFIXLEN) { + const NMPlatformIP4Address addr = (NMPlatformIP4Address){ + .ifindex = NM_LOOPBACK_IFINDEX, + .address = NM_IPV4LO_ADDR1, + .peer_address = NM_IPV4LO_ADDR1, + .plen = NM_IPV4LO_PREFIXLEN, + .use_ip4_broadcast_address = TRUE, + }; + + if (nm_platform_ip4_address_cmp(a4, + &addr, + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) + == 0) { + continue; + } + } + } else { const NMPlatformIP6Address *a6 = NMP_OBJECT_CAST_IP6_ADDRESS(obj); if (NM_FLAGS_HAS(a6->n_ifa_flags, IFA_F_SECONDARY) @@ -4710,9 +4668,6 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, GPtrArray *routes_prune = NULL; const NMDedupMultiHeadEntry *head_entry; CList *iter; - NMPlatformIP4Route rt_local4; - NMPlatformIP6Route rt_local6; - NMPlatformIP6Route rt_mcast6; const NMPlatformLink *pllink; const NMPlatformLnkVrf *lnk_vrf; guint32 local_table; @@ -4737,10 +4692,6 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, lnk_vrf = nm_platform_link_get_lnk_vrf(self, pllink->master, NULL); local_table = lnk_vrf ? lnk_vrf->table : RT_TABLE_LOCAL; - rt_local4.plen = 0; - rt_local6.plen = 0; - rt_mcast6.plen = 0; - c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; const NMPlatformIPXRoute *rt = NMP_OBJECT_CAST_IPX_ROUTE(obj); @@ -4762,6 +4713,44 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, * pruning them. */ if (NM_IS_IPv4(addr_family)) { + if (ifindex == NM_LOOPBACK_IFINDEX + && NM_IN_SET(rt->r4.network, NM_IPV4LO_ADDR1, NM_IPV4LO_NETWORK)) { + NMPlatformIP4Route r; + + if (rt->r4.network == NM_IPV4LO_ADDR1) { + r = (NMPlatformIP4Route){ + .ifindex = NM_LOOPBACK_IFINDEX, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .table_coerced = nm_platform_route_table_coerce(local_table), + .network = NM_IPV4LO_ADDR1, + .plen = 32, + .metric = 0, + .rt_source = NM_IPV4LO_ADDR1, + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .pref_src = NM_IPV4LO_ADDR1, + }; + } else { + r = (NMPlatformIP4Route){ + .ifindex = NM_LOOPBACK_IFINDEX, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .table_coerced = nm_platform_route_table_coerce(local_table), + .network = NM_IPV4LO_NETWORK, + .plen = NM_IPV4LO_PREFIXLEN, + .metric = 0, + .rt_source = NM_IPV4LO_ADDR1, + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .pref_src = NM_IPV4LO_ADDR1, + }; + } + + if (nm_platform_ip4_route_cmp(&rt->r4, + &r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + == 0) { + continue; + } + } + /* for each IPv4 address kernel adds a route like * * local $ADDR dev $IFACE table local proto kernel scope host src $PRIMARY_ADDR @@ -4772,29 +4761,26 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, && rt->rx.metric == 0 && rt->r4.scope_inv == nm_platform_route_scope_inv(RT_SCOPE_HOST) && rt->r4.gateway == INADDR_ANY) { - if (rt_local4.plen == 0) { - rt_local4 = (NMPlatformIP4Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), - .plen = 32, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, - .metric = 0, - .table_coerced = nm_platform_route_table_coerce(local_table), - .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), - .gateway = INADDR_ANY, - }; - } - - /* the possible "network" depends on the addresses we have. We don't check that - * carefully. If the other parameters match, we assume that this route is the one - * generated by kernel. */ - rt_local4.network = rt->r4.network; - rt_local4.pref_src = rt->r4.pref_src; + const NMPlatformIP4Route r = { + .ifindex = ifindex, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .plen = 32, + .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, + .metric = 0, + .table_coerced = nm_platform_route_table_coerce(local_table), + .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), + .gateway = INADDR_ANY, + /* the possible "network" depends on the addresses we have. We don't check that + * carefully. If the other parameters match, we assume that this route is the one + * generated by kernel. */ + .network = rt->r4.network, + .pref_src = rt->r4.pref_src, + }; /* to be more confident about comparing the value, use our nm_platform_ip4_route_cmp() * implementation. That will also consider parameters that we leave unspecified here. */ if (nm_platform_ip4_route_cmp(&rt->r4, - &rt_local4, + &r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; @@ -4809,23 +4795,20 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, && rt->rx.plen == 128 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL && rt->rx.metric == 0 && rt->r6.rt_pref == NM_ICMPV6_ROUTER_PREF_MEDIUM && IN6_IS_ADDR_UNSPECIFIED(&rt->r6.gateway)) { - if (rt_local6.plen == 0) { - rt_local6 = (NMPlatformIP6Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), - .plen = 128, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, - .metric = 0, - .table_coerced = nm_platform_route_table_coerce(local_table), - .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, - .gateway = IN6ADDR_ANY_INIT, - }; - } - - rt_local6.network = rt->r6.network; + const NMPlatformIP6Route r = { + .ifindex = ifindex, + .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), + .plen = 128, + .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, + .metric = 0, + .table_coerced = nm_platform_route_table_coerce(local_table), + .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, + .gateway = IN6ADDR_ANY_INIT, + .network = rt->r6.network, + }; if (nm_platform_ip6_route_cmp(&rt->r6, - &rt_local6, + &r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; @@ -4847,22 +4830,21 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, && rt->rx.plen == 8 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_BOOT && rt->rx.metric == 256 && rt->r6.rt_pref == NM_ICMPV6_ROUTER_PREF_MEDIUM && IN6_IS_ADDR_UNSPECIFIED(&rt->r6.gateway)) { - if (rt_mcast6.plen == 0) { - rt_mcast6 = (NMPlatformIP6Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_UNICAST), - .plen = 8, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_BOOT, - .metric = 256, - .table_coerced = nm_platform_route_table_coerce(local_table), - .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, - .gateway = IN6ADDR_ANY_INIT, - .network = {{{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}, - }; - } + const NMPlatformIP6Route r = { + .ifindex = ifindex, + .type_coerced = nm_platform_route_type_coerce(RTN_UNICAST), + .plen = 8, + .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_BOOT, + .metric = 256, + .table_coerced = nm_platform_route_table_coerce(local_table), + .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, + .gateway = IN6ADDR_ANY_INIT, + .network = + NM_IN6ADDR_INIT(0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + }; if (nm_platform_ip6_route_cmp(&rt->r6, - &rt_mcast6, + &r, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; @@ -5251,15 +5233,15 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) r4 = (NMPlatformIP4Route *) route; route->metric = nm_platform_ip4_route_get_effective_metric(r4); route->metric_any = FALSE; - r4->network = nm_utils_ip4_address_clear_host_address(r4->network, r4->plen); + r4->network = nm_ip4_addr_clear_host_address(r4->network, r4->plen); r4->scope_inv = _ip_route_scope_inv_get_normalized(r4); break; case AF_INET6: r6 = (NMPlatformIP6Route *) route; route->metric = nm_platform_ip6_route_get_effective_metric(r6); route->metric_any = FALSE; - nm_utils_ip6_address_clear_host_address(&r6->network, &r6->network, r6->plen); - nm_utils_ip6_address_clear_host_address(&r6->src, &r6->src, r6->src_plen); + nm_ip6_addr_clear_host_address(&r6->network, &r6->network, r6->plen); + nm_ip6_addr_clear_host_address(&r6->src, &r6->src, r6->src_plen); break; default: nm_assert_not_reached(); @@ -5268,55 +5250,107 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) } static int -_ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) +_ip_route_add(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack) { char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; int ifindex; _CHECK_SELF(self, klass, FALSE); - nm_assert(route); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + /* The caller already ensures that this is a stack allocated copy, that + * - stays alive for the duration of the call. + * - that the ip_route_add() implementation is allowed to modify. + */ + nm_assert(obj_stack); + nm_assert(NMP_OBJECT_IS_STACKINIT(obj_stack)); + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_stack), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + nm_assert(NMP_OBJECT_GET_TYPE(obj_stack) != NMP_OBJECT_TYPE_IP4_ROUTE + || obj_stack->ip4_route.n_nexthops <= 1u || obj_stack->_ip4_route.extra_nexthops); + + nm_platform_ip_route_normalize(NMP_OBJECT_GET_ADDR_FAMILY((obj_stack)), + NMP_OBJECT_CAST_IP_ROUTE(obj_stack)); + + ifindex = obj_stack->ip_route.ifindex; - ifindex = ((const NMPlatformIPRoute *) route)->ifindex; _LOG3D("route: %-10s IPv%c route: %s", _nmp_nlm_flag_to_string(flags & NMP_NLM_FLAG_FMASK), - nm_utils_addr_family_to_char(addr_family), - NM_IS_IPv4(addr_family) ? nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf)) - : nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); - - return klass->ip_route_add(self, flags, addr_family, route); + 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))); + + /* At this point, we pass "obj_stack" to the klass->ip_route_add() implementation. + * The callee can rely on: + * - the object being normalized and validated. + * - staying fully alive until the function returns. In this case it + * is stack allocated (and the potential "extra_nexthops" array is + * guaranteed to stay alive too). + */ + return klass->ip_route_add(self, flags, obj_stack); } int -nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route) +nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *obj) { - int addr_family; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; + NMPObject obj_stack; - switch (NMP_OBJECT_GET_TYPE(route)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - addr_family = AF_INET; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - addr_family = AF_INET6; - break; - default: - g_return_val_if_reached(FALSE); + nm_assert( + NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + + nmp_object_stackinit(&obj_stack, NMP_OBJECT_GET_TYPE(obj), &obj->ip_route); + + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE && obj->ip4_route.n_nexthops > 1u) { + /* Ensure @obj stays alive, so we can alias extra_nexthops from the stackallocated + * @obj_stack. */ + nm_assert(obj->_ip4_route.extra_nexthops); + obj_keep_alive = nmp_object_ref(obj); + obj_stack._ip4_route.extra_nexthops = obj->_ip4_route.extra_nexthops; } - return _ip_route_add(self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE(route)); + return _ip_route_add(self, flags, &obj_stack); } int -nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) +nm_platform_ip4_route_add(NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops) { - return _ip_route_add(self, flags, AF_INET, route); + gs_free NMPlatformIP4RtNextHop *extra_nexthops_free = NULL; + NMPObject obj; + + nm_assert(route); + nm_assert(route->n_nexthops <= 1u || extra_nexthops); + + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + + if (route->n_nexthops > 1u) { + nm_assert(extra_nexthops); + /* we need to ensure that @extra_nexthops stays alive until the function returns. + * Copy the buffer. + * + * This is probably not necessary, because likely the caller will somehow ensure that + * the extra_nexthops stay alive. Still do it, because it is a very unusual case and + * likely cheap. */ + obj._ip4_route.extra_nexthops = + nm_memdup_maybe_a(500u, + extra_nexthops, + sizeof(extra_nexthops[0]) * (route->n_nexthops - 1u), + &extra_nexthops_free); + } + + return _ip_route_add(self, flags, &obj); } int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) { - return _ip_route_add(self, flags, AF_INET6, route); + NMPObject obj; + + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + return _ip_route_add(self, flags, &obj); } gboolean @@ -5364,7 +5398,7 @@ nm_platform_ip_route_get(NMPlatform *self, char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_auto_nmpobj NMPObject *route = NULL; int result; - char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf[NM_INET_ADDRSTRLEN]; char buf_oif[64]; _CHECK_SELF(self, klass, FALSE); @@ -5839,37 +5873,6 @@ nm_platform_vlan_qos_mapping_to_string(const char *name, return buf; } -static const char * -_lifetime_to_string(guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size) -{ - if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT) - return "forever"; - - g_snprintf(buf, - buf_size, - "%usec", - nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now)); - return buf; -} - -static const char * -_lifetime_summary_to_string(gint32 now, - guint32 timestamp, - guint32 preferred, - guint32 lifetime, - char *buf, - size_t buf_size) -{ - g_snprintf(buf, - buf_size, - " lifetime %d-%u[%u,%u]", - (signed) now, - (unsigned) timestamp, - (unsigned) preferred, - (unsigned) lifetime); - return buf; -} - /** * nm_platform_link_to_string: * @route: pointer to NMPlatformLink address structure @@ -5893,7 +5896,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) char str_address[_NM_UTILS_HWADDR_LEN_MAX * 3]; char str_perm_address[_NM_UTILS_HWADDR_LEN_MAX * 3]; char str_broadcast[_NM_UTILS_HWADDR_LEN_MAX * 3]; - char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN]; + char str_inet6_token[NM_INET_ADDRSTRLEN]; const char *str_link_type; if (!nm_utils_to_string_buffer_init_null(link, &buf, &len)) @@ -6180,7 +6183,7 @@ nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize le char target[INET_ADDRSTRLEN]; nm_strbuf_append_c(&buf, &len, ' '); - nm_strbuf_append_str(&buf, &len, _nm_utils_inet4_ntop(lnk->arp_ip_target[i], target)); + nm_strbuf_append_str(&buf, &len, nm_inet4_ntop(lnk->arp_ip_target[i], target)); } } return buf; @@ -6190,18 +6193,18 @@ const char * nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tos[30]; char str_parent_ifindex[30]; char str_input_flags[30]; char str_output_flags[30]; char str_input_key[30]; - char str_input_key1[NM_UTILS_INET_ADDRSTRLEN]; + char str_input_key1[NM_INET_ADDRSTRLEN]; char str_output_key[30]; - char str_output_key1[NM_UTILS_INET_ADDRSTRLEN]; + char str_output_key1[NM_INET_ADDRSTRLEN]; if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) return buf; @@ -6222,13 +6225,11 @@ nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) "%s" /* okey */ "", lnk->is_tap ? "tap" : "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", @@ -6241,12 +6242,12 @@ nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) NM_FLAGS_HAS(lnk->input_flags, GRE_KEY) || lnk->input_key ? nm_sprintf_buf(str_input_key, " ikey %s", - _nm_utils_inet4_ntop(lnk->input_key, str_input_key1)) + nm_inet4_ntop(lnk->input_key, str_input_key1)) : "", NM_FLAGS_HAS(lnk->output_flags, GRE_KEY) || lnk->output_key ? nm_sprintf_buf(str_output_key, " okey %s", - _nm_utils_inet4_ntop(lnk->output_key, str_output_key1)) + nm_inet4_ntop(lnk->output_key, str_output_key1)) : ""); return buf; } @@ -6275,9 +6276,9 @@ const char * nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tclass[30]; char str_flow[30]; @@ -6294,32 +6295,32 @@ nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsiz else str_type = "ip6tnl"; - g_snprintf( - buf, - len, - "%s" /* type */ - "%s" /* remote */ - "%s" /* local */ - "%s" /* parent_ifindex */ - "%s" /* ttl */ - "%s" /* tclass */ - "%s" /* encap limit */ - "%s" /* flow label */ - "%s" /* proto */ - " flags 0x%x" - "", - str_type, - nm_sprintf_buf(str_remote, " remote %s", _nm_utils_inet6_ntop(&lnk->remote, str_remote1)), - nm_sprintf_buf(str_local, " local %s", _nm_utils_inet6_ntop(&lnk->local, str_local1)), - lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) - : "", - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", - lnk->tclass == 1 ? " tclass inherit" - : nm_sprintf_buf(str_tclass, " tclass 0x%x", lnk->tclass), - nm_sprintf_buf(str_encap, " encap-limit %u", lnk->encap_limit), - nm_sprintf_buf(str_flow, " flow-label 0x05%x", lnk->flow_label), - nm_sprintf_buf(str_proto, " proto %u", lnk->proto), - (guint) lnk->flags); + g_snprintf(buf, + len, + "%s" /* type */ + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ttl */ + "%s" /* tclass */ + "%s" /* encap limit */ + "%s" /* flow label */ + "%s" /* proto */ + " flags 0x%x" + "", + str_type, + nm_sprintf_buf(str_remote, " remote %s", nm_inet6_ntop(&lnk->remote, str_remote1)), + nm_sprintf_buf(str_local, " local %s", nm_inet6_ntop(&lnk->local, str_local1)), + lnk->parent_ifindex + ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) + : "", + lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", + lnk->tclass == 1 ? " tclass inherit" + : nm_sprintf_buf(str_tclass, " tclass 0x%x", lnk->tclass), + nm_sprintf_buf(str_encap, " encap-limit %u", lnk->encap_limit), + nm_sprintf_buf(str_flow, " flow-label 0x05%x", lnk->flow_label), + nm_sprintf_buf(str_proto, " proto %u", lnk->proto), + (guint) lnk->flags); return buf; } @@ -6327,9 +6328,9 @@ const char * nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tos[30]; char str_parent_ifindex[30]; @@ -6348,13 +6349,11 @@ nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize le "%s" /* tos */ "%s" /* path_mtu_discovery */ "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", @@ -6417,9 +6416,9 @@ const char * nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len) { char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; char str_ttl[30]; char str_tos[30]; char str_flags[30]; @@ -6442,13 +6441,11 @@ nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len) "%s" /* flags */ "%s" /* proto */ "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "", lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", @@ -6502,19 +6499,112 @@ const char * nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len) { char *b; + char protocol[32]; if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) return buf; b = buf; + switch (lnk->protocol) { + case ETH_P_8021AD: + nm_sprintf_buf(protocol, "802.1ad"); + break; + case ETH_P_8021Q: + nm_sprintf_buf(protocol, "802.1Q"); + break; + default: + nm_sprintf_buf(protocol, "0x%04hx", lnk->protocol); + break; + } + nm_strbuf_append(&b, &len, "vlan %u", lnk->id); + nm_strbuf_append(&b, &len, " protocol %s", protocol); if (lnk->flags) nm_strbuf_append(&b, &len, " flags 0x%x", lnk->flags); return buf; } const char * +nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len) +{ + char str_local[30 + NM_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; + char str_remote[30 + NM_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; + char str_ikey[30]; + char str_okey[30]; + char str_fwmark[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) + return buf; + + g_snprintf( + buf, + len, + "vti" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ikey */ + "%s" /* okey */ + "%s" /* fwmark */ + "", + lnk->remote + ? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1)) + : "", + lnk->local ? nm_sprintf_buf(str_local, " local %s", nm_inet4_ntop(lnk->local, str_local1)) + : "", + lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) + : "", + lnk->ikey ? nm_sprintf_buf(str_ikey, " ikey %u", lnk->ikey) : "", + lnk->okey ? nm_sprintf_buf(str_okey, " okey %u", lnk->okey) : "", + lnk->fwmark ? nm_sprintf_buf(str_fwmark, " fwmark 0x%x", lnk->fwmark) : ""); + return buf; +} + +const char * +nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len) +{ + char str_local[30 + NM_INET_ADDRSTRLEN]; + char str_local1[NM_INET_ADDRSTRLEN]; + char str_remote[30 + NM_INET_ADDRSTRLEN]; + char str_remote1[NM_INET_ADDRSTRLEN]; + char str_ikey[30]; + char str_okey[30]; + char str_fwmark[30]; + char str_parent_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) + return buf; + + g_snprintf( + buf, + len, + "vti6" + "%s" /* remote */ + "%s" /* local */ + "%s" /* parent_ifindex */ + "%s" /* ikey */ + "%s" /* okey */ + "%s" /* fwmark */ + "", + IN6_IS_ADDR_UNSPECIFIED(&lnk->remote) + ? "" + : nm_sprintf_buf(str_remote, " remote %s", nm_inet6_ntop(&lnk->remote, str_remote1)), + IN6_IS_ADDR_UNSPECIFIED(&lnk->local) + ? "" + : nm_sprintf_buf(str_local, " local %s", nm_inet6_ntop(&lnk->local, str_local1)), + lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) + : "", + lnk->ikey ? nm_sprintf_buf(str_ikey, " ikey %u", lnk->ikey) : "", + lnk->okey ? nm_sprintf_buf(str_okey, " okey %u", lnk->okey) : "", + lnk->fwmark ? nm_sprintf_buf(str_fwmark, " fwmark 0x%x", lnk->fwmark) : ""); + return buf; +} + +const char * nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len) { char *b; @@ -6535,13 +6625,13 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize char str_group6[100]; char str_local[100]; char str_local6[100]; - char str_dev[25]; + char str_dev[30]; char str_limit[25]; char str_src_port[35]; char str_dst_port[25]; char str_tos[25]; char str_ttl[25]; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + char sbuf[NM_INET_ADDRSTRLEN]; if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) return buf; @@ -6553,7 +6643,7 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize sizeof(str_group), " %s %s", IN_MULTICAST(ntohl(lnk->group)) ? "group" : "remote", - _nm_utils_inet4_ntop(lnk->group, sbuf)); + nm_inet4_ntop(lnk->group, sbuf)); } if (IN6_IS_ADDR_UNSPECIFIED(&lnk->group6)) str_group6[0] = '\0'; @@ -6563,16 +6653,13 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize " %s%s %s", IN6_IS_ADDR_MULTICAST(&lnk->group6) ? "group" : "remote", str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - _nm_utils_inet6_ntop(&lnk->group6, sbuf)); + nm_inet6_ntop(&lnk->group6, sbuf)); } if (lnk->local == 0) str_local[0] = '\0'; else { - g_snprintf(str_local, - sizeof(str_local), - " local %s", - _nm_utils_inet4_ntop(lnk->local, sbuf)); + g_snprintf(str_local, sizeof(str_local), " local %s", nm_inet4_ntop(lnk->local, sbuf)); } if (IN6_IS_ADDR_UNSPECIFIED(&lnk->local6)) str_local6[0] = '\0'; @@ -6581,7 +6668,7 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize sizeof(str_local6), " local%s %s", str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - _nm_utils_inet6_ntop(&lnk->local6, sbuf)); + nm_inet6_ntop(&lnk->local6, sbuf)); } g_snprintf( @@ -6609,7 +6696,7 @@ nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize str_group6, str_local, str_local6, - lnk->parent_ifindex ? nm_sprintf_buf(str_dev, " dev %d", lnk->parent_ifindex) : "", + _to_string_dev(str_dev, lnk->parent_ifindex), lnk->src_port_min || lnk->src_port_max ? nm_sprintf_buf(str_src_port, " srcport %u %u", lnk->src_port_min, lnk->src_port_max) : "", @@ -6631,9 +6718,9 @@ nm_platform_wireguard_peer_to_string(const NMPWireGuardPeer *peer, char *buf, gs { char *buf0 = buf; gs_free char *public_key_b64 = NULL; - char s_sockaddr[NM_UTILS_INET_ADDRSTRLEN + 100]; + char s_sockaddr[NM_INET_ADDRSTRLEN + 100]; char s_endpoint[20 + sizeof(s_sockaddr)]; - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; char s_keepalive[100]; guint i; @@ -6677,7 +6764,7 @@ nm_platform_wireguard_peer_to_string(const NMPWireGuardPeer *peer, char *buf, gs nm_strbuf_append(&buf, &len, " %s/%u", - nm_utils_inet_ntop(allowed_ip->family, &allowed_ip->addr, s_addr), + nm_inet_ntop(allowed_ip->family, &allowed_ip->addr, s_addr), allowed_ip->mask); } @@ -6713,246 +6800,6 @@ nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf return buf; } -/** - * nm_platform_ip4_address_to_string: - * @route: pointer to NMPlatformIP4Address address structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting an address struct into a string representation. - * - * Example output: "" - * - * Returns: a string representation of the address. - */ -const char * -nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len) -{ - char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; - char s_address[INET_ADDRSTRLEN]; - char s_peer[INET_ADDRSTRLEN]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_label[32]; - char str_lft[30], str_pref[30], str_time[50], s_source[50]; - char *str_peer = NULL; - const char *str_lft_p, *str_pref_p, *str_time_p; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); - in_addr_t broadcast_address; - char str_broadcast[INET_ADDRSTRLEN]; - - if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) - return buf; - - inet_ntop(AF_INET, &address->address, s_address, sizeof(s_address)); - - if (address->peer_address != address->address) { - inet_ntop(AF_INET, &address->peer_address, s_peer, sizeof(s_peer)); - str_peer = g_strconcat(" ptp ", s_peer, NULL); - } - - _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev)); - - if (*address->label) - g_snprintf(str_label, sizeof(str_label), " label %s", address->label); - else - str_label[0] = 0; - - str_lft_p = _lifetime_to_string(address->timestamp, - address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_lft, - sizeof(str_lft)), - str_pref_p = - (address->lifetime == address->preferred) - ? str_lft_p - : (_lifetime_to_string(address->timestamp, - address->lifetime ? MIN(address->preferred, address->lifetime) - : NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_pref, - sizeof(str_pref))); - str_time_p = _lifetime_summary_to_string(now, - address->timestamp, - address->preferred, - address->lifetime, - str_time, - sizeof(str_time)); - - broadcast_address = nm_platform_ip4_broadcast_address_from_addr(address); - - g_snprintf( - buf, - len, - "%s/%d" - "%s%s" /* broadcast */ - " lft %s" - " pref %s" - "%s" /* time */ - "%s" /* peer */ - "%s" /* dev */ - "%s" /* flags */ - "%s" /* label */ - " src %s" - "%s" /* a_acd_not_ready */ - "%s" /* a_force_commit */ - "%s" /* a_no_auto_noprefixroute */ - "", - s_address, - address->plen, - broadcast_address != 0u || address->use_ip4_broadcast_address - ? (address->use_ip4_broadcast_address ? " brd " : " brd* ") - : "", - broadcast_address != 0u || address->use_ip4_broadcast_address - ? _nm_utils_inet4_ntop(broadcast_address, str_broadcast) - : "", - str_lft_p, - str_pref_p, - str_time_p, - str_peer ?: "", - str_dev, - _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), - str_label, - nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_acd_not_ready ? " ip4acd-not-ready" : "", - address->a_force_commit ? " force-commit" : "", - address->a_no_auto_noprefixroute ? " no-auto-noprefixroute" : ""); - g_free(str_peer); - return buf; -} - -NM_UTILS_FLAGS2STR_DEFINE(nm_platform_link_flags2str, - unsigned, - NM_UTILS_FLAGS2STR(IFF_LOOPBACK, "loopback"), - NM_UTILS_FLAGS2STR(IFF_BROADCAST, "broadcast"), - NM_UTILS_FLAGS2STR(IFF_POINTOPOINT, "pointopoint"), - NM_UTILS_FLAGS2STR(IFF_MULTICAST, "multicast"), - NM_UTILS_FLAGS2STR(IFF_NOARP, "noarp"), - NM_UTILS_FLAGS2STR(IFF_ALLMULTI, "allmulti"), - NM_UTILS_FLAGS2STR(IFF_PROMISC, "promisc"), - NM_UTILS_FLAGS2STR(IFF_MASTER, "master"), - NM_UTILS_FLAGS2STR(IFF_SLAVE, "slave"), - NM_UTILS_FLAGS2STR(IFF_DEBUG, "debug"), - NM_UTILS_FLAGS2STR(IFF_DYNAMIC, "dynamic"), - NM_UTILS_FLAGS2STR(IFF_AUTOMEDIA, "automedia"), - NM_UTILS_FLAGS2STR(IFF_PORTSEL, "portsel"), - NM_UTILS_FLAGS2STR(IFF_NOTRAILERS, "notrailers"), - NM_UTILS_FLAGS2STR(IFF_UP, "up"), - NM_UTILS_FLAGS2STR(IFF_RUNNING, "running"), - NM_UTILS_FLAGS2STR(IFF_LOWER_UP, "lowerup"), - NM_UTILS_FLAGS2STR(IFF_DORMANT, "dormant"), - NM_UTILS_FLAGS2STR(IFF_ECHO, "echo"), ); - -NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str, - guint8, - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_NONE, "none"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), ); - -G_STATIC_ASSERT(IFA_F_SECONDARY == IFA_F_TEMPORARY); - -NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str, - unsigned, - NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"), - NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"), - NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"), - NM_UTILS_FLAGS2STR(IFA_F_DADFAILED, "dadfailed"), - NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"), - NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"), - NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), - NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"), - NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"), - NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"), - NM_UTILS_FLAGS2STR(IFA_F_MCAUTOJOIN, "mcautojoin"), - NM_UTILS_FLAGS2STR(IFA_F_STABLE_PRIVACY, "stable-privacy"), ); - -NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str, - int, - NM_UTILS_ENUM2STR(RT_SCOPE_NOWHERE, "nowhere"), - NM_UTILS_ENUM2STR(RT_SCOPE_HOST, "host"), - NM_UTILS_ENUM2STR(RT_SCOPE_LINK, "link"), - NM_UTILS_ENUM2STR(RT_SCOPE_SITE, "site"), - NM_UTILS_ENUM2STR(RT_SCOPE_UNIVERSE, "global"), ); - -/** - * nm_platform_ip6_address_to_string: - * @route: pointer to NMPlatformIP6Address address structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting an address struct into a string representation. - * - * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1" - * - * Returns: a string representation of the address. - */ -const char * -nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len) -{ - char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; - char s_address[INET6_ADDRSTRLEN]; - char s_peer[INET6_ADDRSTRLEN]; - char str_lft[30], str_pref[30], str_time[50], s_source[50]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char *str_peer = NULL; - const char *str_lft_p, *str_pref_p, *str_time_p; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); - - if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) - return buf; - - inet_ntop(AF_INET6, &address->address, s_address, sizeof(s_address)); - - if (!IN6_IS_ADDR_UNSPECIFIED(&address->peer_address)) { - inet_ntop(AF_INET6, &address->peer_address, s_peer, sizeof(s_peer)); - str_peer = g_strconcat(" ptp ", s_peer, NULL); - } - - _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev)); - - str_lft_p = _lifetime_to_string(address->timestamp, - address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_lft, - sizeof(str_lft)), - str_pref_p = - (address->lifetime == address->preferred) - ? str_lft_p - : (_lifetime_to_string(address->timestamp, - address->lifetime ? MIN(address->preferred, address->lifetime) - : NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_pref, - sizeof(str_pref))); - str_time_p = _lifetime_summary_to_string(now, - address->timestamp, - address->preferred, - address->lifetime, - str_time, - sizeof(str_time)); - - g_snprintf( - buf, - len, - "%s/%d lft %s pref %s%s%s%s%s src %s" - "%s" /* a_force_commit */ - "%s" /* a_no_auto_noprefixroute */ - "", - s_address, - address->plen, - str_lft_p, - str_pref_p, - str_time_p, - str_peer ?: "", - str_dev, - _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), - nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_force_commit ? " force-commit" : "", - address->a_no_auto_noprefixroute ? " no-auto-noprefixroute" : ""); - g_free(str_peer); - return buf; -} - static NM_UTILS_FLAGS2STR_DEFINE(_rtm_flags_to_string, unsigned, NM_UTILS_FLAGS2STR(RTNH_F_DEAD, "dead"), @@ -6990,6 +6837,10 @@ _rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) /** * nm_platform_ip4_route_to_string: * @route: pointer to NMPlatformIP4Route route structure + * @extra_nexthops: (allow-none): the route might be a ECMP multihop route + * (with n_nexthops > 1). In that case, provide the list of extra hops + * to print too. It is allowed for a multihop route to omit the extra hops + * by passing NULL. * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * @@ -7000,30 +6851,40 @@ _rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) * Returns: a string representation of the route. */ const char * -nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) -{ - char s_network[INET_ADDRSTRLEN]; - char s_gateway[INET_ADDRSTRLEN]; - char s_pref_src[INET_ADDRSTRLEN]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_mss[32]; - char str_table[30]; - char str_scope[30]; - char s_source[50]; - char str_tos[32]; - 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_type[30]; - char str_metric[30]; +nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops, + char *buf, + gsize len) +{ + char *buf0; + char s_network[INET_ADDRSTRLEN]; + char s_gateway[INET_ADDRSTRLEN]; + char s_pref_src[INET_ADDRSTRLEN]; + char str_dev[30]; + char str_mss[32]; + char str_table[30]; + char str_scope[30]; + char s_source[50]; + char str_tos[32]; + 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_type[30]; + char str_metric[30]; + char weight_str[20]; + guint n_nexthops; if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) return buf; + buf0 = buf; + + n_nexthops = nm_platform_ip4_route_get_n_nexthops(route); + inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network)); if (route->gateway == 0) @@ -7031,16 +6892,15 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz else inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway)); - _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev)); - - g_snprintf( - buf, - len, + nm_strbuf_append( + &buf, + &len, "type %s " /* type */ "%s" /* table */ "%s/%d" "%s%s" /* gateway */ - "%s" + "%s%s" /* weight */ + "%s" /* dev/ifindex */ " metric %s" "%s" /* mss */ " rt-src %s" /* protocol */ @@ -7068,9 +6928,13 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz : ""), s_network, route->plen, - s_gateway[0] ? " via " : "", - s_gateway, - str_dev, + n_nexthops <= 1 && s_gateway[0] ? " via " : "", + n_nexthops <= 1 ? s_gateway : "", + NM_PRINT_FMT_QUOTED2(n_nexthops <= 1 && route->weight != 0, + " weight ", + nm_sprintf_buf(weight_str, "%u", route->weight), + ""), + n_nexthops <= 1 ? _to_string_dev(str_dev, route->ifindex) : "", route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), @@ -7120,7 +6984,50 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->mtu) : "", route->r_force_commit ? " force-commit" : ""); - return buf; + + if ((n_nexthops == 1 && route->ifindex > 0) || n_nexthops == 0) { + /* A plain single hop route. Nothing extra to remark. */ + } else { + nm_strbuf_append(&buf, &len, " n_nexthops %u", n_nexthops); + if (n_nexthops > 1) { + nm_strbuf_append(&buf, + &len, + " nexthop" + "%s%s" /* gateway */ + " weight %s" /* weight */ + "%s" /* dev/ifindex */ + "", + s_gateway[0] ? " via " : "", + s_gateway, + nm_sprintf_buf(weight_str, "%u", route->weight), + _to_string_dev(str_dev, route->ifindex)); + if (!extra_nexthops) + nm_strbuf_append_str(&buf, &len, " nexthops [...]"); + else { + guint i; + + for (i = 1; i < n_nexthops; i++) { + const NMPlatformIP4RtNextHop *nexthop = &extra_nexthops[i - 1]; + + nm_strbuf_append( + &buf, + &len, + " nexthop" + "%s" /* ifindex */ + "%s%s" /* gateway */ + " weight %s" /* weight */ + "", + NM_PRINT_FMT_QUOTED2(nexthop->gateway != 0 || nexthop->ifindex <= 0, + " via ", + nm_inet4_ntop(nexthop->gateway, s_gateway), + ""), + _to_string_dev(str_dev, nexthop->ifindex), + nm_sprintf_buf(weight_str, "%u", nexthop->weight)); + } + } + } + } + return buf0; } /** @@ -7147,7 +7054,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz char str_table[30]; char str_pref[40]; char str_pref2[30]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_dev[30]; char str_mss[32]; char s_source[50]; char str_window[32]; @@ -7174,8 +7081,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz else inet_ntop(AF_INET6, &route->pref_src, s_pref_src, sizeof(s_pref_src)); - _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev)); - g_snprintf( buf, len, @@ -7213,7 +7118,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz route->plen, s_gateway[0] ? " via " : "", s_gateway, - str_dev, + _to_string_dev(str_dev, route->ifindex), route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), @@ -7225,7 +7130,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz route->src_plen || !IN6_IS_ADDR_UNSPECIFIED(&route->src) ? nm_sprintf_buf(s_src_all, " src %s/%u", - _nm_utils_inet6_ntop(&route->src, s_src), + nm_inet6_ntop(&route->src, s_src), (unsigned) route->src_plen) : "", _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags), @@ -7279,7 +7184,7 @@ _routing_rule_addr_to_string(char **buf, guint8 plen, gboolean is_src) { - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; gboolean is_zero; gsize addr_size; @@ -7300,7 +7205,7 @@ _routing_rule_addr_to_string(char **buf, nm_strbuf_append_str(buf, len, is_src ? " from " : " to "); - nm_strbuf_append_str(buf, len, nm_utils_inet_ntop(addr_family, addr, s_addr)); + nm_strbuf_append_str(buf, len, nm_inet_ntop(addr_family, addr, s_addr)); if (plen != (addr_size * 8)) nm_strbuf_append(buf, len, "/%u", plen); @@ -7495,7 +7400,7 @@ nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, ch const char * nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_dev[30]; const char *buf0; if (!nm_utils_to_string_buffer_init_null(qdisc, &buf, &len)) @@ -7507,7 +7412,7 @@ nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len) &len, "%s%s family %u handle %x parent %x info %x", qdisc->kind, - _to_string_dev(NULL, qdisc->ifindex, str_dev, sizeof(str_dev)), + _to_string_dev(str_dev, qdisc->ifindex), qdisc->addr_family, qdisc->handle, qdisc->parent, @@ -7584,9 +7489,7 @@ nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h) } int -nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle) +nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle) { NM_CMP_SELF(a, b); NM_CMP_FIELD(a, b, ifindex); @@ -7623,16 +7526,10 @@ nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, return 0; } -int -nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b) -{ - return nm_platform_qdisc_cmp_full(a, b, TRUE); -} - const char * nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len) { - char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_dev[30]; char act_buf[300]; char *p; gsize l; @@ -7673,7 +7570,7 @@ nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len, "%s%s family %u handle %x parent %x info %x%s", tfilter->kind, - _to_string_dev(NULL, tfilter->ifindex, str_dev, sizeof(str_dev)), + _to_string_dev(str_dev, tfilter->ifindex), tfilter->addr_family, tfilter->handle, tfilter->parent, @@ -7741,7 +7638,7 @@ static NM_UTILS_FLAGS2STR_DEFINE(_mptcp_flags_to_string, const char * nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *buf, gsize len) { - char str_addr[30 + NM_UTILS_INET_ADDRSTRLEN]; + char str_addr[30 + NM_INET_ADDRSTRLEN]; char str_port[30]; char str_id[30]; char str_flags[200]; @@ -7754,7 +7651,7 @@ nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *bu if (mptcp_addr->addr_family == 0) nm_sprintf_buf(str_addr, "no-addr"); else if (NM_IN_SET(mptcp_addr->addr_family, AF_INET, AF_INET6)) - nm_utils_inet_ntop(mptcp_addr->addr_family, &mptcp_addr->addr, str_addr); + nm_inet_ntop(mptcp_addr->addr_family, &mptcp_addr->addr, str_addr); else nm_sprintf_buf(str_addr, "af %d", mptcp_addr->addr_family); @@ -8363,7 +8260,7 @@ nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b) void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h) { - nm_hash_update_vals(h, obj->id, obj->flags); + nm_hash_update_vals(h, obj->id, obj->protocol, obj->flags); } int @@ -8371,6 +8268,7 @@ nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) { NM_CMP_SELF(a, b); NM_CMP_FIELD(a, b, id); + NM_CMP_FIELD(a, b, protocol); NM_CMP_FIELD(a, b, flags); return 0; } @@ -8390,6 +8288,56 @@ nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b) } void +nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ikey, + obj->okey, + obj->fwmark); +} + +int +nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, parent_ifindex); + NM_CMP_FIELD(a, b, local); + NM_CMP_FIELD(a, b, remote); + NM_CMP_FIELD(a, b, ikey); + NM_CMP_FIELD(a, b, okey); + NM_CMP_FIELD(a, b, fwmark); + return 0; +} + +void +nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->local, + obj->remote, + obj->parent_ifindex, + obj->ikey, + obj->okey, + obj->fwmark); +} + +int +nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, parent_ifindex); + NM_CMP_FIELD_MEMCMP(a, b, local); + NM_CMP_FIELD_MEMCMP(a, b, remote); + NM_CMP_FIELD(a, b, ikey); + NM_CMP_FIELD(a, b, okey); + NM_CMP_FIELD(a, b, fwmark); + return 0; +} + +void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h) { nm_hash_update_vals(h, @@ -8458,275 +8406,18 @@ nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformL return 0; } -static int -_address_pretty_sort_get_prio_4(in_addr_t addr) -{ - if (nm_utils_ip4_address_is_link_local(addr)) - return 0; - return 1; -} - -int -nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, - const NMPlatformIP4Address *a2) -{ - in_addr_t n1; - in_addr_t n2; - - nm_assert(a1); - nm_assert(a2); - - /* Sort by address type. For example link local will - * be sorted *after* a global address. */ - NM_CMP_DIRECT(_address_pretty_sort_get_prio_4(a2->address), - _address_pretty_sort_get_prio_4(a1->address)); - - /* Sort the addresses based on their source. */ - NM_CMP_DIRECT(a2->addr_source, a1->addr_source); - - NM_CMP_DIRECT((a2->label[0] == '\0'), (a1->label[0] == '\0')); - - /* Finally, sort addresses lexically. We compare only the - * network part so that the order of addresses in the same - * subnet (and thus also the primary/secondary role) is - * preserved. - */ - n1 = nm_utils_ip4_address_clear_host_address(a1->address, a1->plen); - n2 = nm_utils_ip4_address_clear_host_address(a2->address, a2->plen); - NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); - return 0; -} - -static int -_address_pretty_sort_get_prio_6(const struct in6_addr *addr) -{ - if (IN6_IS_ADDR_V4MAPPED(addr)) - return 0; - if (IN6_IS_ADDR_V4COMPAT(addr)) - return 1; - if (IN6_IS_ADDR_UNSPECIFIED(addr)) - return 2; - if (IN6_IS_ADDR_LOOPBACK(addr)) - return 3; - if (IN6_IS_ADDR_LINKLOCAL(addr)) - return 4; - if (IN6_IS_ADDR_SITELOCAL(addr)) - return 5; - return 6; -} - -static int -_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) -{ - guint32 lifetime_a; - guint32 lifetime_b; - guint32 preferred_a; - guint32 preferred_b; - gint32 now = 0; - - lifetime_a = - nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); - lifetime_b = - nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); - - NM_CMP_DIRECT(lifetime_a, lifetime_b); - NM_CMP_DIRECT(preferred_a, preferred_b); - return 0; -} - -int -nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, - const NMPlatformIP6Address *a2, - gboolean prefer_temp) -{ - gboolean ipv6_privacy1; - gboolean ipv6_privacy2; - - nm_assert(a1); - nm_assert(a2); - - /* tentative addresses are always sorted back... */ - /* sort tentative addresses after non-tentative. */ - NM_CMP_DIRECT(NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE), - NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_TENTATIVE)); - - /* Sort by address type. For example link local will - * be sorted *after* site local or global. */ - NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address), - _address_pretty_sort_get_prio_6(&a1->address)); - - ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); - ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); - if (ipv6_privacy1 || ipv6_privacy2) { - gboolean public1 = TRUE; - gboolean public2 = TRUE; - - if (ipv6_privacy1) { - if (a1->n_ifa_flags & IFA_F_SECONDARY) - public1 = prefer_temp; - else - public1 = !prefer_temp; - } - if (ipv6_privacy2) { - if (a2->n_ifa_flags & IFA_F_SECONDARY) - public2 = prefer_temp; - else - public2 = !prefer_temp; - } - - NM_CMP_DIRECT(public2, public1); - } - - /* Sort the addresses based on their source. */ - NM_CMP_DIRECT(a2->addr_source, a1->addr_source); - - /* sort permanent addresses before non-permanent. */ - NM_CMP_DIRECT(NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_PERMANENT), - NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_PERMANENT)); - - /* finally sort addresses lexically */ - NM_CMP_DIRECT_IN6ADDR(&a1->address, &a2->address); - NM_CMP_DIRECT_MEMCMP(a1, a2, sizeof(*a1)); - return 0; -} - void -nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h) +nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, + gboolean for_id, + NMHashState *h) { - nm_hash_update_vals(h, - obj->ifindex, - obj->addr_source, - obj->use_ip4_broadcast_address ? obj->broadcast_address : ((in_addr_t) 0u), - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, - obj->use_ip4_broadcast_address, - obj->a_acd_not_ready, - obj->a_force_commit, - obj->a_no_auto_noprefixroute)); - nm_hash_update_strarr(h, obj->label); -} + guint8 w; -int -nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, - const NMPlatformIP4Address *b, - NMPlatformIPAddressCmpType cmp_type) -{ - NM_CMP_SELF(a, b); - - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD(a, b, address); - - switch (cmp_type) { - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: - /* for IPv4 addresses, you can add the same local address with differing peer-address - * (IFA_ADDRESS), provided that their net-part differs. */ - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); - return 0; - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, peer_address); - NM_CMP_FIELD_STR(a, b, label); - if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { - NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, - (const NMPlatformIPAddress *) b)); - - /* Most flags are set by kernel. We only compare the ones that - * NetworkManager actively sets. - * - * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), - * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. - * There are thus no flags to compare for IPv4. */ - - NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), - nm_platform_ip4_broadcast_address_from_addr(b)); - } else { - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); - if (a->use_ip4_broadcast_address) - NM_CMP_FIELD(a, b, broadcast_address); - NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - NM_CMP_FIELD_UNSAFE(a, b, a_no_auto_noprefixroute); - } - return 0; - } - return nm_assert_unreachable_val(0); -} - -void -nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) -{ - nm_hash_update_vals( - h, - obj->ifindex, - obj->addr_source, - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit, obj->a_no_auto_noprefixroute)); -} - -int -nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, - const NMPlatformIP6Address *b, - NMPlatformIPAddressCmpType cmp_type) -{ - const struct in6_addr *p_a, *p_b; - - NM_CMP_SELF(a, b); + nm_assert(obj); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD_IN6ADDR(a, b, address); + w = for_id ? NM_MAX(obj->weight, 1u) : obj->weight; - switch (cmp_type) { - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - return 0; - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: - case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, plen); - p_a = nm_platform_ip6_address_get_peer(a); - p_b = nm_platform_ip6_address_get_peer(b); - NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); - if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { - NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, - (const NMPlatformIPAddress *) b)); - - /* Most flags are set by kernel. We only compare the ones that - * NetworkManager actively sets. - * - * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, - * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. - * We thus only care about IFA_F_MANAGETEMPADDR. */ - NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, - b->n_ifa_flags & IFA_F_MANAGETEMPADDR); - } else { - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - NM_CMP_FIELD_UNSAFE(a, b, a_no_auto_noprefixroute); - } - return 0; - } - return nm_assert_unreachable_val(0); + nm_hash_update_vals(h, obj->ifindex, obj->gateway, w); } void @@ -8736,48 +8427,48 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, { switch (cmp_type) { case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: - nm_hash_update_vals( - h, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), - obj->plen, - obj->metric, - obj->tos, - NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); - break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: nm_hash_update_vals( h, - obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), + nm_ip4_addr_clear_host_address(obj->network, obj->plen), obj->plen, obj->metric, obj->tos, - /* on top of WEAK_ID: */ - obj->ifindex, - nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), - _ip_route_scope_inv_get_normalized(obj), - obj->gateway, - obj->mss, - obj->pref_src, - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->rto_min, - obj->r_rtm_flags & RTNH_F_ONLINK, - NM_HASH_COMBINE_BOOLS(guint16, - obj->metric_any, - obj->table_any, - obj->quickack, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu, - obj->lock_mss)); + NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); + if (NM_IN_SET(cmp_type, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID)) { + nm_hash_update_vals(h, + obj->type_coerced, + nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), + _ip_route_scope_inv_get_normalized(obj), + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + obj->rto_min, + obj->r_rtm_flags & RTNH_F_ONLINK, + NM_HASH_COMBINE_BOOLS(guint16, + obj->quickack, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu, + obj->lock_mss)); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + nm_hash_update_vals(h, + obj->ifindex, + nm_platform_ip4_route_get_n_nexthops(obj), + obj->gateway, + (guint8) MAX(obj->weight, 1u)); + } + } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: nm_hash_update_vals( @@ -8785,10 +8476,12 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), obj->ifindex, - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), + nm_ip4_addr_clear_host_address(obj->network, obj->plen), obj->plen, obj->metric, + nm_platform_ip4_route_get_n_nexthops(obj), obj->gateway, + (guint8) MAX(obj->weight, 1u), nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), _ip_route_scope_inv_get_normalized(obj), obj->tos, @@ -8821,6 +8514,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->plen, obj->metric, obj->gateway, + obj->n_nexthops, + obj->weight, obj->rt_source, obj->scope_inv, obj->tos, @@ -8849,30 +8544,55 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, } int +nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a, + const NMPlatformIP4RtNextHop *b, + gboolean for_id) +{ + guint8 w_a; + guint8 w_b; + + /* Note that weight zero is not valid (in kernel). We thus treat + * weight zero usually the same as 1. + * + * Not here for cmp/hash_update functions. These functions check for the exact + * bit-pattern, and not the it means at other places. */ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, gateway); + + w_a = for_id ? NM_MAX(a->weight, 1u) : a->weight; + w_b = for_id ? NM_MAX(b->weight, 1u) : b->weight; + NM_CMP_DIRECT(w_a, w_b); + + return 0; +} + +int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type) { NM_CMP_SELF(a, b); switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_FIELD_UNSAFE(a, b, table_any); NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); NM_CMP_FIELD(a, b, tos); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { - NM_CMP_FIELD(a, b, ifindex); + if (NM_IN_SET(cmp_type, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID)) { NM_CMP_FIELD(a, b, type_coerced); NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a), _ip_route_scope_inv_get_normalized(b)); - NM_CMP_FIELD(a, b, gateway); NM_CMP_FIELD(a, b, mss); NM_CMP_FIELD(a, b, pref_src); NM_CMP_FIELD(a, b, window); @@ -8881,7 +8601,13 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); NM_CMP_FIELD(a, b, rto_min); + + /* Note that for NetworkManager, the onlink flag is only part of the entire route. + * For kernel, each next hop has it's own onlink flag (rtnh_flags). This means, + * we can only merge ECMP routes, if they agree with their onlink flag, and then + * all next hops are onlink (or not). */ NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK); + NM_CMP_FIELD_UNSAFE(a, b, quickack); NM_CMP_FIELD_UNSAFE(a, b, lock_window); NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); @@ -8889,6 +8615,13 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); NM_CMP_FIELD_UNSAFE(a, b, lock_mss); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, gateway); + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); + } } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: @@ -8902,13 +8635,22 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, 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_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); else NM_CMP_FIELD(a, b, network); NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); + } else + NM_CMP_FIELD(a, b, n_nexthops); NM_CMP_FIELD(a, b, gateway); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); + else + NM_CMP_FIELD(a, b, weight); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); @@ -8958,22 +8700,25 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, nm_hash_update_vals( h, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), + *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), + *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)); break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + nm_assert_not_reached(); + /* fall-through */ case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: nm_hash_update_vals( h, obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), + *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), + *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: */ @@ -8986,12 +8731,12 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->type_coerced, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), obj->ifindex, - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), + *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, obj->gateway, obj->pref_src, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), + *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), obj->mss, @@ -9057,16 +8802,19 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, { NM_CMP_SELF(a, b); switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + nm_assert_not_reached(); + /* fall-through */ case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_FIELD_UNSAFE(a, b, table_any); NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, 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); @@ -9085,7 +8833,7 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, 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_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); else NM_CMP_FIELD_IN6ADDR(a, b, network); NM_CMP_FIELD(a, b, plen); @@ -9094,7 +8842,7 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, 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_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); + NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); NM_CMP_FIELD(a, b, src_plen); NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); @@ -9463,9 +9211,9 @@ nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, if (addr->plen == 0) return NULL; - network_4 = nm_utils_ip4_address_clear_host_address(addr->peer_address, addr->plen); + network_4 = nm_ip4_addr_clear_host_address(addr->peer_address, addr->plen); - if (nm_utils_ip4_address_is_zeronet(network_4)) { + if (nm_ip4_addr_is_zeronet(network_4)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ return NULL; @@ -9570,7 +9318,10 @@ log_ip4_route(NMPlatform *self, _LOG3D("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf))); + nmp_object_to_string(NMP_OBJECT_UP_CAST(route), + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); } static void @@ -9585,7 +9336,10 @@ log_ip6_route(NMPlatform *self, _LOG3D("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); + nmp_object_to_string(NMP_OBJECT_UP_CAST(route), + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); } static void @@ -9750,36 +9504,91 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns) /*****************************************************************************/ -const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { - .v4 = - { - .is_ip4 = TRUE, - .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, - .addr_family = AF_INET, - .sizeof_address = sizeof(NMPlatformIP4Address), - .address_cmp = - (int (*)(const NMPlatformIPXAddress *a, - const NMPlatformIPXAddress *b, - NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, - .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, - char *buf, - gsize len)) nm_platform_ip4_address_to_string, - }, - .v6 = - { - .is_ip4 = FALSE, - .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, - .addr_family = AF_INET6, - .sizeof_address = sizeof(NMPlatformIP6Address), - .address_cmp = - (int (*)(const NMPlatformIPXAddress *a, - const NMPlatformIPXAddress *b, - NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, - .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, - char *buf, - gsize len)) nm_platform_ip6_address_to_string, - }, -}; +typedef struct { + struct in6_addr address; + CList lst; + gint64 timestamp_nsec; + int ifindex; +} IP6DadFailedAddr; + +static void +ip6_dadfailed_addr_free(IP6DadFailedAddr *addr) +{ + c_list_unlink_stale(&addr->lst); + nm_g_slice_free(addr); +} + +static void +ip6_dadfailed_prune_old(NMPlatform *self, gint64 now_nsec) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + IP6DadFailedAddr *addr; + IP6DadFailedAddr *safe; + + c_list_for_each_entry_safe (addr, safe, &priv->ip6_dadfailed_lst_head, lst) { + if (addr->timestamp_nsec + (10 * NM_UTILS_NSEC_PER_SEC) > now_nsec) + break; + ip6_dadfailed_addr_free(addr); + } +} + +gboolean +nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + IP6DadFailedAddr *addr; + + ip6_dadfailed_prune_old(self, nm_utils_get_monotonic_timestamp_nsec()); + + c_list_for_each_entry_prev (addr, &priv->ip6_dadfailed_lst_head, lst) { + if (addr->ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&addr->address, ip6)) { + return TRUE; + } + } + return FALSE; +} + +/* + * If an IPv6 address fails DAD and has infinite lifetime, kernel just + * sets the DADFAILED flag. However when the address has a finite + * lifetime kernel deletes it immediately and the RTM_DELLINK netlink + * message contains the DADFAILED flag. In the second case, we remove + * the address from the platform cache and there is no way for + * platform's clients to check whether DAD failed. To work around + * this, we store all deleted-with-DADFAILED addresses and provide a + * mechanism to access them. + */ +void +nm_platform_ip6_dadfailed_set(NMPlatform *self, + int ifindex, + const struct in6_addr *ip6, + gboolean failed) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + gint64 now_nsec = nm_utils_get_monotonic_timestamp_nsec(); + IP6DadFailedAddr *addr; + IP6DadFailedAddr *safe; + + ip6_dadfailed_prune_old(self, now_nsec); + + if (failed) { + addr = g_slice_new(IP6DadFailedAddr); + *addr = (IP6DadFailedAddr){ + .address = *ip6, + .ifindex = ifindex, + .timestamp_nsec = now_nsec, + }; + c_list_link_tail(&priv->ip6_dadfailed_lst_head, &addr->lst); + } else { + c_list_for_each_entry_safe (addr, safe, &priv->ip6_dadfailed_lst_head, lst) { + if (addr->ifindex == ifindex && IN6_ARE_ADDR_EQUAL(&addr->address, ip6)) { + ip6_dadfailed_addr_free(addr); + } + } + } +} + +/*****************************************************************************/ const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { .v4 = @@ -9819,6 +9628,20 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); switch (prop_id) { + case PROP_MULTI_IDX: + /* construct-only */ + { + NMDedupMultiIndex *multi_idx; + + multi_idx = g_value_get_pointer(value); + if (!multi_idx) + multi_idx = nm_dedup_multi_index_new(); + else + multi_idx = nm_dedup_multi_index_ref(multi_idx); + + priv->multi_idx = multi_idx; + break; + } case PROP_NETNS_SUPPORT: /* construct-only */ if (g_value_get_boolean(value)) { @@ -9865,9 +9688,10 @@ constructor(GType type, guint n_construct_params, GObjectConstructParam *constru self = NM_PLATFORM(object); priv = NM_PLATFORM_GET_PRIVATE(self); - priv->multi_idx = nm_dedup_multi_index_new(); + nm_assert(priv->multi_idx); priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev); + c_list_init(&priv->ip6_dadfailed_lst_head); return object; } @@ -9877,6 +9701,7 @@ finalize(GObject *object) { NMPlatform *self = NM_PLATFORM(object); NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); + IP6DadFailedAddr *addr; nm_clear_g_source(&priv->ip4_dev_route_blacklist_check_id); nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id); @@ -9885,6 +9710,10 @@ finalize(GObject *object) nm_dedup_multi_index_unref(priv->multi_idx); nmp_cache_free(priv->cache); + while ((addr = c_list_first_entry(&priv->ip6_dadfailed_lst_head, IP6DadFailedAddr, lst))) { + ip6_dadfailed_addr_free(addr); + } + G_OBJECT_CLASS(nm_platform_parent_class)->finalize(object); } @@ -9903,6 +9732,14 @@ nm_platform_class_init(NMPlatformClass *platform_class) g_object_class_install_property( object_class, + PROP_MULTI_IDX, + g_param_spec_pointer(NM_PLATFORM_MULTI_IDX, + "", + "", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + object_class, PROP_NETNS_SUPPORT, g_param_spec_boolean(NM_PLATFORM_NETNS_SUPPORT, "", diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 30d0b506..1cd9c6c6 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -8,9 +8,10 @@ #include "libnm-platform/nmp-base.h" #include "libnm-base/nm-base.h" +#include "nmp-plobj.h" #define NM_TYPE_PLATFORM (nm_platform_get_type()) -#define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform)) +#define NM_PLATFORM(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform)) #define NM_PLATFORM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PLATFORM, NMPlatformClass)) #define NM_IS_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PLATFORM)) @@ -22,16 +23,11 @@ /*****************************************************************************/ +#define NM_PLATFORM_CACHE_TC "cache-tc" +#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" +#define NM_PLATFORM_MULTI_IDX "multi-idx" #define NM_PLATFORM_NETNS_SUPPORT "netns-support" #define NM_PLATFORM_USE_UDEV "use-udev" -#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" -#define NM_PLATFORM_CACHE_TC "cache-tc" - -/*****************************************************************************/ - -/* IFNAMSIZ is both defined in <linux/if.h> and <net/if.h>. In the past, these - * headers conflicted, so we cannot simply include either of them in a header-file.*/ -#define NMP_IFNAMSIZ 16 /*****************************************************************************/ @@ -41,24 +37,8 @@ struct udev_device; typedef gboolean (*NMPObjectPredicateFunc)(const NMPObject *obj, gpointer user_data); -/* workaround for older libnl version, that does not define these flags. */ -#ifndef IFA_F_MANAGETEMPADDR -#define IFA_F_MANAGETEMPADDR 0x100 -#endif -#ifndef IFA_F_NOPREFIXROUTE -#define IFA_F_NOPREFIXROUTE 0x200 -#endif - #define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ -/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers - * that don't define it. */ -#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ -#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ -#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ -#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ -#define NM_IN6_ADDR_GEN_MODE_RANDOM 3 /* IN6_ADDR_GEN_MODE_RANDOM */ - #define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */ #define NM_MPTCP_PM_ADDR_FLAG_SIGNAL ((guint32) (1 << 0)) @@ -101,14 +81,6 @@ typedef enum { } NMPNlmFlags; typedef enum { - NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, - - NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, - - NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, -} NMPlatformIPAddressCmpType; - -typedef enum { /* compare fields which kernel considers as similar routes. * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID * and means that `ip route add` would fail to add two routes @@ -131,6 +103,11 @@ typedef enum { */ NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + /* IPv4 route can have multiple hops. This is the ID, by which multiple + * routes are merged according to the next hop. This is basically NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + * which ignores the next hops. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID, + /* compare all fields as they make sense for kernel. For example, * a route destination 192.168.1.5/24 is not accepted by kernel and * we treat it identical to 192.168.1.0/24. Semantically these @@ -171,50 +148,11 @@ G_STATIC_ASSERT(_nm_alignof(NMPLinkAddress) == 1); gconstpointer nmp_link_address_get(const NMPLinkAddress *addr, size_t *length); GBytes *nmp_link_address_get_as_bytes(const NMPLinkAddress *addr); -typedef enum { - - /* match-flags are strictly inclusive. That means, - * by default nothing is matched, but if you enable a particular - * flag, a candidate that matches passes the check. - * - * In other words: adding more flags can only extend the result - * set of matching objects. - * - * Also, the flags form partitions. Like, an address can be either of - * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for - * the ADDRSTATE match types. - */ - NM_PLATFORM_MATCH_WITH_NONE = 0, - - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL = (1LL << 0), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1), - NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY = - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL, - - NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL = (1LL << 2), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED = (1LL << 5), - NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY = - NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED, -} NMPlatformMatchFlags; - #define NM_PLATFORM_LINK_OTHER_NETNS (-1) -struct _NMPlatformObject { - /* the object type has no fields of its own, it is only used to having - * a special pointer type that can be used to indicate "any" type. */ - char _dummy_don_t_use_me; -}; - -#define __NMPlatformObjWithIfindex_COMMON \ - int ifindex; \ - ; - struct _NMPlatformObjWithIfindex { __NMPlatformObjWithIfindex_COMMON; -}; +} _nm_alignas(NMPlatformObject); struct _NMPlatformLink { __NMPlatformObjWithIfindex_COMMON; @@ -271,7 +209,7 @@ struct _NMPlatformLink { bool connected : 1; bool initialized : 1; -}; +} _nm_alignas(NMPlatformObject); typedef enum { NM_PLATFORM_SIGNAL_ID_NONE, @@ -295,129 +233,6 @@ typedef enum { NM_PLATFORM_SIGNAL_REMOVED, } NMPlatformSignalChangeType; -#define NM_PLATFORM_IP_ADDRESS_CAST(address) \ - NM_CONSTCAST(NMPlatformIPAddress, \ - (address), \ - NMPlatformIPXAddress, \ - NMPlatformIP4Address, \ - NMPlatformIP6Address) - -#define __NMPlatformIPAddress_COMMON \ - __NMPlatformObjWithIfindex_COMMON; \ - NMIPConfigSource addr_source; \ - \ - /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). - * - * The rules are: - * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses - * are permanent. This rule is so that unset addresses (calloc) are permanent by default. - * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly - * set to 0). Such addresses are permanent. - * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp - * and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred - * is anchored at @timestamp. - * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time - * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). - * - * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the - * lease was received. Addresses from kernel might have the @timestamp based on the last modification - * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring - * @lifetime and @preferred. - */ \ - guint32 timestamp; \ - guint32 lifetime; /* seconds since timestamp */ \ - guint32 preferred; /* seconds since timestamp */ \ - \ - /* ifa_flags in 'struct ifaddrmsg' from <linux/if_addr.h>, extended to 32 bit by - * IFA_FLAGS attribute. */ \ - guint32 n_ifa_flags; \ - \ - bool use_ip4_broadcast_address : 1; \ - \ - /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be - * used by the upper layers which use NMPlatformIPRoute to track addresses that - * should be configured. */ \ - bool a_force_commit : 1; \ - \ - /* nm_platform_ip_address_sync() likes to add IFA_F_NOPREFIXROUTE flag for all - * addresses, regardless of a_ifi_flags property. By setting this boolean, that - * automatism can be suppressed, and the noprefixroute flag does not get added - * automatically. */ \ - bool a_no_auto_noprefixroute : 1; \ - \ - /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then - * be unclear how the following fields get merged. We could also use a zero bitfield, - * but instead we just have there the uint8 field. */ \ - guint8 plen; \ - ; - -/** - * NMPlatformIPAddress: - * - * Common parts of NMPlatformIP4Address and NMPlatformIP6Address. - **/ -typedef struct { - __NMPlatformIPAddress_COMMON; - _nm_alignas(NMIPAddr) guint8 address_ptr[]; -} NMPlatformIPAddress; - -/** - * NMPlatformIP4Address: - * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() - **/ -struct _NMPlatformIP4Address { - __NMPlatformIPAddress_COMMON; - - /* The local address IFA_LOCAL. */ - _nm_alignas(NMIPAddr) in_addr_t address; - - /* The IFA_ADDRESS PTP peer address. This field is rather important, because - * it constitutes the identifier for the IPv4 address (e.g. you can add two - * addresses that only differ by their peer's network-part. - * - * Beware that for most cases, NetworkManager doesn't want to set an explicit - * peer-address. However, that corresponds to setting the peer address to @address - * itself. Leaving peer-address unset/zero, means explicitly setting the peer - * address to 0.0.0.0, which you probably don't want. - * */ - in_addr_t peer_address; /* PTP peer address */ - - /* IFA_BROADCAST. - * - * This parameter is ignored unless use_ip4_broadcast_address is TRUE. - * See nm_platform_ip4_broadcast_address_from_addr(). */ - in_addr_t broadcast_address; - - char label[NMP_IFNAMSIZ]; - - /* Whether the address is ready to be configured. By default, an address is, but this - * flag may indicate that the address is just for tracking purpose only, but the ACD - * state is not yet ready for the address to be configured. */ - bool a_acd_not_ready : 1; -}; - -/** - * NMPlatformIP6Address: - * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() - **/ -struct _NMPlatformIP6Address { - __NMPlatformIPAddress_COMMON; - _nm_alignas(NMIPAddr) struct in6_addr address; - struct in6_addr peer_address; -}; - -typedef union { - NMPlatformIPAddress ax; - NMPlatformIP4Address a4; - NMPlatformIP6Address a6; -} NMPlatformIPXAddress; - -#undef __NMPlatformIPAddress_COMMON - -#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__})) - -#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__})) - /* Default value for adding an IPv4 route. This is also what iproute2 does. * Note that contrary to IPv6, you can add routes with metric 0 and it is even * the default. @@ -545,16 +360,16 @@ typedef union { */ \ guint8 type_coerced; \ \ - /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then + /* Don't have a bitfield as last field in __NMPlatformIPRoute_COMMON. It would then * be unclear how the following fields get merged. We could also use a zero bitfield, - * but instead we just have there the uint8 field. */ \ + * but instead we just have there the uint8 field. */ \ guint8 plen; \ ; typedef struct { __NMPlatformIPRoute_COMMON; _nm_alignas(NMIPAddr) guint8 network_ptr[]; -} NMPlatformIPRoute; +} _nm_alignas(NMPlatformObject) NMPlatformIPRoute; #define NM_PLATFORM_IP_ROUTE_CAST(route) \ NM_CONSTCAST(NMPlatformIPRoute, \ @@ -567,9 +382,27 @@ typedef struct { struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; + in_addr_t network; - /* RTA_GATEWAY. The gateway is part of the primary key for a route */ + /* If n_nexthops is zero, the the address has no next hops. That applies + * to certain route types like blackhole. + * If n_nexthops is 1, then the fields "ifindex", "gateway" and "weight" + * are the first next-hop. There are no further nexthops. + * If n_nexthops is greater than 1, the first next hop is in the fields + * "ifindex", "gateway", "weight", and the (n_nexthops-1) hops are in + * NMPObjectIP4Route.extra_nexthops field (outside the NMPlatformIP4Route + * struct). + * + * For convenience, if ifindex > 0 and n_nexthops == 0, we assume that n_nexthops + * is in fact 1. If ifindex is <= 0, n_nexthops must be zero. + * See nm_platform_ip4_route_get_n_nexthops(). */ + guint n_nexthops; + + /* RTA_GATEWAY. The gateway is part of the primary key for a route. + * If n_nexthops is zero, this value is undefined (should be zero). + * If n_nexthops is greater or equal to one, this is the gateway of + * the first hop. */ in_addr_t gateway; /* RTA_PREFSRC (called "src" by iproute2). @@ -578,6 +411,20 @@ struct _NMPlatformIP4Route { * pref_src must match, unless set to 0.0.0.0 to match any. */ in_addr_t pref_src; + /* This is the weight of for the first next-hop, in case of n_nexthops > 1. + * + * If n_nexthops is zero, this value is undefined (should be zero). + * If n_nexthops is 1, this also doesn't matter, but it's usually set to + * zero. + * If n_nexthops is greater or equal to one, this is the weight of + * the first hop. + * + * Note that upper layers (nm_utils_ip_route_attribute_to_platform()) use this flag to indicate + * whether this is a multihop route. Single-hop, non-ECMP routes will have a weight of zero. + * + * The valid range for weight in kernel is 1-256. */ + guint16 weight; + /* rtm_tos (iproute2: tos) * * For IPv4, tos is part of the weak-id (like metric). @@ -597,7 +444,7 @@ struct _NMPlatformIP4Route { * For IPv6 routes, the scope is ignored and kernel always assumes global scope. * Hence, this field is only in NMPlatformIP4Route. */ guint8 scope_inv; -}; +} _nm_alignas(NMPlatformObject); struct _NMPlatformIP6Route { __NMPlatformIPRoute_COMMON; @@ -630,7 +477,7 @@ struct _NMPlatformIP6Route { * The type is guint8 to keep the struct size small. But the values are compatible with * the NMIcmpv6RouterPref enum. */ guint8 rt_pref; -}; +} _nm_alignas(NMPlatformObject); typedef union { NMPlatformIPRoute rx; @@ -684,7 +531,7 @@ typedef struct { guint8 ip_proto; /* FRA_IP_PROTO */ bool uid_range_has : 1; /* has(FRA_UID_RANGE) */ -} NMPlatformRoutingRule; +} _nm_alignas(NMPlatformObject) NMPlatformRoutingRule; #define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0)) @@ -752,7 +599,7 @@ typedef struct { NMPlatformQdiscSfq sfq; NMPlatformQdiscTbf tbf; }; -} NMPlatformQdisc; +} _nm_alignas(NMPlatformObject) NMPlatformQdisc; typedef struct { char sdata[32]; @@ -792,28 +639,7 @@ typedef struct { guint32 parent; guint32 info; NMPlatformAction action; -} NMPlatformTfilter; - -typedef struct { - bool is_ip4; - NMPObjectType obj_type; - gint8 addr_family; - guint8 sizeof_address; - int (*address_cmp)(const NMPlatformIPXAddress *a, - const NMPlatformIPXAddress *b, - NMPlatformIPAddressCmpType cmp_type); - const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); -} NMPlatformVTableAddress; - -typedef union { - struct { - NMPlatformVTableAddress v6; - NMPlatformVTableAddress v4; - }; - NMPlatformVTableAddress vx[2]; -} _NMPlatformVTableAddressUnion; - -extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; +} _nm_alignas(NMPlatformObject) NMPlatformTfilter; typedef struct { bool is_ip4; @@ -843,6 +669,25 @@ typedef struct { } NMPlatformVFVlan; typedef struct { + int ifindex; + in_addr_t gateway; + /* The valid range for weight is 1-256. Single hop routes in kernel + * don't have a weight, we assign them weight zero (to indicate the + * weight is missing). + * + * Upper layers (nm_utils_ip_route_attribute_to_platform()) care about + * the distinction of unset weight (no-ECMP). They express no-ECMP as + * zero. + */ + guint16 weight; + + /* FIXME: each next hop in kernel also has a rtnh_flags (for example to + * set RTNH_F_ONLINK). As the next hop is part of the identifier of an + * IPv4 route, so is their flags. We must also track the flag, otherwise + * two routes that look different for kernel, get merged by platform cache. */ +} NMPlatformIP4RtNextHop; + +typedef struct { guint num_vlans; guint32 index; guint32 min_tx_rate; @@ -887,7 +732,7 @@ typedef struct { bool mcast_snooping : 1; bool stp_state : 1; bool vlan_stats_enabled : 1; -} NMPlatformLnkBridge; +} _nm_alignas(NMPlatformObject) NMPlatformLnkBridge; extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default; @@ -929,7 +774,7 @@ typedef struct { bool tlb_dynamic_lb_has : 1; bool updelay_has : 1; bool use_carrier : 1; -} NMPlatformLnkBond; +} _nm_alignas(NMPlatformObject) NMPlatformLnkBond; typedef struct { int parent_ifindex; @@ -943,12 +788,12 @@ typedef struct { guint8 tos; bool path_mtu_discovery : 1; bool is_tap : 1; -} NMPlatformLnkGre; +} _nm_alignas(NMPlatformObject) NMPlatformLnkGre; typedef struct { int p_key; const char *mode; -} NMPlatformLnkInfiniband; +} _nm_alignas(NMPlatformObject) NMPlatformLnkInfiniband; typedef struct { struct in6_addr local; @@ -968,7 +813,7 @@ typedef struct { guint16 output_flags; bool is_tap : 1; bool is_gre : 1; -} NMPlatformLnkIp6Tnl; +} _nm_alignas(NMPlatformObject) NMPlatformLnkIp6Tnl; typedef struct { int parent_ifindex; @@ -977,9 +822,28 @@ typedef struct { guint8 ttl; guint8 tos; bool path_mtu_discovery : 1; -} NMPlatformLnkIpIp; +} _nm_alignas(NMPlatformObject) NMPlatformLnkIpIp; + +typedef struct { + int parent_ifindex; + in_addr_t local; + in_addr_t remote; + guint32 ikey; + guint32 okey; + guint32 fwmark; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVti; + +typedef struct { + int parent_ifindex; + struct in6_addr local; + struct in6_addr remote; + guint32 ikey; + guint32 okey; + guint32 fwmark; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVti6; typedef struct { + int parent_ifindex; guint64 sci; /* host byte order */ guint64 cipher_suite; guint32 window; @@ -992,13 +856,13 @@ typedef struct { bool es : 1; bool scb : 1; bool replay_protect : 1; -} NMPlatformLnkMacsec; +} _nm_alignas(NMPlatformObject) NMPlatformLnkMacsec; typedef struct { guint mode; bool no_promisc : 1; bool tap : 1; -} NMPlatformLnkMacvlan; +} _nm_alignas(NMPlatformObject) NMPlatformLnkMacvlan; typedef struct { int parent_ifindex; @@ -1009,7 +873,7 @@ typedef struct { guint8 tos; guint8 proto; bool path_mtu_discovery : 1; -} NMPlatformLnkSit; +} _nm_alignas(NMPlatformObject) NMPlatformLnkSit; typedef struct { guint32 owner; @@ -1024,17 +888,17 @@ typedef struct { bool vnet_hdr : 1; bool multi_queue : 1; bool persist : 1; -} NMPlatformLnkTun; +} _nm_alignas(NMPlatformObject) NMPlatformLnkTun; typedef struct { - /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ guint16 id; + guint16 protocol; _NMVlanFlags flags; -} NMPlatformLnkVlan; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVlan; typedef struct { guint32 table; -} NMPlatformLnkVrf; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVrf; typedef struct { struct in6_addr group6; @@ -1055,7 +919,7 @@ typedef struct { bool rsc : 1; bool l2miss : 1; bool l3miss : 1; -} NMPlatformLnkVxlan; +} _nm_alignas(NMPlatformObject) NMPlatformLnkVxlan; #define NMP_WIREGUARD_PUBLIC_KEY_LEN 32 #define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32 @@ -1065,7 +929,7 @@ typedef struct { guint16 listen_port; guint8 private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; -} NMPlatformLnkWireGuard; +} _nm_alignas(NMPlatformObject) NMPlatformLnkWireGuard; typedef enum { NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE = 0, @@ -1370,10 +1234,7 @@ typedef struct { struct in6_addr address, guint8 plen); - int (*ip_route_add)(NMPlatform *self, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route); + int (*ip_route_add)(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack); int (*ip_route_get)(NMPlatform *self, int addr_family, gconstpointer address, @@ -1426,29 +1287,6 @@ GType nm_platform_get_type(void); /*****************************************************************************/ -static inline in_addr_t -nm_platform_ip4_broadcast_address_create(in_addr_t address, guint8 plen) -{ - return address | ~_nm_utils_ip4_prefix_to_netmask(plen); -} - -static inline in_addr_t -nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr) -{ - nm_assert(addr); - - if (addr->use_ip4_broadcast_address) - return addr->broadcast_address; - - /* the set broadcast-address gets ignored, and we determine a default brd base - * on the peer IFA_ADDRESS. */ - if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */) - return nm_platform_ip4_broadcast_address_create(addr->peer_address, addr->plen); - return 0u; -} - -/*****************************************************************************/ - /** * nm_platform_route_table_coerce: * @table: the route table, in its original value as received @@ -1589,9 +1427,9 @@ nm_platform_ip4_address_get_scope(in_addr_t addr) /* For IPv4 addresses, we can set any scope we want (for any address). * However, there are scopes that make sense based on the address, * so choose those. */ - return nm_utils_ip4_address_is_loopback(addr) ? (254 /* RT_SCOPE_HOST */) - : nm_utils_ip4_address_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */) - : (0 /* RT_SCOPE_UNIVERSE */); + return nm_ip4_addr_is_loopback(addr) ? (254 /* RT_SCOPE_HOST */) + : nm_ip4_addr_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */) + : (0 /* RT_SCOPE_UNIVERSE */); } static inline guint8 @@ -1612,7 +1450,7 @@ nm_platform_ip_address_get_scope(int addr_family, gconstpointer addr) /* Note that this function returns the scope as we configure * it in kernel (for IPv4) or as kernel chooses it (for IPv6). * - * That means, rfc1918 private addresses nm_utils_ip_is_site_local() are + * That means, rfc1918 private addresses nm_ip_addr_is_site_local() are * considered RT_SCOPE_UNIVERSE. * * Also, the deprecated IN6_IS_ADDR_SITELOCAL() addresses (fec0::/10) @@ -1851,28 +1689,13 @@ nm_platform_link_sit_add(NMPlatform *self, } static inline int -nm_platform_link_vlan_add(NMPlatform *self, - const char *name, - int parent, - int vlanid, - guint32 vlanflags, - const NMPlatformLink **out_link) +nm_platform_link_vlan_add(NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkVlan *props, + const NMPlatformLink **out_link) { - g_return_val_if_fail(parent >= 0, -NME_BUG); - g_return_val_if_fail(vlanid >= 0, -NME_BUG); - - return nm_platform_link_add(self, - NM_LINK_TYPE_VLAN, - name, - parent, - NULL, - 0, - 0, - &((NMPlatformLnkVlan){ - .id = vlanid, - .flags = vlanflags, - }), - out_link); + return nm_platform_link_add(self, NM_LINK_TYPE_VLAN, name, parent, NULL, 0, 0, props, out_link); } static inline int @@ -1885,6 +1708,24 @@ nm_platform_link_vrf_add(NMPlatform *self, } static inline int +nm_platform_link_vti_add(NMPlatform *self, + const char *name, + const NMPlatformLnkVti *props, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add(self, NM_LINK_TYPE_VTI, name, 0, NULL, 0, 0, props, out_link); +} + +static inline int +nm_platform_link_vti6_add(NMPlatform *self, + const char *name, + const NMPlatformLnkVti6 *props, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add(self, NM_LINK_TYPE_VTI6, name, 0, NULL, 0, 0, props, out_link); +} + +static inline int nm_platform_link_vxlan_add(NMPlatform *self, const char *name, const NMPlatformLnkVxlan *props, @@ -2173,6 +2014,10 @@ const NMPlatformLnkVlan * nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkVrf * nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVti * +nm_platform_link_get_lnk_vti(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVti6 * +nm_platform_link_get_lnk_vti6(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkVxlan * nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkWireGuard * @@ -2238,9 +2083,6 @@ guint16 nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex); gboolean nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr); gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel); -void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); -const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); - const NMPObject *nm_platform_ip_address_get(NMPlatform *self, int addr_family, int ifindex, @@ -2252,6 +2094,9 @@ const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self, guint8 plen, in_addr_t peer_address); +const NMPlatformIP6Address * +nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address); + int nm_platform_link_sit_add(NMPlatform *self, const char *name, const NMPlatformLnkSit *props, @@ -2274,9 +2119,6 @@ int nm_platform_link_wireguard_change(NMPlatform * guint peers_len, NMPlatformWireGuardChangeFlags change_flags); -const NMPlatformIP6Address * -nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address); - gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *route); gboolean nm_platform_ip4_address_add(NMPlatform *self, @@ -2328,11 +2170,17 @@ nm_platform_ip_address_delete(NMPlatform * } } -gboolean nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray *known_addresses, - GPtrArray *addresses_prune); +typedef enum { + NMP_IP_ADDRESS_SYNC_FLAGS_NONE = 0, + NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE = (1 << 0), +} NMPIPAddressSyncFlags; + +gboolean nm_platform_ip_address_sync(NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *known_addresses, + GPtrArray *addresses_prune, + NMPIPAddressSyncFlags flags); GPtrArray * nm_platform_ip_address_get_prune_list(NMPlatform *self, @@ -2343,17 +2191,6 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, gboolean nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex); -static inline gpointer -nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr) -{ - nm_assert_addr_family(addr_family); - nm_assert(addr); - - if (NM_IS_IPv4(addr_family)) - return &((NMPlatformIP4Address *) addr)->peer_address; - return &((NMPlatformIP6Address *) addr)->peer_address; -} - void nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route); static inline guint32 @@ -2365,6 +2202,23 @@ nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r) : r->metric; } +static inline guint +nm_platform_ip4_route_get_n_nexthops(const NMPlatformIP4Route *r) +{ + /* The first hop of the "n_nexthops" is in NMPlatformIP4Route + * itself. Thus, if the caller only sets ifindex and leaves + * n_nexthops at zero, the number of next hops is still 1 + * (for convenience of the user who wants to initialize a + * single hop route). */ + if (r->n_nexthops >= 1) { + nm_assert(r->ifindex > 0); + return r->n_nexthops; + } + if (r->ifindex > 0) + return 1; + return 0; +} + static inline guint32 nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r) { @@ -2398,7 +2252,10 @@ nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route } int nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route); -int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +int nm_platform_ip4_route_add(NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops); int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform *self, @@ -2449,14 +2306,23 @@ const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len); const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len); const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vti_to_string(const NMPlatformLnkVti *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vti6_to_string(const NMPlatformLnkVti6 *lnk, char *buf, gsize len); const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len); const char * nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len); -const char * -nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len); -const char * -nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len); -const char *nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len); + +const char *nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops, + char *buf, + gsize len); + +static inline const char * +nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) +{ + return nm_platform_ip4_route_to_string_full(route, NULL, buf, len); +} + const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len); const char * nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len); @@ -2492,79 +2358,52 @@ int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b); int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b); int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b); +int nm_platform_lnk_vti_cmp(const NMPlatformLnkVti *a, const NMPlatformLnkVti *b); +int nm_platform_lnk_vti6_cmp(const NMPlatformLnkVti6 *a, const NMPlatformLnkVti6 *b); int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b); -int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, - const NMPlatformIP4Address *b, - NMPlatformIPAddressCmpType cmp_type); -int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, - const NMPlatformIP6Address *b, - NMPlatformIPAddressCmpType cmp_type); - -static inline int -nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) -{ - return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); -} - -static inline int -nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) -{ - return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); -} - -int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, - const NMPlatformIP4Address *a2); - -int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, - const NMPlatformIP6Address *a2, - gboolean prefer_temp); GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex); int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type); +int nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a, + const NMPlatformIP4RtNextHop *b, + gboolean for_id); int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type); -static inline int -nm_platform_ip4_route_cmp_full(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) -{ - return nm_platform_ip4_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - -static inline int -nm_platform_ip6_route_cmp_full(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) -{ - return nm_platform_ip6_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - int nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b, NMPlatformRoutingRuleCmpType cmp_type); -static inline int -nm_platform_routing_rule_cmp_full(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b) -{ - return nm_platform_routing_rule_cmp(a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); -} +int +nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle); -int nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b); -int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle); int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b); int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b); void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h); -void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h); -void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h); void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); + +static inline guint +nm_platform_ip4_route_hash(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type) +{ + NMHashState h; + + nm_hash_init(&h, 1118769853u); + nm_platform_ip4_route_hash_update(obj, cmp_type, &h); + return nm_hash_complete(&h); +} + +void nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, + gboolean for_id, + NMHashState *h); void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); @@ -2583,6 +2422,8 @@ void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h); void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h); void nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h); +void nm_platform_lnk_vti_hash_update(const NMPlatformLnkVti *obj, NMHashState *h); +void nm_platform_lnk_vti6_hash_update(const NMPlatformLnkVti6 *obj, NMHashState *h); void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h); void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h); @@ -2596,13 +2437,6 @@ gboolean nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpoi #define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162) -const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len); -const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len); -const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len); -const char *nm_platform_route_scope2str(int scope, char *buf, gsize len); - -int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); - gboolean nm_platform_ethtool_set_wake_on_lan(NMPlatform *self, int ifindex, _NMSettingWiredWakeOnLan wol, @@ -2653,21 +2487,6 @@ struct _NMDedupMultiIndex *nm_platform_get_multi_idx(NMPlatform *self); /*****************************************************************************/ -NMPlatformIP4Route *nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, - int ifindex, - guint32 route_table, - guint32 route_metric, - gboolean force_commit, - NMPlatformIP4Route *dst); - -/*****************************************************************************/ - -gboolean nm_platform_ip_address_match(int addr_family, - const NMPlatformIPAddress *addr, - NMPlatformMatchFlags match_flag); - -/*****************************************************************************/ - guint16 nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type); int @@ -2675,4 +2494,35 @@ nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatfo GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self); +gboolean nm_platform_ip6_dadfailed_check(NMPlatform *self, int ifindex, const struct in6_addr *ip6); +void nm_platform_ip6_dadfailed_set(NMPlatform *self, + int ifindex, + const struct in6_addr *ip6, + gboolean failed); + +/*****************************************************************************/ + +static inline NMPlatformIP4Address * +nm_platform_ip4_address_init_loopback_addr1(NMPlatformIP4Address *a) +{ + *a = ((NMPlatformIP4Address){ + .address = NM_IPV4LO_ADDR1, + .peer_address = NM_IPV4LO_ADDR1, + .ifindex = NM_LOOPBACK_IFINDEX, + .plen = NM_IPV4LO_PREFIXLEN, + }); + return a; +} + +static inline NMPlatformIP6Address * +nm_platform_ip6_address_init_loopback(NMPlatformIP6Address *a) +{ + *a = ((NMPlatformIP6Address){ + .address = IN6ADDR_LOOPBACK_INIT, + .ifindex = NM_LOOPBACK_IFINDEX, + .plen = 128, + }); + return a; +} + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/libnm-platform/nmp-base.c b/src/libnm-platform/nmp-base.c new file mode 100644 index 00000000..c23190b3 --- /dev/null +++ b/src/libnm-platform/nmp-base.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 - 2018 Red Hat, Inc. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nmp-base.h" + +#include <linux/rtnetlink.h> +#include <linux/if.h> +#include "nm-compat-headers/linux/if_addr.h" + +/*****************************************************************************/ + +NM_UTILS_FLAGS2STR_DEFINE(nm_platform_link_flags2str, + unsigned, + NM_UTILS_FLAGS2STR(IFF_LOOPBACK, "loopback"), + NM_UTILS_FLAGS2STR(IFF_BROADCAST, "broadcast"), + NM_UTILS_FLAGS2STR(IFF_POINTOPOINT, "pointopoint"), + NM_UTILS_FLAGS2STR(IFF_MULTICAST, "multicast"), + NM_UTILS_FLAGS2STR(IFF_NOARP, "noarp"), + NM_UTILS_FLAGS2STR(IFF_ALLMULTI, "allmulti"), + NM_UTILS_FLAGS2STR(IFF_PROMISC, "promisc"), + NM_UTILS_FLAGS2STR(IFF_MASTER, "master"), + NM_UTILS_FLAGS2STR(IFF_SLAVE, "slave"), + NM_UTILS_FLAGS2STR(IFF_DEBUG, "debug"), + NM_UTILS_FLAGS2STR(IFF_DYNAMIC, "dynamic"), + NM_UTILS_FLAGS2STR(IFF_AUTOMEDIA, "automedia"), + NM_UTILS_FLAGS2STR(IFF_PORTSEL, "portsel"), + NM_UTILS_FLAGS2STR(IFF_NOTRAILERS, "notrailers"), + NM_UTILS_FLAGS2STR(IFF_UP, "up"), + NM_UTILS_FLAGS2STR(IFF_RUNNING, "running"), + NM_UTILS_FLAGS2STR(IFF_LOWER_UP, "lowerup"), + NM_UTILS_FLAGS2STR(IFF_DORMANT, "dormant"), + NM_UTILS_FLAGS2STR(IFF_ECHO, "echo"), ); + +NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str, + guint8, + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_NONE, "none"), + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"), + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"), + NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), ); + +NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str, + unsigned, + NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"), + NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"), + NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"), + NM_UTILS_FLAGS2STR(IFA_F_DADFAILED, "dadfailed"), + NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"), + NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"), + NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), + NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"), + NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"), + NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"), + NM_UTILS_FLAGS2STR(IFA_F_MCAUTOJOIN, "mcautojoin"), + NM_UTILS_FLAGS2STR(IFA_F_STABLE_PRIVACY, "stable-privacy"), ); + +G_STATIC_ASSERT(IFA_F_SECONDARY == IFA_F_TEMPORARY); + +NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str, + int, + NM_UTILS_ENUM2STR(RT_SCOPE_NOWHERE, "nowhere"), + NM_UTILS_ENUM2STR(RT_SCOPE_HOST, "host"), + NM_UTILS_ENUM2STR(RT_SCOPE_LINK, "link"), + NM_UTILS_ENUM2STR(RT_SCOPE_SITE, "site"), + NM_UTILS_ENUM2STR(RT_SCOPE_UNIVERSE, "global"), ); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index a6ee3df1..80d254b2 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -11,6 +11,22 @@ /*****************************************************************************/ +/* IFNAMSIZ is both defined in <linux/if.h> and <net/if.h>. In the past, these + * headers conflicted, so we cannot simply include either of them in a header-file.*/ +#define NMP_IFNAMSIZ 16 + +/*****************************************************************************/ + +/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers + * that don't define it. */ +#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ +#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ +#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ +#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ +#define NM_IN6_ADDR_GEN_MODE_RANDOM 3 /* IN6_ADDR_GEN_MODE_RANDOM */ + +/*****************************************************************************/ + typedef enum { NM_PLATFORM_LINK_DUPLEX_UNKNOWN, NM_PLATFORM_LINK_DUPLEX_HALF, @@ -145,6 +161,8 @@ typedef enum _nm_packed { NMP_OBJECT_TYPE_LNK_TUN, NMP_OBJECT_TYPE_LNK_VLAN, NMP_OBJECT_TYPE_LNK_VRF, + NMP_OBJECT_TYPE_LNK_VTI, + NMP_OBJECT_TYPE_LNK_VTI6, NMP_OBJECT_TYPE_LNK_VXLAN, NMP_OBJECT_TYPE_LNK_WIREGUARD, NMP_OBJECT_TYPE_LNK_BOND, @@ -189,4 +207,11 @@ typedef enum { NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE, } NMIPRouteTableSyncMode; +/*****************************************************************************/ + +const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len); +const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len); +const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len); +const char *nm_platform_route_scope2str(int scope, char *buf, gsize len); + #endif /* __NMP_FWD_H__ */ diff --git a/src/libnm-platform/nmp-global-tracker.c b/src/libnm-platform/nmp-global-tracker.c index 12869cb2..09f1e217 100644 --- a/src/libnm-platform/nmp-global-tracker.c +++ b/src/libnm-platform/nmp-global-tracker.c @@ -800,7 +800,7 @@ nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply) /* Now, drop all duplicates addresses. Only keep the first one. */ for (i = 0, j = 0; i < entries->len; i++) { - const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i); + const MptcpSyncData *d = &nm_g_array_index(entries, MptcpSyncData, i); const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj); obj_data = g_hash_table_lookup(entries_hash_by_addr, (gpointer) mptcp_addr); @@ -826,7 +826,7 @@ nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply) nm_assert_not_reached(); if (i != j) - *(nm_g_array_index_p(entries, MptcpSyncData, j)) = *d; + (nm_g_array_index(entries, MptcpSyncData, j)) = *d; j++; if (j >= MPTCP_PM_ADDR_MAX) { @@ -931,7 +931,7 @@ keep_and_next: if (entries) { for (i = 0; i < entries->len; i++) { - const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i); + const MptcpSyncData *d = &nm_g_array_index(entries, MptcpSyncData, i); const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj); const NMPObject *kobj; diff --git a/src/libnm-platform/nmp-netns.c b/src/libnm-platform/nmp-netns.c index 2563e8f6..c18f67ab 100644 --- a/src/libnm-platform/nmp-netns.c +++ b/src/libnm-platform/nmp-netns.c @@ -66,26 +66,28 @@ __ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "netns" -#define _NMLOG(level, netns, ...) \ - G_STMT_START \ - { \ - NMLogLevel _level = (level); \ - \ - if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ - NMPNetns *_netns = (netns); \ - char _sbuf[20]; \ - \ - _nm_log(_level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ - : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG(level, netns, ...) \ + G_STMT_START \ + { \ + NMLogLevel _level = (level); \ + \ + if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ + NMPNetns *_netns = (netns); \ + char _sbuf[32]; \ + \ + _nm_log(_level, \ + _NMLOG_DOMAIN, \ + 0, \ + NULL, \ + NULL, \ + "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (_netns ? nm_sprintf_buf(_sbuf, \ + "[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + NM_HASH_OBFUSCATE_PTR(_netns)) \ + : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -194,7 +196,7 @@ _stack_current_netns(GArray *netns_stack, int ns_types) for (j = netns_stack->len; ns_types && j >= 1;) { NetnsInfo *info; - info = &g_array_index(netns_stack, NetnsInfo, --j); + info = &nm_g_array_index(netns_stack, NetnsInfo, --j); if (NM_FLAGS_ALL(info->ns_types, ns_types)) return info->netns; @@ -218,7 +220,7 @@ _stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) for (j = netns_stack->len; ns_types && j >= 1;) { NetnsInfo *info; - info = &g_array_index(netns_stack, NetnsInfo, --j); + info = &nm_g_array_index(netns_stack, NetnsInfo, --j); if (info->netns != netns) { ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); continue; @@ -240,7 +242,7 @@ static NetnsInfo * _stack_peek(GArray *netns_stack) { if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + return &nm_g_array_last(netns_stack, NetnsInfo); return NULL; } @@ -248,7 +250,7 @@ static NetnsInfo * _stack_bottom(GArray *netns_stack) { if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, 0); + return &nm_g_array_first(netns_stack, NetnsInfo); return NULL; } @@ -262,9 +264,7 @@ _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); - g_array_set_size(netns_stack, netns_stack->len + 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + info = nm_g_array_append_new(netns_stack, NetnsInfo); *info = (NetnsInfo){ .netns = g_object_ref(netns), .ns_types = ns_types, @@ -280,7 +280,7 @@ _stack_pop(GArray *netns_stack) nm_assert(netns_stack); nm_assert(netns_stack->len > 1); - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); + info = &nm_g_array_last(netns_stack, NetnsInfo); nm_assert(NMP_IS_NETNS(info->netns)); nm_assert(info->count == 1); diff --git a/src/libnm-platform/nmp-netns.h b/src/libnm-platform/nmp-netns.h index b18bd03e..baeae4f1 100644 --- a/src/libnm-platform/nmp-netns.h +++ b/src/libnm-platform/nmp-netns.h @@ -11,7 +11,7 @@ /*****************************************************************************/ #define NMP_TYPE_NETNS (nmp_netns_get_type()) -#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) +#define NMP_NETNS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) #define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) #define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) #define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index db7bd314..9ba027ba 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -216,7 +216,7 @@ nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst, const char * nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len) { - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_addr[NM_INET_ADDRSTRLEN]; char s_scope_id[40]; if (!nm_utils_to_string_buffer_init_null(sa, &buf, &len)) @@ -232,14 +232,14 @@ nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len) g_snprintf(buf, len, "%s:%u", - _nm_utils_inet4_ntop(sa->in.sin_addr.s_addr, s_addr), + nm_inet4_ntop(sa->in.sin_addr.s_addr, s_addr), (guint) htons(sa->in.sin_port)); break; case AF_INET6: g_snprintf(buf, len, "[%s%s]:%u", - _nm_utils_inet6_ntop(&sa->in6.sin6_addr, s_addr), + nm_inet6_ntop(&sa->in6.sin6_addr, s_addr), (sa->in6.sin6_scope_id != 0 ? nm_sprintf_buf(s_scope_id, "%u", sa->in6.sin6_scope_id) : ""), @@ -735,6 +735,12 @@ _vt_cmd_obj_dispose_link(NMPObject *obj) } static void +_vt_cmd_obj_dispose_ip4_route(NMPObject *obj) +{ + nm_clear_g_free((gpointer *) &obj->_ip4_route.extra_nexthops); +} + +static void _vt_cmd_obj_dispose_lnk_vlan(NMPObject *obj) { g_free((gpointer) obj->_lnk_vlan.ingress_qos_map); @@ -762,17 +768,23 @@ _vt_cmd_obj_dispose_lnk_wireguard(NMPObject *obj) _wireguard_clear(&obj->_lnk_wireguard); } +static gsize +_NMP_OBJECT_STRUCT_SIZE(const NMPClass *klass) +{ + nm_assert(klass); + nm_assert(klass->sizeof_public > 0); + nm_assert(klass->sizeof_public <= klass->sizeof_data); + + return klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object); +} + static NMPObject * _nmp_object_new_from_class(const NMPClass *klass) { NMPObject *obj; - nm_assert(klass); - nm_assert(klass->sizeof_data > 0); - nm_assert(klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data); - - obj = g_slice_alloc0(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object)); - obj->_class = klass; + obj = g_slice_alloc0(_NMP_OBJECT_STRUCT_SIZE(klass)); + obj->_class = klass; obj->parent._ref_count = 1; return obj; } @@ -805,9 +817,8 @@ static NMPObject * _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) { nm_assert(obj); - nm_assert(klass); - memset(obj, 0, sizeof(NMPObject)); + memset(obj, 0, _NMP_OBJECT_STRUCT_SIZE(klass)); obj->_class = klass; obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; @@ -843,14 +854,12 @@ nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src) if (klass->cmd_plobj_id_copy) klass->cmd_plobj_id_copy(&obj->object, &src->object); else { - /* This object must not implement cmd_obj_copy(). - * If it would, it would mean that we require a deep copy - * of the data. As @obj is stack-allocated, it cannot track - * ownership. The caller must not use nmp_object_stackinit_id() - * with an object of such a type. */ - nm_assert(!klass->cmd_obj_copy); - - /* plain memcpy of the public part suffices. */ + /* plain memcpy. + * + * Note that for NMPObjectIP4Route this also copies extra_nexthops + * pointer, aliasing it without taking ownership. That is potentially + * dangerous, but when using a stack allocated instance, you must + * always take care of ownership. */ memcpy(&obj->object, &src->object, klass->sizeof_data); } return obj; @@ -907,30 +916,33 @@ nmp_object_to_string(const NMPObject *obj, klass = NMP_OBJECT_GET_CLASS(obj); - if (klass->cmd_obj_to_string) + if (klass->cmd_obj_to_string) { + nm_assert(!klass->cmd_plobj_to_string); + nm_assert(!klass->cmd_plobj_to_string_id); return klass->cmd_obj_to_string(obj, to_string_mode, buf, buf_size); + } + + nm_assert(klass->cmd_plobj_to_string); switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: - if (!klass->cmd_plobj_to_string_id) { - g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); - return buf; - } - return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); + if (klass->cmd_plobj_to_string_id) + return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); + g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); + return buf; case NMP_OBJECT_TO_STRING_ALL: - g_snprintf( - buf, - buf_size, - "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", - klass->obj_type_name, - NM_HASH_OBFUSCATE_PTR(obj), - obj->parent._ref_count, - nmp_object_is_alive(obj) ? '+' : '-', - nmp_object_is_visible(obj) ? '+' : '-', - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2))); + g_snprintf(buf, + buf_size, + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", + klass->obj_type_name, + NM_HASH_OBFUSCATE_PTR(obj), + obj->parent._ref_count, + nmp_object_is_alive(obj) ? '+' : '-', + nmp_object_is_visible(obj) ? '+' : '-', + klass->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2))); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); + klass->cmd_plobj_to_string(&obj->object, buf, buf_size); return buf; default: g_return_val_if_reached("ERROR"); @@ -948,7 +960,8 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: - return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); + g_snprintf(buf, buf_size, "%d", obj->link.ifindex); + return buf; case NMP_OBJECT_TO_STRING_ALL: nm_strbuf_append(&b, &buf_size, @@ -961,7 +974,7 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, nmp_object_is_visible(obj) ? '+' : '-', obj->_link.netlink.is_in_netlink ? '+' : '-', NM_HASH_OBFUSCATE_PTR(obj->_link.udev.device)); - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); + nm_platform_link_to_string(&obj->link, b, buf_size); nm_strbuf_seek_end(&b, &buf_size); if (obj->_link.netlink.lnk) { nm_strbuf_append_str(&b, &buf_size, "; "); @@ -971,7 +984,7 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, nm_strbuf_append_c(&b, &buf_size, ']'); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); + nm_platform_link_to_string(&obj->link, b, buf_size); if (obj->_link.netlink.lnk) { nm_strbuf_seek_end(&b, &buf_size); nm_strbuf_append_str(&b, &buf_size, "; "); @@ -984,6 +997,41 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, } static const char * +_vt_cmd_obj_to_string_ip4_route(const NMPObject *obj, + NMPObjectToStringMode to_string_mode, + char *buf, + gsize buf_size) +{ + const NMPClass *klass; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + klass = NMP_OBJECT_GET_CLASS(obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_PUBLIC: + case NMP_OBJECT_TO_STRING_ID: + nm_platform_ip4_route_to_string_full(&obj->ip4_route, + obj->_ip4_route.extra_nexthops, + buf, + buf_size); + return buf; + case NMP_OBJECT_TO_STRING_ALL: + g_snprintf(buf, + buf_size, + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", + klass->obj_type_name, + NM_HASH_OBFUSCATE_PTR(obj), + obj->parent._ref_count, + nmp_object_is_alive(obj) ? '+' : '-', + nmp_object_is_visible(obj) ? '+' : '-', + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2))); + return buf; + default: + g_return_val_if_reached("ERROR"); + } +} + +static const char * _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, @@ -1013,7 +1061,7 @@ _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2))); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); + nm_platform_lnk_vlan_to_string(&obj->lnk_vlan, buf, buf_size); b = buf; l = strlen(b); @@ -1092,8 +1140,7 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); - + nm_platform_lnk_wireguard_to_string(&obj->lnk_wireguard, buf, buf_size); return buf; default: g_return_val_if_reached("ERROR"); @@ -1106,41 +1153,38 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, gsize buf_len) \ { \ plat_type *const obj = (plat_type *) _obj; \ - _nm_unused char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ - _nm_unused char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ + _nm_unused char buf1[NM_INET_ADDRSTRLEN]; \ + _nm_unused char buf2[NM_INET_ADDRSTRLEN]; \ \ g_snprintf(buf, buf_len, __VA_ARGS__); \ return buf; \ } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON -_vt_cmd_plobj_to_string_id(link, NMPlatformLink, "%d", obj->ifindex); - -_vt_cmd_plobj_to_string_id(ip4_address, - NMPlatformIP4Address, - "%d: %s/%d%s%s", - obj->ifindex, - _nm_utils_inet4_ntop(obj->address, buf1), - obj->plen, - obj->peer_address != obj->address ? "," : "", - obj->peer_address != obj->address ? _nm_utils_inet4_ntop( - nm_utils_ip4_address_clear_host_address(obj->peer_address, - obj->plen), - buf2) - : ""); +_vt_cmd_plobj_to_string_id( + ip4_address, + NMPlatformIP4Address, + "%d: %s/%d%s%s", + obj->ifindex, + nm_inet4_ntop(obj->address, buf1), + obj->plen, + obj->peer_address != obj->address ? "," : "", + obj->peer_address != obj->address + ? nm_inet4_ntop(nm_ip4_addr_clear_host_address(obj->peer_address, obj->plen), buf2) + : ""); _vt_cmd_plobj_to_string_id(ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, - _nm_utils_inet6_ntop(&obj->address, buf1)); + nm_inet6_ntop(&obj->address, buf1)); _vt_cmd_plobj_to_string_id(qdisc, NMPlatformQdisc, "%d: %d", obj->ifindex, obj->parent); _vt_cmd_plobj_to_string_id(tfilter, NMPlatformTfilter, "%d: %d", obj->ifindex, obj->parent); void -nmp_object_hash_update(const NMPObject *obj, NMHashState *h) +nmp_object_hash_update_full(const NMPObject *obj, gboolean for_id, NMHashState *h) { const NMPClass *klass; @@ -1148,20 +1192,44 @@ nmp_object_hash_update(const NMPObject *obj, NMHashState *h) klass = NMP_OBJECT_GET_CLASS(obj); + nm_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); + nm_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); + nm_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); + + nm_assert((!!klass->cmd_obj_hash_update) ^ (!!klass->cmd_plobj_hash_update)); + nm_assert((!klass->cmd_obj_hash_update) || (!klass->cmd_plobj_id_hash_update)); + nm_hash_update_val(h, klass->obj_type); + + if (for_id) { + if (klass->cmd_obj_hash_update) + klass->cmd_obj_hash_update(obj, TRUE, h); + else if (klass->cmd_plobj_id_hash_update) + klass->cmd_plobj_id_hash_update(&obj->object, h); + else { + /* The klass doesn't implement ID compare. It means, to use pointer + * equality. */ + nm_hash_update_val(h, obj); + } + return; + } + if (klass->cmd_obj_hash_update) - klass->cmd_obj_hash_update(obj, h); - else if (klass->cmd_plobj_hash_update) - klass->cmd_plobj_hash_update(&obj->object, h); + klass->cmd_obj_hash_update(obj, FALSE, h); else - nm_hash_update_val(h, obj); + klass->cmd_plobj_hash_update(&obj->object, h); } static void -_vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h) +_vt_cmd_obj_hash_update_link(const NMPObject *obj, gboolean for_id, NMHashState *h) { nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK); + if (for_id) { + nm_hash_update_val(h, obj->link.ifindex); + return; + } + nm_platform_link_hash_update(&obj->link, h); nm_hash_update_vals(h, obj->_link.netlink.is_in_netlink, obj->_link.udev.device); if (obj->_link.netlink.lnk) @@ -1169,10 +1237,30 @@ _vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h) } static void -_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, NMHashState *h) +_vt_cmd_obj_hash_update_ip4_route(const NMPObject *obj, gboolean for_id, NMHashState *h) +{ + guint i; + + nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + nm_platform_ip4_route_hash_update(&obj->ip4_route, + for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + : NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, + h); + for (i = 1u; i < obj->ip4_route.n_nexthops; i++) + nm_platform_ip4_rt_nexthop_hash_update(&obj->_ip4_route.extra_nexthops[i - 1u], for_id, h); +} + +static void +_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, gboolean for_id, NMHashState *h) { nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_VLAN); + if (for_id) { + nm_hash_update_val(h, obj); + return; + } + nm_platform_lnk_vlan_hash_update(&obj->lnk_vlan, h); _vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_ingress_qos_map, obj->_lnk_vlan.ingress_qos_map, @@ -1183,12 +1271,17 @@ _vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, NMHashState *h) } static void -_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, NMHashState *h) +_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, gboolean for_id, NMHashState *h) { guint i; nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_WIREGUARD); + if (for_id) { + nm_hash_update_val(h, obj); + return; + } + nm_platform_lnk_wireguard_hash_update(&obj->lnk_wireguard, h); nm_hash_update_val(h, obj->_lnk_wireguard.peers_len); @@ -1199,47 +1292,85 @@ _vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, NMHashState *h) int nmp_object_cmp_full(const NMPObject *obj1, const NMPObject *obj2, NMPObjectCmpFlags flags) { - const NMPClass *klass1; + const NMPClass *klass; const NMPClass *klass2; NMPObject obj_stackcopy; + nm_assert( + !NM_FLAGS_ANY(flags, ~(NMP_OBJECT_CMP_FLAGS_ID | NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX))); + + /* The ID flag (currently) cannot be combined with other flags. That's partly because + * it's not implemented, but also because some objects use only pointer equality. So it's + * not clear how that combines with other flags (well, they'd be ignored). */ + nm_assert(!NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_ID) || flags == NMP_OBJECT_CMP_FLAGS_ID); + NM_CMP_SELF(obj1, obj2); g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), -1); g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), 1); - klass1 = NMP_OBJECT_GET_CLASS(obj1); + klass = NMP_OBJECT_GET_CLASS(obj1); + + nm_assert(klass); + + nm_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); + nm_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); + nm_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); + + nm_assert((!!klass->cmd_obj_cmp) ^ (!!klass->cmd_plobj_cmp)); + nm_assert((!klass->cmd_obj_cmp) || (!klass->cmd_plobj_id_cmp)); + klass2 = NMP_OBJECT_GET_CLASS(obj2); - if (klass1 != klass2) { - nm_assert(klass1->obj_type != klass2->obj_type); - return klass1->obj_type < klass2->obj_type ? -1 : 1; + if (klass != klass2) { + nm_assert(klass2); + NM_CMP_DIRECT(klass->obj_type, klass2->obj_type); + return nm_assert_unreachable_val(0); + } + + if (NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_ID)) { + if (klass->cmd_obj_cmp) + return klass->cmd_obj_cmp(obj1, obj2, TRUE); + if (klass->cmd_plobj_id_cmp) + return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object); + + /* the klass doesn't implement ID cmp(). That means, different objects + * never compare equal, but the cmp() according to their pointer value. */ + NM_CMP_DIRECT_PTR(obj1, obj2); + return nm_assert_unreachable_val(0); } if (NM_FLAGS_HAS(flags, NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX)) { - if (!NM_IN_SET(klass1, - nmp_class_from_type(NMP_OBJECT_TYPE_IP4_ADDRESS), - nmp_class_from_type(NMP_OBJECT_TYPE_IP6_ADDRESS), - nmp_class_from_type(NMP_OBJECT_TYPE_IP4_ROUTE), - nmp_class_from_type(NMP_OBJECT_TYPE_IP6_ROUTE))) { + if (!NM_IN_SET(klass->obj_type, + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)) { /* This flag is currently only implemented for certain types. * That is, because we just create a stack copy, and that naive * approach only knows for types where we know that it works. */ } else if (obj1->obj_with_ifindex.ifindex != obj2->obj_with_ifindex.ifindex) { - nmp_object_stackinit(&obj_stackcopy, klass1->obj_type, &obj2->obj_with_ifindex); + nmp_object_stackinit(&obj_stackcopy, klass->obj_type, &obj2->obj_with_ifindex); obj_stackcopy.obj_with_ifindex.ifindex = obj1->obj_with_ifindex.ifindex; - obj2 = &obj_stackcopy; + if (klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) + obj_stackcopy._ip4_route.extra_nexthops = obj2->_ip4_route.extra_nexthops; + obj2 = &obj_stackcopy; } } - if (klass1->cmd_obj_cmp) - return klass1->cmd_obj_cmp(obj1, obj2); - return klass1->cmd_plobj_cmp(&obj1->object, &obj2->object); + if (klass->cmd_obj_cmp) + return klass->cmd_obj_cmp(obj1, obj2, FALSE); + return klass->cmd_plobj_cmp(&obj1->object, &obj2->object); } static int -_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2) +_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { + if (for_id) { + NM_CMP_FIELD(obj1, obj2, link.ifindex); + return 0; + } + NM_CMP_RETURN(nm_platform_link_cmp(&obj1->link, &obj2->link)); NM_CMP_DIRECT(obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink); NM_CMP_RETURN(nmp_object_cmp(obj1->_link.netlink.lnk, obj2->_link.netlink.lnk)); @@ -1261,10 +1392,37 @@ _vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2) } static int -_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2) +_vt_cmd_obj_cmp_ip4_route(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) +{ + int c; + guint i; + + c = nm_platform_ip4_route_cmp(&obj1->ip4_route, + &obj2->ip4_route, + for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + : NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); + NM_CMP_RETURN_DIRECT(c); + + for (i = 1u; i < obj1->ip4_route.n_nexthops; i++) { + c = nm_platform_ip4_rt_nexthop_cmp(&obj1->_ip4_route.extra_nexthops[i - 1u], + &obj2->_ip4_route.extra_nexthops[i - 1u], + for_id); + NM_CMP_RETURN_DIRECT(c); + } + + return 0; +} + +static int +_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { int c; + if (for_id) { + NM_CMP_DIRECT_PTR(obj1, obj2); + return nm_assert_unreachable_val(0); + } + c = nm_platform_lnk_vlan_cmp(&obj1->lnk_vlan, &obj2->lnk_vlan); if (c) return c; @@ -1287,10 +1445,15 @@ _vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2) } static int -_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2) +_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { guint i; + if (for_id) { + NM_CMP_DIRECT_PTR(obj1, obj2); + return nm_assert_unreachable_val(0); + } + NM_CMP_RETURN(nm_platform_lnk_wireguard_cmp(&obj1->lnk_wireguard, &obj2->lnk_wireguard)); NM_CMP_FIELD(obj1, obj2, _lnk_wireguard.peers_len); @@ -1354,6 +1517,28 @@ _vt_cmd_obj_copy_link(NMPObject *dst, const NMPObject *src) } static void +_vt_cmd_obj_copy_ip4_route(NMPObject *dst, const NMPObject *src) +{ + nm_assert(dst != src); + + if (src->ip4_route.n_nexthops <= 1) { + nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops); + } else if (src->ip4_route.n_nexthops != dst->ip4_route.n_nexthops + || !nm_memeq_n(src->_ip4_route.extra_nexthops, + src->ip4_route.n_nexthops - 1u, + dst->_ip4_route.extra_nexthops, + dst->ip4_route.n_nexthops - 1u, + sizeof(NMPlatformIP4RtNextHop))) { + nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops); + dst->_ip4_route.extra_nexthops = + nm_memdup(src->_ip4_route.extra_nexthops, + sizeof(NMPlatformIP4RtNextHop) * (src->ip4_route.n_nexthops - 1u)); + } + + dst->ip4_route = src->ip4_route; +} + +static void _vt_cmd_obj_copy_lnk_vlan(NMPObject *dst, const NMPObject *src) { dst->lnk_vlan = src->lnk_vlan; @@ -1451,39 +1636,6 @@ nmp_object_clone(const NMPObject *obj, gboolean id_only) return dst; } -int -nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) -{ - const NMPClass *klass, *klass2; - - NM_CMP_SELF(obj1, obj2); - - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), FALSE); - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), FALSE); - - klass = NMP_OBJECT_GET_CLASS(obj1); - nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); - - klass2 = NMP_OBJECT_GET_CLASS(obj2); - nm_assert(klass); - if (klass != klass2) { - nm_assert(klass2); - NM_CMP_DIRECT(klass->obj_type, klass2->obj_type); - /* resort to pointer comparison */ - NM_CMP_DIRECT_PTR(klass, klass2); - return 0; - } - - if (!klass->cmd_plobj_id_cmp) { - /* the klass doesn't implement ID cmp(). That means, different objects - * never compare equal, but the cmp() according to their pointer value. */ - NM_CMP_DIRECT_PTR(obj1, obj2); - return 0; - } - - return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object); -} - #define _vt_cmd_plobj_id_cmp(type, plat_type, cmd) \ static int _vt_cmd_plobj_id_cmp_##type(const NMPlatformObject *_obj1, \ const NMPlatformObject *_obj2) \ @@ -1499,8 +1651,6 @@ nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON -_vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); }); - static int _vt_cmd_plobj_id_cmp_ip4_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { @@ -1528,14 +1678,6 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { }); static int -_vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) -{ - return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1, - (const NMPlatformIP4Route *) obj2, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); -} - -static int _vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, @@ -1582,28 +1724,6 @@ _vt_cmd_plobj_id_cmp(mptcp_addr, NMPlatformMptcpAddr, { NM_CMP_FIELD(obj1, obj2, port); }); -void -nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h) -{ - const NMPClass *klass; - - g_return_if_fail(NMP_OBJECT_IS_VALID(obj)); - - klass = NMP_OBJECT_GET_CLASS(obj); - - nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); - - if (!klass->cmd_plobj_id_hash_update) { - /* The klass doesn't implement ID compare. It means, to use pointer - * equality. */ - nm_hash_update_val(h, obj); - return; - } - - nm_hash_update_val(h, klass->obj_type); - klass->cmd_plobj_id_hash_update(&obj->object, h); -} - guint nmp_object_id_hash(const NMPObject *obj) { @@ -1627,8 +1747,6 @@ nmp_object_id_hash(const NMPObject *obj) } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON -_vt_cmd_plobj_id_hash_update(link, NMPlatformLink, { nm_hash_update_val(h, obj->ifindex); }); - _vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, { nm_hash_update_vals( h, @@ -1636,7 +1754,7 @@ _vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, { obj->plen, obj->address, /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - nm_utils_ip4_address_clear_host_address(obj->peer_address, obj->plen)); + nm_ip4_addr_clear_host_address(obj->peer_address, obj->plen)); }); _vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, { @@ -1647,10 +1765,6 @@ _vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, { obj->address); }); -_vt_cmd_plobj_id_hash_update(ip4_route, NMPlatformIP4Route, { - nm_platform_ip4_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); -}); - _vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, { nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }); @@ -1674,19 +1788,19 @@ _vt_cmd_plobj_id_hash_update(mptcp_addr, NMPlatformMptcpAddr, { }); static void -_vt_cmd_plobj_hash_update_ip4_route(const NMPlatformObject *obj, NMHashState *h) +_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) { - return nm_platform_ip4_route_hash_update((const NMPlatformIP4Route *) obj, + return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, h); } -static void -_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) +static int +_vt_cmd_plobj_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, - h); + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, + (const NMPlatformIP6Route *) obj2, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static void @@ -1697,6 +1811,24 @@ _vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState h); } +static inline int +_vt_cmd_plobj_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1, + (const NMPlatformRoutingRule *) obj2, + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); +} + +static int +_vt_cmd_plobj_cmp_qdisc(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_qdisc_cmp((const NMPlatformQdisc *) obj1, + (const NMPlatformQdisc *) obj2, + TRUE); +} + +/*****************************************************************************/ + guint nmp_object_indirect_id_hash(gconstpointer a) { @@ -1859,7 +1991,7 @@ _vt_dedup_obj_destroy(NMDedupMultiObj *obj) klass = o->_class; if (klass->cmd_obj_dispose) klass->cmd_obj_dispose(o); - g_slice_free1(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object), o); + g_slice_free1(_NMP_OBJECT_STRUCT_SIZE(klass), o); } static const NMDedupMultiObj * @@ -2827,6 +2959,7 @@ nmp_cache_update_netlink_route(NMPCache *cache, NMPObject *obj_hand_over, gboolean is_dump, guint16 nlmsgflags, + gboolean route_is_alive, const NMPObject **out_obj_old, const NMPObject **out_obj_new, const NMPObject **out_obj_replace, @@ -2845,30 +2978,60 @@ nmp_cache_update_netlink_route(NMPCache *cache, nm_assert(cache); nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over)); nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over)); - /* A link object from netlink must have the udev related fields unset. - * We could implement to handle that, but there is no need to support such - * a use-case */ nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_hand_over), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over); + if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE)) { + /* This means, that the message indicates that another route was replaced. + * Since we don't cache all routes (see "route_is_alive"), we cannot know + * with certainty which route was replaced. + * + * Even if we would cache *all* routes (which we cannot, if kernel adds new + * routing features that modify the known nmp_object_id_equal()), it would + * be hard to find the right route that was replaced. Well, probably we + * would have to keep NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID sorted by order + * of notifications, which is hard. The code below actually makes an effort + * to do that, but it's not actually used, because we just resync. + * + * The only proper solution for this would be to improve kernel with [1] + * and [2]. + * + * [1] https://bugzilla.redhat.com/show_bug.cgi?id=1337855 + * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860 + * + * We need to resync. + */ + if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_IP4_ROUTE + && !nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { + /* For IPv4, we can do a small optimization. We skip the resync, if we have + * no conflicting routes (by weak-id). + * + * This optimization does not work for IPv6 (maybe should be fixed). + */ + } else { + entry_replace = NULL; + resync_required = TRUE; + goto out; + } + } + entry_old = _lookup_entry(cache, obj_hand_over); entry_new = NULL; NM_SET_OUT(out_obj_old, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_old))); - if (!entry_old) { - if (!nmp_object_is_alive(obj_hand_over)) - goto update_done; + is_alive = route_is_alive && nmp_object_is_alive(obj_hand_over); - _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new); - ops_type = NMP_CACHE_OPS_ADDED; + if (!entry_old) { + if (is_alive) { + _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new); + ops_type = NMP_CACHE_OPS_ADDED; + } goto update_done; } - is_alive = nmp_object_is_alive(obj_hand_over); - if (!is_alive) { /* the update would make @entry_old invalid. Remove it. */ _idxcache_update(cache, entry_old, NULL, FALSE, NULL); @@ -2901,19 +3064,11 @@ update_done: if (is_dump) goto out; - if (!entry_new) { - if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE) - && nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { - /* hm. @obj_hand_over was not added, meaning it was not alive. - * However, we track some other objects with the same weak-id. - * It's unclear what that means. To be sure, resync. */ - resync_required = TRUE; - } + if (!entry_new) goto out; - } - /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID - * index. For all other indexes their order becomes messed up. */ + /* For routes, we only maintain the order correctly for the BY_WEAK_ID + * index. For all other indexes, their order is not preserved. */ entry_cur = _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj); if (!entry_cur) { @@ -3138,29 +3293,23 @@ typedef int (*CmdPlobjCmpFunc)(const NMPlatformObject *obj1, const NMPlatformObj const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { [NMP_OBJECT_TYPE_LINK - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LINK, - .sizeof_data = sizeof(NMPObjectLink), - .sizeof_public = sizeof(NMPlatformLink), - .obj_type_name = "link", - .rtm_gettype = RTM_GETLINK, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, - .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, - .supported_cache_ids = _supported_cache_ids_link, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, - .cmd_obj_cmp = _vt_cmd_obj_cmp_link, - .cmd_obj_copy = _vt_cmd_obj_copy_link, - .cmd_obj_dispose = _vt_cmd_obj_dispose_link, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, - .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, - .cmd_obj_to_string = _vt_cmd_obj_to_string_link, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_link, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_link, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_link_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_link_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_link_cmp, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LINK, + .sizeof_data = sizeof(NMPObjectLink), + .sizeof_public = sizeof(NMPlatformLink), + .obj_type_name = "link", + .rtm_gettype = RTM_GETLINK, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, + .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, + .supported_cache_ids = _supported_cache_ids_link, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, + .cmd_obj_cmp = _vt_cmd_obj_cmp_link, + .cmd_obj_copy = _vt_cmd_obj_copy_link, + .cmd_obj_dispose = _vt_cmd_obj_dispose_link, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, + .cmd_obj_to_string = _vt_cmd_obj_to_string_link, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, }, [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { @@ -3206,23 +3355,22 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, - .sizeof_data = sizeof(NMPObjectIP4Route), - .sizeof_public = sizeof(NMPlatformIP4Route), - .obj_type_name = "ip4-route", - .addr_family = AF_INET, - .rtm_gettype = RTM_GETROUTE, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, - .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_route, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, - .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_route_cmp_full, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, + .sizeof_data = sizeof(NMPObjectIP4Route), + .sizeof_public = sizeof(NMPlatformIP4Route), + .obj_type_name = "ip4-route", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETROUTE, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, + .supported_cache_ids = _supported_cache_ids_ipx_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_ip4_route, + .cmd_obj_cmp = _vt_cmd_obj_cmp_ip4_route, + .cmd_obj_copy = _vt_cmd_obj_copy_ip4_route, + .cmd_obj_dispose = _vt_cmd_obj_dispose_ip4_route, + .cmd_obj_to_string = _vt_cmd_obj_to_string_ip4_route, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { @@ -3242,7 +3390,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_route_to_string, .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_route_cmp_full, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip6_route, }, [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = { @@ -3261,7 +3409,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_routing_rule_to_string, .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_routing_rule_cmp_full, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_routing_rule, }, [NMP_OBJECT_TYPE_QDISC - 1] = { @@ -3280,7 +3428,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_qdisc, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_qdisc_to_string, .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_qdisc_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_qdisc_cmp, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_qdisc, }, [NMP_OBJECT_TYPE_TFILTER - 1] = { @@ -3460,20 +3608,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, - .sizeof_data = sizeof(NMPObjectLnkVlan), - .sizeof_public = sizeof(NMPlatformLnkVlan), - .obj_type_name = "vlan", - .lnk_link_type = NM_LINK_TYPE_VLAN, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, - .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, - .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, - .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, - .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vlan_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vlan_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vlan_cmp, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, + .sizeof_data = sizeof(NMPObjectLnkVlan), + .sizeof_public = sizeof(NMPlatformLnkVlan), + .obj_type_name = "vlan", + .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, + .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, + .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, + .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, + .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, }, [NMP_OBJECT_TYPE_LNK_VRF - 1] = { @@ -3487,6 +3632,30 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vrf_hash_update, .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vrf_cmp, }, + [NMP_OBJECT_TYPE_LNK_VTI - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_VTI, + .sizeof_data = sizeof(NMPObjectLnkVti), + .sizeof_public = sizeof(NMPlatformLnkVti), + .obj_type_name = "vti", + .lnk_link_type = NM_LINK_TYPE_VTI, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti_cmp, + }, + [NMP_OBJECT_TYPE_LNK_VTI6 - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_VTI6, + .sizeof_data = sizeof(NMPObjectLnkVti6), + .sizeof_public = sizeof(NMPlatformLnkVti6), + .obj_type_name = "vti6", + .lnk_link_type = NM_LINK_TYPE_VTI6, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vti6_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vti6_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vti6_cmp, + }, [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), @@ -3501,20 +3670,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_LNK_WIREGUARD - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_WIREGUARD, - .sizeof_data = sizeof(NMPObjectLnkWireGuard), - .sizeof_public = sizeof(NMPlatformLnkWireGuard), - .obj_type_name = "wireguard", - .lnk_link_type = NM_LINK_TYPE_WIREGUARD, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_wireguard, - .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_wireguard, - .cmd_obj_copy = _vt_cmd_obj_copy_lnk_wireguard, - .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard, - .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_wireguard_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_wireguard_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_wireguard_cmp, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_WIREGUARD, + .sizeof_data = sizeof(NMPObjectLnkWireGuard), + .sizeof_public = sizeof(NMPlatformLnkWireGuard), + .obj_type_name = "wireguard", + .lnk_link_type = NM_LINK_TYPE_WIREGUARD, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_wireguard, + .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_wireguard, + .cmd_obj_copy = _vt_cmd_obj_copy_lnk_wireguard, + .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard, + .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard, }, [NMP_OBJECT_TYPE_LNK_BOND - 1] = { diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index 25156fe2..0d5f84b3 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -183,25 +183,26 @@ typedef struct { /* Only for NMPObjectLnk* types. */ NMLinkType lnk_link_type; - void (*cmd_obj_hash_update)(const NMPObject *obj, NMHashState *h); - int (*cmd_obj_cmp)(const NMPObject *obj1, const NMPObject *obj2); - void (*cmd_obj_copy)(NMPObject *dst, const NMPObject *src); - void (*cmd_obj_dispose)(NMPObject *obj); gboolean (*cmd_obj_is_alive)(const NMPObject *obj); gboolean (*cmd_obj_is_visible)(const NMPObject *obj); + void (*cmd_obj_copy)(NMPObject *dst, const NMPObject *src); + void (*cmd_obj_dispose)(NMPObject *obj); + + void (*cmd_obj_hash_update)(const NMPObject *obj, gboolean for_id, NMHashState *h); + int (*cmd_obj_cmp)(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id); const char *(*cmd_obj_to_string)(const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); /* functions that operate on NMPlatformObject */ + void (*cmd_plobj_hash_update)(const NMPlatformObject *obj, NMHashState *h); + int (*cmd_plobj_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); void (*cmd_plobj_id_copy)(NMPlatformObject *dst, const NMPlatformObject *src); int (*cmd_plobj_id_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); void (*cmd_plobj_id_hash_update)(const NMPlatformObject *obj, NMHashState *h); const char *(*cmd_plobj_to_string_id)(const NMPlatformObject *obj, char *buf, gsize buf_size); const char *(*cmd_plobj_to_string)(const NMPlatformObject *obj, char *buf, gsize len); - void (*cmd_plobj_hash_update)(const NMPlatformObject *obj, NMHashState *h); - int (*cmd_plobj_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; extern const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX]; @@ -296,6 +297,14 @@ typedef struct { } NMPObjectLnkVrf; typedef struct { + NMPlatformLnkVti _public; +} NMPObjectLnkVti; + +typedef struct { + NMPlatformLnkVti6 _public; +} NMPObjectLnkVti6; + +typedef struct { NMPlatformLnkVxlan _public; } NMPObjectLnkVxlan; @@ -313,6 +322,13 @@ typedef struct { typedef struct { NMPlatformIP4Route _public; + + /* The first hop is embedded in _public (in the + * ifindex, gateway and weight fields). + * Only if _public.n_nexthops is greater than 1, then + * this contains the remaining(!!) (_public.n_nexthops - 1) + * extra hops for ECMP multihop routes. */ + const NMPlatformIP4RtNextHop *extra_nexthops; } NMPObjectIP4Route; typedef struct { @@ -388,6 +404,12 @@ struct _NMPObject { NMPlatformLnkVrf lnk_vrf; NMPObjectLnkVrf _lnk_vrf; + NMPlatformLnkVti lnk_vti; + NMPObjectLnkVti _lnk_vti; + + NMPlatformLnkVti6 lnk_vti6; + NMPObjectLnkVti6 _lnk_vti6; + NMPlatformLnkVxlan lnk_vxlan; NMPObjectLnkVxlan _lnk_vxlan; @@ -419,7 +441,7 @@ struct _NMPObject { NMPlatformMptcpAddr mptcp_addr; NMPObjectMptcpAddr _mptcp_addr; }; -}; +} _nm_alignas(NMDedupMultiObj); /*****************************************************************************/ @@ -446,9 +468,12 @@ NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { NMPObject *obj; - obj = plobj ? (NMPObject *) (&(((char *) plobj)[-((int) G_STRUCT_OFFSET(NMPObject, object))])) + obj = plobj ? NM_CAST_ALIGN(NMPObject, + &(((char *) plobj)[-((int) G_STRUCT_OFFSET(NMPObject, object))])) : NULL; + nm_assert(!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID(obj->_class))); + return obj; } #define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST((const NMPlatformObject *) (plobj))) @@ -517,6 +542,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) case NMP_OBJECT_TYPE_LNK_TUN: case NMP_OBJECT_TYPE_LNK_VLAN: case NMP_OBJECT_TYPE_LNK_VRF: + case NMP_OBJECT_TYPE_LNK_VTI: + case NMP_OBJECT_TYPE_LNK_VTI6: case NMP_OBJECT_TYPE_LNK_VXLAN: case NMP_OBJECT_TYPE_LNK_WIREGUARD: @@ -534,6 +561,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) return FALSE; } +#define NMP_OBJECT_TYPE_NAME(obj_type) (nmp_class_from_type(obj_type)->obj_type_name) + #define NMP_OBJECT_CAST_OBJECT(obj) \ ({ \ typeof(obj) _obj = (obj); \ @@ -717,14 +746,35 @@ const char *nmp_object_to_string(const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); -void nmp_object_hash_update(const NMPObject *obj, NMHashState *h); + +void nmp_object_hash_update_full(const NMPObject *obj, gboolean for_id, NMHashState *h); + +static inline void +nmp_object_hash_update(const NMPObject *obj, NMHashState *h) +{ + return nmp_object_hash_update_full(obj, FALSE, h); +} typedef enum { NMP_OBJECT_CMP_FLAGS_NONE = 0, + /* Only compare for the ID. This is what nmp_object_id_cmp() does. + * + * In most cases, the identity of an object is a (non-strict) subset + * of the attributes of the object. + * + * However, for some objects (like NMPObjectLnk) there is on concept + * of identity. They implement object identity based on pointer equality + * (in that case, the ID is not a subset of the object's attributes). + * + * That's why this flag (currently) cannot be meaningfully combined with + * other flags. + */ + NMP_OBJECT_CMP_FLAGS_ID = NM_BIT(0), + /* Warning: this flag is currently only implemented for certain object types * (address and routes). */ - NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX = (1llu << 0), + NMP_OBJECT_CMP_FLAGS_IGNORE_IFINDEX = NM_BIT(1), } NMPObjectCmpFlags; int nmp_object_cmp_full(const NMPObject *obj1, const NMPObject *obj2, NMPObjectCmpFlags flags); @@ -744,8 +794,18 @@ nmp_object_equal(const NMPObject *obj1, const NMPObject *obj2) void nmp_object_copy(NMPObject *dst, const NMPObject *src, gboolean id_only); NMPObject *nmp_object_clone(const NMPObject *obj, gboolean id_only); -int nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2); -void nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h); +static inline int +nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) +{ + return nmp_object_cmp_full(obj1, obj2, NMP_OBJECT_CMP_FLAGS_ID); +} + +static inline void +nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h) +{ + return nmp_object_hash_update_full(obj, TRUE, h); +} + guint nmp_object_id_hash(const NMPObject *obj); static inline gboolean @@ -915,6 +975,7 @@ NMPCacheOpsType nmp_cache_update_netlink_route(NMPCache *cache, NMPObject *obj_hand_over, gboolean is_dump, guint16 nlmsgflags, + gboolean route_is_alive, const NMPObject **out_obj_old, const NMPObject **out_obj_new, const NMPObject **out_obj_replace, diff --git a/src/libnm-platform/nmp-plobj.c b/src/libnm-platform/nmp-plobj.c new file mode 100644 index 00000000..c27aa1fc --- /dev/null +++ b/src/libnm-platform/nmp-plobj.c @@ -0,0 +1,637 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 - 2018 Red Hat, Inc. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nmp-plobj.h" + +#include "nm-compat-headers/linux/if_addr.h" + +#include "libnm-glib-aux/nm-time-utils.h" +#include "nm-platform-utils.h" + +/*****************************************************************************/ + +#define TO_STRING_IFA_FLAGS_BUF_SIZE 256 + +static const char * +_to_string_ifa_flags(guint32 ifa_flags, char *buf, gsize size) +{ +#define S_FLAGS_PREFIX " flags " + nm_assert(buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN(S_FLAGS_PREFIX)); + + if (!ifa_flags) + buf[0] = '\0'; + else { + nm_platform_addr_flags2str(ifa_flags, + &buf[NM_STRLEN(S_FLAGS_PREFIX)], + size - NM_STRLEN(S_FLAGS_PREFIX)); + if (buf[NM_STRLEN(S_FLAGS_PREFIX)] == '\0') + buf[0] = '\0'; + else + memcpy(buf, S_FLAGS_PREFIX, NM_STRLEN(S_FLAGS_PREFIX)); + } + return buf; +} + +#define _to_string_dev(arr, ifindex) \ + ({ \ + const int _ifindex = (ifindex); \ + \ + _ifindex ? nm_sprintf_buf((arr), " dev %d", ifindex) : nm_str_truncate((arr)); \ + }) + +static const char * +_lifetime_to_string(guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size) +{ + if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT) + return "forever"; + + g_snprintf(buf, + buf_size, + "%usec", + nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now)); + return buf; +} + +static const char * +_lifetime_summary_to_string(gint32 now, + guint32 timestamp, + guint32 preferred, + guint32 lifetime, + char *buf, + size_t buf_size) +{ + g_snprintf(buf, + buf_size, + " lifetime %d-%u[%u,%u]", + (signed) now, + (unsigned) timestamp, + (unsigned) preferred, + (unsigned) lifetime); + return buf; +} + +static int +_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) +{ + guint32 lifetime_a; + guint32 lifetime_b; + guint32 preferred_a; + guint32 preferred_b; + gint32 now = 0; + + lifetime_a = + nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); + lifetime_b = + nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); + + NM_CMP_DIRECT(lifetime_a, lifetime_b); + NM_CMP_DIRECT(preferred_a, preferred_b); + return 0; +} + +/*****************************************************************************/ + +void +nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->ifindex, + obj->addr_source, + obj->use_ip4_broadcast_address ? obj->broadcast_address : ((in_addr_t) 0u), + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, + obj->use_ip4_broadcast_address, + obj->a_acd_not_ready, + obj->a_force_commit)); + nm_hash_update_strarr(h, obj->label); +} + +int +nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type) +{ + NM_CMP_SELF(a, b); + + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, plen); + NM_CMP_FIELD(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv4 addresses, you can add the same local address with differing peer-address + * (IFA_ADDRESS), provided that their net-part differs. */ + NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, peer_address); + NM_CMP_FIELD_STR(a, b, label); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), + * where nm_platform_ip_address_sync() sets IFA_F_NOPREFIXROUTE depending on + * NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE. + * There are thus no flags to compare for IPv4. */ + + NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), + nm_platform_ip4_broadcast_address_from_addr(b)); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); + if (a->use_ip4_broadcast_address) + NM_CMP_FIELD(a, b, broadcast_address); + NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); +} + +/*****************************************************************************/ + +void +nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit)); +} + +int +nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type) +{ + const struct in6_addr *p_a, *p_b; + + NM_CMP_SELF(a, b); + + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD_IN6ADDR(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, plen); + p_a = nm_platform_ip6_address_get_peer(a); + p_b = nm_platform_ip6_address_get_peer(b); + NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, + * where nm_platform_ip_address_sync() sets IFA_F_NOPREFIXROUTE depending on + * NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE. + * We thus only care about IFA_F_MANAGETEMPADDR. */ + NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, + b->n_ifa_flags & IFA_F_MANAGETEMPADDR); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); +} + +/*****************************************************************************/ + +static int +_address_pretty_sort_get_prio_4(in_addr_t addr) +{ + if (nm_ip4_addr_is_link_local(addr)) + return 0; + return 1; +} + +static int +_address_pretty_sort_get_prio_6(const struct in6_addr *addr) +{ + if (IN6_IS_ADDR_V4MAPPED(addr)) + return 0; + if (IN6_IS_ADDR_V4COMPAT(addr)) + return 1; + if (IN6_IS_ADDR_UNSPECIFIED(addr)) + return 2; + if (IN6_IS_ADDR_LOOPBACK(addr)) + return 3; + if (IN6_IS_ADDR_LINKLOCAL(addr)) + return 4; + if (IN6_IS_ADDR_SITELOCAL(addr)) + return 5; + return 6; +} + +int +nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, + const NMPlatformIP4Address *a2) +{ + in_addr_t n1; + in_addr_t n2; + + nm_assert(a1); + nm_assert(a2); + + /* Sort by address type. For example link local will + * be sorted *after* a global address. */ + NM_CMP_DIRECT(_address_pretty_sort_get_prio_4(a2->address), + _address_pretty_sort_get_prio_4(a1->address)); + + /* Sort the addresses based on their source. */ + NM_CMP_DIRECT(a2->addr_source, a1->addr_source); + + NM_CMP_DIRECT((a2->label[0] == '\0'), (a1->label[0] == '\0')); + + /* Finally, sort addresses lexically. We compare only the + * network part so that the order of addresses in the same + * subnet (and thus also the primary/secondary role) is + * preserved. + */ + n1 = nm_ip4_addr_clear_host_address(a1->address, a1->plen); + n2 = nm_ip4_addr_clear_host_address(a2->address, a2->plen); + NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); + return 0; +} + +int +nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, + const NMPlatformIP6Address *a2, + gboolean prefer_temp) +{ + gboolean ipv6_privacy1; + gboolean ipv6_privacy2; + + nm_assert(a1); + nm_assert(a2); + + /* tentative addresses are always sorted back... */ + /* sort tentative addresses after non-tentative. */ + NM_CMP_DIRECT(NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE), + NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_TENTATIVE)); + + /* Sort by address type. For example link local will + * be sorted *after* site local or global. */ + NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address), + _address_pretty_sort_get_prio_6(&a1->address)); + + ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); + ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_SECONDARY); + if (ipv6_privacy1 || ipv6_privacy2) { + gboolean public1 = TRUE; + gboolean public2 = TRUE; + + if (ipv6_privacy1) { + if (a1->n_ifa_flags & IFA_F_SECONDARY) + public1 = prefer_temp; + else + public1 = !prefer_temp; + } + if (ipv6_privacy2) { + if (a2->n_ifa_flags & IFA_F_SECONDARY) + public2 = prefer_temp; + else + public2 = !prefer_temp; + } + + NM_CMP_DIRECT(public2, public1); + } + + /* Sort the addresses based on their source. */ + NM_CMP_DIRECT(a2->addr_source, a1->addr_source); + + /* sort permanent addresses before non-permanent. */ + NM_CMP_DIRECT(NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_PERMANENT), + NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_PERMANENT)); + + /* finally sort addresses lexically */ + NM_CMP_DIRECT_IN6ADDR(&a1->address, &a2->address); + NM_CMP_DIRECT_MEMCMP(a1, a2, sizeof(*a1)); + return 0; +} + +void +nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) +{ + nm_assert(plen <= 32); + + addr->address = address; + addr->peer_address = address; + addr->plen = plen; +} + +const struct in6_addr * +nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr) +{ + if (IN6_IS_ADDR_UNSPECIFIED(&addr->peer_address) + || IN6_ARE_ADDR_EQUAL(&addr->peer_address, &addr->address)) + return &addr->address; + return &addr->peer_address; +} + +/*****************************************************************************/ + +/** + * nm_platform_ip4_address_to_string: + * @route: pointer to NMPlatformIP4Address address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. + * + * A method for converting an address struct into a string representation. + * + * Example output: "" + * + * Returns: a string representation of the address. + */ +const char * +nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len) +{ + char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; + char s_address[INET_ADDRSTRLEN]; + char s_peer[INET_ADDRSTRLEN]; + char str_dev[30]; + char str_label[32]; + char str_lft[30]; + char str_pref[30]; + char str_time[50]; + char s_source[50]; + char *str_peer = NULL; + const char *str_lft_p, *str_pref_p, *str_time_p; + gint32 now = nm_utils_get_monotonic_timestamp_sec(); + in_addr_t broadcast_address; + char str_broadcast[INET_ADDRSTRLEN]; + + if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) + return buf; + + inet_ntop(AF_INET, &address->address, s_address, sizeof(s_address)); + + if (address->peer_address != address->address) { + inet_ntop(AF_INET, &address->peer_address, s_peer, sizeof(s_peer)); + str_peer = g_strconcat(" ptp ", s_peer, NULL); + } + + if (*address->label) + g_snprintf(str_label, sizeof(str_label), " label %s", address->label); + else + str_label[0] = 0; + + str_lft_p = _lifetime_to_string(address->timestamp, + address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_lft, + sizeof(str_lft)), + str_pref_p = + (address->lifetime == address->preferred) + ? str_lft_p + : (_lifetime_to_string(address->timestamp, + address->lifetime ? MIN(address->preferred, address->lifetime) + : NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_pref, + sizeof(str_pref))); + str_time_p = _lifetime_summary_to_string(now, + address->timestamp, + address->preferred, + address->lifetime, + str_time, + sizeof(str_time)); + + broadcast_address = nm_platform_ip4_broadcast_address_from_addr(address); + + g_snprintf( + buf, + len, + "%s/%d" + "%s%s" /* broadcast */ + " lft %s" + " pref %s" + "%s" /* time */ + "%s" /* peer */ + "%s" /* dev */ + "%s" /* flags */ + "%s" /* label */ + " src %s" + "%s" /* a_acd_not_ready */ + "%s" /* a_force_commit */ + "", + s_address, + address->plen, + broadcast_address != 0u || address->use_ip4_broadcast_address + ? (address->use_ip4_broadcast_address ? " brd " : " brd* ") + : "", + broadcast_address != 0u || address->use_ip4_broadcast_address + ? nm_inet4_ntop(broadcast_address, str_broadcast) + : "", + str_lft_p, + str_pref_p, + str_time_p, + str_peer ?: "", + _to_string_dev(str_dev, address->ifindex), + _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), + str_label, + nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), + address->a_acd_not_ready ? " ip4acd-not-ready" : "", + address->a_force_commit ? " force-commit" : ""); + g_free(str_peer); + return buf; +} + +/** + * nm_platform_ip6_address_to_string: + * @route: pointer to NMPlatformIP6Address address structure + * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. + * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. + * + * A method for converting an address struct into a string representation. + * + * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1" + * + * Returns: a string representation of the address. + */ +const char * +nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len) +{ + char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; + char s_address[INET6_ADDRSTRLEN]; + char s_peer[INET6_ADDRSTRLEN]; + char str_lft[30]; + char str_pref[30]; + char str_time[50]; + char s_source[50]; + char str_dev[30]; + char *str_peer = NULL; + const char *str_lft_p, *str_pref_p, *str_time_p; + gint32 now = nm_utils_get_monotonic_timestamp_sec(); + + if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) + return buf; + + inet_ntop(AF_INET6, &address->address, s_address, sizeof(s_address)); + + if (!IN6_IS_ADDR_UNSPECIFIED(&address->peer_address)) { + inet_ntop(AF_INET6, &address->peer_address, s_peer, sizeof(s_peer)); + str_peer = g_strconcat(" ptp ", s_peer, NULL); + } + + str_lft_p = _lifetime_to_string(address->timestamp, + address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_lft, + sizeof(str_lft)), + str_pref_p = + (address->lifetime == address->preferred) + ? str_lft_p + : (_lifetime_to_string(address->timestamp, + address->lifetime ? MIN(address->preferred, address->lifetime) + : NM_PLATFORM_LIFETIME_PERMANENT, + now, + str_pref, + sizeof(str_pref))); + str_time_p = _lifetime_summary_to_string(now, + address->timestamp, + address->preferred, + address->lifetime, + str_time, + sizeof(str_time)); + + g_snprintf( + buf, + len, + "%s/%d lft %s pref %s%s%s%s%s src %s" + "%s" /* a_force_commit */ + "", + s_address, + address->plen, + str_lft_p, + str_pref_p, + str_time_p, + str_peer ?: "", + _to_string_dev(str_dev, address->ifindex), + _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), + nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), + address->a_force_commit ? " force-commit" : ""); + g_free(str_peer); + return buf; +} + +/*****************************************************************************/ + +gboolean +nm_platform_ip_address_match(int addr_family, + const NMPlatformIPAddress *address, + NMPlatformMatchFlags match_flag) +{ + nm_assert(!NM_FLAGS_ANY( + match_flag, + ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); + nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY)); + nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)); + + if (addr_family == AF_INET) { + if (nm_ip4_addr_is_link_local(((NMPlatformIP4Address *) address)->address)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) + return FALSE; + } else { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) + return FALSE; + } + } else { + if (IN6_IS_ADDR_LINKLOCAL(address->address_ptr)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) + return FALSE; + } else { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) + return FALSE; + } + } + + if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DADFAILED)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED)) + return FALSE; + } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_OPTIMISTIC)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) + return FALSE; + } else if (NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_DEPRECATED)) { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED)) + return FALSE; + } else { + if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { + .v4 = + { + .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, + .addr_family = AF_INET, + .sizeof_address = sizeof(NMPlatformIP4Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip4_address_to_string, + }, + .v6 = + { + .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .addr_family = AF_INET6, + .sizeof_address = sizeof(NMPlatformIP6Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip6_address_to_string, + }, +}; diff --git a/src/libnm-platform/nmp-plobj.h b/src/libnm-platform/nmp-plobj.h new file mode 100644 index 00000000..e149150d --- /dev/null +++ b/src/libnm-platform/nmp-plobj.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 - 2018 Red Hat, Inc. + */ + +#ifndef __NMP_PLOBJ_H__ +#define __NMP_PLOBJ_H__ + +#include "libnm-base/nm-base.h" + +#include "nmp-base.h" + +/*****************************************************************************/ + +/* This is used with _nm_align() on the platform structs. Note that + * "align" attribute can only increase the natural alignment, unless + * also "packed" is specified. That's what we want. + * https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes. + */ +#define _NMPlatformObject_Align (MAX(_nm_alignof(void *), _nm_alignof(gint64))) + +struct _NMPlatformObject { + /* the object type has no fields of its own, it is only used to having + * a special pointer type that can be used to indicate "any" type. */ + char _dummy_don_t_use_me; +} _nm_align(_NMPlatformObject_Align); + +/*****************************************************************************/ + +#define __NMPlatformObjWithIfindex_COMMON \ + int ifindex; \ + ; + +/*****************************************************************************/ + +#define __NMPlatformIPAddress_COMMON \ + __NMPlatformObjWithIfindex_COMMON; \ + NMIPConfigSource addr_source; \ + \ + /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). + * + * The rules are: + * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses + * are permanent. This rule is so that unset addresses (calloc) are permanent by default. + * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly + * set to 0). Such addresses are permanent. + * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp + * and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred + * is anchored at @timestamp. + * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time + * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). + * + * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the + * lease was received. Addresses from kernel might have the @timestamp based on the last modification + * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring + * @lifetime and @preferred. + */ \ + guint32 timestamp; \ + guint32 lifetime; /* seconds since timestamp */ \ + guint32 preferred; /* seconds since timestamp */ \ + \ + /* ifa_flags in 'struct ifaddrmsg' from <linux/if_addr.h>, extended to 32 bit by + * IFA_FLAGS attribute. */ \ + guint32 n_ifa_flags; \ + \ + bool use_ip4_broadcast_address : 1; \ + \ + /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be + * used by the upper layers which use NMPlatformIPRoute to track addresses that + * should be configured. */ \ + bool a_force_commit : 1; \ + \ + /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then + * be unclear how the following fields get merged. We could also use a zero bitfield, + * but instead we just have there the uint8 field. */ \ + guint8 plen; \ + ; + +/** + * NMPlatformIPAddress: + * + * Common parts of NMPlatformIP4Address and NMPlatformIP6Address. + **/ +typedef struct { + __NMPlatformIPAddress_COMMON; + _nm_alignas(NMIPAddr) guint8 address_ptr[]; +} _nm_alignas(NMPlatformObject) NMPlatformIPAddress; + +/** + * NMPlatformIP4Address: + * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() + **/ +struct _NMPlatformIP4Address { + __NMPlatformIPAddress_COMMON; + + /* The local address IFA_LOCAL. */ + _nm_alignas(NMIPAddr) in_addr_t address; + + /* The IFA_ADDRESS PTP peer address. This field is rather important, because + * it constitutes the identifier for the IPv4 address (e.g. you can add two + * addresses that only differ by their peer's network-part. + * + * Beware that for most cases, NetworkManager doesn't want to set an explicit + * peer-address. However, that corresponds to setting the peer address to @address + * itself. Leaving peer-address unset/zero, means explicitly setting the peer + * address to 0.0.0.0, which you probably don't want. + * */ + in_addr_t peer_address; /* PTP peer address */ + + /* IFA_BROADCAST. + * + * This parameter is ignored unless use_ip4_broadcast_address is TRUE. + * See nm_platform_ip4_broadcast_address_from_addr(). */ + in_addr_t broadcast_address; + + char label[NMP_IFNAMSIZ]; + + /* Whether the address is ready to be configured. By default, an address is, but this + * flag may indicate that the address is just for tracking purpose only, but the ACD + * state is not yet ready for the address to be configured. */ + bool a_acd_not_ready : 1; +} _nm_alignas(NMPlatformObject); + +/** + * NMPlatformIP6Address: + * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() + **/ +struct _NMPlatformIP6Address { + __NMPlatformIPAddress_COMMON; + _nm_alignas(NMIPAddr) struct in6_addr address; + struct in6_addr peer_address; +} _nm_alignas(NMPlatformObject); + +typedef union { + NMPlatformIPAddress ax; + NMPlatformIP4Address a4; + NMPlatformIP6Address a6; +} NMPlatformIPXAddress; + +#undef __NMPlatformIPAddress_COMMON + +/*****************************************************************************/ + +typedef enum { + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, +} NMPlatformIPAddressCmpType; + +#define NM_PLATFORM_IP_ADDRESS_CAST(address) \ + NM_CONSTCAST(NMPlatformIPAddress, \ + (address), \ + NMPlatformIPXAddress, \ + NMPlatformIP4Address, \ + NMPlatformIP6Address) + +#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__})) + +#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__})) + +/*****************************************************************************/ + +typedef struct { + bool is_ip4; + NMPObjectType obj_type; + gint8 addr_family; + guint8 sizeof_address; + int (*address_cmp)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type); + const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); +} NMPlatformVTableAddress; + +typedef union { + struct { + NMPlatformVTableAddress v6; + NMPlatformVTableAddress v4; + }; + NMPlatformVTableAddress vx[2]; +} _NMPlatformVTableAddressUnion; + +extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; + +void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h); + +int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type); + +static inline int +nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +{ + return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} + +void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h); + +int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type); + +static inline int +nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +{ + return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} + +int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, + const NMPlatformIP4Address *a2); + +int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, + const NMPlatformIP6Address *a2, + gboolean prefer_temp); + +static inline in_addr_t +nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr) +{ + nm_assert(addr); + + if (addr->use_ip4_broadcast_address) + return addr->broadcast_address; + + /* the set broadcast-address gets ignored, and we determine a default brd base + * on the peer IFA_ADDRESS. */ + if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */) + return nm_ip4_addr_get_broadcast_address(addr->peer_address, addr->plen); + return 0u; +} + +void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); + +const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); + +static inline gpointer +nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr) +{ + nm_assert_addr_family(addr_family); + nm_assert(addr); + + if (NM_IS_IPv4(addr_family)) + return &((NMPlatformIP4Address *) addr)->peer_address; + return &((NMPlatformIP6Address *) addr)->peer_address; +} + +const char * +nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len); +const char * +nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len); + +int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); + +NMPlatformIP4Route *nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, + int ifindex, + guint32 route_table, + guint32 route_metric, + gboolean force_commit, + NMPlatformIP4Route *dst); + +typedef enum { + + /* match-flags are strictly inclusive. That means, + * by default nothing is matched, but if you enable a particular + * flag, a candidate that matches passes the check. + * + * In other words: adding more flags can only extend the result + * set of matching objects. + * + * Also, the flags form partitions. Like, an address can be either of + * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for + * the ADDRSTATE match types. + */ + NM_PLATFORM_MATCH_WITH_NONE = 0, + + NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL = (1LL << 0), + NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1), + NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY = + NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL, + + NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL = (1LL << 2), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4), + NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED = (1LL << 5), + NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY = + NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE + | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DEPRECATED, +} NMPlatformMatchFlags; + +gboolean nm_platform_ip_address_match(int addr_family, + const NMPlatformIPAddress *addr, + NMPlatformMatchFlags match_flag); + +/*****************************************************************************/ + +#endif /* __NMP_PLOBJ_H__ */ diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c index cd54df92..90a29045 100644 --- a/src/libnm-platform/tests/test-nm-platform.c +++ b/src/libnm-platform/tests/test-nm-platform.c @@ -6,16 +6,67 @@ #include "libnm-platform/nm-netlink.h" #include "libnm-platform/nmp-netns.h" #include "libnm-platform/nm-platform-utils.h" +#include "libnm-platform/nmp-object.h" #include "libnm-glib-aux/nm-test-utils.h" /*****************************************************************************/ -void -_nm_logging_clear_platform_logging_cache(void) -{ - /* this symbols is required by nm-log-core library. */ -} +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP4Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP4Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLink)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBond)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBridge)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGre)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkInfiniband)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIp6Tnl)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIpIp)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkMacsec)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkMacvlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkSit)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkTun)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVrf)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti6)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVxlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkWireGuard)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectQdisc)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectRoutingRule)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectTfilter)); + +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP4Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP4Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP6Address)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP6Route)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPAddress)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPAddress)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPRoute)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXAddress)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXRoute)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLink)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBond)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBridge)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGre)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkInfiniband)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIp6Tnl)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIpIp)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkMacsec)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkMacvlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkSit)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkTun)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVrf)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti6)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVxlan)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkWireGuard)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformObjWithIfindex)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformQdisc)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformRoutingRule)); +G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformTfilter)); /*****************************************************************************/ @@ -140,6 +191,57 @@ test_nmp_link_mode_all_advertised_modes_bits(void) /*****************************************************************************/ +static void +test_nmpclass_consistency(void) +{ + NMPObjectType obj_type; + NMPObjectType obj_type2; + + G_STATIC_ASSERT(G_N_ELEMENTS(_nmp_classes) == NMP_OBJECT_TYPE_MAX); + + for (obj_type = 1; obj_type <= NMP_OBJECT_TYPE_MAX; obj_type++) { + const NMPClass *klass = nmp_class_from_type(obj_type); + gboolean is_lnk; + + g_assert(klass); + g_assert(klass == &_nmp_classes[obj_type - 1]); + + g_assert_cmpint(klass->obj_type, ==, obj_type); + g_assert(klass->obj_type_name); + + g_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); + g_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); + g_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); + + g_assert((!!klass->cmd_obj_cmp) != (!!klass->cmd_plobj_cmp)); + g_assert((!!klass->cmd_obj_hash_update) != (!!klass->cmd_plobj_hash_update)); + + g_assert((!klass->cmd_obj_cmp) || (!klass->cmd_plobj_id_cmp)); + g_assert((!klass->cmd_obj_hash_update) || (!klass->cmd_plobj_id_hash_update)); + + g_assert_cmpint(klass->sizeof_public, >, 0); + g_assert_cmpint(klass->sizeof_data, >=, klass->sizeof_public); + + g_assert((!!klass->cmd_obj_to_string) != (!!klass->cmd_plobj_to_string)); + g_assert(!klass->cmd_plobj_to_string_id || klass->cmd_plobj_to_string); + + is_lnk = (obj_type >= NMP_OBJECT_TYPE_LNK_BRIDGE && obj_type <= NMP_OBJECT_TYPE_LNK_BOND); + if (klass->lnk_link_type == NM_LINK_TYPE_NONE) { + G_STATIC_ASSERT(NM_LINK_TYPE_NONE == 0); + g_assert(!is_lnk); + } else + g_assert(is_lnk); + + for (obj_type2 = 1; obj_type2 < obj_type; obj_type2++) { + const NMPClass *klass2 = nmp_class_from_type(obj_type2); + + g_assert_cmpstr(klass->obj_type_name, !=, klass2->obj_type_name); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -150,6 +252,7 @@ main(int argc, char **argv) g_test_add_func("/nm-platform/test_use_symbols", test_use_symbols); g_test_add_func("/nm-platform/test_nmp_link_mode_all_advertised_modes_bits", test_nmp_link_mode_all_advertised_modes_bits); + g_test_add_func("/nm-platform/test_nmpclass_consistency", test_nmpclass_consistency); return g_test_run(); } diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h index 4783c572..094c0ff0 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h @@ -12,7 +12,7 @@ #define NM_TYPE_WIFI_UTILS_NL80211 (nm_wifi_utils_nl80211_get_type()) #define NM_WIFI_UTILS_NL80211(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211)) #define NM_WIFI_UTILS_NL80211_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class)) #define NM_IS_WIFI_UTILS_NL80211(obj) \ diff --git a/src/libnm-platform/wifi/nm-wifi-utils-wext.c b/src/libnm-platform/wifi/nm-wifi-utils-wext.c index 678d71fe..eac3c929 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-wext.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-wext.c @@ -90,7 +90,7 @@ dispose(GObject *object) { NMWifiUtilsWext *wext = NM_WIFI_UTILS_WEXT(object); - wext->fd = nm_close(wext->fd); + nm_clear_fd(&wext->fd); } static gboolean diff --git a/src/libnm-platform/wifi/nm-wifi-utils-wext.h b/src/libnm-platform/wifi/nm-wifi-utils-wext.h index d6f3453c..28a74eba 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-wext.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-wext.h @@ -10,7 +10,7 @@ #define NM_TYPE_WIFI_UTILS_WEXT (nm_wifi_utils_wext_get_type()) #define NM_WIFI_UTILS_WEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWext)) + (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWext)) #define NM_WIFI_UTILS_WEXT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWextClass)) #define NM_IS_WIFI_UTILS_WEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS_WEXT)) diff --git a/src/libnm-platform/wifi/nm-wifi-utils.h b/src/libnm-platform/wifi/nm-wifi-utils.h index fd636418..aa5a34cd 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.h +++ b/src/libnm-platform/wifi/nm-wifi-utils.h @@ -13,7 +13,7 @@ typedef struct NMWifiUtils NMWifiUtils; #define NM_TYPE_WIFI_UTILS (nm_wifi_utils_get_type()) -#define NM_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils)) +#define NM_WIFI_UTILS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils)) #define NM_WIFI_UTILS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass)) #define NM_IS_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS)) diff --git a/src/libnm-platform/wpan/nm-wpan-utils.h b/src/libnm-platform/wpan/nm-wpan-utils.h index eea5587d..67c5f412 100644 --- a/src/libnm-platform/wpan/nm-wpan-utils.h +++ b/src/libnm-platform/wpan/nm-wpan-utils.h @@ -11,7 +11,7 @@ typedef struct NMWpanUtils NMWpanUtils; #define NM_TYPE_WPAN_UTILS (nm_wpan_utils_get_type()) -#define NM_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils)) +#define NM_WPAN_UTILS(obj) (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils)) #define NM_WPAN_UTILS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass)) #define NM_IS_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WPAN_UTILS)) |