net/sched: Allow flower to match on GTP options
authorWojciech Drewek <wojciech.drewek@intel.com>
Fri, 4 Mar 2022 16:40:45 +0000 (17:40 +0100)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Fri, 11 Mar 2022 16:28:27 +0000 (08:28 -0800)
Options are as follows: PDU_TYPE:QFI and they refernce to
the fields from the  PDU Session Protocol. PDU Session data
is conveyed in GTP-U Extension Header.

GTP-U Extension Header is described in 3GPP TS 29.281.
PDU Session Protocol is described in 3GPP TS 38.415.

PDU_TYPE -  indicates the type of the PDU Session Information (4 bits)
QFI      -  QoS Flow Identifier (6 bits)

  # ip link add gtp_dev type gtp role sgsn
  # tc qdisc add dev gtp_dev ingress
  # tc filter add dev gtp_dev protocol ip parent ffff: \
      flower \
        enc_key_id 11 \
        gtp_opts 1:8/ff:ff \
      action mirred egress redirect dev eth0

Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
include/net/gtp.h
include/uapi/linux/if_tunnel.h
include/uapi/linux/pkt_cls.h
net/sched/cls_flower.c

index 0e12c37..23c2aaa 100644 (file)
@@ -58,6 +58,11 @@ struct gtp1u_packet {
        struct gtp_ie ie;
 } __packed;
 
+struct gtp_pdu_session_info {  /* According to 3GPP TS 38.415. */
+       u8      pdu_type;
+       u8      qfi;
+};
+
 #define GTP1_F_NPDU    0x01
 #define GTP1_F_SEQ     0x02
 #define GTP1_F_EXTHDR  0x04
index 7d91055..1021196 100644 (file)
@@ -176,8 +176,10 @@ enum {
 #define TUNNEL_VXLAN_OPT       __cpu_to_be16(0x1000)
 #define TUNNEL_NOCACHE         __cpu_to_be16(0x2000)
 #define TUNNEL_ERSPAN_OPT      __cpu_to_be16(0x4000)
+#define TUNNEL_GTP_OPT         __cpu_to_be16(0x8000)
 
 #define TUNNEL_OPTIONS_PRESENT \
-               (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
+               (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT | \
+               TUNNEL_GTP_OPT)
 
 #endif /* _UAPI_IF_TUNNEL_H_ */
index ee38b35..404f97f 100644 (file)
@@ -616,6 +616,10 @@ enum {
                                         * TCA_FLOWER_KEY_ENC_OPT_ERSPAN_
                                         * attributes
                                         */
+       TCA_FLOWER_KEY_ENC_OPTS_GTP,    /* Nested
+                                        * TCA_FLOWER_KEY_ENC_OPT_GTP_
+                                        * attributes
+                                        */
        __TCA_FLOWER_KEY_ENC_OPTS_MAX,
 };
 
@@ -655,6 +659,17 @@ enum {
                (__TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX - 1)
 
 enum {
+       TCA_FLOWER_KEY_ENC_OPT_GTP_UNSPEC,
+       TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE,            /* u8 */
+       TCA_FLOWER_KEY_ENC_OPT_GTP_QFI,                 /* u8 */
+
+       __TCA_FLOWER_KEY_ENC_OPT_GTP_MAX,
+};
+
+#define TCA_FLOWER_KEY_ENC_OPT_GTP_MAX \
+               (__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX - 1)
+
+enum {
        TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC,
        TCA_FLOWER_KEY_MPLS_OPTS_LSE,
        __TCA_FLOWER_KEY_MPLS_OPTS_MAX,
index 1a9b1f1..c80fc49 100644 (file)
@@ -25,6 +25,7 @@
 #include <net/geneve.h>
 #include <net/vxlan.h>
 #include <net/erspan.h>
+#include <net/gtp.h>
 
 #include <net/dst.h>
 #include <net/dst_metadata.h>
@@ -723,6 +724,7 @@ enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = {
        [TCA_FLOWER_KEY_ENC_OPTS_GENEVE]        = { .type = NLA_NESTED },
        [TCA_FLOWER_KEY_ENC_OPTS_VXLAN]         = { .type = NLA_NESTED },
        [TCA_FLOWER_KEY_ENC_OPTS_ERSPAN]        = { .type = NLA_NESTED },
+       [TCA_FLOWER_KEY_ENC_OPTS_GTP]           = { .type = NLA_NESTED },
 };
 
 static const struct nla_policy
@@ -747,6 +749,12 @@ erspan_opt_policy[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1] = {
 };
 
 static const struct nla_policy
+gtp_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1] = {
+       [TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]      = { .type = NLA_U8 },
+       [TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]           = { .type = NLA_U8 },
+};
+
+static const struct nla_policy
 mpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = {
        [TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH]    = { .type = NLA_U8 },
        [TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL]      = { .type = NLA_U8 },
@@ -1262,6 +1270,49 @@ static int fl_set_erspan_opt(const struct nlattr *nla, struct fl_flow_key *key,
        return sizeof(*md);
 }
 
+static int fl_set_gtp_opt(const struct nlattr *nla, struct fl_flow_key *key,
+                         int depth, int option_len,
+                         struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1];
+       struct gtp_pdu_session_info *sinfo;
+       u8 len = key->enc_opts.len;
+       int err;
+
+       sinfo = (struct gtp_pdu_session_info *)&key->enc_opts.data[len];
+       memset(sinfo, 0xff, option_len);
+
+       if (!depth)
+               return sizeof(*sinfo);
+
+       if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GTP) {
+               NL_SET_ERR_MSG_MOD(extack, "Non-gtp option type for mask");
+               return -EINVAL;
+       }
+
+       err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_GTP_MAX, nla,
+                              gtp_opt_policy, extack);
+       if (err < 0)
+               return err;
+
+       if (!option_len &&
+           (!tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE] ||
+            !tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI])) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Missing tunnel key gtp option pdu type or qfi");
+               return -EINVAL;
+       }
+
+       if (tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE])
+               sinfo->pdu_type =
+                       nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]);
+
+       if (tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI])
+               sinfo->qfi = nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]);
+
+       return sizeof(*sinfo);
+}
+
 static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
                          struct fl_flow_key *mask,
                          struct netlink_ext_ack *extack)
