Message ID | 20230721071532.613888-4-marcin.szycik@linux.intel.com (mailing list archive) |
---|---|
State | Awaiting Upstream |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | ice: Add PFCP filter support | expand |
On Fri, Jul 21, 2023 at 09:15:29AM +0200, Marcin Szycik wrote: > From: Wojciech Drewek <wojciech.drewek@intel.com> > > Packet Forwarding Control Protocol (PFCP) is a 3GPP Protocol > used between the control plane and the user plane function. > It is specified in TS 29.244[1]. > > Note that this module is not designed to support this Protocol > in the kernel space. There is no support for parsing any PFCP messages. > There is no API that could be used by any userspace daemon. > Basically it does not support PFCP. This protocol is sophisticated > and there is no need for implementing it in the kernel. The purpose > of this module is to allow users to setup software and hardware offload > of PFCP packets using tc tool. > > When user requests to create a PFCP device, a new socket is created. > The socket is set up with port number 8805 which is specific for > PFCP [29.244 4.2.2]. This allow to receive PFCP request messages, > response messages use other ports. > > Note that only one PFCP netdev can be created. > > Only IPv4 is supported at this time. > > [1] https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3111 > Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com> Co-developed-by: Marcin...? > Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com> ... > +/* PFCP according to 3GPP TS 29.244 > + * > + * Copyright (C) 2022, Intel Corporation. > + * (C) 2022 by Wojciech Drewek <wojciech.drewek@intel.com> Is it approved by our Legal? First time I see such (c) together with Intel's and correct authorship. > + * Author: Wojciech Drewek <wojciech.drewek@intel.com> > + */ ... > +struct pfcp_dev { > + struct list_head list; This is defined in types.h which is missing. > + struct socket *sock; > + struct net_device *dev; > + struct net *net; > +}; ... > + dev->needs_free_netdev = true; Single space is enough. ... > +static int pfcp_newlink(struct net *net, struct net_device *dev, > + struct nlattr *tb[], struct nlattr *data[], > + struct netlink_ext_ack *extack) > +{ > + struct pfcp_dev *pfcp = netdev_priv(dev); > + struct pfcp_net *pn; > + int err; > + > + pfcp->net = net; > + > + err = pfcp_add_sock(pfcp); > + if (err) { > + netdev_dbg(dev, "failed to add pfcp socket %d\n", err); > + goto exit; > + } > + > + err = register_netdevice(dev); > + if (err) { > + netdev_dbg(dev, "failed to register pfcp netdev %d\n", err); > + goto exit_reg_netdev; > + } > + > + pn = net_generic(dev_net(dev), pfcp_net_id); > + list_add_rcu(&pfcp->list, &pn->pfcp_dev_list); > + > + netdev_dbg(dev, "registered new PFCP interface\n"); > + > + return 0; > + > +exit_reg_netdev: The label naming should tell what _will_ happen if goto $LABEL. Something like exit_del_pfcp_sock: Ditto for all labels in your code. > + pfcp_del_sock(pfcp); > +exit: Shouldn't here be ->net = NULL; ? > + return err; > +} ... > +#ifndef _PFCP_H_ > +#define _PFCP_H_ Missing headers: For net_device internals, bool type, and strcpm() call. > +#define PFCP_PORT 8805 > + > +static inline bool netif_is_pfcp(const struct net_device *dev) > +{ > + return dev->rtnl_link_ops && > + !strcmp(dev->rtnl_link_ops->kind, "pfcp"); > +} > + > +#endif
On 21.07.2023 16:54, Andy Shevchenko wrote: > On Fri, Jul 21, 2023 at 09:15:29AM +0200, Marcin Szycik wrote: >> From: Wojciech Drewek <wojciech.drewek@intel.com> >> >> Packet Forwarding Control Protocol (PFCP) is a 3GPP Protocol >> used between the control plane and the user plane function. >> It is specified in TS 29.244[1]. >> >> Note that this module is not designed to support this Protocol >> in the kernel space. There is no support for parsing any PFCP messages. >> There is no API that could be used by any userspace daemon. >> Basically it does not support PFCP. This protocol is sophisticated >> and there is no need for implementing it in the kernel. The purpose >> of this module is to allow users to setup software and hardware offload >> of PFCP packets using tc tool. >> >> When user requests to create a PFCP device, a new socket is created. >> The socket is set up with port number 8805 which is specific for >> PFCP [29.244 4.2.2]. This allow to receive PFCP request messages, >> response messages use other ports. >> >> Note that only one PFCP netdev can be created. >> >> Only IPv4 is supported at this time. >> >> [1] https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3111 > >> Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com> > > Co-developed-by: Marcin...? In this case I'm only a sender, I didn't help in development. > >> Signed-off-by: Marcin Szycik <marcin.szycik@linux.intel.com> > > ... > >> +/* PFCP according to 3GPP TS 29.244 >> + * >> + * Copyright (C) 2022, Intel Corporation. > >> + * (C) 2022 by Wojciech Drewek <wojciech.drewek@intel.com> > > Is it approved by our Legal? First time I see such (c) together with Intel's > and correct authorship. Right, I'll leave only first (c) line. >> + * Author: Wojciech Drewek <wojciech.drewek@intel.com> >> + */ > > ... > >> +struct pfcp_dev { >> + struct list_head list; > > This is defined in types.h which is missing. Will add. > >> + struct socket *sock; >> + struct net_device *dev; >> + struct net *net; >> +}; > > ... > >> + dev->needs_free_netdev = true; > > Single space is enough. Will fix. > > ... > >> +static int pfcp_newlink(struct net *net, struct net_device *dev, >> + struct nlattr *tb[], struct nlattr *data[], >> + struct netlink_ext_ack *extack) >> +{ >> + struct pfcp_dev *pfcp = netdev_priv(dev); >> + struct pfcp_net *pn; >> + int err; >> + >> + pfcp->net = net; >> + >> + err = pfcp_add_sock(pfcp); >> + if (err) { >> + netdev_dbg(dev, "failed to add pfcp socket %d\n", err); >> + goto exit; >> + } >> + >> + err = register_netdevice(dev); >> + if (err) { >> + netdev_dbg(dev, "failed to register pfcp netdev %d\n", err); >> + goto exit_reg_netdev; >> + } >> + >> + pn = net_generic(dev_net(dev), pfcp_net_id); >> + list_add_rcu(&pfcp->list, &pn->pfcp_dev_list); >> + >> + netdev_dbg(dev, "registered new PFCP interface\n"); >> + >> + return 0; >> + >> +exit_reg_netdev: > > The label naming should tell what _will_ happen if goto $LABEL. > Something like > > exit_del_pfcp_sock: Another convention I've seen is `err_what_failed`. But yeah, exit_reg_netdev doesn't match either convention, will change to your suggestion. > > Ditto for all labels in your code. > >> + pfcp_del_sock(pfcp); >> +exit: > > Shouldn't here be > > ->net = NULL; Good catch, will add. > > ? > >> + return err; >> +} > > ... > >> +#ifndef _PFCP_H_ >> +#define _PFCP_H_ > > Missing headers: > For net_device internals, bool type, and strcpm() call. Will add. > >> +#define PFCP_PORT 8805 >> + >> +static inline bool netif_is_pfcp(const struct net_device *dev) >> +{ >> + return dev->rtnl_link_ops && >> + !strcmp(dev->rtnl_link_ops->kind, "pfcp"); >> +} >> + >> +#endif > Thank you for review! Marcin
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 368c6f5b327e..8f94b8b2b2e4 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -290,6 +290,19 @@ config GTP To compile this drivers as a module, choose M here: the module will be called gtp. +config PFCP + tristate "Packet Forwarding Control Protocol (PFCP)" + depends on INET + select NET_UDP_TUNNEL + help + This allows one to create PFCP virtual interfaces that allows to + set up software and hardware offload of PFCP packets. + Note that this module does not support PFCP protocol in the kernel space. + There is no support for parsing any PFCP messages. + + To compile this drivers as a module, choose M here: the module + will be called pfcp. + config AMT tristate "Automatic Multicast Tunneling (AMT)" depends on INET && IP_MULTICAST diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e26f98f897c5..2cded0a3ed4b 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_GENEVE) += geneve.o obj-$(CONFIG_BAREUDP) += bareudp.o obj-$(CONFIG_GTP) += gtp.o obj-$(CONFIG_NLMON) += nlmon.o +obj-$(CONFIG_PFCP) += pfcp.o obj-$(CONFIG_NET_VRF) += vrf.o obj-$(CONFIG_VSOCKMON) += vsockmon.o obj-$(CONFIG_MHI_NET) += mhi_net.o diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c new file mode 100644 index 000000000000..3ab2e93e0b45 --- /dev/null +++ b/drivers/net/pfcp.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* PFCP according to 3GPP TS 29.244 + * + * Copyright (C) 2022, Intel Corporation. + * (C) 2022 by Wojciech Drewek <wojciech.drewek@intel.com> + * + * Author: Wojciech Drewek <wojciech.drewek@intel.com> + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/rculist.h> +#include <linux/skbuff.h> + +#include <net/udp.h> +#include <net/udp_tunnel.h> +#include <net/pfcp.h> + +struct pfcp_dev { + struct list_head list; + + struct socket *sock; + struct net_device *dev; + struct net *net; +}; + +static unsigned int pfcp_net_id __read_mostly; + +struct pfcp_net { + struct list_head pfcp_dev_list; +}; + +static void pfcp_del_sock(struct pfcp_dev *pfcp) +{ + udp_tunnel_sock_release(pfcp->sock); + pfcp->sock = NULL; +} + +static void pfcp_dev_uninit(struct net_device *dev) +{ + struct pfcp_dev *pfcp = netdev_priv(dev); + + pfcp_del_sock(pfcp); +} + +static int pfcp_dev_init(struct net_device *dev) +{ + struct pfcp_dev *pfcp = netdev_priv(dev); + + pfcp->dev = dev; + + return 0; +} + +static const struct net_device_ops pfcp_netdev_ops = { + .ndo_init = pfcp_dev_init, + .ndo_uninit = pfcp_dev_uninit, + .ndo_get_stats64 = dev_get_tstats64, +}; + +static const struct device_type pfcp_type = { + .name = "pfcp", +}; + +static void pfcp_link_setup(struct net_device *dev) +{ + dev->netdev_ops = &pfcp_netdev_ops; + dev->needs_free_netdev = true; + SET_NETDEV_DEVTYPE(dev, &pfcp_type); + + dev->hard_header_len = 0; + dev->addr_len = 0; + + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->priv_flags |= IFF_NO_QUEUE; + + netif_keep_dst(dev); +} + +static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp) +{ + struct udp_tunnel_sock_cfg tuncfg = {}; + struct udp_port_cfg udp_conf = { + .local_ip.s_addr = htonl(INADDR_ANY), + .family = AF_INET, + }; + struct net *net = pfcp->net; + struct socket *sock; + int err; + + udp_conf.local_udp_port = htons(PFCP_PORT); + + err = udp_sock_create(net, &udp_conf, &sock); + if (err) + return ERR_PTR(err); + + setup_udp_tunnel_sock(net, sock, &tuncfg); + + return sock; +} + +static int pfcp_add_sock(struct pfcp_dev *pfcp) +{ + pfcp->sock = pfcp_create_sock(pfcp); + + return PTR_ERR_OR_ZERO(pfcp->sock); +} + +static int pfcp_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct pfcp_dev *pfcp = netdev_priv(dev); + struct pfcp_net *pn; + int err; + + pfcp->net = net; + + err = pfcp_add_sock(pfcp); + if (err) { + netdev_dbg(dev, "failed to add pfcp socket %d\n", err); + goto exit; + } + + err = register_netdevice(dev); + if (err) { + netdev_dbg(dev, "failed to register pfcp netdev %d\n", err); + goto exit_reg_netdev; + } + + pn = net_generic(dev_net(dev), pfcp_net_id); + list_add_rcu(&pfcp->list, &pn->pfcp_dev_list); + + netdev_dbg(dev, "registered new PFCP interface\n"); + + return 0; + +exit_reg_netdev: + pfcp_del_sock(pfcp); +exit: + return err; +} + +static void pfcp_dellink(struct net_device *dev, struct list_head *head) +{ + struct pfcp_dev *pfcp = netdev_priv(dev); + + list_del_rcu(&pfcp->list); + unregister_netdevice_queue(dev, head); +} + +static struct rtnl_link_ops pfcp_link_ops __read_mostly = { + .kind = "pfcp", + .priv_size = sizeof(struct pfcp_dev), + .setup = pfcp_link_setup, + .newlink = pfcp_newlink, + .dellink = pfcp_dellink, +}; + +static int __net_init pfcp_net_init(struct net *net) +{ + struct pfcp_net *pn = net_generic(net, pfcp_net_id); + + INIT_LIST_HEAD(&pn->pfcp_dev_list); + return 0; +} + +static void __net_exit pfcp_net_exit(struct net *net) +{ + struct pfcp_net *pn = net_generic(net, pfcp_net_id); + struct pfcp_dev *pfcp; + LIST_HEAD(list); + + rtnl_lock(); + list_for_each_entry(pfcp, &pn->pfcp_dev_list, list) + pfcp_dellink(pfcp->dev, &list); + + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations pfcp_net_ops = { + .init = pfcp_net_init, + .exit = pfcp_net_exit, + .id = &pfcp_net_id, + .size = sizeof(struct pfcp_net), +}; + +static int __init pfcp_init(void) +{ + int err; + + err = register_pernet_subsys(&pfcp_net_ops); + if (err) + goto exit; + + err = rtnl_link_register(&pfcp_link_ops); + if (err) + goto exit_subsys; + return 0; + +exit_subsys: + unregister_pernet_subsys(&pfcp_net_ops); +exit: + pr_err("loading PFCP module failed: err %d\n", err); + return err; +} +late_initcall(pfcp_init); + +static void __exit pfcp_exit(void) +{ + rtnl_link_unregister(&pfcp_link_ops); + unregister_pernet_subsys(&pfcp_net_ops); + + pr_info("PFCP module unloaded\n"); +} +module_exit(pfcp_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wojciech Drewek <wojciech.drewek@intel.com>"); +MODULE_DESCRIPTION("Interface driver for PFCP encapsulated traffic"); +MODULE_ALIAS_RTNL_LINK("pfcp"); diff --git a/include/net/pfcp.h b/include/net/pfcp.h new file mode 100644 index 000000000000..88f0815e40d2 --- /dev/null +++ b/include/net/pfcp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PFCP_H_ +#define _PFCP_H_ + +#define PFCP_PORT 8805 + +static inline bool netif_is_pfcp(const struct net_device *dev) +{ + return dev->rtnl_link_ops && + !strcmp(dev->rtnl_link_ops->kind, "pfcp"); +} + +#endif