pfcp: always set pfcp metadata

In PFCP receive path set metadata needed by flower code to do correct
classification based on this metadata.

Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Michal Swiatkowski 2024-03-27 16:23:56 +01:00 committed by David S. Miller
parent 76c8764ef3
commit 6dd514f481
7 changed files with 282 additions and 6 deletions

View File

@ -21,6 +21,8 @@ struct pfcp_dev {
struct socket *sock; struct socket *sock;
struct net_device *dev; struct net_device *dev;
struct net *net; struct net *net;
struct gro_cells gro_cells;
}; };
static unsigned int pfcp_net_id __read_mostly; static unsigned int pfcp_net_id __read_mostly;
@ -29,6 +31,78 @@ struct pfcp_net {
struct list_head pfcp_dev_list; struct list_head pfcp_dev_list;
}; };
static void
pfcp_session_recv(struct pfcp_dev *pfcp, struct sk_buff *skb,
struct pfcp_metadata *md)
{
struct pfcphdr_session *unparsed = pfcp_hdr_session(skb);
md->seid = unparsed->seid;
md->type = PFCP_TYPE_SESSION;
}
static void
pfcp_node_recv(struct pfcp_dev *pfcp, struct sk_buff *skb,
struct pfcp_metadata *md)
{
md->type = PFCP_TYPE_NODE;
}
static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
IP_TUNNEL_DECLARE_FLAGS(flags) = { };
struct metadata_dst *tun_dst;
struct pfcp_metadata *md;
struct pfcphdr *unparsed;
struct pfcp_dev *pfcp;
if (unlikely(!pskb_may_pull(skb, PFCP_HLEN)))
goto drop;
pfcp = rcu_dereference_sk_user_data(sk);
if (unlikely(!pfcp))
goto drop;
unparsed = pfcp_hdr(skb);
ip_tunnel_flags_zero(flags);
tun_dst = udp_tun_rx_dst(skb, sk->sk_family, flags, 0,
sizeof(*md));
if (unlikely(!tun_dst))
goto drop;
md = ip_tunnel_info_opts(&tun_dst->u.tun_info);
if (unlikely(!md))
goto drop;
if (unparsed->flags & PFCP_SEID_FLAG)
pfcp_session_recv(pfcp, skb, md);
else
pfcp_node_recv(pfcp, skb, md);
__set_bit(IP_TUNNEL_PFCP_OPT_BIT, flags);
ip_tunnel_info_opts_set(&tun_dst->u.tun_info, md, sizeof(*md),
flags);
if (unlikely(iptunnel_pull_header(skb, PFCP_HLEN, skb->protocol,
!net_eq(sock_net(sk),
dev_net(pfcp->dev)))))
goto drop;
skb_dst_set(skb, (struct dst_entry *)tun_dst);
skb_reset_network_header(skb);
skb_reset_mac_header(skb);
skb->dev = pfcp->dev;
gro_cells_receive(&pfcp->gro_cells, skb);
return 0;
drop:
kfree_skb(skb);
return 0;
}
static void pfcp_del_sock(struct pfcp_dev *pfcp) static void pfcp_del_sock(struct pfcp_dev *pfcp)
{ {
udp_tunnel_sock_release(pfcp->sock); udp_tunnel_sock_release(pfcp->sock);
@ -39,6 +113,7 @@ static void pfcp_dev_uninit(struct net_device *dev)
{ {
struct pfcp_dev *pfcp = netdev_priv(dev); struct pfcp_dev *pfcp = netdev_priv(dev);
gro_cells_destroy(&pfcp->gro_cells);
pfcp_del_sock(pfcp); pfcp_del_sock(pfcp);
} }
@ -48,7 +123,7 @@ static int pfcp_dev_init(struct net_device *dev)
pfcp->dev = dev; pfcp->dev = dev;
return 0; return gro_cells_init(&pfcp->gro_cells, dev);
} }
static const struct net_device_ops pfcp_netdev_ops = { static const struct net_device_ops pfcp_netdev_ops = {
@ -94,6 +169,10 @@ static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp)
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
tuncfg.sk_user_data = pfcp;
tuncfg.encap_rcv = pfcp_encap_recv;
tuncfg.encap_type = 1;
setup_udp_tunnel_sock(net, sock, &tuncfg); setup_udp_tunnel_sock(net, sock, &tuncfg);
return sock; return sock;

View File

@ -216,6 +216,7 @@ static inline void ip_tunnel_set_options_present(unsigned long *flags)
__set_bit(IP_TUNNEL_VXLAN_OPT_BIT, present); __set_bit(IP_TUNNEL_VXLAN_OPT_BIT, present);
__set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, present); __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, present);
__set_bit(IP_TUNNEL_GTP_OPT_BIT, present); __set_bit(IP_TUNNEL_GTP_OPT_BIT, present);
__set_bit(IP_TUNNEL_PFCP_OPT_BIT, present);
ip_tunnel_flags_or(flags, flags, present); ip_tunnel_flags_or(flags, flags, present);
} }
@ -228,6 +229,7 @@ static inline void ip_tunnel_clear_options_present(unsigned long *flags)
__set_bit(IP_TUNNEL_VXLAN_OPT_BIT, present); __set_bit(IP_TUNNEL_VXLAN_OPT_BIT, present);
__set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, present); __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, present);
__set_bit(IP_TUNNEL_GTP_OPT_BIT, present); __set_bit(IP_TUNNEL_GTP_OPT_BIT, present);
__set_bit(IP_TUNNEL_PFCP_OPT_BIT, present);
__ipt_flag_op(bitmap_andnot, flags, flags, present); __ipt_flag_op(bitmap_andnot, flags, flags, present);
} }
@ -240,6 +242,7 @@ static inline bool ip_tunnel_is_options_present(const unsigned long *flags)
__set_bit(IP_TUNNEL_VXLAN_OPT_BIT, present); __set_bit(IP_TUNNEL_VXLAN_OPT_BIT, present);
__set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, present); __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, present);
__set_bit(IP_TUNNEL_GTP_OPT_BIT, present); __set_bit(IP_TUNNEL_GTP_OPT_BIT, present);
__set_bit(IP_TUNNEL_PFCP_OPT_BIT, present);
return ip_tunnel_flags_intersect(flags, present); return ip_tunnel_flags_intersect(flags, present);
} }