@@ -1386,6 +1437,38 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
                                return -EINVAL;
                        }
                        break;
+               case TCA_FLOWER_KEY_ENC_OPTS_GTP:
+                       if (key->enc_opts.dst_opt_type) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Duplicate type for gtp options");
+                               return -EINVAL;
+                       }
+                       option_len = 0;
+                       key->enc_opts.dst_opt_type = TUNNEL_GTP_OPT;
+                       option_len = fl_set_gtp_opt(nla_opt_key, key,
+                                                   key_depth, option_len,
+                                                   extack);
+                       if (option_len < 0)
+                               return option_len;
+
+                       key->enc_opts.len += option_len;
+                       /* At the same time we need to parse through the mask
+                        * in order to verify exact and mask attribute lengths.
+                        */
+                       mask->enc_opts.dst_opt_type = TUNNEL_GTP_OPT;
+                       option_len = fl_set_gtp_opt(nla_opt_msk, mask,
+                                                   msk_depth, option_len,
+                                                   extack);
+                       if (option_len < 0)
+                               return option_len;
+
+                       mask->enc_opts.len += option_len;
+                       if (key->enc_opts.len != mask->enc_opts.len) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Key and mask miss aligned");
+                               return -EINVAL;
+                       }
+                       break;
                default:
                        NL_SET_ERR_MSG(extack, "Unknown tunnel option type");
                        return -EINVAL;
@@ -2761,6 +2844,34 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
+static int fl_dump_key_gtp_opt(struct sk_buff *skb,
+                              struct flow_dissector_key_enc_opts *enc_opts)
+
+{
+       struct gtp_pdu_session_info *session_info;
+       struct nlattr *nest;
+
+       nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_GTP);
+       if (!nest)
+               goto nla_put_failure;
+
+       session_info = (struct gtp_pdu_session_info *)&enc_opts->data[0];
+
+       if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE,
+                      session_info->pdu_type))
+               goto nla_put_failure;
+
+       if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GTP_QFI, session_info->qfi))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nest);
+       return -EMSGSIZE;
+}
+
 static int fl_dump_key_ct(struct sk_buff *skb,
                          struct flow_dissector_key_ct *key,
                          struct flow_dissector_key_ct *mask)
@@ -2824,6 +2935,11 @@ static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type,
                if (err)
                        goto nla_put_failure;
                break;
+       case TUNNEL_GTP_OPT:
+               err = fl_dump_key_gtp_opt(skb, enc_opts);
+               if (err)
+                       goto nla_put_failure;
+               break;
        default:
                goto nla_put_failure;
        }