]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
f_flower: implement pfcp opts
authorMichal Swiatkowski <michal.swiatkowski@linux.intel.com>
Mon, 22 Apr 2024 12:05:51 +0000 (14:05 +0200)
committerDavid Ahern <dsahern@kernel.org>
Tue, 23 Apr 2024 15:34:19 +0000 (15:34 +0000)
Allow adding tc filter for PFCP header.

Add support for parsing TCA_FLOWER_KEY_ENC_OPTS_PFCP.
Options are as follows: TYPE:SEID.

TYPE is a 8-bit value represented in hex and can be  1
for session header and 0 for node header. In PFCP packet
this is S flag in header.

SEID is a 64-bit session id value represented in hex.

This patch enables adding hardware filters using PFCP fields, see [1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit/?id=d823265dd45bbf14bd67aa476057108feb4143ce

Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: David Ahern <dsahern@kernel.org>
include/libnetlink.h
man/man8/tc-flower.8
tc/f_flower.c

index 35a9bb57d685a5f01d0f8c5e78f0c8c2e58e8a79..30f0c2d22d49c87175b58973d2b34a42310a4b33 100644 (file)
@@ -274,6 +274,12 @@ static inline __u64 rta_getattr_uint(const struct rtattr *rta)
        }
        return -1ULL;
 }
+
+static inline __be64 rta_getattr_be64(const struct rtattr *rta)
+{
+       return htobe64(rta_getattr_u64(rta));
+}
+
 static inline __s32 rta_getattr_s32(const struct rtattr *rta)
 {
        return *(__s32 *)RTA_DATA(rta);
index 832458138c740bf9158cd14c646547906bee449f..6b56640503d55cddc6c883b3840ad9f9bb99ec3c 100644 (file)
@@ -97,6 +97,8 @@ flower \- flow based traffic control filter
 .B erspan_opts
 |
 .B gtp_opts
+|
+.B pfcp_opts
 }
 .IR OPTIONS " | "
 .BR ip_flags
@@ -453,6 +455,8 @@ Match the connection zone, and can be masked.
 .BI erspan_opts " OPTIONS"
 .TQ
 .BI gtp_opts " OPTIONS"
+.TQ
+.BI pfcp_opts " OPTIONS"
 Match on IP tunnel metadata. Key id
 .I NUMBER
 is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
@@ -494,6 +498,13 @@ doesn't support multiple options, and it consists of a key followed by a slash
 and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
 match. The option can be described in the form PDU_TYPE:QFI/PDU_TYPE_MASK:QFI_MASK
 where both PDU_TYPE and QFI are represented as a 8bit hexadecimal values.
+pfcp_opts
+.I OPTIONS
+does not support multiple options. It consists of a key followed by a slash
+and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
+match. The option can be described in the form TYPE:SEID/TYPE_MASK:SEID_MASK
+where TYPE is represented as a 8bit number, SEID is represented by 64bit. Both
+TYPE and SEID are provided in hex.
 .TP
 .BI ip_flags " IP_FLAGS"
 .I IP_FLAGS
index cfcd7b2f6ddf84b4e5077994bade6d98f3ec3d79..08c1001af7b4e5fb1d25c5e93291e9daa156e036 100644 (file)
@@ -91,6 +91,7 @@ static void explain(void)
                "                       vxlan_opts MASKED-OPTIONS |\n"
                "                       erspan_opts MASKED-OPTIONS |\n"
                "                       gtp_opts MASKED-OPTIONS |\n"
+               "                       pfcp_opts MASKED-OPTIONS |\n"
                "                       ip_flags IP-FLAGS |\n"
                "                       l2_miss L2_MISS |\n"
                "                       enc_dst_port [ port_number ] |\n"
@@ -1152,6 +1153,58 @@ static int flower_parse_gtp_opt(char *str, struct nlmsghdr *n)
        return 0;
 }
 