View File

@ -2,12 +2,85 @@
#ifndef _PFCP_H_ #ifndef _PFCP_H_
#define _PFCP_H_ #define _PFCP_H_
#include <uapi/linux/if_ether.h>
#include <net/dst_metadata.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <uapi/linux/ipv6.h>
#include <net/udp_tunnel.h>
#include <uapi/linux/udp.h>
#include <uapi/linux/ip.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/bits.h>
#define PFCP_PORT 8805 #define PFCP_PORT 8805
/* PFCP protocol header */
struct pfcphdr {
u8 flags;
u8 message_type;
__be16 message_length;
};
/* PFCP header flags */
#define PFCP_SEID_FLAG BIT(0)
#define PFCP_MP_FLAG BIT(1)
#define PFCP_VERSION_MASK GENMASK(4, 0)
#define PFCP_HLEN (sizeof(struct udphdr) + sizeof(struct pfcphdr))
/* PFCP node related messages */
struct pfcphdr_node {
u8 seq_number[3];
u8 reserved;
};
/* PFCP session related messages */
struct pfcphdr_session {
__be64 seid;
u8 seq_number[3];
#ifdef __LITTLE_ENDIAN_BITFIELD
u8 message_priority:4,
reserved:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
u8 reserved:4,
message_priprity:4;
#else
#error "Please fix <asm/byteorder>"
#endif
};
struct pfcp_metadata {
u8 type;
__be64 seid;
} __packed;
enum {
PFCP_TYPE_NODE = 0,
PFCP_TYPE_SESSION = 1,
};
#define PFCP_HEADROOM (sizeof(struct iphdr) + sizeof(struct udphdr) + \
sizeof(struct pfcphdr) + sizeof(struct ethhdr))
#define PFCP6_HEADROOM (sizeof(struct ipv6hdr) + sizeof(struct udphdr) + \
sizeof(struct pfcphdr) + sizeof(struct ethhdr))
static inline struct pfcphdr *pfcp_hdr(struct sk_buff *skb)
{
return (struct pfcphdr *)(udp_hdr(skb) + 1);
}
static inline struct pfcphdr_node *pfcp_hdr_node(struct sk_buff *skb)
{
return (struct pfcphdr_node *)(pfcp_hdr(skb) + 1);
}
static inline struct pfcphdr_session *pfcp_hdr_session(struct sk_buff *skb)
{
return (struct pfcphdr_session *)(pfcp_hdr(skb) + 1);
}
static inline bool netif_is_pfcp(const struct net_device *dev) static inline bool netif_is_pfcp(const struct net_device *dev)
{ {
return dev->rtnl_link_ops && return dev->rtnl_link_ops &&

View File

@ -212,6 +212,9 @@ enum {
IP_TUNNEL_VTI_BIT, IP_TUNNEL_VTI_BIT,
IP_TUNNEL_SIT_ISATAP_BIT = IP_TUNNEL_VTI_BIT, IP_TUNNEL_SIT_ISATAP_BIT = IP_TUNNEL_VTI_BIT,
/* Flags starting from here are not available via the old UAPI */
IP_TUNNEL_PFCP_OPT_BIT, /* OPTIONS_PRESENT */
__IP_TUNNEL_FLAG_NUM, __IP_TUNNEL_FLAG_NUM,
}; };

View File

@ -587,6 +587,10 @@ enum {
* TCA_FLOWER_KEY_ENC_OPT_GTP_ * TCA_FLOWER_KEY_ENC_OPT_GTP_
* attributes * attributes
*/ */
TCA_FLOWER_KEY_ENC_OPTS_PFCP, /* Nested
* TCA_FLOWER_KEY_ENC_IPT_PFCP
* attributes
*/
__TCA_FLOWER_KEY_ENC_OPTS_MAX, __TCA_FLOWER_KEY_ENC_OPTS_MAX,
}; };
@ -636,6 +640,16 @@ enum {
#define TCA_FLOWER_KEY_ENC_OPT_GTP_MAX \ #define TCA_FLOWER_KEY_ENC_OPT_GTP_MAX \
(__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX - 1) (__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX - 1)
enum {
TCA_FLOWER_KEY_ENC_OPT_PFCP_UNSPEC,
TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE, /* u8 */
TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID, /* be64 */
__TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX,
};
#define TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX \
(__TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX - 1)
enum { enum {
TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC, TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC,
TCA_FLOWER_KEY_MPLS_OPTS_LSE, TCA_FLOWER_KEY_MPLS_OPTS_LSE,

View File

@ -335,11 +335,8 @@ static const struct ip_tunnel_flags_test ip_tunnel_flags_test[] = {
ip_tunnel_flags_1), ip_tunnel_flags_1),
IP_TUNNEL_FLAGS_TEST("conflict", ip_tunnel_flags_2_src, true, IP_TUNNEL_FLAGS_TEST("conflict", ip_tunnel_flags_2_src, true,
VTI_ISVTI, ip_tunnel_flags_2_exp), VTI_ISVTI, ip_tunnel_flags_2_exp),
IP_TUNNEL_FLAGS_TEST("new", ip_tunnel_flags_3_src, IP_TUNNEL_FLAGS_TEST("new", ip_tunnel_flags_3_src, false,
/* This must be set to ``false`` once cpu_to_be16(BIT(IP_TUNNEL_VXLAN_OPT_BIT)),
* ``__IP_TUNNEL_FLAG_NUM`` goes above 17.
*/
true, cpu_to_be16(BIT(IP_TUNNEL_VXLAN_OPT_BIT)),
ip_tunnel_flags_3_exp), ip_tunnel_flags_3_exp),
}; };

