From 9934a6a0e3e60368cd3c7423a6b21fa5e92d740c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 14 Feb 2019 13:08:12 +0100 Subject: [PATCH] platform: add support for routing-rule objects and cache them in platform Add and implement NMPlatformRoutingRule types and let the platform cache handle rules. Rules are special in two ways: - they don't have an ifindex. That makes them different from all other currently existing NMPlatform* types, which have an "ifindex" field and "implement" NMPlatformObjWithIfindex. - they have an address family, but contrary to addresses and routes, there is only one NMPlatformRoutingRule object to handle both address families. Both of these points require some special considerations. Kernel treats routing-rules quite similar to routes. That is, kernel allows to add different rules/routes, as long as they differ in certain fields. These "fields" make up the identity of the rules/routes. But in practice, it's not defined which fields contribute to the identity of these objects. That makes using the netlink API very hard. For example, when kernel gains support for a new attribute which NetworkManager does not know yet, then users can add two rules/routes that look the same to NetworkManager. That can easily result in cache inconsistencies. Another problem is, that older kernel versions may not yet support all fields, which NetworkManager (and newer kernels) considers for identity. The older kernel will not simply reject netlink messages with these unknown keys, instead it will proceed adding the route/rule without it. That means, the added route/rule will have a different identity than what NetworkManager intended to add. --- src/nm-types.h | 1 + src/platform/nm-linux-platform.c | 270 ++++++++++++++++-- src/platform/nm-platform.c | 457 ++++++++++++++++++++++++++++++- src/platform/nm-platform.h | 62 +++++ src/platform/nmp-object.c | 87 ++++++ src/platform/nmp-object.h | 23 ++ 6 files changed, 874 insertions(+), 26 deletions(-) diff --git a/src/nm-types.h b/src/nm-types.h index f30cc19920..03ee99299b 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -193,6 +193,7 @@ typedef enum { NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE, NMP_OBJECT_TYPE_QDISC, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 7d299aba9d..18c9847324 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,19 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +#define FRA_TUN_ID 12 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_PAD 18 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define FRA_PROTOCOL 21 +#define FRA_IP_PROTO 22 +#define FRA_SPORT_RANGE 23 +#define FRA_DPORT_RANGE 24 + +/*****************************************************************************/ + #define IFLA_MACSEC_UNSPEC 0 #define IFLA_MACSEC_SCI 1 #define IFLA_MACSEC_PORT 2 @@ -291,8 +305,10 @@ typedef enum { REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, REFRESH_ALL_TYPE_IP4_ROUTES = 3, REFRESH_ALL_TYPE_IP6_ROUTES = 4, - REFRESH_ALL_TYPE_QDISCS = 5, - REFRESH_ALL_TYPE_TFILTERS = 6, + REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, + REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, + REFRESH_ALL_TYPE_QDISCS = 7, + REFRESH_ALL_TYPE_TFILTERS = 8, _REFRESH_ALL_TYPE_NUM, } RefreshAllType; @@ -313,22 +329,28 @@ typedef enum { DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F (2, REFRESH_ALL_TYPE_IP6_ADDRESSES), DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F (3, REFRESH_ALL_TYPE_IP4_ROUTES), DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F (4, REFRESH_ALL_TYPE_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (5, REFRESH_ALL_TYPE_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (6, REFRESH_ALL_TYPE_TFILTERS), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 << F (5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 << F (6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (7, REFRESH_ALL_TYPE_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (8, REFRESH_ALL_TYPE_TFILTERS), #undef F - DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 7, - DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 8, - DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 9, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 10, + DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, + DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, + DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, __DELAYED_ACTION_TYPE_MAX, + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, + DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, @@ -3354,6 +3376,165 @@ rta_multipath_done: return g_steal_pointer (&obj); } +static NMPObject * +_new_from_nl_routing_rule (struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [FRA_UNSPEC] = { }, + [FRA_DST] = { /* struct in_addr, struct in6_addr */ }, + [FRA_SRC] = { /* struct in_addr, struct in6_addr */ }, + [FRA_IIFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [FRA_GOTO] = { .type = NLA_U32, }, + [FRA_UNUSED2] = { }, + [FRA_PRIORITY] = { .type = NLA_U32, }, + [FRA_UNUSED3] = { }, + [FRA_UNUSED4] = { }, + [FRA_UNUSED5] = { }, + [FRA_FWMARK] = { .type = NLA_U32, }, + [FRA_FLOW] = { .type = NLA_U32, }, + [FRA_TUN_ID] = { .type = NLA_U64, }, + [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32, }, + [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32, }, + [FRA_TABLE] = { .type = NLA_U32, }, + [FRA_FWMASK] = { .type = NLA_U32, }, + [FRA_OIFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [FRA_PAD] = { .type = NLA_U32, }, + [FRA_L3MDEV] = { .type = NLA_U8, }, + [FRA_UID_RANGE] = { .minlen = sizeof(NMFibRuleUidRange), + .maxlen = sizeof(NMFibRuleUidRange), }, + [FRA_PROTOCOL] = { .type = NLA_U8, }, + [FRA_IP_PROTO] = { .type = NLA_U8, }, + [FRA_SPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), }, + [FRA_DPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), }, + }; + struct nlattr *tb[G_N_ELEMENTS (policy)]; + const struct fib_rule_hdr *frh; + NMPlatformRoutingRule *props; + nm_auto_nmpobj NMPObject *obj = NULL; + int addr_family; + guint8 addr_size; + + if (nlmsg_parse_arr (nlh, sizeof (*frh), tb, policy) < 0) + return NULL; + + frh = nlmsg_data (nlh); + + addr_family = frh->family; + + if (!NM_IN_SET (addr_family, AF_INET, AF_INET6)) { + /* we don't care about other address families. */ + return NULL; + } + + addr_size = nm_utils_addr_family_to_size (addr_family); + + obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL); + props = &obj->routing_rule; + + props->addr_family = addr_family; + props->action = frh->action; + props->flags = frh->flags; + props->tos = frh->tos; + + props->table = tb[FRA_TABLE] + ? nla_get_u32 (tb[FRA_TABLE]) + : frh->table; + + if (tb[FRA_SUPPRESS_PREFIXLEN]) + props->suppress_prefixlen_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_PREFIXLEN]); + + if (tb[FRA_SUPPRESS_IFGROUP]) + props->suppress_ifgroup_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_IFGROUP]); + + if (tb[FRA_IIFNAME]) + nla_strlcpy (props->iifname, tb[FRA_IIFNAME], sizeof (props->iifname)); + + if (tb[FRA_OIFNAME]) + nla_strlcpy (props->oifname, tb[FRA_OIFNAME], sizeof (props->oifname)); + + if (tb[FRA_PRIORITY]) + props->priority = nla_get_u32 (tb[FRA_PRIORITY]); + + if (tb[FRA_FWMARK]) + props->fwmark = nla_get_u32 (tb[FRA_FWMARK]); + + if (tb[FRA_FWMASK]) + props->fwmask = nla_get_u32 (tb[FRA_FWMASK]); + + if (tb[FRA_GOTO]) + props->goto_target = nla_get_u32 (tb[FRA_GOTO]); + + props->src_len = frh->src_len; + if (props->src_len > addr_size * 8) + return NULL; + if (!tb[FRA_SRC]) { + if (props->src_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted (addr_family, + &props->src, + nla_data (tb[FRA_SRC]), + nla_len (tb[FRA_SRC]), + NULL)) + return NULL; + + props->dst_len = frh->dst_len; + if (props->dst_len > addr_size * 8) + return NULL; + if (!tb[FRA_DST]) { + if (props->dst_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted (addr_family, + &props->dst, + nla_data (tb[FRA_DST]), + nla_len (tb[FRA_DST]), + NULL)) + return NULL; + + if (tb[FRA_FLOW]) + props->flow = nla_get_u32 (tb[FRA_FLOW]); + + if (tb[FRA_TUN_ID]) + props->tun_id = nla_get_be64 (tb[FRA_TUN_ID]); + + if (tb[FRA_L3MDEV]) { + /* actually, kernel only allows this attribute to be missing or + * "1". Still, encode it as full uint8. + * + * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */ + props->l3mdev = nla_get_u8 (tb[FRA_L3MDEV]); + } + + if (tb[FRA_PROTOCOL]) + props->protocol = nla_get_u8 (tb[FRA_PROTOCOL]); + else + nm_assert (props->protocol == RTPROT_UNSPEC); + + if (tb[FRA_IP_PROTO]) + props->ip_proto = nla_get_u8 (tb[FRA_IP_PROTO]); + + G_STATIC_ASSERT_EXPR (sizeof (NMFibRulePortRange) == 4); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, start) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, end) == 2); + + nla_memcpy_checked_size (&props->sport_range, tb[FRA_SPORT_RANGE], sizeof (props->sport_range)); + nla_memcpy_checked_size (&props->dport_range, tb[FRA_DPORT_RANGE], sizeof (props->dport_range)); + + G_STATIC_ASSERT_EXPR (sizeof (NMFibRuleUidRange) == 8); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, start) == 0); + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, end) == 4); + + if (tb[FRA_UID_RANGE]) { + nla_memcpy_checked_size (&props->uid_range, tb[FRA_UID_RANGE], sizeof (props->uid_range)); + props->uid_range_has = TRUE; + } + + return g_steal_pointer (&obj); +} + static NMPObject * _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) { @@ -3453,6 +3634,10 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m case RTM_DELROUTE: case RTM_GETROUTE: return _new_from_nl_route (msghdr, id_only); + case RTM_NEWRULE: + case RTM_DELRULE: + case RTM_GETRULE: + return _new_from_nl_routing_rule (msghdr, id_only); case RTM_NEWQDISC: case RTM_DELQDISC: case RTM_GETQDISC: @@ -4372,6 +4557,8 @@ refresh_all_type_get_info (RefreshAllType refresh_all_type) R (REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), R (REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), R (REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), + R (REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), + R (REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), R (REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), R (REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), #undef R @@ -4391,6 +4578,8 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_type_to_refresh_all_type, Delaye NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), @@ -4410,7 +4599,7 @@ delayed_action_type_from_refresh_all_type (RefreshAllType refresh_all_type) return t; } -static DelayedActionType +static RefreshAllType refresh_all_type_from_needle_object (const NMPObject *obj_needle) { switch (NMP_OBJECT_GET_TYPE (obj_needle)) { @@ -4421,6 +4610,13 @@ refresh_all_type_from_needle_object (const NMPObject *obj_needle) case NMP_OBJECT_TYPE_IP6_ROUTE: return REFRESH_ALL_TYPE_IP6_ROUTES; case NMP_OBJECT_TYPE_QDISC: return REFRESH_ALL_TYPE_QDISCS; case NMP_OBJECT_TYPE_TFILTER: return REFRESH_ALL_TYPE_TFILTERS; + case NMP_OBJECT_TYPE_ROUTING_RULE: + switch (NMP_OBJECT_CAST_ROUTING_RULE (obj_needle)->addr_family) { + case AF_INET: return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; + case AF_INET6: return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; + } + nm_assert_not_reached (); + return 0; default: nm_assert_not_reached (); return 0; @@ -4439,6 +4635,12 @@ refresh_all_type_init_lookup (RefreshAllType refresh_all_type, nm_assert (refresh_all_info); + if (NM_IN_SET (refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { + return nmp_lookup_init_object_by_addr_family (lookup, + refresh_all_info->obj_type, + refresh_all_info->addr_family); + } + /* not yet implemented. */ nm_assert (refresh_all_info->addr_family == AF_UNSPEC); @@ -4459,6 +4661,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, "refresh-all-routing-rules-ip4"), + NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, "refresh-all-routing-rules-ip6"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), @@ -4467,6 +4671,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX), ); @@ -4955,6 +5160,7 @@ cache_on_change (NMPlatform *platform, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -5313,6 +5519,7 @@ _nl_msg_new_dump (NMPObjectType obj_type, case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: { const struct rtgenmsg gmsg = { .rtgen_family = preferred_addr_family, @@ -5333,12 +5540,30 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionType action_type_prune; DelayedActionType iflags; nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; - FOR_EACH_DELAYED_ACTION (iflags, action_type) { + action_type_prune = action_type; + + /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra + * cache lookup for every entry. + * + * Avoid that, by special casing routing-rules here. */ + if (NM_FLAGS_ALL (action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { + NMPLookup lookup; + + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), + &lookup); + action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; + } + + FOR_EACH_DELAYED_ACTION (iflags, action_type_prune) { RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); NMPLookup lookup; @@ -5495,6 +5720,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, RTM_DELADDR, RTM_DELROUTE, + RTM_DELRULE, RTM_DELQDISC, RTM_DELTFILTER)) { /* The event notifies about a deleted object. We don't need to initialize all @@ -5513,6 +5739,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event && NM_IN_SET (msghdr->nlmsg_type, RTM_NEWADDR, RTM_NEWLINK, RTM_NEWROUTE, + RTM_NEWRULE, RTM_NEWQDISC, RTM_NEWTFILTER)) { is_dump = delayed_action_refresh_all_in_progress (platform, @@ -5532,10 +5759,11 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event switch (msghdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: case RTM_GETLINK: + case RTM_NEWADDR: + case RTM_NEWLINK: case RTM_NEWQDISC: + case RTM_NEWRULE: case RTM_NEWTFILTER: cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { @@ -5630,10 +5858,11 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event break; } - case RTM_DELLINK: case RTM_DELADDR: - case RTM_DELROUTE: + case RTM_DELLINK: case RTM_DELQDISC: + case RTM_DELROUTE: + case RTM_DELRULE: case RTM_DELTFILTER: cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { @@ -7817,7 +8046,8 @@ qdisc_add (NMPlatform *platform, if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) return 0; - + if (seq_result < 0) + return seq_result; return -NME_UNSPEC; } @@ -8134,6 +8364,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); @@ -8399,9 +8630,13 @@ constructed (GObject *_object) g_assert (!nle); nle = nl_socket_add_memberships (priv->nlh, + RTNLGRP_IPV4_IFADDR, + RTNLGRP_IPV4_ROUTE, + RTNLGRP_IPV4_RULE, + RTNLGRP_IPV6_RULE, + RTNLGRP_IPV6_IFADDR, + RTNLGRP_IPV6_ROUTE, RTNLGRP_LINK, - RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, - RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, RTNLGRP_TC, 0); g_assert (!nle); @@ -8428,6 +8663,7 @@ constructed (GObject *_object) DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f0fb2a528d..5e6744db09 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -6063,6 +6064,253 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi return buf; } +static void +_routing_rule_addr_to_string (char **buf, + gsize *len, + int addr_family, + const NMIPAddr *addr, + guint8 plen, + gboolean is_src) +{ + char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + gboolean is_zero; + gsize addr_size; + + nm_assert_addr_family (addr_family); + nm_assert (addr); + + addr_size = nm_utils_addr_family_to_size (addr_family); + + is_zero = nm_utils_memeqzero (addr, addr_size); + + if ( plen == 0 + && is_zero) { + if (is_src) + nm_utils_strbuf_append_str (buf, len, " from all"); + else + nm_utils_strbuf_append_str (buf, len, ""); + return; + } + + nm_utils_strbuf_append_str (buf, len, is_src ? " from " : " to "); + + nm_utils_strbuf_append_str (buf, len, nm_utils_inet_ntop (addr_family, addr, s_addr)); + + if (plen != (addr_size * 8)) + nm_utils_strbuf_append (buf, len, "/%u", plen); +} + +static void +_routing_rule_port_range_to_string (char **buf, + gsize *len, + const NMFibRulePortRange *port_range, + const char *name) +{ + if ( port_range->start == 0 + && port_range->end == 0) + nm_utils_strbuf_append_str (buf, len, ""); + else { + nm_utils_strbuf_append (buf, len, " %s %u", name, port_range->start); + if (port_range->start != port_range->end) + nm_utils_strbuf_append (buf, len, "-%u", port_range->end); + } +} + +const char * +nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *routing_rule, char *buf, gsize len) +{ + const char *buf0; + guint32 rr_flags; + + if (!nm_utils_to_string_buffer_init_null (routing_rule, &buf, &len)) + return buf; + + if (!NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6)) { + /* invalid addr-family. The other fields are undefined. */ + if (routing_rule->addr_family == AF_UNSPEC) + g_snprintf (buf, len, "[routing-rule]"); + else + g_snprintf (buf, len, "[routing-rule family:%u]", routing_rule->addr_family); + return buf; + } + + buf0 = buf; + + rr_flags = routing_rule->flags; + + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_INVERT); + nm_utils_strbuf_append (&buf, &len, + "[%c] " /* addr-family */ + "%u:" /* priority */ + "%s", /* not/FIB_RULE_INVERT */ + nm_utils_addr_family_to_char (routing_rule->addr_family), + routing_rule->priority, + ( NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_INVERT) + ? " not" + : "")); + + _routing_rule_addr_to_string (&buf, &len, + routing_rule->addr_family, + &routing_rule->src, + routing_rule->src_len, + TRUE); + + _routing_rule_addr_to_string (&buf, &len, + routing_rule->addr_family, + &routing_rule->dst, + routing_rule->dst_len, + FALSE); + + if (routing_rule->tos) + nm_utils_strbuf_append (&buf, &len, " tos 0x%02x", routing_rule->tos); + + if ( routing_rule->fwmark != 0 + || routing_rule->fwmask != 0) { + nm_utils_strbuf_append (&buf, &len, " fwmark %#x", (unsigned) routing_rule->fwmark); + if (routing_rule->fwmark != 0xFFFFFFFFu) + nm_utils_strbuf_append (&buf, &len, "/%#x", (unsigned) routing_rule->fwmask); + } + + if (routing_rule->iifname[0]) { + nm_utils_strbuf_append (&buf, &len, " iif %s", routing_rule->iifname); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_IIF_DETACHED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_IIF_DETACHED)) + nm_utils_strbuf_append_str (&buf, &len, " [detached]"); + } + + if (routing_rule->oifname[0]) { + nm_utils_strbuf_append (&buf, &len, " oif %s", routing_rule->oifname); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_OIF_DETACHED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_OIF_DETACHED)) + nm_utils_strbuf_append_str (&buf, &len, " [detached]"); + } + + if (routing_rule->l3mdev != 0) { + if (routing_rule->l3mdev == 1) + nm_utils_strbuf_append_str (&buf, &len, " lookup [l3mdev-table]"); + else { + nm_utils_strbuf_append (&buf, &len, " lookup [l3mdev-table/%u]", (unsigned) routing_rule->l3mdev); + } + } + + if ( routing_rule->uid_range_has + || routing_rule->uid_range.start + || routing_rule->uid_range.end) { + nm_utils_strbuf_append (&buf, &len, + " uidrange %u-%u%s", + routing_rule->uid_range.start, + routing_rule->uid_range.end, + routing_rule->uid_range_has ? "" : "(?)"); + } + + if (routing_rule->ip_proto != 0) { + /* we don't call getprotobynumber(), just print the numeric value. + * This differs from what ip-rule prints. */ + nm_utils_strbuf_append (&buf, &len, + " ipproto %u", + routing_rule->ip_proto); + } + + _routing_rule_port_range_to_string (&buf, &len, + &routing_rule->sport_range, + "sport"); + + _routing_rule_port_range_to_string (&buf, &len, + &routing_rule->dport_range, + "dport"); + + if (routing_rule->tun_id != 0) { + nm_utils_strbuf_append (&buf, &len, + " tun_id %"G_GUINT64_FORMAT, + routing_rule->tun_id); + } + + if (routing_rule->table != 0) { + nm_utils_strbuf_append (&buf, &len, + " lookup %u", + routing_rule->table); + } + + if (routing_rule->suppress_prefixlen_inverse != 0) { + nm_utils_strbuf_append (&buf, &len, + " suppress_prefixlen %d", + (int) (~routing_rule->suppress_prefixlen_inverse)); + } + + if (routing_rule->suppress_ifgroup_inverse != 0) { + nm_utils_strbuf_append (&buf, &len, + " suppress_ifgroup %d", + (int) (~routing_rule->suppress_ifgroup_inverse)); + } + + if (routing_rule->flow) { + /* FRA_FLOW is only for IPv4, but we want to print the value for all address-families, + * to see when it is set. In practice, this should not be set except for IPv4. + * + * We don't follow the style how ip-rule prints flow/realms. It's confusing. Just + * print the value hex. */ + nm_utils_strbuf_append (&buf, &len, + " realms 0x%08x", + routing_rule->flow); + } + + if (routing_rule->action == RTN_NAT) { + G_STATIC_ASSERT_EXPR (RTN_NAT == 10); + + /* NAT is deprecated for many years. We don't support RTA_GATEWAY/FRA_UNUSED2 + * for the gateway, and so do recent kernels ignore that parameter. */ + nm_utils_strbuf_append_str (&buf, &len, " masquerade"); + } else if (routing_rule->action == FR_ACT_GOTO) { + if (routing_rule->goto_target != 0) + nm_utils_strbuf_append (&buf, &len, " goto %u", routing_rule->goto_target); + else + nm_utils_strbuf_append_str (&buf, &len, " goto none"); + rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_UNRESOLVED); + if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_UNRESOLVED)) + nm_utils_strbuf_append_str (&buf, &len, " unresolved"); + } else if (routing_rule->action != FR_ACT_TO_TBL) { + const char *ss; + char ss_buf[60]; + +#define _V(v1, v2) ((sizeof (char[(((int) (v1)) == ((int) (v2))) ? 1 : -1]) * 0) + (v1)) + switch (routing_rule->action) { + case _V (FR_ACT_UNSPEC, RTN_UNSPEC) : ss = "none"; break; + case _V (FR_ACT_TO_TBL, RTN_UNICAST) : ss = "unicast"; break; + case _V (FR_ACT_GOTO, RTN_LOCAL) : ss = "local"; break; + case _V (FR_ACT_NOP, RTN_BROADCAST) : ss = "nop"; break; + case _V (FR_ACT_RES3, RTN_ANYCAST) : ss = "anycast"; break; + case _V (FR_ACT_RES4, RTN_MULTICAST) : ss = "multicast"; break; + case _V (FR_ACT_BLACKHOLE, RTN_BLACKHOLE) : ss = "blackhole"; break; + case _V (FR_ACT_UNREACHABLE, RTN_UNREACHABLE) : ss = "unreachable"; break; + case _V (FR_ACT_PROHIBIT, RTN_PROHIBIT) : ss = "prohibit"; break; + case RTN_THROW : ss = "throw"; break; + case RTN_NAT : ss = "nat"; break; + case RTN_XRESOLVE : ss = "xresolve"; break; + default: + ss = nm_sprintf_buf (ss_buf, "action-%u", routing_rule->action); + break; + } +#undef _V + nm_utils_strbuf_append (&buf, &len, " %s", ss); + } + + if (routing_rule->protocol != RTPROT_UNSPEC) + nm_utils_strbuf_append (&buf, &len, " protocol %u", routing_rule->protocol); + + if ( routing_rule->goto_target != 0 + && routing_rule->action != FR_ACT_GOTO) { + /* a trailing target is set for an unexpected action. Print it. */ + nm_utils_strbuf_append (&buf, &len, " goto-target %u", routing_rule->goto_target); + } + + if (rr_flags != 0) { + /* we have some flags we didn't print about yet. */ + nm_utils_strbuf_append (&buf, &len, " remaining-flags %x", rr_flags); + } + + return buf0; +} + const char * nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) { @@ -7021,6 +7269,186 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route return 0; } +#define _ROUTING_RULE_FLAGS_IGNORE ( FIB_RULE_UNRESOLVED \ + | FIB_RULE_IIF_DETACHED \ + | FIB_RULE_OIF_DETACHED) + +void +nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, + NMPlatformRoutingRuleCmpType cmp_type, + NMHashState *h) +{ + gboolean cmp_full = TRUE; + gsize addr_size; + guint32 flags_mask = G_MAXUINT32; + + if (G_UNLIKELY (!NM_IN_SET (obj->addr_family, AF_INET, AF_INET6))) { + /* the address family is not one of the supported ones. That means, the + * instance will only compare equal to itself (pointer-equality). */ + nm_hash_update_val (h, (gconstpointer) obj); + return; + } + + switch (cmp_type) { + + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: + + flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: + + cmp_full = FALSE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: + + + nm_hash_update_vals (h, + obj->addr_family, + obj->tun_id, + obj->table, + obj->flags & flags_mask, + obj->priority, + obj->fwmark, + obj->fwmask, + ( ( cmp_full + || ( cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY + && obj->action == FR_ACT_GOTO)) + ? obj->goto_target + : (guint32) 0u), + ( ( cmp_full + || obj->addr_family == AF_INET) + ? obj->flow + : (guint32) 0u), + NM_HASH_COMBINE_BOOLS (guint8, + obj->uid_range_has), + obj->suppress_prefixlen_inverse, + obj->suppress_ifgroup_inverse, + ( cmp_full + ? obj->l3mdev + : (guint8) !!obj->l3mdev), + obj->action, + obj->tos, + obj->src_len, + obj->dst_len, + obj->protocol, + obj->ip_proto); + addr_size = nm_utils_addr_family_to_size (obj->addr_family); + if (cmp_full || obj->src_len > 0) + nm_hash_update (h, &obj->src, addr_size); + if (cmp_full || obj->dst_len > 0) + nm_hash_update (h, &obj->dst, addr_size); + if (cmp_full || obj->uid_range_has) + nm_hash_update_valp (h, &obj->uid_range); + nm_hash_update_valp (h, &obj->sport_range); + nm_hash_update_valp (h, &obj->dport_range); + nm_hash_update_str (h, obj->iifname); + nm_hash_update_str (h, obj->oifname); + return; + } + + nm_assert_not_reached (); +} + +int +nm_platform_routing_rule_cmp (const NMPlatformRoutingRule *a, + const NMPlatformRoutingRule *b, + NMPlatformRoutingRuleCmpType cmp_type) +{ + gboolean cmp_full = TRUE; + gsize addr_size; + bool valid; + guint32 flags_mask = G_MAXUINT32; + + NM_CMP_SELF (a, b); + + valid = NM_IN_SET (a->addr_family, AF_INET, AF_INET6); + NM_CMP_DIRECT (valid, + (bool) NM_IN_SET (b->addr_family, AF_INET, AF_INET6)); + + if (G_UNLIKELY (!valid)) { + /* the address family is not one of the supported ones. That means, the + * instance will only compare equal to itself. */ + NM_CMP_DIRECT ((uintptr_t) a, (uintptr_t) b); + nm_assert_not_reached (); + return 0; + } + + switch (cmp_type) { + + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: + + flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: + + cmp_full = FALSE; + + /* fall-through */ + case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: + NM_CMP_FIELD (a, b, addr_family); + NM_CMP_FIELD (a, b, action); + NM_CMP_FIELD (a, b, priority); + NM_CMP_FIELD (a, b, tun_id); + + if (cmp_full) + NM_CMP_FIELD (a, b, l3mdev); + else + NM_CMP_FIELD_BOOL (a, b, l3mdev); + + if (cmp_full || !a->l3mdev) + NM_CMP_FIELD (a, b, table); + + NM_CMP_DIRECT (a->flags & flags_mask, b->flags & flags_mask); + + NM_CMP_FIELD (a, b, fwmark); + NM_CMP_FIELD (a, b, fwmask); + + if ( cmp_full + || ( cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY + && a->action == FR_ACT_GOTO)) + NM_CMP_FIELD (a, b, goto_target); + + NM_CMP_FIELD (a, b, suppress_prefixlen_inverse); + NM_CMP_FIELD (a, b, suppress_ifgroup_inverse); + NM_CMP_FIELD (a, b, tos); + + if (cmp_full || a->addr_family == AF_INET) + NM_CMP_FIELD (a, b, flow); + + NM_CMP_FIELD (a, b, protocol); + NM_CMP_FIELD (a, b, ip_proto); + addr_size = nm_utils_addr_family_to_size (a->addr_family); + + NM_CMP_FIELD (a, b, src_len); + if (cmp_full || a->src_len > 0) + NM_CMP_FIELD_MEMCMP_LEN (a, b, src, addr_size); + + NM_CMP_FIELD (a, b, dst_len); + if (cmp_full || a->dst_len > 0) + NM_CMP_FIELD_MEMCMP_LEN (a, b, dst, addr_size); + + NM_CMP_FIELD_UNSAFE (a, b, uid_range_has); + if (cmp_full || a->uid_range_has) { + NM_CMP_FIELD (a, b, uid_range.start); + NM_CMP_FIELD (a, b, uid_range.end); + } + + NM_CMP_FIELD (a, b, sport_range.start); + NM_CMP_FIELD (a, b, sport_range.end); + NM_CMP_FIELD (a, b, dport_range.start); + NM_CMP_FIELD (a, b, dport_range.end); + NM_CMP_FIELD_STR (a, b, iifname); + NM_CMP_FIELD_STR (a, b, oifname); + return 0; + } + + nm_assert_not_reached (); + return 0; +} + /** * nm_platform_ip_address_cmp_expiry: * @a: a NMPlatformIPAddress to compare @@ -7117,6 +7545,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform _LOG3D ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0)); } +static void +log_routing_rule (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformRoutingRule *routing_rule, NMPlatformSignalChangeType change_type, gpointer user_data) +{ + /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */ + _LOG3D ("signal: rt-rule %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_routing_rule_to_string (routing_rule, NULL, 0)); +} + static void log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data) { @@ -7183,10 +7618,13 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, return; } - ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (o)->ifindex; - klass = NMP_OBJECT_GET_CLASS (o); + if (klass->obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) + ifindex = 0; + else + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (o)->ifindex; + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) @@ -7400,11 +7838,12 @@ nm_platform_class_init (NMPlatformClass *platform_class) } G_STMT_END /* Signals */ - SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); - SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); - SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); - SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); + SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); + SIGNAL (NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, log_routing_rule); + SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); + SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 9b626fb4a0..4e7cd5cd59 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -151,6 +151,14 @@ typedef enum { } NMPlatformIPRouteCmpType; +typedef enum { + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, +} NMPlatformRoutingRuleCmpType; + typedef enum { /* match-flags are strictly inclusive. That means, @@ -256,6 +264,7 @@ typedef enum { /*< skip >*/ NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, + NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_ID_TFILTER, _NM_PLATFORM_SIGNAL_ID_LAST, @@ -545,6 +554,48 @@ typedef union { #undef __NMPlatformIPRoute_COMMON +typedef struct { + /* struct fib_rule_uid_range */ + guint32 start; + guint32 end; +} NMFibRuleUidRange; + +typedef struct { + /* struct fib_rule_port_range */ + guint16 start; + guint16 end; +} NMFibRulePortRange; + +typedef struct { + NMIPAddr src; /* FRA_SRC */ + NMIPAddr dst; /* FRA_DST */ + guint64 tun_id; /* betoh64(FRA_TUN_ID) */ + guint32 table; /* (struct fib_rule_hdr).table, FRA_TABLE */ + guint32 flags; /* (struct fib_rule_hdr).flags */ + guint32 priority; /* RA_PRIORITY */ + guint32 fwmark; /* FRA_FWMARK */ + guint32 fwmask; /* FRA_FWMASK */ + guint32 goto_target; /* FRA_GOTO */ + guint32 flow; /* FRA_FLOW */ + guint32 suppress_prefixlen_inverse; /* ~(FRA_SUPPRESS_PREFIXLEN) */ + guint32 suppress_ifgroup_inverse; /* ~(FRA_SUPPRESS_IFGROUP) */ + NMFibRuleUidRange uid_range; /* FRA_UID_RANGE */ + NMFibRulePortRange sport_range; /* FRA_SPORT_RANGE */ + NMFibRulePortRange dport_range; /* FRA_DPORT_RANGE */ + char iifname[NMP_IFNAMSIZ]; /* FRA_IIFNAME */ + char oifname[NMP_IFNAMSIZ]; /* FRA_OIFNAME */ + guint8 addr_family; /* (struct fib_rule_hdr).family */ + guint8 action; /* (struct fib_rule_hdr).action */ + guint8 tos; /* (struct fib_rule_hdr).tos */ + guint8 src_len; /* (struct fib_rule_hdr).src_len */ + guint8 dst_len; /* (struct fib_rule_hdr).dst_len */ + guint8 l3mdev; /* FRA_L3MDEV */ + guint8 protocol; /* FRA_PROTOCOL */ + guint8 ip_proto; /* FRA_IP_PROTO */ + + bool uid_range_has:1; /* has(FRA_UID_RANGE) */ +} NMPlatformRoutingRule; + typedef struct { __NMPlatformObjWithIfindex_COMMON; const char *kind; @@ -1012,6 +1063,7 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED "ip6-address-changed" #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" +#define NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED "routing-rule-changed" #define NM_PLATFORM_SIGNAL_QDISC_CHANGED "qdisc-changed" #define NM_PLATFORM_SIGNAL_TFILTER_CHANGED "tfilter-changed" @@ -1521,6 +1573,7 @@ const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *addre 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_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); const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len); const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len); const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len); @@ -1565,6 +1618,14 @@ nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6 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); int nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b); @@ -1573,6 +1634,7 @@ void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHas 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); void nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj, NMPlatformRoutingRuleCmpType cmp_type, NMHashState *h); void nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h); void nm_platform_lnk_infiniband_hash_update (const NMPlatformLnkInfiniband *obj, NMHashState *h); void nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState *h); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index f6ba0542cf..ae8fb6d4bb 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -427,6 +427,25 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, } return 1; + case NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY: + obj_type = NMP_OBJECT_GET_TYPE (obj_a); + /* currently, only routing rules are supported for this cache-id-type. */ + if ( obj_type != NMP_OBJECT_TYPE_ROUTING_RULE + || !NM_IN_SET (obj_a->routing_rule.addr_family, AF_INET, AF_INET6)) { + if (h) + nm_hash_update_val (h, obj_a); + return 0; + } + if (obj_b) { + return NMP_OBJECT_GET_TYPE (obj_b) == NMP_OBJECT_TYPE_ROUTING_RULE + && obj_a->routing_rule.addr_family == obj_b->routing_rule.addr_family; + } + if (h) { + nm_hash_update_vals (h, idx_type->cache_id_type, + obj_a->routing_rule.addr_family); + } + return 1; + case NMP_CACHE_ID_TYPE_NONE: case __NMP_CACHE_ID_TYPE_MAX: break; @@ -1359,6 +1378,10 @@ _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { *dst = *src; nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); +_vt_cmd_plobj_id_copy (routing_rule, NMPlatformRoutingRule, { + *dst = *src; + nm_assert (nm_platform_routing_rule_cmp (dst, src, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +}); /* Uses internally nmp_object_copy(), hence it also violates the const * promise for @obj. @@ -1459,6 +1482,12 @@ _vt_cmd_plobj_id_cmp_ip6_route (const NMPlatformObject *obj1, const NMPlatformOb return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } +static int +_vt_cmd_plobj_id_cmp_routing_rule (const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_routing_rule_cmp ((NMPlatformRoutingRule *) obj1, (NMPlatformRoutingRule *) obj2, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); +} + void nmp_object_id_hash_update (const NMPObject *obj, NMHashState *h) { @@ -1524,6 +1553,9 @@ _vt_cmd_plobj_id_hash_update (ip4_route, NMPlatformIP4Route, { _vt_cmd_plobj_id_hash_update (ip6_route, NMPlatformIP6Route, { nm_platform_ip6_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }) +_vt_cmd_plobj_id_hash_update (routing_rule, NMPlatformRoutingRule, { + nm_platform_routing_rule_hash_update (obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h); +}) _vt_cmd_plobj_id_hash_update (qdisc, NMPlatformQdisc, { nm_hash_update_vals (h, obj->ifindex, @@ -1547,6 +1579,12 @@ _vt_cmd_plobj_hash_update_ip6_route (const NMPlatformObject *obj, NMHashState *h 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_routing_rule (const NMPlatformObject *obj, NMHashState *h) +{ + return nm_platform_routing_rule_hash_update ((const NMPlatformRoutingRule *) obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, h); +} + gboolean nmp_object_is_alive (const NMPObject *obj) { @@ -1596,6 +1634,12 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) && !NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED); } +static gboolean +_vt_cmd_obj_is_alive_routing_rule (const NMPObject *obj) +{ + return NM_IN_SET (obj->routing_rule.addr_family, AF_INET, AF_INET6); +} + static gboolean _vt_cmd_obj_is_alive_qdisc (const NMPObject *obj) { @@ -1663,6 +1707,12 @@ static const guint8 _supported_cache_ids_ipx_route[] = { 0, }; +static const guint8 _supported_cache_ids_routing_rules[] = { + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, + 0, +}; + /*****************************************************************************/ static void @@ -1940,6 +1990,7 @@ nmp_lookup_init_obj_type (NMPLookup *lookup, case NMP_OBJECT_TYPE_IP6_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: case NMP_OBJECT_TYPE_QDISC: case NMP_OBJECT_TYPE_TFILTER: _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); @@ -2088,6 +2139,23 @@ nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, return _L (lookup); } +const NMPLookup * +nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, + NMPObjectType obj_type, + int addr_family) +{ + NMPObject *o; + + nm_assert (lookup); + nm_assert_addr_family (addr_family); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); + NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family = addr_family; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; + return _L (lookup); +} + /*****************************************************************************/ GArray * @@ -3077,6 +3145,25 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full, }, + [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_ROUTING_RULE, + .sizeof_data = sizeof (NMPObjectRoutingRule), + .sizeof_public = sizeof (NMPlatformRoutingRule), + .obj_type_name = "routing-rule", + .rtm_gettype = RTM_GETRULE, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, + .signal_type = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, + .supported_cache_ids = _supported_cache_ids_routing_rules, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_routing_rule, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_routing_rule, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_routing_rule, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_routing_rule_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_routing_rule_to_string, + .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_routing_rule_cmp_full, + }, [NMP_OBJECT_TYPE_QDISC - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_QDISC, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 15f660a042..8bbbcd9e6e 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -174,6 +174,11 @@ typedef enum { /*< skip >*/ * cache-resync. */ NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + /* a filter for objects that track an explicit address family. + * + * Note that currently on NMPObjectRoutingRule is indexed by this filter. */ + NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, + __NMP_CACHE_ID_TYPE_MAX, NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, } NMPCacheIdType; @@ -324,6 +329,10 @@ typedef struct { NMPlatformIP6Route _public; } NMPObjectIP6Route; +typedef struct { + NMPlatformRoutingRule _public; +} NMPObjectRoutingRule; + typedef struct { NMPlatformQdisc _public; } NMPObjectQdisc; @@ -392,6 +401,9 @@ struct _NMPObject { NMPObjectIP4Route _ip4_route; NMPObjectIP6Route _ip6_route; + NMPlatformRoutingRule routing_rule; + NMPObjectRoutingRule _routing_rule; + NMPlatformQdisc qdisc; NMPObjectQdisc _qdisc; NMPlatformTfilter tfilter; @@ -595,6 +607,14 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ }) +#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \ + }) + #define NMP_OBJECT_CAST_QDISC(obj) \ ({ \ typeof (obj) _obj = (obj); \ @@ -764,6 +784,9 @@ const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, guint32 metric, const struct in6_addr *src, guint8 src_plen); +const NMPLookup *nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, + NMPObjectType obj_type, + int addr_family); GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, NMPObjectType obj_type,