Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <linux/file.h>
4#include <linux/net.h>
5#include <linux/rcupdate.h>
6#include <linux/tcp.h>
7
8#include <net/ip.h>
9#include <net/psp.h>
10#include "psp.h"
11
12struct psp_dev *psp_dev_get_for_sock(struct sock *sk)
13{
14 struct psp_dev *psd = NULL;
15 struct dst_entry *dst;
16
17 rcu_read_lock();
18 dst = __sk_dst_get(sk);
19 if (dst) {
20 psd = rcu_dereference(dst_dev_rcu(dst)->psp_dev);
21 if (psd && !psp_dev_tryget(psd))
22 psd = NULL;
23 }
24 rcu_read_unlock();
25
26 return psd;
27}
28
29static struct sk_buff *
30psp_validate_xmit(struct sock *sk, struct net_device *dev, struct sk_buff *skb)
31{
32 struct psp_assoc *pas;
33 bool good;
34
35 rcu_read_lock();
36 pas = psp_skb_get_assoc_rcu(skb);
37 good = !pas || rcu_access_pointer(dev->psp_dev) == pas->psd;
38 rcu_read_unlock();
39 if (!good) {
40 sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_PSP_OUTPUT);
41 return NULL;
42 }
43
44 return skb;
45}
46
47struct psp_assoc *psp_assoc_create(struct psp_dev *psd)
48{
49 struct psp_assoc *pas;
50
51 lockdep_assert_held(&psd->lock);
52
53 pas = kzalloc(struct_size(pas, drv_data, psd->caps->assoc_drv_spc),
54 GFP_KERNEL_ACCOUNT);
55 if (!pas)
56 return NULL;
57
58 pas->psd = psd;
59 pas->dev_id = psd->id;
60 pas->generation = psd->generation;
61 psp_dev_get(psd);
62 refcount_set(&pas->refcnt, 1);
63
64 list_add_tail(&pas->assocs_list, &psd->active_assocs);
65
66 return pas;
67}
68
69static struct psp_assoc *psp_assoc_dummy(struct psp_assoc *pas)
70{
71 struct psp_dev *psd = pas->psd;
72 size_t sz;
73
74 lockdep_assert_held(&psd->lock);
75
76 sz = struct_size(pas, drv_data, psd->caps->assoc_drv_spc);
77 return kmemdup(pas, sz, GFP_KERNEL);
78}
79
80static int psp_dev_tx_key_add(struct psp_dev *psd, struct psp_assoc *pas,
81 struct netlink_ext_ack *extack)
82{
83 return psd->ops->tx_key_add(psd, pas, extack);
84}
85
86void psp_dev_tx_key_del(struct psp_dev *psd, struct psp_assoc *pas)
87{
88 if (pas->tx.spi)
89 psd->ops->tx_key_del(psd, pas);
90 list_del(&pas->assocs_list);
91}
92
93static void psp_assoc_free(struct work_struct *work)
94{
95 struct psp_assoc *pas = container_of(work, struct psp_assoc, work);
96 struct psp_dev *psd = pas->psd;
97
98 mutex_lock(&psd->lock);
99 if (psd->ops)
100 psp_dev_tx_key_del(psd, pas);
101 mutex_unlock(&psd->lock);
102 psp_dev_put(psd);
103 kfree(pas);
104}
105
106static void psp_assoc_free_queue(struct rcu_head *head)
107{
108 struct psp_assoc *pas = container_of(head, struct psp_assoc, rcu);
109
110 INIT_WORK(&pas->work, psp_assoc_free);
111 schedule_work(&pas->work);
112}
113
114/**
115 * psp_assoc_put() - release a reference on a PSP association
116 * @pas: association to release
117 */
118void psp_assoc_put(struct psp_assoc *pas)
119{
120 if (pas && refcount_dec_and_test(&pas->refcnt))
121 call_rcu(&pas->rcu, psp_assoc_free_queue);
122}
123
124void psp_sk_assoc_free(struct sock *sk)
125{
126 struct psp_assoc *pas = rcu_dereference_protected(sk->psp_assoc, 1);
127
128 rcu_assign_pointer(sk->psp_assoc, NULL);
129 psp_assoc_put(pas);
130}
131
132int psp_sock_assoc_set_rx(struct sock *sk, struct psp_assoc *pas,
133 struct psp_key_parsed *key,
134 struct netlink_ext_ack *extack)
135{
136 int err;
137
138 memcpy(&pas->rx, key, sizeof(*key));
139
140 lock_sock(sk);
141
142 if (psp_sk_assoc(sk)) {
143 NL_SET_ERR_MSG(extack, "Socket already has PSP state");
144 err = -EBUSY;
145 goto exit_unlock;
146 }
147
148 refcount_inc(&pas->refcnt);
149 rcu_assign_pointer(sk->psp_assoc, pas);
150 err = 0;
151
152exit_unlock:
153 release_sock(sk);
154
155 return err;
156}
157
158static int psp_sock_recv_queue_check(struct sock *sk, struct psp_assoc *pas)
159{
160 struct psp_skb_ext *pse;
161 struct sk_buff *skb;
162
163 skb_rbtree_walk(skb, &tcp_sk(sk)->out_of_order_queue) {
164 pse = skb_ext_find(skb, SKB_EXT_PSP);
165 if (!psp_pse_matches_pas(pse, pas))
166 return -EBUSY;
167 }
168
169 skb_queue_walk(&sk->sk_receive_queue, skb) {
170 pse = skb_ext_find(skb, SKB_EXT_PSP);
171 if (!psp_pse_matches_pas(pse, pas))
172 return -EBUSY;
173 }
174 return 0;
175}
176
177int psp_sock_assoc_set_tx(struct sock *sk, struct psp_dev *psd,
178 u32 version, struct psp_key_parsed *key,
179 struct netlink_ext_ack *extack)
180{
181 struct inet_connection_sock *icsk;
182 struct psp_assoc *pas, *dummy;
183 int err;
184
185 lock_sock(sk);
186
187 pas = psp_sk_assoc(sk);
188 if (!pas) {
189 NL_SET_ERR_MSG(extack, "Socket has no Rx key");
190 err = -EINVAL;
191 goto exit_unlock;
192 }
193 if (pas->psd != psd) {
194 NL_SET_ERR_MSG(extack, "Rx key from different device");
195 err = -EINVAL;
196 goto exit_unlock;
197 }
198 if (pas->version != version) {
199 NL_SET_ERR_MSG(extack,
200 "PSP version mismatch with existing state");
201 err = -EINVAL;
202 goto exit_unlock;
203 }
204 if (pas->tx.spi) {
205 NL_SET_ERR_MSG(extack, "Tx key already set");
206 err = -EBUSY;
207 goto exit_unlock;
208 }
209
210 err = psp_sock_recv_queue_check(sk, pas);
211 if (err) {
212 NL_SET_ERR_MSG(extack, "Socket has incompatible segments already in the recv queue");
213 goto exit_unlock;
214 }
215
216 /* Pass a fake association to drivers to make sure they don't
217 * try to store pointers to it. For re-keying we'll need to
218 * re-allocate the assoc structures.
219 */
220 dummy = psp_assoc_dummy(pas);
221 if (!dummy) {
222 err = -ENOMEM;
223 goto exit_unlock;
224 }
225
226 memcpy(&dummy->tx, key, sizeof(*key));
227 err = psp_dev_tx_key_add(psd, dummy, extack);
228 if (err)
229 goto exit_free_dummy;
230
231 memcpy(pas->drv_data, dummy->drv_data, psd->caps->assoc_drv_spc);
232 memcpy(&pas->tx, key, sizeof(*key));
233
234 WRITE_ONCE(sk->sk_validate_xmit_skb, psp_validate_xmit);
235 tcp_write_collapse_fence(sk);
236 pas->upgrade_seq = tcp_sk(sk)->rcv_nxt;
237
238 icsk = inet_csk(sk);
239 icsk->icsk_ext_hdr_len += psp_sk_overhead(sk);
240 icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
241
242exit_free_dummy:
243 kfree(dummy);
244exit_unlock:
245 release_sock(sk);
246 return err;
247}
248
249void psp_assocs_key_rotated(struct psp_dev *psd)
250{
251 struct psp_assoc *pas, *next;
252
253 /* Mark the stale associations as invalid, they will no longer
254 * be able to Rx any traffic.
255 */
256 list_for_each_entry_safe(pas, next, &psd->prev_assocs, assocs_list) {
257 pas->generation |= ~PSP_GEN_VALID_MASK;
258 psd->stats.stales++;
259 }
260 list_splice_init(&psd->prev_assocs, &psd->stale_assocs);
261 list_splice_init(&psd->active_assocs, &psd->prev_assocs);
262
263 /* TODO: we should inform the sockets that got shut down */
264}
265
266void psp_twsk_init(struct inet_timewait_sock *tw, const struct sock *sk)
267{
268 struct psp_assoc *pas = psp_sk_assoc(sk);
269
270 if (pas)
271 refcount_inc(&pas->refcnt);
272 rcu_assign_pointer(tw->psp_assoc, pas);
273 tw->tw_validate_xmit_skb = psp_validate_xmit;
274}
275
276void psp_twsk_assoc_free(struct inet_timewait_sock *tw)
277{
278 struct psp_assoc *pas = rcu_dereference_protected(tw->psp_assoc, 1);
279
280 rcu_assign_pointer(tw->psp_assoc, NULL);
281 psp_assoc_put(pas);
282}
283
284void psp_reply_set_decrypted(const struct sock *sk, struct sk_buff *skb)
285{
286 struct psp_assoc *pas;
287
288 rcu_read_lock();
289 pas = psp_sk_get_assoc_rcu(sk);
290 if (pas && pas->tx.spi)
291 skb->decrypted = 1;
292 rcu_read_unlock();
293}
294EXPORT_IPV6_MOD_GPL(psp_reply_set_decrypted);