View File

@ -28,6 +28,7 @@
#include <net/vxlan.h> #include <net/vxlan.h>
#include <net/erspan.h> #include <net/erspan.h>
#include <net/gtp.h> #include <net/gtp.h>
#include <net/pfcp.h>
#include <net/tc_wrapper.h> #include <net/tc_wrapper.h>
#include <net/dst.h> #include <net/dst.h>
@ -741,6 +742,7 @@ enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = {
[TCA_FLOWER_KEY_ENC_OPTS_VXLAN] = { .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_ERSPAN] = { .type = NLA_NESTED },
[TCA_FLOWER_KEY_ENC_OPTS_GTP] = { .type = NLA_NESTED }, [TCA_FLOWER_KEY_ENC_OPTS_GTP] = { .type = NLA_NESTED },
[TCA_FLOWER_KEY_ENC_OPTS_PFCP] = { .type = NLA_NESTED },
}; };
static const struct nla_policy static const struct nla_policy
@ -770,6 +772,12 @@ gtp_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1] = {
[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI] = { .type = NLA_U8 }, [TCA_FLOWER_KEY_ENC_OPT_GTP_QFI] = { .type = NLA_U8 },
}; };
static const struct nla_policy
pfcp_opt_policy[TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX + 1] = {
[TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE] = { .type = NLA_U8 },
[TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID] = { .type = NLA_U64 },
};
static const struct nla_policy static const struct nla_policy
mpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = { 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_DEPTH] = { .type = NLA_U8 },
@ -1419,6 +1427,44 @@ static int fl_set_gtp_opt(const struct nlattr *nla, struct fl_flow_key *key,
return sizeof(*sinfo); return sizeof(*sinfo);
} }
static int fl_set_pfcp_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_PFCP_MAX + 1];
struct pfcp_metadata *md;
int err;
md = (struct pfcp_metadata *)&key->enc_opts.data[key->enc_opts.len];
memset(md, 0xff, sizeof(*md));
if (!depth)
return sizeof(*md);
if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_PFCP) {
NL_SET_ERR_MSG_MOD(extack, "Non-pfcp option type for mask");
return -EINVAL;
}
err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_PFCP_MAX, nla,
pfcp_opt_policy, extack);
if (err < 0)
return err;
if (!option_len && !tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE]) {
NL_SET_ERR_MSG_MOD(extack, "Missing tunnel key pfcp option type");
return -EINVAL;
}
if (tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE])
md->type = nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE]);
if (tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID])
md->seid = nla_get_be64(tb[TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID]);
return sizeof(*md);
}
static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
struct fl_flow_key *mask, struct fl_flow_key *mask,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
@ -1576,6 +1622,36 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
return -EINVAL; return -EINVAL;
} }
break; break;
case TCA_FLOWER_KEY_ENC_OPTS_PFCP:
if (key->enc_opts.dst_opt_type) {
NL_SET_ERR_MSG_MOD(extack, "Duplicate type for pfcp options");
return -EINVAL;
}
option_len = 0;
key->enc_opts.dst_opt_type = IP_TUNNEL_PFCP_OPT_BIT;
option_len = fl_set_pfcp_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 = IP_TUNNEL_PFCP_OPT_BIT;
option_len = fl_set_pfcp_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: default:
NL_SET_ERR_MSG(extack, "Unknown tunnel option type"); NL_SET_ERR_MSG(extack, "Unknown tunnel option type");
return -EINVAL; return -EINVAL;
@ -3118,6 +3194,32 @@ nla_put_failure:
return -EMSGSIZE; return -EMSGSIZE;
} }
static int fl_dump_key_pfcp_opt(struct sk_buff *skb,
struct flow_dissector_key_enc_opts *enc_opts)
{
struct pfcp_metadata *md;
struct nlattr *nest;
nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_PFCP);
if (!nest)
goto nla_put_failure;
md = (struct pfcp_metadata *)&enc_opts->data[0];
if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_PFCP_TYPE, md->type))
goto nla_put_failure;
if (nla_put_be64(skb, TCA_FLOWER_KEY_ENC_OPT_PFCP_SEID,
md->seid, 0))
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, static int fl_dump_key_ct(struct sk_buff *skb,
struct flow_dissector_key_ct *key, struct flow_dissector_key_ct *key,
struct flow_dissector_key_ct *mask) struct flow_dissector_key_ct *mask)
@ -3223,6 +3325,11 @@ static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type,
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
break; break;
case IP_TUNNEL_PFCP_OPT_BIT:
err = fl_dump_key_pfcp_opt(skb, enc_opts);
if (err)
goto nla_put_failure;
break;
default: default:
goto nla_put_failure; goto nla_put_failure;
} }