]>
Commit | Line | Data |
---|---|---|
bc49d816 JK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Management Component Transport Protocol (MCTP) | |
4 | * | |
5 | * Copyright (c) 2021 Code Construct | |
6 | * Copyright (c) 2021 Google | |
7 | */ | |
8 | ||
583be982 | 9 | #include <linux/if_arp.h> |
8f601a1e JK |
10 | #include <linux/net.h> |
11 | #include <linux/mctp.h> | |
bc49d816 | 12 | #include <linux/module.h> |
8f601a1e JK |
13 | #include <linux/socket.h> |
14 | ||
583be982 JK |
15 | #include <net/mctp.h> |
16 | #include <net/mctpdevice.h> | |
8f601a1e JK |
17 | #include <net/sock.h> |
18 | ||
583be982 JK |
19 | /* socket implementation */ |
20 | ||
8f601a1e JK |
21 | static int mctp_release(struct socket *sock) |
22 | { | |
23 | struct sock *sk = sock->sk; | |
24 | ||
25 | if (sk) { | |
26 | sock->sk = NULL; | |
27 | sk->sk_prot->close(sk, 0); | |
28 | } | |
29 | ||
30 | return 0; | |
31 | } | |
32 | ||
33 | static int mctp_bind(struct socket *sock, struct sockaddr *addr, int addrlen) | |
34 | { | |
833ef3b9 JK |
35 | struct sock *sk = sock->sk; |
36 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); | |
37 | struct sockaddr_mctp *smctp; | |
38 | int rc; | |
39 | ||
40 | if (addrlen < sizeof(*smctp)) | |
41 | return -EINVAL; | |
42 | ||
43 | if (addr->sa_family != AF_MCTP) | |
44 | return -EAFNOSUPPORT; | |
45 | ||
46 | if (!capable(CAP_NET_BIND_SERVICE)) | |
47 | return -EACCES; | |
48 | ||
49 | /* it's a valid sockaddr for MCTP, cast and do protocol checks */ | |
50 | smctp = (struct sockaddr_mctp *)addr; | |
51 | ||
52 | lock_sock(sk); | |
53 | ||
54 | /* TODO: allow rebind */ | |
55 | if (sk_hashed(sk)) { | |
56 | rc = -EADDRINUSE; | |
57 | goto out_release; | |
58 | } | |
59 | msk->bind_net = smctp->smctp_network; | |
60 | msk->bind_addr = smctp->smctp_addr.s_addr; | |
61 | msk->bind_type = smctp->smctp_type & 0x7f; /* ignore the IC bit */ | |
62 | ||
63 | rc = sk->sk_prot->hash(sk); | |
64 | ||
65 | out_release: | |
66 | release_sock(sk); | |
67 | ||
68 | return rc; | |
8f601a1e JK |
69 | } |
70 | ||
71 | static int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |
72 | { | |
833ef3b9 JK |
73 | DECLARE_SOCKADDR(struct sockaddr_mctp *, addr, msg->msg_name); |
74 | const int hlen = MCTP_HEADER_MAXLEN + sizeof(struct mctp_hdr); | |
75 | int rc, addrlen = msg->msg_namelen; | |
76 | struct sock *sk = sock->sk; | |
77 | struct mctp_skb_cb *cb; | |
78 | struct mctp_route *rt; | |
79 | struct sk_buff *skb; | |
80 | ||
81 | if (addr) { | |
82 | if (addrlen < sizeof(struct sockaddr_mctp)) | |
83 | return -EINVAL; | |
84 | if (addr->smctp_family != AF_MCTP) | |
85 | return -EINVAL; | |
86 | if (addr->smctp_tag & ~(MCTP_TAG_MASK | MCTP_TAG_OWNER)) | |
87 | return -EINVAL; | |
88 | ||
89 | } else { | |
90 | /* TODO: connect()ed sockets */ | |
91 | return -EDESTADDRREQ; | |
92 | } | |
93 | ||
94 | if (!capable(CAP_NET_RAW)) | |
95 | return -EACCES; | |
96 | ||
97 | rt = mctp_route_lookup(sock_net(sk), addr->smctp_network, | |
98 | addr->smctp_addr.s_addr); | |
99 | if (!rt) | |
100 | return -EHOSTUNREACH; | |
101 | ||
102 | skb = sock_alloc_send_skb(sk, hlen + 1 + len, | |
103 | msg->msg_flags & MSG_DONTWAIT, &rc); | |
104 | if (!skb) | |
105 | return rc; | |
106 | ||
107 | skb_reserve(skb, hlen); | |
108 | ||
109 | /* set type as fist byte in payload */ | |
110 | *(u8 *)skb_put(skb, 1) = addr->smctp_type; | |
111 | ||
112 | rc = memcpy_from_msg((void *)skb_put(skb, len), msg, len); | |
113 | if (rc < 0) { | |
114 | kfree_skb(skb); | |
115 | return rc; | |
116 | } | |
117 | ||
118 | /* set up cb */ | |
119 | cb = __mctp_cb(skb); | |
120 | cb->net = addr->smctp_network; | |
121 | ||
122 | rc = mctp_local_output(sk, rt, skb, addr->smctp_addr.s_addr, | |
123 | addr->smctp_tag); | |
124 | ||
125 | return rc ? : len; | |
8f601a1e JK |
126 | } |
127 | ||
128 | static int mctp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, | |
129 | int flags) | |
130 | { | |
833ef3b9 JK |
131 | DECLARE_SOCKADDR(struct sockaddr_mctp *, addr, msg->msg_name); |
132 | struct sock *sk = sock->sk; | |
133 | struct sk_buff *skb; | |
134 | size_t msglen; | |
135 | u8 type; | |
136 | int rc; | |
137 | ||
138 | if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK)) | |
139 | return -EOPNOTSUPP; | |
140 | ||
141 | skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &rc); | |
142 | if (!skb) | |
143 | return rc; | |
144 | ||
145 | if (!skb->len) { | |
146 | rc = 0; | |
147 | goto out_free; | |
148 | } | |
149 | ||
150 | /* extract message type, remove from data */ | |
151 | type = *((u8 *)skb->data); | |
152 | msglen = skb->len - 1; | |
153 | ||
154 | if (len < msglen) | |
155 | msg->msg_flags |= MSG_TRUNC; | |
156 | else | |
157 | len = msglen; | |
158 | ||
159 | rc = skb_copy_datagram_msg(skb, 1, msg, len); | |
160 | if (rc < 0) | |
161 | goto out_free; | |
162 | ||
163 | sock_recv_ts_and_drops(msg, sk, skb); | |
164 | ||
165 | if (addr) { | |
166 | struct mctp_skb_cb *cb = mctp_cb(skb); | |
167 | /* TODO: expand mctp_skb_cb for header fields? */ | |
168 | struct mctp_hdr *hdr = mctp_hdr(skb); | |
169 | ||
170 | hdr = mctp_hdr(skb); | |
171 | addr = msg->msg_name; | |
172 | addr->smctp_family = AF_MCTP; | |
173 | addr->smctp_network = cb->net; | |
174 | addr->smctp_addr.s_addr = hdr->src; | |
175 | addr->smctp_type = type; | |
176 | addr->smctp_tag = hdr->flags_seq_tag & | |
177 | (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO); | |
178 | msg->msg_namelen = sizeof(*addr); | |
179 | } | |
180 | ||
181 | rc = len; | |
182 | ||
183 | if (flags & MSG_TRUNC) | |
184 | rc = msglen; | |
185 | ||
186 | out_free: | |
187 | skb_free_datagram(sk, skb); | |
188 | return rc; | |
8f601a1e JK |
189 | } |
190 | ||
191 | static int mctp_setsockopt(struct socket *sock, int level, int optname, | |
192 | sockptr_t optval, unsigned int optlen) | |
193 | { | |
194 | return -EINVAL; | |
195 | } | |
196 | ||
197 | static int mctp_getsockopt(struct socket *sock, int level, int optname, | |
198 | char __user *optval, int __user *optlen) | |
199 | { | |
200 | return -EINVAL; | |
201 | } | |
202 | ||
203 | static const struct proto_ops mctp_dgram_ops = { | |
204 | .family = PF_MCTP, | |
205 | .release = mctp_release, | |
206 | .bind = mctp_bind, | |
207 | .connect = sock_no_connect, | |
208 | .socketpair = sock_no_socketpair, | |
209 | .accept = sock_no_accept, | |
210 | .getname = sock_no_getname, | |
211 | .poll = datagram_poll, | |
212 | .ioctl = sock_no_ioctl, | |
213 | .gettstamp = sock_gettstamp, | |
214 | .listen = sock_no_listen, | |
215 | .shutdown = sock_no_shutdown, | |
216 | .setsockopt = mctp_setsockopt, | |
217 | .getsockopt = mctp_getsockopt, | |
218 | .sendmsg = mctp_sendmsg, | |
219 | .recvmsg = mctp_recvmsg, | |
220 | .mmap = sock_no_mmap, | |
221 | .sendpage = sock_no_sendpage, | |
222 | }; | |
223 | ||
833ef3b9 JK |
224 | static int mctp_sk_init(struct sock *sk) |
225 | { | |
226 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); | |
227 | ||
228 | INIT_HLIST_HEAD(&msk->keys); | |
229 | return 0; | |
230 | } | |
231 | ||
8f601a1e JK |
232 | static void mctp_sk_close(struct sock *sk, long timeout) |
233 | { | |
234 | sk_common_release(sk); | |
235 | } | |
236 | ||
833ef3b9 JK |
237 | static int mctp_sk_hash(struct sock *sk) |
238 | { | |
239 | struct net *net = sock_net(sk); | |
240 | ||
241 | mutex_lock(&net->mctp.bind_lock); | |
242 | sk_add_node_rcu(sk, &net->mctp.binds); | |
243 | mutex_unlock(&net->mctp.bind_lock); | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | static void mctp_sk_unhash(struct sock *sk) | |
249 | { | |
250 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); | |
251 | struct net *net = sock_net(sk); | |
252 | struct mctp_sk_key *key; | |
253 | struct hlist_node *tmp; | |
254 | unsigned long flags; | |
255 | ||
256 | /* remove from any type-based binds */ | |
257 | mutex_lock(&net->mctp.bind_lock); | |
258 | sk_del_node_init_rcu(sk); | |
259 | mutex_unlock(&net->mctp.bind_lock); | |
260 | ||
261 | /* remove tag allocations */ | |
262 | spin_lock_irqsave(&net->mctp.keys_lock, flags); | |
263 | hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { | |
264 | hlist_del_rcu(&key->sklist); | |
265 | hlist_del_rcu(&key->hlist); | |
4a992bbd JK |
266 | |
267 | spin_lock(&key->reasm_lock); | |
268 | if (key->reasm_head) | |
269 | kfree_skb(key->reasm_head); | |
270 | key->reasm_head = NULL; | |
271 | key->reasm_dead = true; | |
272 | spin_unlock(&key->reasm_lock); | |
273 | ||
833ef3b9 JK |
274 | kfree_rcu(key, rcu); |
275 | } | |
276 | spin_unlock_irqrestore(&net->mctp.keys_lock, flags); | |
277 | ||
278 | synchronize_rcu(); | |
279 | } | |
280 | ||
8f601a1e JK |
281 | static struct proto mctp_proto = { |
282 | .name = "MCTP", | |
283 | .owner = THIS_MODULE, | |
284 | .obj_size = sizeof(struct mctp_sock), | |
833ef3b9 | 285 | .init = mctp_sk_init, |
8f601a1e | 286 | .close = mctp_sk_close, |
833ef3b9 JK |
287 | .hash = mctp_sk_hash, |
288 | .unhash = mctp_sk_unhash, | |
8f601a1e JK |
289 | }; |
290 | ||
291 | static int mctp_pf_create(struct net *net, struct socket *sock, | |
292 | int protocol, int kern) | |
293 | { | |
294 | const struct proto_ops *ops; | |
295 | struct proto *proto; | |
296 | struct sock *sk; | |
297 | int rc; | |
298 | ||
299 | if (protocol) | |
300 | return -EPROTONOSUPPORT; | |
301 | ||
302 | /* only datagram sockets are supported */ | |
303 | if (sock->type != SOCK_DGRAM) | |
304 | return -ESOCKTNOSUPPORT; | |
305 | ||
306 | proto = &mctp_proto; | |
307 | ops = &mctp_dgram_ops; | |
308 | ||
309 | sock->state = SS_UNCONNECTED; | |
310 | sock->ops = ops; | |
311 | ||
312 | sk = sk_alloc(net, PF_MCTP, GFP_KERNEL, proto, kern); | |
313 | if (!sk) | |
314 | return -ENOMEM; | |
315 | ||
316 | sock_init_data(sock, sk); | |
317 | ||
318 | rc = 0; | |
319 | if (sk->sk_prot->init) | |
320 | rc = sk->sk_prot->init(sk); | |
321 | ||
322 | if (rc) | |
323 | goto err_sk_put; | |
324 | ||
325 | return 0; | |
326 | ||
327 | err_sk_put: | |
328 | sock_orphan(sk); | |
329 | sock_put(sk); | |
330 | return rc; | |
331 | } | |
332 | ||
333 | static struct net_proto_family mctp_pf = { | |
334 | .family = PF_MCTP, | |
335 | .create = mctp_pf_create, | |
336 | .owner = THIS_MODULE, | |
337 | }; | |
338 | ||
339 | static __init int mctp_init(void) | |
340 | { | |
341 | int rc; | |
342 | ||
833ef3b9 JK |
343 | /* ensure our uapi tag definitions match the header format */ |
344 | BUILD_BUG_ON(MCTP_TAG_OWNER != MCTP_HDR_FLAG_TO); | |
345 | BUILD_BUG_ON(MCTP_TAG_MASK != MCTP_HDR_TAG_MASK); | |
346 | ||
8f601a1e JK |
347 | pr_info("mctp: management component transport protocol core\n"); |
348 | ||
349 | rc = sock_register(&mctp_pf); | |
350 | if (rc) | |
351 | return rc; | |
352 | ||
353 | rc = proto_register(&mctp_proto, 0); | |
354 | if (rc) | |
355 | goto err_unreg_sock; | |
356 | ||
889b7da2 JK |
357 | rc = mctp_routes_init(); |
358 | if (rc) | |
359 | goto err_unreg_proto; | |
360 | ||
4d8b9319 MJ |
361 | rc = mctp_neigh_init(); |
362 | if (rc) | |
363 | goto err_unreg_proto; | |
364 | ||
583be982 JK |
365 | mctp_device_init(); |
366 | ||
8f601a1e JK |
367 | return 0; |
368 | ||
889b7da2 JK |
369 | err_unreg_proto: |
370 | proto_unregister(&mctp_proto); | |
8f601a1e JK |
371 | err_unreg_sock: |
372 | sock_unregister(PF_MCTP); | |
373 | ||
374 | return rc; | |
375 | } | |
376 | ||
377 | static __exit void mctp_exit(void) | |
378 | { | |
583be982 | 379 | mctp_device_exit(); |
4d8b9319 | 380 | mctp_neigh_exit(); |
889b7da2 | 381 | mctp_routes_exit(); |
8f601a1e JK |
382 | proto_unregister(&mctp_proto); |
383 | sock_unregister(PF_MCTP); | |
384 | } | |
385 | ||
386 | module_init(mctp_init); | |
387 | module_exit(mctp_exit); | |
bc49d816 JK |
388 | |
389 | MODULE_DESCRIPTION("MCTP core"); | |
390 | MODULE_LICENSE("GPL v2"); | |
391 | MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); | |
8f601a1e JK |
392 | |
393 | MODULE_ALIAS_NETPROTO(PF_MCTP); |