]>
Commit | Line | Data |
---|---|---|
c0c77d8f BT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* XDP sockets | |
3 | * | |
4 | * AF_XDP sockets allows a channel between XDP programs and userspace | |
5 | * applications. | |
6 | * Copyright(c) 2018 Intel Corporation. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms and conditions of the GNU General Public License, | |
10 | * version 2, as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * Author(s): Björn Töpel <bjorn.topel@intel.com> | |
18 | * Magnus Karlsson <magnus.karlsson@intel.com> | |
19 | */ | |
20 | ||
21 | #define pr_fmt(fmt) "AF_XDP: %s: " fmt, __func__ | |
22 | ||
23 | #include <linux/if_xdp.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/sched/mm.h> | |
26 | #include <linux/sched/signal.h> | |
27 | #include <linux/sched/task.h> | |
28 | #include <linux/socket.h> | |
29 | #include <linux/file.h> | |
30 | #include <linux/uaccess.h> | |
31 | #include <linux/net.h> | |
32 | #include <linux/netdevice.h> | |
33 | #include <net/xdp_sock.h> | |
34 | ||
35 | #include "xdp_umem.h" | |
36 | ||
37 | static struct xdp_sock *xdp_sk(struct sock *sk) | |
38 | { | |
39 | return (struct xdp_sock *)sk; | |
40 | } | |
41 | ||
42 | static int xsk_release(struct socket *sock) | |
43 | { | |
44 | struct sock *sk = sock->sk; | |
45 | struct net *net; | |
46 | ||
47 | if (!sk) | |
48 | return 0; | |
49 | ||
50 | net = sock_net(sk); | |
51 | ||
52 | local_bh_disable(); | |
53 | sock_prot_inuse_add(net, sk->sk_prot, -1); | |
54 | local_bh_enable(); | |
55 | ||
56 | sock_orphan(sk); | |
57 | sock->sk = NULL; | |
58 | ||
59 | sk_refcnt_debug_release(sk); | |
60 | sock_put(sk); | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | static int xsk_setsockopt(struct socket *sock, int level, int optname, | |
66 | char __user *optval, unsigned int optlen) | |
67 | { | |
68 | struct sock *sk = sock->sk; | |
69 | struct xdp_sock *xs = xdp_sk(sk); | |
70 | int err; | |
71 | ||
72 | if (level != SOL_XDP) | |
73 | return -ENOPROTOOPT; | |
74 | ||
75 | switch (optname) { | |
76 | case XDP_UMEM_REG: | |
77 | { | |
78 | struct xdp_umem_reg mr; | |
79 | struct xdp_umem *umem; | |
80 | ||
81 | if (xs->umem) | |
82 | return -EBUSY; | |
83 | ||
84 | if (copy_from_user(&mr, optval, sizeof(mr))) | |
85 | return -EFAULT; | |
86 | ||
87 | mutex_lock(&xs->mutex); | |
88 | err = xdp_umem_create(&umem); | |
89 | ||
90 | err = xdp_umem_reg(umem, &mr); | |
91 | if (err) { | |
92 | kfree(umem); | |
93 | mutex_unlock(&xs->mutex); | |
94 | return err; | |
95 | } | |
96 | ||
97 | /* Make sure umem is ready before it can be seen by others */ | |
98 | smp_wmb(); | |
99 | ||
100 | xs->umem = umem; | |
101 | mutex_unlock(&xs->mutex); | |
102 | return 0; | |
103 | } | |
104 | default: | |
105 | break; | |
106 | } | |
107 | ||
108 | return -ENOPROTOOPT; | |
109 | } | |
110 | ||
111 | static struct proto xsk_proto = { | |
112 | .name = "XDP", | |
113 | .owner = THIS_MODULE, | |
114 | .obj_size = sizeof(struct xdp_sock), | |
115 | }; | |
116 | ||
117 | static const struct proto_ops xsk_proto_ops = { | |
118 | .family = PF_XDP, | |
119 | .owner = THIS_MODULE, | |
120 | .release = xsk_release, | |
121 | .bind = sock_no_bind, | |
122 | .connect = sock_no_connect, | |
123 | .socketpair = sock_no_socketpair, | |
124 | .accept = sock_no_accept, | |
125 | .getname = sock_no_getname, | |
126 | .poll = sock_no_poll, | |
127 | .ioctl = sock_no_ioctl, | |
128 | .listen = sock_no_listen, | |
129 | .shutdown = sock_no_shutdown, | |
130 | .setsockopt = xsk_setsockopt, | |
131 | .getsockopt = sock_no_getsockopt, | |
132 | .sendmsg = sock_no_sendmsg, | |
133 | .recvmsg = sock_no_recvmsg, | |
134 | .mmap = sock_no_mmap, | |
135 | .sendpage = sock_no_sendpage, | |
136 | }; | |
137 | ||
138 | static void xsk_destruct(struct sock *sk) | |
139 | { | |
140 | struct xdp_sock *xs = xdp_sk(sk); | |
141 | ||
142 | if (!sock_flag(sk, SOCK_DEAD)) | |
143 | return; | |
144 | ||
145 | xdp_put_umem(xs->umem); | |
146 | ||
147 | sk_refcnt_debug_dec(sk); | |
148 | } | |
149 | ||
150 | static int xsk_create(struct net *net, struct socket *sock, int protocol, | |
151 | int kern) | |
152 | { | |
153 | struct sock *sk; | |
154 | struct xdp_sock *xs; | |
155 | ||
156 | if (!ns_capable(net->user_ns, CAP_NET_RAW)) | |
157 | return -EPERM; | |
158 | if (sock->type != SOCK_RAW) | |
159 | return -ESOCKTNOSUPPORT; | |
160 | ||
161 | if (protocol) | |
162 | return -EPROTONOSUPPORT; | |
163 | ||
164 | sock->state = SS_UNCONNECTED; | |
165 | ||
166 | sk = sk_alloc(net, PF_XDP, GFP_KERNEL, &xsk_proto, kern); | |
167 | if (!sk) | |
168 | return -ENOBUFS; | |
169 | ||
170 | sock->ops = &xsk_proto_ops; | |
171 | ||
172 | sock_init_data(sock, sk); | |
173 | ||
174 | sk->sk_family = PF_XDP; | |
175 | ||
176 | sk->sk_destruct = xsk_destruct; | |
177 | sk_refcnt_debug_inc(sk); | |
178 | ||
179 | xs = xdp_sk(sk); | |
180 | mutex_init(&xs->mutex); | |
181 | ||
182 | local_bh_disable(); | |
183 | sock_prot_inuse_add(net, &xsk_proto, 1); | |
184 | local_bh_enable(); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | static const struct net_proto_family xsk_family_ops = { | |
190 | .family = PF_XDP, | |
191 | .create = xsk_create, | |
192 | .owner = THIS_MODULE, | |
193 | }; | |
194 | ||
195 | static int __init xsk_init(void) | |
196 | { | |
197 | int err; | |
198 | ||
199 | err = proto_register(&xsk_proto, 0 /* no slab */); | |
200 | if (err) | |
201 | goto out; | |
202 | ||
203 | err = sock_register(&xsk_family_ops); | |
204 | if (err) | |
205 | goto out_proto; | |
206 | ||
207 | return 0; | |
208 | ||
209 | out_proto: | |
210 | proto_unregister(&xsk_proto); | |
211 | out: | |
212 | return err; | |
213 | } | |
214 | ||
215 | fs_initcall(xsk_init); |