Commit e3acda7a authored by Wojciech Drewek's avatar Wojciech Drewek Committed by Tony Nguyen
Browse files

net/sched: Allow flower to match on GTP options



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: default avatarWojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent d33bd757
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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
+3 −1
Original line number Diff line number Diff line
@@ -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_ */
+15 −0
Original line number Diff line number Diff line
@@ -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,
};

@@ -654,6 +658,17 @@ enum {
#define TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX \
		(__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,
+116 −0
Original line number Diff line number Diff line
@@ -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
@@ -746,6 +748,12 @@ erspan_opt_policy[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1] = {
	[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID]       = { .type = NLA_U8 },
};

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 },
@@ -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 @@ static int fl_dump_key_erspan_opt(struct sk_buff *skb,
	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;
	}