+static int flower_parse_pfcp_opt(char *str, struct nlmsghdr *n)
+{
+       struct rtattr *nest;
+       char *token;
+       int i, err;
+
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_FLOWER_KEY_ENC_OPTS_PFCP | NLA_F_NESTED);
+
+       i = 1;
+       token = strsep(&str, ":");
+       while (token) {
+               switch (i) {
+               case TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 16);
+                       if (err)
+                               return err;
+
+                       addattr8(n, MAX_MSG, i, opt_type);
+                       break;
+               }
+               case TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID:
+               {
+                       __be64 opt_seid;;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_be64(&opt_seid, token, 16);
+                       if (err)
+                               return err;
+
+                       addattr64(n, MAX_MSG, i, opt_seid);
+                       break;
+               }
+               default:
+                       fprintf(stderr, "Unknown \"pfcp_opts\" type\n");
+                       return -1;
+               }
+
+               token = strsep(&str, ":");
+               i++;
+       }
+       addattr_nest_end(n, nest);
+
+       return 0;
+}
+
 static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
 {
        char *token;
@@ -1370,6 +1423,44 @@ static int flower_parse_enc_opts_gtp(char *str, struct nlmsghdr *n)
        return 0;
 }
 
+static int flower_parse_enc_opts_pfcp(char *str, struct nlmsghdr *n)
+{
+       char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
+       struct rtattr *nest;
+       char *slash;
+       int err;
+
+
+       slash = strchr(str, '/');
+       if (slash) {
+               *slash++ = '\0';
+               if (strlen(slash) > XATTR_SIZE_MAX)
+                       return -1;
+               strcpy(mask, slash);
+       } else {
+               strcpy(mask, "ff:ffffffffffffffff");
+       }
+
+       if (strlen(str) > XATTR_SIZE_MAX)
+               return -1;
+       strcpy(key, str);
+
+       nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED);
+       err = flower_parse_pfcp_opt(key, n);
+       if (err)
+               return err;
+       addattr_nest_end(n, nest);
+
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED);
+       err = flower_parse_pfcp_opt(mask, n);
+       if (err)
+               return err;
+       addattr_nest_end(n, nest);
+
+       return 0;
+}
+
 static int flower_parse_mpls_lse(int *argc_p, char ***argv_p,
                                 struct nlmsghdr *nlh)
 {
@@ -2150,6 +2241,13 @@ static int flower_parse_opt(const struct filter_util *qu, char *handle,
                                fprintf(stderr, "Illegal \"gtp_opts\"\n");
                                return -1;
                        }
+               } else if (!strcmp(*argv, "pfcp_opts")) {
+                       NEXT_ARG();
+                       ret = flower_parse_enc_opts_pfcp(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"pfcp_opts\"\n");
+                               return -1;
+                       }
                } else if (matches(*argv, "action") == 0) {
                        NEXT_ARG();
                        ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
@@ -2646,6 +2744,29 @@ static void flower_print_gtp_opts(const char *name, struct rtattr *attr,
        snprintf(strbuf, len, "%02x:%02x", pdu_type, qfi);
 }
 
+static void flower_print_pfcp_opts(const char *name, struct rtattr *attr,
+                                  char *strbuf, int len)
+{
+       struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       __be64 seid;
+       __u8 type;
+
+       parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX, i, rem);
+       type = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE]);
+       seid = rta_getattr_be64(tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID]);
+
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_JSON, "type", NULL, type);
+       print_uint(PRINT_JSON, "seid", NULL, seid);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+
+       snprintf(strbuf, len, "%02x:%llx", type, seid);
+}
+
 static void __attribute__((format(printf, 2, 0)))
 flower_print_enc_parts(const char *name, const char *namefrm,
                       struct rtattr *attr, char *key, char *mask)
@@ -2738,6 +2859,18 @@ static void flower_print_enc_opts(const char *name, struct rtattr *attr,
 
                flower_print_enc_parts(name, "  gtp_opts %s", attr, key,
                                       msk);
+       } else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_PFCP]) {
+               flower_print_pfcp_opts("pfcp_opt_key",
+                               key_tb[TCA_FLOWER_KEY_ENC_OPTS_PFCP],
+                               key, len);
+
+               if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_PFCP])
+                       flower_print_pfcp_opts("pfcp_opt_mask",
+                               msk_tb[TCA_FLOWER_KEY_ENC_OPTS_PFCP],
+                               msk, len);
+
+               flower_print_enc_parts(name, "  pfcp_opts %s", attr, key,
+                                      msk);
        }
 
        free(msk);