summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-02-14 13:08:12 +0100
committerThomas Haller <thaller@redhat.com>2019-03-04 14:28:19 +0100
commitf2deacc7aa13e8730c94471a973a0468af3daa11 (patch)
treed2733aa273c83c4ee714d691aadb0687aea00bd3
parent169eb23299595fdb96445c8661ba4d6123298dbc (diff)
downloadNetworkManager-th/routing-rule.tar.gz
platform: add support for routing-ruleth/routing-rule
https://bugzilla.redhat.com/show_bug.cgi?id=1652653
-rw-r--r--src/nm-types.h1
-rw-r--r--src/platform/nm-linux-platform.c230
-rw-r--r--src/platform/nm-platform.c451
-rw-r--r--src/platform/nm-platform.h62
-rw-r--r--src/platform/nmp-object.c87
-rw-r--r--src/platform/nmp-object.h23
6 files changed, 836 insertions, 18 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 a4816d4197..c687758c9f 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -26,6 +26,7 @@
#include <endian.h>
#include <fcntl.h>
#include <libudev.h>
+#include <linux/fib_rules.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
#include <linux/if_link.h>
@@ -291,8 +292,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,14 +316,16 @@ 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,
@@ -329,6 +334,8 @@ typedef enum {
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_IP4 |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 |
DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
@@ -3353,6 +3360,166 @@ rta_multipath_done:
}
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(struct fib_rule_uid_range),
+ .maxlen = sizeof(struct fib_rule_uid_range) },
+ [FRA_PROTOCOL] = { .type = NLA_U8 },
+ [FRA_IP_PROTO] = { .type = NLA_U8 },
+ [FRA_SPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range),
+ .maxlen = sizeof(struct fib_rule_port_range) },
+ [FRA_DPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range),
+ .maxlen = sizeof(struct fib_rule_port_range) },
+ };
+ 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;
+
+ if (tb[FRA_TABLE])
+ props->table_coerced = nm_platform_route_table_coerce (nla_get_u32 (tb[FRA_TABLE]));
+ else
+ props->table_coerced = nm_platform_route_table_coerce (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) == sizeof (struct fib_rule_port_range));
+ G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, start) == G_STRUCT_OFFSET (struct fib_rule_port_range, start));
+ G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, end) == G_STRUCT_OFFSET (struct fib_rule_port_range, end));
+
+ 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));
+
+ if (tb[FRA_UID_RANGE]) {
+ const struct fib_rule_uid_range *r;
+
+ r = nla_data_as (struct fib_rule_uid_range, tb[FRA_UID_RANGE]);
+ props->uid_range = (NMFibRuleUidRange) {
+ .start = r->start,
+ .end = r->end,
+ };
+ props->uid_range_has = TRUE;
+ }
+
+ return g_steal_pointer (&obj);
+}
+
+static NMPObject *
_new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only)
{
static const struct nla_policy policy[] = {
@@ -3451,6 +3618,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:
@@ -4370,6 +4541,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
@@ -4389,6 +4562,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 (),
@@ -4419,6 +4594,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;
@@ -4426,12 +4608,23 @@ refresh_all_type_from_needle_object (const NMPObject *obj_needle)
}
static const NMPLookup *
-refresh_all_info_init_lookup (const RefreshAllInfo *refresh_all_info,
+refresh_all_type_init_lookup (RefreshAllType refresh_all_type,
NMPLookup *lookup)
{
+ const RefreshAllInfo *refresh_all_info;
+
nm_assert (lookup);
+
+ refresh_all_info = refresh_all_type_get_info (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);
@@ -4452,6 +4645,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"),
@@ -4864,7 +5059,7 @@ cache_prune_all (NMPlatform *platform)
priv->pruning[refresh_all_type] -= 1;
if (priv->pruning[refresh_all_type] > 0)
continue;
- refresh_all_info_init_lookup (refresh_all_type_get_info (refresh_all_type),
+ refresh_all_type_init_lookup (refresh_all_type,
&lookup);
cache_prune_one_type (platform, &lookup);
}
@@ -4938,6 +5133,8 @@ 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_IP4 |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 |
DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
NULL);
@@ -5296,6 +5493,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,
@@ -5326,7 +5524,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
NMPLookup lookup;
priv->pruning[refresh_all_type] += 1;
- refresh_all_info_init_lookup (refresh_all_type_get_info (refresh_all_type),
+ refresh_all_type_init_lookup (refresh_all_type,
&lookup);
nmp_cache_dirty_set_all (nm_platform_get_cache (platform),
&lookup);
@@ -5478,6 +5676,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
@@ -5496,6 +5695,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,
@@ -5518,6 +5718,8 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
case RTM_NEWLINK:
case RTM_NEWADDR:
case RTM_GETLINK:
+ case RTM_NEWRULE:
+ //XXX
case RTM_NEWQDISC:
case RTM_NEWTFILTER:
cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new);
@@ -5616,6 +5818,8 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
case RTM_DELLINK:
case RTM_DELADDR:
case RTM_DELROUTE:
+ case RTM_DELRULE:
+ //XXX
case RTM_DELQDISC:
case RTM_DELTFILTER:
cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new);
@@ -8117,6 +8321,8 @@ 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_IP4 |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 |
DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
NULL);
@@ -8411,6 +8617,8 @@ 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_IP4 |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 |
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 a72159fb86..320234259c 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -28,6 +28,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
+#include <linux/fib_rules.h>
#include <linux/ip.h>
#include <linux/if.h>
#include <linux/if_tun.h>
@@ -6062,6 +6063,258 @@ 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");
+
+ if (!is_zero)
+ nm_utils_strbuf_append_str (buf, len, nm_utils_inet_ntop (addr_family, addr, s_addr));
+ else
+ nm_utils_strbuf_append_str (buf, len, "0");
+
+ 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_table;
+ 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,
+ " uid-range %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);
+ }
+
+ rr_table = nm_platform_route_table_uncoerce (routing_rule->table_coerced, FALSE);
+ if (rr_table != 0) {
+ /* ip-rule prints this as "lookup". I find that confusing, deviate from
+ * that. */
+ nm_utils_strbuf_append (&buf, &len,
+ " table %u",
+ rr_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 == FRA_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];
+
+ switch (routing_rule->action) {
+ case FR_ACT_UNSPEC /*RTN_UNSPEC*/ : ss = "none"; break;
+ case FR_ACT_TO_TBL /*RTN_UNICAST*/ : ss = "unicast"; break;
+ case FR_ACT_GOTO /*RTN_LOCAL*/ : ss = "local"; break;
+ case FR_ACT_NOP /*RTN_BROADCAST*/ : ss = "nop"; break;
+ case FR_ACT_RES3 /*RTN_ANYCAST*/ : ss = "anycast"; break;
+ case FR_ACT_RES4 /*RTN_MULTICAST*/ : ss = "multicast"; break;
+ case FR_ACT_BLACKHOLE /*RTN_BLACKHOLE*/ : ss = "blackhole"; break;
+ case FR_ACT_UNREACHABLE /*RTN_UNREACHABLE*/ : ss = "unreachable"; break;
+ case 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;
+ }
+ 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 != FRA_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)
{
@@ -7020,6 +7273,182 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
return 0;
}
+void
+nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj,
+ NMPlatformRoutingRuleCmpType cmp_type,
+ NMHashState *h)
+{
+ gboolean cmp_full = TRUE;
+ gsize addr_size;
+ guint32 table;
+
+ // see systemd: routing_policy_rule_hash_func
+
+ 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:
+ /* for the moment, ID is the same as a full (sementical) comparison. We might
+ * refine that in the future. */
+
+ /* fall-through */
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY:
+ cmp_full = FALSE;
+ /* fall-through */
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL:
+
+ /* for semantic comparison, we cannot hash the table for IPv4
+ * rules. See also nm_platform_routing_rule_cmp(). */
+ if (cmp_full)
+ table = obj->table_coerced;
+ else if (obj->l3mdev)
+ table = 0;
+ else if (obj->addr_family != AF_INET)
+ table = obj->table_coerced;
+ else
+ table = 0;
+
+ nm_hash_update_vals (h,
+ obj->addr_family,
+ obj->tun_id,
+ table,
+ obj->flags,
+ obj->priority,
+ obj->fwmark,
+ obj->fwmask,
+ obj->goto_target,
+ obj->flow,
+ 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);
+ nm_hash_update (h, &obj->src, addr_size);
+ 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;
+
+ 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:
+ /* for the moment, ID is the same as a full (sementical) comparison. We might
+ * refine that in the future. */
+
+ /* 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, tun_id);
+ if (cmp_full)
+ NM_CMP_FIELD (a, b, l3mdev);
+ else
+ NM_CMP_FIELD_BOOL (a, b, l3mdev);
+
+ if (cmp_full)
+ NM_CMP_FIELD (a, b, table_coerced);
+ else if (a->l3mdev) {
+ /* the table is ignored for semantic comparison. */
+ } else if (a->addr_family != AF_INET)
+ NM_CMP_FIELD (a, b, table_coerced);
+ else {
+ guint32 tb_a = nm_platform_route_table_uncoerce (a->table_coerced, FALSE);
+ guint32 tb_b = nm_platform_route_table_uncoerce (b->table_coerced, FALSE);
+
+ /* `ip rule add table 0` lets kernel create a new table and use
+ * it. For semantic comparison, if one of the tables is RT_TABLE_UNSPEC,
+ * we assume that the rules might match.
+ *
+ * This special case is done by kernel only for IPv4.
+ *
+ * For IPv6, kernel considers RT_TABLE_UNSPEC as invalid, so it's fine
+ * to always compare the tables (above). */
+ if ( tb_a != RT_TABLE_UNSPEC
+ && tb_b != RT_TABLE_UNSPEC) {
+ NM_CMP_DIRECT (tb_a, tb_b);
+ }
+ }
+
+ NM_CMP_FIELD (a, b, flags);
+ NM_CMP_FIELD (a, b, priority);
+ NM_CMP_FIELD (a, b, fwmark);
+ NM_CMP_FIELD (a, b, fwmask);
+ 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, action);
+ NM_CMP_FIELD (a, b, tos);
+ NM_CMP_FIELD (a, b, src_len);
+ NM_CMP_FIELD (a, b, dst_len);
+ 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_MEMCMP_LEN (a, b, src, addr_size);
+ 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 +7546,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform
}
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)
{
_LOG3D ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0));
@@ -7399,11 +7835,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..a8a62f897b 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -152,6 +152,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,
* by default nothing is matched, but if you enable a particular
@@ -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,
@@ -546,6 +555,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_coerced; /* (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;
int addr_family;
@@ -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 0b5a0dbd80..91195b65eb 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)
{
@@ -1597,6 +1635,12 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj)
}
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)
{
return NMP_OBJECT_CAST_QDISC (obj)->ifindex > 0;
@@ -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 *
@@ -3067,6 +3135,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 f0665ae7a5..30d9697a15 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -161,6 +161,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;
@@ -312,6 +317,10 @@ typedef struct {
} NMPObjectIP6Route;
typedef struct {
+ NMPlatformRoutingRule _public;
+} NMPObjectRoutingRule;
+
+typedef struct {
NMPlatformQdisc _public;
} NMPObjectQdisc;
@@ -379,6 +388,9 @@ struct _NMPObject {
NMPObjectIP4Route _ip4_route;
NMPObjectIP6Route _ip6_route;
+ NMPlatformRoutingRule routing_rule;
+ NMPObjectRoutingRule _routing_rule;
+
NMPlatformQdisc qdisc;
NMPObjectQdisc _qdisc;
NMPlatformTfilter tfilter;
@@ -582,6 +594,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 ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \
+ _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \
+ })
+
#define NMP_OBJECT_CAST_QDISC(obj) \
({ \
typeof (obj) _obj = (obj); \
@@ -751,6 +771,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,