Skip to content

Commit 76c8764

Browse files
WojDrewdavem330
authored andcommitted
pfcp: add PFCP module
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 <[email protected]> Signed-off-by: Marcin Szycik <[email protected]> Reviewed-by: Simon Horman <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5b2be2a commit 76c8764

File tree

4 files changed

+254
-0
lines changed

4 files changed

+254
-0
lines changed

drivers/net/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,19 @@ config GTP
290290
To compile this drivers as a module, choose M here: the module
291291
will be called gtp.
292292

293+
config PFCP
294+
tristate "Packet Forwarding Control Protocol (PFCP)"
295+
depends on INET
296+
select NET_UDP_TUNNEL
297+
help
298+
This allows one to create PFCP virtual interfaces that allows to
299+
set up software and hardware offload of PFCP packets.
300+
Note that this module does not support PFCP protocol in the kernel space.
301+
There is no support for parsing any PFCP messages.
302+
303+
To compile this drivers as a module, choose M here: the module
304+
will be called pfcp.
305+
293306
config AMT
294307
tristate "Automatic Multicast Tunneling (AMT)"
295308
depends on INET && IP_MULTICAST

drivers/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ obj-$(CONFIG_GENEVE) += geneve.o
3838
obj-$(CONFIG_BAREUDP) += bareudp.o
3939
obj-$(CONFIG_GTP) += gtp.o
4040
obj-$(CONFIG_NLMON) += nlmon.o
41+
obj-$(CONFIG_PFCP) += pfcp.o
4142
obj-$(CONFIG_NET_VRF) += vrf.o
4243
obj-$(CONFIG_VSOCKMON) += vsockmon.o
4344
obj-$(CONFIG_MHI_NET) += mhi_net.o

