From b0fd3ecbaf800c7c70104f0809b0f1e2fb18e1f8 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 15 Nov 2017 20:36:35 +0100 Subject: [PATCH] platform: add support for traffic filters --- src/nm-types.h | 3 + src/platform/nm-linux-platform.c | 206 +++++++++++++++++++++++++++++-- src/platform/nm-platform.c | 121 +++++++++++++++++- src/platform/nm-platform.h | 37 ++++++ src/platform/nmp-object.c | 41 +++++- src/platform/nmp-object.h | 13 ++ 6 files changed, 408 insertions(+), 13 deletions(-) diff --git a/src/nm-types.h b/src/nm-types.h index 75c691942e..cc657397ea 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -176,8 +176,11 @@ typedef enum { NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_QDISC, + NMP_OBJECT_TYPE_TFILTER, + NMP_OBJECT_TYPE_LNK_GRE, NMP_OBJECT_TYPE_LNK_INFINIBAND, NMP_OBJECT_TYPE_LNK_IP6TNL, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 31b5580499..9f7d837181 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -251,6 +251,7 @@ enum { DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS, + DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS, _DELAYED_ACTION_IDX_REFRESH_ALL_NUM, }; @@ -262,10 +263,11 @@ typedef enum { DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << /* 3 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES), DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << /* 4 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES), DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = (1LL << /* 5 */ DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 6), - DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 10), - DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 11), - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = (1LL << 12), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = (1LL << /* 6 */ DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS), + DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 7), + DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 11), + DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 12), + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = (1LL << 13), __DELAYED_ACTION_TYPE_MAX, DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | @@ -273,7 +275,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_QDISCS, + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, } DelayedActionType; @@ -975,6 +978,8 @@ _nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) case RTM_DELROUTE: s = "RTM_DELROUTE"; break; case RTM_NEWQDISC: s = "RTM_NEWQDISC"; break; case RTM_DELQDISC: s = "RTM_DELQDISC"; break; + case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break; + case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break; case NLMSG_NOOP: s = "NLMSG_NOOP"; break; case NLMSG_ERROR: s = "NLMSG_ERROR"; break; case NLMSG_DONE: s = "NLMSG_DONE"; break; @@ -1022,6 +1027,7 @@ _nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) case RTM_NEWADDR: case RTM_NEWROUTE: case RTM_NEWQDISC: + case RTM_NEWTFILTER: _F (NLM_F_REPLACE, "replace"); _F (NLM_F_EXCL, "excl"); _F (NLM_F_CREATE, "create"); @@ -1031,6 +1037,7 @@ _nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) case RTM_GETADDR: case RTM_GETROUTE: case RTM_DELQDISC: + case RTM_DELTFILTER: _F (NLM_F_DUMP, "dump"); _F (NLM_F_ROOT, "root"); _F (NLM_F_MATCH, "match"); @@ -2368,6 +2375,40 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) return obj; } +static NMPObject * +_new_from_nl_tfilter (struct nlmsghdr *nlh, gboolean id_only) +{ + NMPObject *obj = NULL; + const struct tcmsg *tcm; + struct nlattr *tb[TCA_MAX + 1]; + int err; + static const struct nla_policy policy[TCA_MAX + 1] = { + [TCA_KIND] = { .type = NLA_STRING }, + }; + + if (!nlmsg_valid_hdr (nlh, sizeof (*tcm))) + return NULL; + tcm = nlmsg_data (nlh); + + err = nlmsg_parse (nlh, sizeof (*tcm), tb, TCA_MAX, policy); + if (err < 0) + return NULL; + + if (!tb[TCA_KIND]) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL); + + obj->tfilter.kind = g_intern_string (nla_get_string (tb[TCA_KIND])); + obj->tfilter.ifindex = tcm->tcm_ifindex; + obj->tfilter.addr_family = tcm->tcm_family; + obj->tfilter.handle = tcm->tcm_handle; + obj->tfilter.parent = tcm->tcm_parent; + obj->tfilter.info = tcm->tcm_info; + + return obj; +} + /** * nmp_object_new_from_nl: * @platform: (allow-none): for creating certain objects, the constructor wants to check @@ -2408,6 +2449,10 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m case RTM_DELQDISC: case RTM_GETQDISC: return _new_from_nl_qdisc (msghdr, id_only); + case RTM_NEWTFILTER: + case RTM_DELTFILTER: + case RTM_GETTFILTER: + return _new_from_nl_tfilter (msghdr, id_only); default: return NULL; } @@ -2890,6 +2935,93 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static gboolean +_add_action_simple (struct nl_msg *msg, + const NMPlatformActionSimple *simple) +{ + struct nlattr *act_options; + struct tc_defact sel = { 0, }; + + if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel); + NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata); + + nla_nest_end (msg, act_options); + + return TRUE; + +nla_put_failure: + return FALSE; +} + +static gboolean +_add_action (struct nl_msg *msg, + const NMPlatformAction *action) +{ + struct nlattr *prio; + + if (!(prio = nla_nest_start (msg, 1 /* priority */))) + goto nla_put_failure; + + NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind); + + if (strcmp (action->kind, "simple") == 0) + _add_action_simple (msg, &action->simple); + + nla_nest_end (msg, prio); + + return TRUE; + +nla_put_failure: + return FALSE; +} + +static struct nl_msg * +_nl_msg_new_tfilter (int nlmsg_type, + int nlmsg_flags, + const NMPlatformTfilter *tfilter) +{ + struct nl_msg *msg; + struct nlattr *tc_options; + struct nlattr *act_tab; + struct tcmsg tcm = { + .tcm_family = tfilter->addr_family, + .tcm_ifindex = tfilter->ifindex, + .tcm_handle = tfilter->handle, + .tcm_parent = tfilter->parent, + .tcm_info = tfilter->info, + }; + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + if (!msg) + return NULL; + + if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + NLA_PUT_STRING (msg, TCA_KIND, tfilter->kind); + + if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) + goto nla_put_failure; + + if (!(act_tab = nla_nest_start (msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND + goto nla_put_failure; + + if (tfilter->action.kind) + _add_action (msg, &tfilter->action); + + nla_nest_end (msg, tc_options); + + nla_nest_end (msg, act_tab); + + return msg; +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + /****************************************************************** * NMPlatform types and functions ******************************************************************/ @@ -3292,6 +3424,7 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObj NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ROUTE, DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES), NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ROUTE, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES), NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_QDISC, DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS), + NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_TFILTER, DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), ); @@ -3303,6 +3436,7 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_to_object_type, DelayedA NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, NMP_OBJECT_TYPE_QDISC), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NMP_OBJECT_TYPE_TFILTER), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), ); @@ -3314,6 +3448,7 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_all_to_idx, DelayedActio NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES), NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS), + NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), ); @@ -3325,6 +3460,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType, 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_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"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), @@ -3762,7 +3898,7 @@ cache_on_change (NMPlatform *platform, { int ifindex = 0; - /* if we remove a link (from netlink), we must refresh the addresses, routes and qdiscs */ + /* if we remove a link (from netlink), we must refresh the addresses, routes, qdiscs and tfilters */ if ( cache_op == NMP_CACHE_OPS_REMOVED && obj_old /* <-- nonsensical, make coverity happy */) ifindex = obj_old->link.ifindex; @@ -3778,7 +3914,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_QDISCS, + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); } } @@ -4127,7 +4264,8 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (!nlmsg) continue; - if (klass->obj_type == NMP_OBJECT_TYPE_QDISC) { + if ( klass->obj_type == NMP_OBJECT_TYPE_QDISC + || klass->obj_type == NMP_OBJECT_TYPE_TFILTER) { struct tcmsg tcmsg = { .tcm_family = AF_UNSPEC, }; @@ -4260,6 +4398,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event case RTM_NEWLINK: case RTM_NEWROUTE: case RTM_NEWQDISC: + case RTM_NEWTFILTER: is_dump = delayed_action_refresh_all_in_progress (platform, delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); break; @@ -4284,6 +4423,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event case RTM_NEWADDR: case RTM_GETLINK: case RTM_NEWQDISC: + case RTM_NEWTFILTER: cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { cache_on_change (platform, cache_op, obj_old, obj_new); @@ -4378,6 +4518,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event case RTM_DELADDR: case RTM_DELROUTE: case RTM_DELQDISC: + case RTM_DELTFILTER: cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { cache_on_change (platform, cache_op, obj_old, obj_new); @@ -6244,6 +6385,9 @@ object_delete (NMPlatform *platform, case NMP_OBJECT_TYPE_QDISC: nlmsg = _nl_msg_new_qdisc (RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC (obj)); break; + case NMP_OBJECT_TYPE_TFILTER: + nlmsg = _nl_msg_new_tfilter (RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER (obj)); + break; default: break; } @@ -6373,6 +6517,45 @@ qdisc_add (NMPlatform *platform, /*****************************************************************************/ +static NMPlatformError +tfilter_add (NMPlatform *platform, + NMPNlmFlags flags, + const NMPlatformTfilter *tfilter) +{ + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + int nle; + char s_buf[256]; + nm_auto_nlmsg struct nl_msg *msg = NULL; + + msg = _nl_msg_new_tfilter (RTM_NEWTFILTER, flags, tfilter); + + event_handler_read_netlink (platform, FALSE); + + nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE ("do-add-tfilter: failed sending netlink request \"%s\" (%d)", + nl_geterror (nle), -nle); + return NM_PLATFORM_ERROR_NETLINK; + } + + delayed_action_handle_all (platform, FALSE); + + nm_assert (seq_result); + + _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + ? LOGL_DEBUG + : LOGL_WARN, + "do-add-tfilter: %s", + wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return NM_PLATFORM_ERROR_SUCCESS; + + return NM_PLATFORM_ERROR_UNSPECIFIED; +} + +/*****************************************************************************/ + #define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) #define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) @@ -6655,7 +6838,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_QDISCS, + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); break; default: @@ -6941,7 +7125,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_QDISCS, + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, NULL); delayed_action_handle_all (platform, FALSE); @@ -7108,6 +7293,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip_route_get = ip_route_get; platform_class->qdisc_add = qdisc_add; + platform_class->tfilter_add = tfilter_add; platform_class->check_kernel_support = check_kernel_support; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 09a52574e1..d5d653afe3 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3998,7 +3998,8 @@ nm_platform_object_delete (NMPlatform *self, if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC)) + NMP_OBJECT_TYPE_QDISC, + NMP_OBJECT_TYPE_TFILTER)) g_return_val_if_reached (FALSE); _LOGD ("%s: delete %s", @@ -4399,6 +4400,72 @@ nm_platform_qdisc_sync (NMPlatform *self, /*****************************************************************************/ +NMPlatformError +nm_platform_tfilter_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformTfilter *tfilter) +{ + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + _LOGD ("adding or updating a tfilter: %s", nm_platform_tfilter_to_string (tfilter, NULL, 0)); + return klass->tfilter_add (self, flags, tfilter); +} + +gboolean +nm_platform_tfilter_sync (NMPlatform *self, + int ifindex, + GPtrArray *known_tfilters) +{ + gs_unref_ptrarray GPtrArray *plat_tfilters = NULL; + NMPLookup lookup; + guint i; + gboolean success = TRUE; + gs_unref_hashtable GHashTable *known_tfilters_idx = NULL; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (ifindex > 0); + + known_tfilters_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + + if (known_tfilters) { + for (i = 0; i < known_tfilters->len; i++) { + const NMPObject *q = g_ptr_array_index (known_tfilters, i); + + g_hash_table_insert (known_tfilters_idx, (gpointer) q, (gpointer) q); + } + } + + plat_tfilters = nm_platform_lookup_clone (self, + nmp_lookup_init_object (&lookup, + NMP_OBJECT_TYPE_TFILTER, + ifindex), + NULL, NULL); + + + if (plat_tfilters) { + for (i = 0; i < plat_tfilters->len; i++) { + const NMPObject *q = g_ptr_array_index (plat_tfilters, i); + + if (!g_hash_table_lookup (known_tfilters_idx, q)) + success &= nm_platform_object_delete (self, q); + } + } + + if (known_tfilters) { + for (i = 0; i < known_tfilters->len; i++) { + const NMPObject *q = g_ptr_array_index (known_tfilters, i); + + success &= (nm_platform_tfilter_add (self, NMP_NLM_FLAG_ADD, + NMP_OBJECT_CAST_TFILTER (q)) == NM_PLATFORM_ERROR_SUCCESS); + } + } + + return success; +} + +/*****************************************************************************/ + const char * nm_platform_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, @@ -5276,6 +5343,51 @@ nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) return 0; } +const char * +nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len) +{ + char str_dev[TO_STRING_DEV_BUF_SIZE]; + + if (!nm_utils_to_string_buffer_init_null (tfilter, &buf, &len)) + return buf; + + g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x", + tfilter->kind, + _to_string_dev (NULL, tfilter->ifindex, str_dev, sizeof (str_dev)), + tfilter->addr_family, + tfilter->handle, + tfilter->parent, + tfilter->info); + + return buf; +} + +void +nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h) +{ + nm_hash_update_str (h, obj->kind); + nm_hash_update_vals (h, + obj->ifindex, + obj->addr_family, + obj->handle, + obj->parent, + obj->info); +} + +int +nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b) +{ + NM_CMP_SELF (a, b); + NM_CMP_FIELD (a, b, ifindex); + NM_CMP_FIELD (a, b, parent); + NM_CMP_FIELD_STR_INTERNED (a, b, kind); + NM_CMP_FIELD (a, b, addr_family); + NM_CMP_FIELD (a, b, handle); + NM_CMP_FIELD (a, b, info); + + return 0; +} + void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) { @@ -6095,6 +6207,12 @@ log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdis _LOGD ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0)); } +static void +log_tfilter (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformTfilter *tfilter, NMPlatformSignalChangeType change_type, gpointer user_data) +{ + _LOGD ("signal: tfilter %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_tfilter_to_string (tfilter, NULL, 0)); +} + /*****************************************************************************/ void @@ -6369,4 +6487,5 @@ nm_platform_class_init (NMPlatformClass *platform_class) 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); } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index bae2f35203..897c9d8783 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -237,6 +237,7 @@ typedef enum { /*< skip >*/ NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_ID_QDISC, + NM_PLATFORM_SIGNAL_ID_TFILTER, _NM_PLATFORM_SIGNAL_ID_LAST, } NMPlatformSignalIdType; @@ -539,6 +540,27 @@ typedef struct { guint32 info; } NMPlatformQdisc; +typedef struct { + char sdata[32]; +} NMPlatformActionSimple; + +typedef struct { + const char *kind; + union { + NMPlatformActionSimple simple; + }; +} NMPlatformAction; + +typedef struct { + __NMPlatformObject_COMMON; + const char *kind; + int addr_family; + guint32 handle; + guint32 parent; + guint32 info; + NMPlatformAction action; +} NMPlatformTfilter; + #undef __NMPlatformObject_COMMON @@ -845,6 +867,10 @@ typedef struct { NMPNlmFlags flags, const NMPlatformQdisc *qdisc); + NMPlatformError (*tfilter_add) (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformTfilter *tfilter); + NMPlatformKernelSupportFlags (*check_kernel_support) (NMPlatform * self, NMPlatformKernelSupportFlags request_flags); } NMPlatformClass; @@ -866,6 +892,7 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" #define NM_PLATFORM_SIGNAL_QDISC_CHANGED "qdisc-changed" +#define NM_PLATFORM_SIGNAL_TFILTER_CHANGED "tfilter-changed" const char *nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type); @@ -1268,6 +1295,13 @@ gboolean nm_platform_qdisc_sync (NMPlatform *self, int ifindex, GPtrArray *known_qdiscs); +NMPlatformError nm_platform_tfilter_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformTfilter *tfilter); +gboolean nm_platform_tfilter_sync (NMPlatform *self, + int ifindex, + GPtrArray *known_tfilters); + const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len); @@ -1283,6 +1317,7 @@ const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *addre 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_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_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, @@ -1319,6 +1354,7 @@ nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6 } int nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b); +int nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b); void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h); void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h); @@ -1336,6 +1372,7 @@ void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h); void nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h); +void nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h); NMPlatformKernelSupportFlags nm_platform_check_kernel_support (NMPlatform *self, NMPlatformKernelSupportFlags request_flags); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index b1e5de3410..098c1d45a3 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -191,7 +191,8 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC) + NMP_OBJECT_TYPE_QDISC, + NMP_OBJECT_TYPE_TFILTER) || !nmp_object_is_visible (obj_a)) { if (h) nm_hash_update_val (h, obj_a); @@ -739,6 +740,7 @@ _vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->peer_address != obj->address ? nm_utils_inet4_ntop (nm_utils_ip4_address_clear_host_address (obj->peer_address, obj->plen), buf2) : ""); _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1)); _vt_cmd_plobj_to_string_id (qdisc, NMPlatformQdisc, "%d: %d", obj->ifindex, obj->parent); +_vt_cmd_plobj_to_string_id (tfilter, NMPlatformTfilter, "%d: %d", obj->ifindex, obj->parent); void nmp_object_hash_update (const NMPObject *obj, NMHashState *h) @@ -1035,6 +1037,10 @@ _vt_cmd_plobj_id_cmp (qdisc, NMPlatformQdisc, NM_CMP_FIELD (obj1, obj2, ifindex); NM_CMP_FIELD (obj1, obj2, parent); ) +_vt_cmd_plobj_id_cmp (tfilter, NMPlatformTfilter, + NM_CMP_FIELD (obj1, obj2, ifindex); + NM_CMP_FIELD (obj1, obj2, handle); +) static int _vt_cmd_plobj_id_cmp_ip4_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) @@ -1118,6 +1124,11 @@ _vt_cmd_plobj_id_hash_update (qdisc, NMPlatformQdisc, { obj->ifindex, obj->parent); }) +_vt_cmd_plobj_id_hash_update (tfilter, NMPlatformTfilter, { + nm_hash_update_vals (h, + obj->ifindex, + obj->handle); +}) static inline void _vt_cmd_plobj_hash_update_ip4_route (const NMPlatformObject *obj, NMHashState *h) @@ -1185,6 +1196,12 @@ _vt_cmd_obj_is_alive_qdisc (const NMPObject *obj) return obj->object.ifindex > 0; } +static gboolean +_vt_cmd_obj_is_alive_tfilter (const NMPObject *obj) +{ + return obj->object.ifindex > 0; +} + gboolean nmp_object_is_visible (const NMPObject *obj) { @@ -1520,6 +1537,7 @@ nmp_lookup_init_obj_type (NMPLookup *lookup, case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: case NMP_OBJECT_TYPE_QDISC: + case NMP_OBJECT_TYPE_TFILTER: o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; return _L (lookup); @@ -1558,7 +1576,8 @@ nmp_lookup_init_object (NMPLookup *lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC)); + NMP_OBJECT_TYPE_QDISC, + NMP_OBJECT_TYPE_TFILTER)); if (ifindex <= 0) { return nmp_lookup_init_obj_type (lookup, @@ -2604,6 +2623,24 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_qdisc_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_qdisc_cmp, }, + [NMP_OBJECT_TYPE_TFILTER - 1] = { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_TFILTER, + .sizeof_data = sizeof (NMPObjectTfilter), + .sizeof_public = sizeof (NMPlatformTfilter), + .obj_type_name = "tfilter", + .rtm_gettype = RTM_GETTFILTER, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_TFILTER, + .signal_type = NM_PLATFORM_SIGNAL_TFILTER_CHANGED, + .supported_cache_ids = _supported_cache_ids_object, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_tfilter, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_tfilter, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_tfilter, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_tfilter, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_tfilter_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_tfilter_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_tfilter_cmp, + }, [NMP_OBJECT_TYPE_LNK_GRE - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_GRE, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 3adbadef5e..39e1d6edf2 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -229,6 +229,10 @@ typedef struct { NMPlatformQdisc _public; } NMPObjectQdisc; +typedef struct { + NMPlatformTfilter _public; +} NMPObjectTfilter; + struct _NMPObject { union { NMDedupMultiObj parent; @@ -283,6 +287,8 @@ struct _NMPObject { NMPlatformQdisc qdisc; NMPObjectQdisc _qdisc; + NMPlatformTfilter tfilter; + NMPObjectTfilter _tfilter; }; }; @@ -423,6 +429,13 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj) _obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \ }) +#define NMP_OBJECT_CAST_TFILTER(obj) \ + ({ \ + typeof (obj) _obj = (obj); \ + \ + nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_TFILTER); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ + }) const NMPClass *nmp_class_from_type (NMPObjectType obj_type);