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: Wojciech Drewek <wojciech.drewek@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
parent
d33bd757d3
commit
e3acda7ade
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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,
|
||||
|
@ -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 @@ 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user