drivers/net/pfcp.c

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* PFCP according to 3GPP TS 29.244
4+
*
5+
* Copyright (C) 2022, Intel Corporation.
6+
*/
7+
8+
#include <linux/module.h>
9+
#include <linux/netdevice.h>
10+
#include <linux/rculist.h>
11+
#include <linux/skbuff.h>
12+
#include <linux/types.h>
13+
14+
#include <net/udp.h>
15+
#include <net/udp_tunnel.h>
16+
#include <net/pfcp.h>
17+
18+
struct pfcp_dev {
19+
struct list_head list;
20+
21+
struct socket *sock;
22+
struct net_device *dev;
23+
struct net *net;
24+
};
25+
26+
static unsigned int pfcp_net_id __read_mostly;
27+
28+
struct pfcp_net {
29+
struct list_head pfcp_dev_list;
30+
};
31+
32+
static void pfcp_del_sock(struct pfcp_dev *pfcp)
33+
{
34+
udp_tunnel_sock_release(pfcp->sock);
35+
pfcp->sock = NULL;
36+
}
37+
38+
static void pfcp_dev_uninit(struct net_device *dev)
39+
{
40+
struct pfcp_dev *pfcp = netdev_priv(dev);
41+
42+
pfcp_del_sock(pfcp);
43+
}
44+
45+
static int pfcp_dev_init(struct net_device *dev)
46+
{
47+
struct pfcp_dev *pfcp = netdev_priv(dev);
48+
49+
pfcp->dev = dev;
50+
51+
return 0;
52+
}
53+
54+
static const struct net_device_ops pfcp_netdev_ops = {
55+
.ndo_init = pfcp_dev_init,
56+
.ndo_uninit = pfcp_dev_uninit,
57+
.ndo_get_stats64 = dev_get_tstats64,
58+
};
59+
60+
static const struct device_type pfcp_type = {
61+
.name = "pfcp",
62+
};
63+
64+
static void pfcp_link_setup(struct net_device *dev)
65+
{
66+
dev->netdev_ops = &pfcp_netdev_ops;
67+
dev->needs_free_netdev = true;
68+
SET_NETDEV_DEVTYPE(dev, &pfcp_type);
69+
70+
dev->hard_header_len = 0;
71+
dev->addr_len = 0;
72+
73+
dev->type = ARPHRD_NONE;
74+
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
75+
dev->priv_flags |= IFF_NO_QUEUE;
76+
77+
netif_keep_dst(dev);
78+
}
79+
80+
static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp)
81+
{
82+
struct udp_tunnel_sock_cfg tuncfg = {};
83+
struct udp_port_cfg udp_conf = {
84+
.local_ip.s_addr = htonl(INADDR_ANY),
85+
.family = AF_INET,
86+
};
87+
struct net *net = pfcp->net;
88+
struct socket *sock;
89+
int err;
90+
91+
udp_conf.local_udp_port = htons(PFCP_PORT);
92+
93+
err = udp_sock_create(net, &udp_conf, &sock);
94+
if (err)
95+
return ERR_PTR(err);
96+
97+
setup_udp_tunnel_sock(net, sock, &tuncfg);
98+
99+
return sock;
100+
}
101+
102+
static int pfcp_add_sock(struct pfcp_dev *pfcp)
103+
{
104+
pfcp->sock = pfcp_create_sock(pfcp);
105+
106+
return PTR_ERR_OR_ZERO(pfcp->sock);
107+
}
108+
109+
static int pfcp_newlink(struct net *net, struct net_device *dev,
110+
struct nlattr *tb[], struct nlattr *data[],
111+
struct netlink_ext_ack *extack)
112+
{
113+
struct pfcp_dev *pfcp = netdev_priv(dev);
114+
struct pfcp_net *pn;
115+
int err;
116+
117+
pfcp->net = net;
118+
119+
err = pfcp_add_sock(pfcp);
120+
if (err) {
121+
netdev_dbg(dev, "failed to add pfcp socket %d\n", err);
122+
goto exit_err;
123+
}
124+
125+
err = register_netdevice(dev);
126+
if (err) {
127+
netdev_dbg(dev, "failed to register pfcp netdev %d\n", err);
128+
goto exit_del_pfcp_sock;
129+
}
130+
131+
pn = net_generic(dev_net(dev), pfcp_net_id);
132+
list_add_rcu(&pfcp->list, &pn->pfcp_dev_list);
133+
134+
netdev_dbg(dev, "registered new PFCP interface\n");
135+
136+
return 0;
137+
138+
exit_del_pfcp_sock:
139+
pfcp_del_sock(pfcp);
140+
exit_err:
141+
pfcp->net = NULL;
142+
return err;
143+
}
144+
145+
static void pfcp_dellink(struct net_device *dev, struct list_head *head)
146+
{
147+
struct pfcp_dev *pfcp = netdev_priv(dev);
148+
149+
list_del_rcu(&pfcp->list);
150+
unregister_netdevice_queue(dev, head);
151+
}
152+
153+
static struct rtnl_link_ops pfcp_link_ops __read_mostly = {
154+
.kind = "pfcp",
155+
.priv_size = sizeof(struct pfcp_dev),
156+
.setup = pfcp_link_setup,
157+
.newlink = pfcp_newlink,
158+
.dellink = pfcp_dellink,
159+
};
160+
161+
static int __net_init pfcp_net_init(struct net *net)
162+
{
163+
struct pfcp_net *pn = net_generic(net, pfcp_net_id);
164+
165+
INIT_LIST_HEAD(&pn->pfcp_dev_list);
166+
return 0;
167+
}
168+
169+
static void __net_exit pfcp_net_exit(struct net *net)
170+
{
171+
struct pfcp_net *pn = net_generic(net, pfcp_net_id);
172+
struct pfcp_dev *pfcp;
173+
LIST_HEAD(list);
174+
175+
rtnl_lock();
176+
list_for_each_entry(pfcp, &pn->pfcp_dev_list, list)
177+
pfcp_dellink(pfcp->dev, &list);
178+
179+
unregister_netdevice_many(&list);
180+
rtnl_unlock();
181+
}
182+
183+
static struct pernet_operations pfcp_net_ops = {
184+
.init = pfcp_net_init,
185+
.exit = pfcp_net_exit,
186+
.id = &pfcp_net_id,
187+
.size = sizeof(struct pfcp_net),
188+
};
189+
190+
static int __init pfcp_init(void)
191+
{
192+
int err;
193+
194+
err = register_pernet_subsys(&pfcp_net_ops);
195+
if (err)
196+
goto exit_err;
197+
198+
err = rtnl_link_register(&pfcp_link_ops);
199+
if (err)
200+
goto exit_unregister_subsys;
201+
return 0;
202+
203+
exit_unregister_subsys:
204+
unregister_pernet_subsys(&pfcp_net_ops);
205+
exit_err:
206+
pr_err("loading PFCP module failed: err %d\n", err);
207+
return err;
208+
}
209+
late_initcall(pfcp_init);
210+
211+
static void __exit pfcp_exit(void)
212+
{
213+
rtnl_link_unregister(&pfcp_link_ops);
214+
unregister_pernet_subsys(&pfcp_net_ops);
215+
216+
pr_info("PFCP module unloaded\n");
217+
}
218+
module_exit(pfcp_exit);
219+
220+
MODULE_LICENSE("GPL");
221+
MODULE_AUTHOR("Wojciech Drewek <[email protected]>");
222+
MODULE_DESCRIPTION("Interface driver for PFCP encapsulated traffic");
223+
MODULE_ALIAS_RTNL_LINK("pfcp");

include/net/pfcp.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _PFCP_H_
3+
#define _PFCP_H_
4+
5+
#include <linux/netdevice.h>
6+
#include <linux/string.h>
7+
#include <linux/types.h>
8+
9+
#define PFCP_PORT 8805
10+
11+
static inline bool netif_is_pfcp(const struct net_device *dev)
12+
{
13+
return dev->rtnl_link_ops &&
14+
!strcmp(dev->rtnl_link_ops->kind, "pfcp");
15+
}
16+
17+
#endif

0 commit comments

Comments
 (0)