Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/handshake/netlink.c
50059 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Generic netlink handshake service
4
*
5
* Author: Chuck Lever <[email protected]>
6
*
7
* Copyright (c) 2023, Oracle and/or its affiliates.
8
*/
9
10
#include <linux/types.h>
11
#include <linux/socket.h>
12
#include <linux/kernel.h>
13
#include <linux/module.h>
14
#include <linux/skbuff.h>
15
#include <linux/mm.h>
16
17
#include <net/sock.h>
18
#include <net/genetlink.h>
19
#include <net/netns/generic.h>
20
21
#include <kunit/visibility.h>
22
23
#include <uapi/linux/handshake.h>
24
#include "handshake.h"
25
#include "genl.h"
26
27
#include <trace/events/handshake.h>
28
29
/**
30
* handshake_genl_notify - Notify handlers that a request is waiting
31
* @net: target network namespace
32
* @proto: handshake protocol
33
* @flags: memory allocation control flags
34
*
35
* Returns zero on success or a negative errno if notification failed.
36
*/
37
int handshake_genl_notify(struct net *net, const struct handshake_proto *proto,
38
gfp_t flags)
39
{
40
struct sk_buff *msg;
41
void *hdr;
42
43
/* Disable notifications during unit testing */
44
if (!test_bit(HANDSHAKE_F_PROTO_NOTIFY, &proto->hp_flags))
45
return 0;
46
47
if (!genl_has_listeners(&handshake_nl_family, net,
48
proto->hp_handler_class))
49
return -ESRCH;
50
51
msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, flags);
52
if (!msg)
53
return -ENOMEM;
54
55
hdr = genlmsg_put(msg, 0, 0, &handshake_nl_family, 0,
56
HANDSHAKE_CMD_READY);
57
if (!hdr)
58
goto out_free;
59
60
if (nla_put_u32(msg, HANDSHAKE_A_ACCEPT_HANDLER_CLASS,
61
proto->hp_handler_class) < 0) {
62
genlmsg_cancel(msg, hdr);
63
goto out_free;
64
}
65
66
genlmsg_end(msg, hdr);
67
return genlmsg_multicast_netns(&handshake_nl_family, net, msg,
68
0, proto->hp_handler_class, flags);
69
70
out_free:
71
nlmsg_free(msg);
72
return -EMSGSIZE;
73
}
74
75
/**
76
* handshake_genl_put - Create a generic netlink message header
77
* @msg: buffer in which to create the header
78
* @info: generic netlink message context
79
*
80
* Returns a ready-to-use header, or NULL.
81
*/
82
struct nlmsghdr *handshake_genl_put(struct sk_buff *msg,
83
struct genl_info *info)
84
{
85
return genlmsg_put(msg, info->snd_portid, info->snd_seq,
86
&handshake_nl_family, 0, info->genlhdr->cmd);
87
}
88
EXPORT_SYMBOL(handshake_genl_put);
89
90
int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info)
91
{
92
struct net *net = sock_net(skb->sk);
93
struct handshake_net *hn = handshake_pernet(net);
94
struct handshake_req *req = NULL;
95
struct socket *sock;
96
int class, err;
97
98
err = -EOPNOTSUPP;
99
if (!hn)
100
goto out_status;
101
102
err = -EINVAL;
103
if (GENL_REQ_ATTR_CHECK(info, HANDSHAKE_A_ACCEPT_HANDLER_CLASS))
104
goto out_status;
105
class = nla_get_u32(info->attrs[HANDSHAKE_A_ACCEPT_HANDLER_CLASS]);
106
107
err = -EAGAIN;
108
req = handshake_req_next(hn, class);
109
if (req) {
110
sock = req->hr_sk->sk_socket;
111
112
FD_PREPARE(fdf, O_CLOEXEC, sock->file);
113
if (fdf.err) {
114
err = fdf.err;
115
goto out_complete;
116
}
117
118
get_file(sock->file); /* FD_PREPARE() consumes a reference. */
119
err = req->hr_proto->hp_accept(req, info, fd_prepare_fd(fdf));
120
if (err)
121
goto out_complete; /* Automatic cleanup handles fput */
122
123
trace_handshake_cmd_accept(net, req, req->hr_sk, fd_prepare_fd(fdf));
124
fd_publish(fdf);
125
return 0;
126
}
127
128
out_complete:
129
if (req)
130
handshake_complete(req, -EIO, NULL);
131
out_status:
132
trace_handshake_cmd_accept_err(net, req, NULL, err);
133
return err;
134
}
135
136
int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info)
137
{
138
struct net *net = sock_net(skb->sk);
139
struct handshake_req *req;
140
struct socket *sock;
141
int fd, status, err;
142
143
if (GENL_REQ_ATTR_CHECK(info, HANDSHAKE_A_DONE_SOCKFD))
144
return -EINVAL;
145
fd = nla_get_s32(info->attrs[HANDSHAKE_A_DONE_SOCKFD]);
146
147
sock = sockfd_lookup(fd, &err);
148
if (!sock)
149
return err;
150
151
req = handshake_req_hash_lookup(sock->sk);
152
if (!req) {
153
err = -EBUSY;
154
trace_handshake_cmd_done_err(net, req, sock->sk, err);
155
sockfd_put(sock);
156
return err;
157
}
158
159
trace_handshake_cmd_done(net, req, sock->sk, fd);
160
161
status = -EIO;
162
if (info->attrs[HANDSHAKE_A_DONE_STATUS])
163
status = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);
164
165
handshake_complete(req, status, info);
166
sockfd_put(sock);
167
return 0;
168
}
169
170
static unsigned int handshake_net_id;
171
172
static int __net_init handshake_net_init(struct net *net)
173
{
174
struct handshake_net *hn = net_generic(net, handshake_net_id);
175
unsigned long tmp;
176
struct sysinfo si;
177
178
/*
179
* Arbitrary limit to prevent handshakes that do not make
180
* progress from clogging up the system. The cap scales up
181
* with the amount of physical memory on the system.
182
*/
183
si_meminfo(&si);
184
tmp = si.totalram / (25 * si.mem_unit);
185
hn->hn_pending_max = clamp(tmp, 3UL, 50UL);
186
187
spin_lock_init(&hn->hn_lock);
188
hn->hn_pending = 0;
189
hn->hn_flags = 0;
190
INIT_LIST_HEAD(&hn->hn_requests);
191
return 0;
192
}
193
194
static void __net_exit handshake_net_exit(struct net *net)
195
{
196
struct handshake_net *hn = net_generic(net, handshake_net_id);
197
struct handshake_req *req;
198
LIST_HEAD(requests);
199
200
/*
201
* Drain the net's pending list. Requests that have been
202
* accepted and are in progress will be destroyed when
203
* the socket is closed.
204
*/
205
spin_lock(&hn->hn_lock);
206
set_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags);
207
list_splice_init(&requests, &hn->hn_requests);
208
spin_unlock(&hn->hn_lock);
209
210
while (!list_empty(&requests)) {
211
req = list_first_entry(&requests, struct handshake_req, hr_list);
212
list_del(&req->hr_list);
213
214
/*
215
* Requests on this list have not yet been
216
* accepted, so they do not have an fd to put.
217
*/
218
219
handshake_complete(req, -ETIMEDOUT, NULL);
220
}
221
}
222
223
static struct pernet_operations handshake_genl_net_ops = {
224
.init = handshake_net_init,
225
.exit = handshake_net_exit,
226
.id = &handshake_net_id,
227
.size = sizeof(struct handshake_net),
228
};
229
230
/**
231
* handshake_pernet - Get the handshake private per-net structure
232
* @net: network namespace
233
*
234
* Returns a pointer to the net's private per-net structure for the
235
* handshake module, or NULL if handshake_init() failed.
236
*/
237
struct handshake_net *handshake_pernet(struct net *net)
238
{
239
return handshake_net_id ?
240
net_generic(net, handshake_net_id) : NULL;
241
}
242
EXPORT_SYMBOL_IF_KUNIT(handshake_pernet);
243
244
static int __init handshake_init(void)
245
{
246
int ret;
247
248
ret = handshake_req_hash_init();
249
if (ret) {
250
pr_warn("handshake: hash initialization failed (%d)\n", ret);
251
return ret;
252
}
253
254
ret = genl_register_family(&handshake_nl_family);
255
if (ret) {
256
pr_warn("handshake: netlink registration failed (%d)\n", ret);
257
handshake_req_hash_destroy();
258
return ret;
259
}
260
261
/*
262
* ORDER: register_pernet_subsys must be done last.
263
*
264
* If initialization does not make it past pernet_subsys
265
* registration, then handshake_net_id will remain 0. That
266
* shunts the handshake consumer API to return ENOTSUPP
267
* to prevent it from dereferencing something that hasn't
268
* been allocated.
269
*/
270
ret = register_pernet_subsys(&handshake_genl_net_ops);
271
if (ret) {
272
pr_warn("handshake: pernet registration failed (%d)\n", ret);
273
genl_unregister_family(&handshake_nl_family);
274
handshake_req_hash_destroy();
275
}
276
277
return ret;
278
}
279
280
static void __exit handshake_exit(void)
281
{
282
unregister_pernet_subsys(&handshake_genl_net_ops);
283
handshake_net_id = 0;
284
285
handshake_req_hash_destroy();
286
genl_unregister_family(&handshake_nl_family);
287
}
288
289
module_init(handshake_init);
290
module_exit(handshake_exit);
291
292