]>
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 | ||
63ed1aab | 9 | #include <linux/compat.h> |
583be982 | 10 | #include <linux/if_arp.h> |
8f601a1e JK |
11 | #include <linux/net.h> |
12 | #include <linux/mctp.h> | |
bc49d816 | 13 | #include <linux/module.h> |
8f601a1e JK |
14 | #include <linux/socket.h> |
15 | ||
583be982 JK |
16 | #include <net/mctp.h> |
17 | #include <net/mctpdevice.h> | |
8f601a1e JK |
18 | #include <net/sock.h> |
19 | ||
4f9e1ba6 JK |
20 | #define CREATE_TRACE_POINTS |
21 | #include <trace/events/mctp.h> | |
22 | ||
583be982 JK |
23 | /* socket implementation */ |
24 | ||
63ed1aab MJ |
25 | static void mctp_sk_expire_keys(struct timer_list *timer); |
26 | ||
8f601a1e JK |
27 | static int mctp_release(struct socket *sock) |
28 | { | |
29 | struct sock *sk = sock->sk; | |
30 | ||
31 | if (sk) { | |
32 | sock->sk = NULL; | |
33 | sk->sk_prot->close(sk, 0); | |
34 | } | |
35 | ||
36 | return 0; | |
37 | } | |
38 | ||
1e4b50f0 ES |
39 | /* Generic sockaddr checks, padding checks only so far */ |
40 | static bool mctp_sockaddr_is_ok(const struct sockaddr_mctp *addr) | |
41 | { | |
42 | return !addr->__smctp_pad0 && !addr->__smctp_pad1; | |
43 | } | |
44 | ||
e9ea574e ES |
45 | static bool mctp_sockaddr_ext_is_ok(const struct sockaddr_mctp_ext *addr) |
46 | { | |
47 | return !addr->__smctp_pad0[0] && | |
48 | !addr->__smctp_pad0[1] && | |
49 | !addr->__smctp_pad0[2]; | |
50 | } | |
51 | ||
8f601a1e JK |
52 | static int mctp_bind(struct socket *sock, struct sockaddr *addr, int addrlen) |
53 | { | |
833ef3b9 JK |
54 | struct sock *sk = sock->sk; |
55 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); | |
56 | struct sockaddr_mctp *smctp; | |
57 | int rc; | |
58 | ||
59 | if (addrlen < sizeof(*smctp)) | |
60 | return -EINVAL; | |
61 | ||
62 | if (addr->sa_family != AF_MCTP) | |
63 | return -EAFNOSUPPORT; | |
64 | ||
65 | if (!capable(CAP_NET_BIND_SERVICE)) | |
66 | return -EACCES; | |
67 | ||
68 | /* it's a valid sockaddr for MCTP, cast and do protocol checks */ | |
69 | smctp = (struct sockaddr_mctp *)addr; | |
70 | ||
1e4b50f0 ES |
71 | if (!mctp_sockaddr_is_ok(smctp)) |
72 | return -EINVAL; | |
73 | ||
833ef3b9 JK |
74 | lock_sock(sk); |
75 | ||
76 | /* TODO: allow rebind */ | |
77 | if (sk_hashed(sk)) { | |
78 | rc = -EADDRINUSE; | |
79 | goto out_release; | |
80 | } | |
81 | msk->bind_net = smctp->smctp_network; | |
82 | msk->bind_addr = smctp->smctp_addr.s_addr; | |
83 | msk->bind_type = smctp->smctp_type & 0x7f; /* ignore the IC bit */ | |
84 | ||
85 | rc = sk->sk_prot->hash(sk); | |
86 | ||
87 | out_release: | |
88 | release_sock(sk); | |
89 | ||
90 | return rc; | |
8f601a1e JK |
91 | } |
92 | ||
93 | static int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) | |
94 | { | |
833ef3b9 | 95 | DECLARE_SOCKADDR(struct sockaddr_mctp *, addr, msg->msg_name); |
833ef3b9 JK |
96 | int rc, addrlen = msg->msg_namelen; |
97 | struct sock *sk = sock->sk; | |
99ce45d5 | 98 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); |
833ef3b9 JK |
99 | struct mctp_skb_cb *cb; |
100 | struct mctp_route *rt; | |
4a9dda1c MJ |
101 | struct sk_buff *skb = NULL; |
102 | int hlen; | |
833ef3b9 JK |
103 | |
104 | if (addr) { | |
63ed1aab MJ |
105 | const u8 tagbits = MCTP_TAG_MASK | MCTP_TAG_OWNER | |
106 | MCTP_TAG_PREALLOC; | |
107 | ||
833ef3b9 JK |
108 | if (addrlen < sizeof(struct sockaddr_mctp)) |
109 | return -EINVAL; | |
110 | if (addr->smctp_family != AF_MCTP) | |
111 | return -EINVAL; | |
1e4b50f0 ES |
112 | if (!mctp_sockaddr_is_ok(addr)) |
113 | return -EINVAL; | |
63ed1aab MJ |
114 | if (addr->smctp_tag & ~tagbits) |
115 | return -EINVAL; | |
116 | /* can't preallocate a non-owned tag */ | |
117 | if (addr->smctp_tag & MCTP_TAG_PREALLOC && | |
118 | !(addr->smctp_tag & MCTP_TAG_OWNER)) | |
833ef3b9 JK |
119 | return -EINVAL; |
120 | ||
121 | } else { | |
122 | /* TODO: connect()ed sockets */ | |
123 | return -EDESTADDRREQ; | |
124 | } | |
125 | ||
126 | if (!capable(CAP_NET_RAW)) | |
127 | return -EACCES; | |
128 | ||
03f2bbc4 MJ |
129 | if (addr->smctp_network == MCTP_NET_ANY) |
130 | addr->smctp_network = mctp_default_net(sock_net(sk)); | |
131 | ||
4a9dda1c MJ |
132 | /* direct addressing */ |
133 | if (msk->addr_ext && addrlen >= sizeof(struct sockaddr_mctp_ext)) { | |
134 | DECLARE_SOCKADDR(struct sockaddr_mctp_ext *, | |
135 | extaddr, msg->msg_name); | |
136 | struct net_device *dev; | |
137 | ||
138 | rc = -EINVAL; | |
139 | rcu_read_lock(); | |
140 | dev = dev_get_by_index_rcu(sock_net(sk), extaddr->smctp_ifindex); | |
141 | /* check for correct halen */ | |
142 | if (dev && extaddr->smctp_halen == dev->addr_len) { | |
143 | hlen = LL_RESERVED_SPACE(dev) + sizeof(struct mctp_hdr); | |
144 | rc = 0; | |
145 | } | |
146 | rcu_read_unlock(); | |
147 | if (rc) | |
148 | goto err_free; | |
149 | rt = NULL; | |
150 | } else { | |
151 | rt = mctp_route_lookup(sock_net(sk), addr->smctp_network, | |
152 | addr->smctp_addr.s_addr); | |
153 | if (!rt) { | |
154 | rc = -EHOSTUNREACH; | |
155 | goto err_free; | |
156 | } | |
157 | hlen = LL_RESERVED_SPACE(rt->dev->dev) + sizeof(struct mctp_hdr); | |
158 | } | |
159 | ||
833ef3b9 JK |
160 | skb = sock_alloc_send_skb(sk, hlen + 1 + len, |
161 | msg->msg_flags & MSG_DONTWAIT, &rc); | |
162 | if (!skb) | |
163 | return rc; | |
164 | ||
165 | skb_reserve(skb, hlen); | |
166 | ||
167 | /* set type as fist byte in payload */ | |
168 | *(u8 *)skb_put(skb, 1) = addr->smctp_type; | |
169 | ||
170 | rc = memcpy_from_msg((void *)skb_put(skb, len), msg, len); | |
99ce45d5 JK |
171 | if (rc < 0) |
172 | goto err_free; | |
833ef3b9 JK |
173 | |
174 | /* set up cb */ | |
175 | cb = __mctp_cb(skb); | |
176 | cb->net = addr->smctp_network; | |
177 | ||
4a9dda1c MJ |
178 | if (!rt) { |
179 | /* fill extended address in cb */ | |
99ce45d5 JK |
180 | DECLARE_SOCKADDR(struct sockaddr_mctp_ext *, |
181 | extaddr, msg->msg_name); | |
182 | ||
e9ea574e ES |
183 | if (!mctp_sockaddr_ext_is_ok(extaddr) || |
184 | extaddr->smctp_halen > sizeof(cb->haddr)) { | |
99ce45d5 JK |
185 | rc = -EINVAL; |
186 | goto err_free; | |
187 | } | |
188 | ||
189 | cb->ifindex = extaddr->smctp_ifindex; | |
4a9dda1c | 190 | /* smctp_halen is checked above */ |
99ce45d5 JK |
191 | cb->halen = extaddr->smctp_halen; |
192 | memcpy(cb->haddr, extaddr->smctp_haddr, cb->halen); | |
99ce45d5 JK |
193 | } |
194 | ||
833ef3b9 JK |
195 | rc = mctp_local_output(sk, rt, skb, addr->smctp_addr.s_addr, |
196 | addr->smctp_tag); | |
197 | ||
198 | return rc ? : len; | |
99ce45d5 JK |
199 | |
200 | err_free: | |
201 | kfree_skb(skb); | |
202 | return rc; | |
8f601a1e JK |
203 | } |
204 | ||
205 | static int mctp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, | |
206 | int flags) | |
207 | { | |
833ef3b9 JK |
208 | DECLARE_SOCKADDR(struct sockaddr_mctp *, addr, msg->msg_name); |
209 | struct sock *sk = sock->sk; | |
99ce45d5 | 210 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); |
833ef3b9 JK |
211 | struct sk_buff *skb; |
212 | size_t msglen; | |
213 | u8 type; | |
214 | int rc; | |
215 | ||
216 | if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK)) | |
217 | return -EOPNOTSUPP; | |
218 | ||
f4b41f06 | 219 | skb = skb_recv_datagram(sk, flags, &rc); |
833ef3b9 JK |
220 | if (!skb) |
221 | return rc; | |
222 | ||
223 | if (!skb->len) { | |
224 | rc = 0; | |
225 | goto out_free; | |
226 | } | |
227 | ||
228 | /* extract message type, remove from data */ | |
229 | type = *((u8 *)skb->data); | |
230 | msglen = skb->len - 1; | |
231 | ||
232 | if (len < msglen) | |
233 | msg->msg_flags |= MSG_TRUNC; | |
234 | else | |
235 | len = msglen; | |
236 | ||
237 | rc = skb_copy_datagram_msg(skb, 1, msg, len); | |
238 | if (rc < 0) | |
239 | goto out_free; | |
240 | ||
6fd1d51c | 241 | sock_recv_cmsgs(msg, sk, skb); |
833ef3b9 JK |
242 | |
243 | if (addr) { | |
244 | struct mctp_skb_cb *cb = mctp_cb(skb); | |
245 | /* TODO: expand mctp_skb_cb for header fields? */ | |
246 | struct mctp_hdr *hdr = mctp_hdr(skb); | |
247 | ||
833ef3b9 JK |
248 | addr = msg->msg_name; |
249 | addr->smctp_family = AF_MCTP; | |
1e4b50f0 | 250 | addr->__smctp_pad0 = 0; |
833ef3b9 JK |
251 | addr->smctp_network = cb->net; |
252 | addr->smctp_addr.s_addr = hdr->src; | |
253 | addr->smctp_type = type; | |
254 | addr->smctp_tag = hdr->flags_seq_tag & | |
255 | (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO); | |
1e4b50f0 | 256 | addr->__smctp_pad1 = 0; |
833ef3b9 | 257 | msg->msg_namelen = sizeof(*addr); |
99ce45d5 JK |
258 | |
259 | if (msk->addr_ext) { | |
260 | DECLARE_SOCKADDR(struct sockaddr_mctp_ext *, ae, | |
261 | msg->msg_name); | |
262 | msg->msg_namelen = sizeof(*ae); | |
263 | ae->smctp_ifindex = cb->ifindex; | |
264 | ae->smctp_halen = cb->halen; | |
e9ea574e | 265 | memset(ae->__smctp_pad0, 0x0, sizeof(ae->__smctp_pad0)); |
99ce45d5 JK |
266 | memset(ae->smctp_haddr, 0x0, sizeof(ae->smctp_haddr)); |
267 | memcpy(ae->smctp_haddr, cb->haddr, cb->halen); | |
268 | } | |
833ef3b9 JK |
269 | } |
270 | ||
271 | rc = len; | |
272 | ||
273 | if (flags & MSG_TRUNC) | |
274 | rc = msglen; | |
275 | ||
276 | out_free: | |
277 | skb_free_datagram(sk, skb); | |
278 | return rc; | |
8f601a1e JK |
279 | } |
280 | ||
63ed1aab MJ |
281 | /* We're done with the key; invalidate, stop reassembly, and remove from lists. |
282 | */ | |
283 | static void __mctp_key_remove(struct mctp_sk_key *key, struct net *net, | |
284 | unsigned long flags, unsigned long reason) | |
285 | __releases(&key->lock) | |
286 | __must_hold(&net->mctp.keys_lock) | |
287 | { | |
288 | struct sk_buff *skb; | |
289 | ||
290 | trace_mctp_key_release(key, reason); | |
291 | skb = key->reasm_head; | |
292 | key->reasm_head = NULL; | |
293 | key->reasm_dead = true; | |
294 | key->valid = false; | |
295 | mctp_dev_release_key(key->dev, key); | |
296 | spin_unlock_irqrestore(&key->lock, flags); | |
297 | ||
3a732b46 JK |
298 | if (!hlist_unhashed(&key->hlist)) { |
299 | hlist_del_init(&key->hlist); | |
300 | hlist_del_init(&key->sklist); | |
301 | /* unref for the lists */ | |
302 | mctp_key_unref(key); | |
303 | } | |
63ed1aab MJ |
304 | |
305 | kfree_skb(skb); | |
306 | } | |
307 | ||
8f601a1e JK |
308 | static int mctp_setsockopt(struct socket *sock, int level, int optname, |
309 | sockptr_t optval, unsigned int optlen) | |
310 | { | |
99ce45d5 JK |
311 | struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk); |
312 | int val; | |
313 | ||
314 | if (level != SOL_MCTP) | |
315 | return -EINVAL; | |
316 | ||
317 | if (optname == MCTP_OPT_ADDR_EXT) { | |
318 | if (optlen != sizeof(int)) | |
319 | return -EINVAL; | |
320 | if (copy_from_sockptr(&val, optval, sizeof(int))) | |
321 | return -EFAULT; | |
322 | msk->addr_ext = val; | |
323 | return 0; | |
324 | } | |
325 | ||
326 | return -ENOPROTOOPT; | |
8f601a1e JK |
327 | } |
328 | ||
329 | static int mctp_getsockopt(struct socket *sock, int level, int optname, | |
330 | char __user *optval, int __user *optlen) | |
331 | { | |
99ce45d5 JK |
332 | struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk); |
333 | int len, val; | |
334 | ||
335 | if (level != SOL_MCTP) | |
336 | return -EINVAL; | |
337 | ||
338 | if (get_user(len, optlen)) | |
339 | return -EFAULT; | |
340 | ||
341 | if (optname == MCTP_OPT_ADDR_EXT) { | |
342 | if (len != sizeof(int)) | |
343 | return -EINVAL; | |
344 | val = !!msk->addr_ext; | |
345 | if (copy_to_user(optval, &val, len)) | |
346 | return -EFAULT; | |
347 | return 0; | |
348 | } | |
349 | ||
8f601a1e JK |
350 | return -EINVAL; |
351 | } | |
352 | ||
63ed1aab MJ |
353 | static int mctp_ioctl_alloctag(struct mctp_sock *msk, unsigned long arg) |
354 | { | |
355 | struct net *net = sock_net(&msk->sk); | |
356 | struct mctp_sk_key *key = NULL; | |
357 | struct mctp_ioc_tag_ctl ctl; | |
358 | unsigned long flags; | |
359 | u8 tag; | |
360 | ||
361 | if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl))) | |
362 | return -EFAULT; | |
363 | ||
364 | if (ctl.tag) | |
365 | return -EINVAL; | |
366 | ||
367 | if (ctl.flags) | |
368 | return -EINVAL; | |
369 | ||
370 | key = mctp_alloc_local_tag(msk, ctl.peer_addr, MCTP_ADDR_ANY, | |
371 | true, &tag); | |
372 | if (IS_ERR(key)) | |
373 | return PTR_ERR(key); | |
374 | ||
375 | ctl.tag = tag | MCTP_TAG_OWNER | MCTP_TAG_PREALLOC; | |
376 | if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl))) { | |
3a732b46 JK |
377 | unsigned long fl2; |
378 | /* Unwind our key allocation: the keys list lock needs to be | |
379 | * taken before the individual key locks, and we need a valid | |
380 | * flags value (fl2) to pass to __mctp_key_remove, hence the | |
381 | * second spin_lock_irqsave() rather than a plain spin_lock(). | |
382 | */ | |
383 | spin_lock_irqsave(&net->mctp.keys_lock, flags); | |
384 | spin_lock_irqsave(&key->lock, fl2); | |
385 | __mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_DROPPED); | |
63ed1aab | 386 | mctp_key_unref(key); |
3a732b46 | 387 | spin_unlock_irqrestore(&net->mctp.keys_lock, flags); |
63ed1aab MJ |
388 | return -EFAULT; |
389 | } | |
390 | ||
391 | mctp_key_unref(key); | |
392 | return 0; | |
393 | } | |
394 | ||
395 | static int mctp_ioctl_droptag(struct mctp_sock *msk, unsigned long arg) | |
396 | { | |
397 | struct net *net = sock_net(&msk->sk); | |
398 | struct mctp_ioc_tag_ctl ctl; | |
399 | unsigned long flags, fl2; | |
400 | struct mctp_sk_key *key; | |
401 | struct hlist_node *tmp; | |
402 | int rc; | |
403 | u8 tag; | |
404 | ||
405 | if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl))) | |
406 | return -EFAULT; | |
407 | ||
408 | if (ctl.flags) | |
409 | return -EINVAL; | |
410 | ||
411 | /* Must be a local tag, TO set, preallocated */ | |
412 | if ((ctl.tag & ~MCTP_TAG_MASK) != (MCTP_TAG_OWNER | MCTP_TAG_PREALLOC)) | |
413 | return -EINVAL; | |
414 | ||
415 | tag = ctl.tag & MCTP_TAG_MASK; | |
416 | rc = -EINVAL; | |
417 | ||
418 | spin_lock_irqsave(&net->mctp.keys_lock, flags); | |
419 | hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { | |
420 | /* we do an irqsave here, even though we know the irq state, | |
421 | * so we have the flags to pass to __mctp_key_remove | |
422 | */ | |
423 | spin_lock_irqsave(&key->lock, fl2); | |
424 | if (key->manual_alloc && | |
425 | ctl.peer_addr == key->peer_addr && | |
426 | tag == key->tag) { | |
427 | __mctp_key_remove(key, net, fl2, | |
428 | MCTP_TRACE_KEY_DROPPED); | |
429 | rc = 0; | |
430 | } else { | |
431 | spin_unlock_irqrestore(&key->lock, fl2); | |
432 | } | |
433 | } | |
434 | spin_unlock_irqrestore(&net->mctp.keys_lock, flags); | |
435 | ||
436 | return rc; | |
437 | } | |
438 | ||
439 | static int mctp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | |
440 | { | |
441 | struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk); | |
442 | ||
443 | switch (cmd) { | |
444 | case SIOCMCTPALLOCTAG: | |
445 | return mctp_ioctl_alloctag(msk, arg); | |
446 | case SIOCMCTPDROPTAG: | |
447 | return mctp_ioctl_droptag(msk, arg); | |
448 | } | |
449 | ||
450 | return -EINVAL; | |
451 | } | |
452 | ||
453 | #ifdef CONFIG_COMPAT | |
454 | static int mctp_compat_ioctl(struct socket *sock, unsigned int cmd, | |
455 | unsigned long arg) | |
456 | { | |
457 | void __user *argp = compat_ptr(arg); | |
458 | ||
459 | switch (cmd) { | |
460 | /* These have compatible ptr layouts */ | |
461 | case SIOCMCTPALLOCTAG: | |
462 | case SIOCMCTPDROPTAG: | |
463 | return mctp_ioctl(sock, cmd, (unsigned long)argp); | |
464 | } | |
465 | ||
466 | return -ENOIOCTLCMD; | |
467 | } | |
468 | #endif | |
469 | ||
8f601a1e JK |
470 | static const struct proto_ops mctp_dgram_ops = { |
471 | .family = PF_MCTP, | |
472 | .release = mctp_release, | |
473 | .bind = mctp_bind, | |
474 | .connect = sock_no_connect, | |
475 | .socketpair = sock_no_socketpair, | |
476 | .accept = sock_no_accept, | |
477 | .getname = sock_no_getname, | |
478 | .poll = datagram_poll, | |
63ed1aab | 479 | .ioctl = mctp_ioctl, |
8f601a1e JK |
480 | .gettstamp = sock_gettstamp, |
481 | .listen = sock_no_listen, | |
482 | .shutdown = sock_no_shutdown, | |
483 | .setsockopt = mctp_setsockopt, | |
484 | .getsockopt = mctp_getsockopt, | |
485 | .sendmsg = mctp_sendmsg, | |
486 | .recvmsg = mctp_recvmsg, | |
487 | .mmap = sock_no_mmap, | |
488 | .sendpage = sock_no_sendpage, | |
63ed1aab MJ |
489 | #ifdef CONFIG_COMPAT |
490 | .compat_ioctl = mctp_compat_ioctl, | |
491 | #endif | |
8f601a1e JK |
492 | }; |
493 | ||
7b14e15a JK |
494 | static void mctp_sk_expire_keys(struct timer_list *timer) |
495 | { | |
496 | struct mctp_sock *msk = container_of(timer, struct mctp_sock, | |
497 | key_expiry); | |
498 | struct net *net = sock_net(&msk->sk); | |
63ed1aab | 499 | unsigned long next_expiry, flags, fl2; |
7b14e15a JK |
500 | struct mctp_sk_key *key; |
501 | struct hlist_node *tmp; | |
502 | bool next_expiry_valid = false; | |
503 | ||
504 | spin_lock_irqsave(&net->mctp.keys_lock, flags); | |
505 | ||
506 | hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { | |
63ed1aab MJ |
507 | /* don't expire. manual_alloc is immutable, no locking |
508 | * required. | |
509 | */ | |
510 | if (key->manual_alloc) | |
511 | continue; | |
7b14e15a | 512 | |
63ed1aab | 513 | spin_lock_irqsave(&key->lock, fl2); |
7b14e15a | 514 | if (!time_after_eq(key->expiry, jiffies)) { |
63ed1aab MJ |
515 | __mctp_key_remove(key, net, fl2, |
516 | MCTP_TRACE_KEY_TIMEOUT); | |
7b14e15a JK |
517 | continue; |
518 | } | |
519 | ||
520 | if (next_expiry_valid) { | |
521 | if (time_before(key->expiry, next_expiry)) | |
522 | next_expiry = key->expiry; | |
523 | } else { | |
524 | next_expiry = key->expiry; | |
525 | next_expiry_valid = true; | |
526 | } | |
63ed1aab | 527 | spin_unlock_irqrestore(&key->lock, fl2); |
7b14e15a JK |
528 | } |
529 | ||
530 | spin_unlock_irqrestore(&net->mctp.keys_lock, flags); | |
531 | ||
532 | if (next_expiry_valid) | |
533 | mod_timer(timer, next_expiry); | |
534 | } | |
535 | ||
833ef3b9 JK |
536 | static int mctp_sk_init(struct sock *sk) |
537 | { | |
538 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); | |
539 | ||
540 | INIT_HLIST_HEAD(&msk->keys); | |
7b14e15a | 541 | timer_setup(&msk->key_expiry, mctp_sk_expire_keys, 0); |
833ef3b9 JK |
542 | return 0; |
543 | } | |
544 | ||
8f601a1e JK |
545 | static void mctp_sk_close(struct sock *sk, long timeout) |
546 | { | |
547 | sk_common_release(sk); | |
548 | } | |
549 | ||
833ef3b9 JK |
550 | static int mctp_sk_hash(struct sock *sk) |
551 | { | |
552 | struct net *net = sock_net(sk); | |
553 | ||
554 | mutex_lock(&net->mctp.bind_lock); | |
555 | sk_add_node_rcu(sk, &net->mctp.binds); | |
556 | mutex_unlock(&net->mctp.bind_lock); | |
557 | ||
558 | return 0; | |
559 | } | |
560 | ||
561 | static void mctp_sk_unhash(struct sock *sk) | |
562 | { | |
563 | struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); | |
564 | struct net *net = sock_net(sk); | |
63ed1aab | 565 | unsigned long flags, fl2; |
833ef3b9 JK |
566 | struct mctp_sk_key *key; |
567 | struct hlist_node *tmp; | |
833ef3b9 JK |
568 | |
569 | /* remove from any type-based binds */ | |
570 | mutex_lock(&net->mctp.bind_lock); | |
571 | sk_del_node_init_rcu(sk); | |
572 | mutex_unlock(&net->mctp.bind_lock); | |
573 | ||
574 | /* remove tag allocations */ | |
575 | spin_lock_irqsave(&net->mctp.keys_lock, flags); | |
576 | hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) { | |
63ed1aab MJ |
577 | spin_lock_irqsave(&key->lock, fl2); |
578 | __mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_CLOSED); | |
833ef3b9 | 579 | } |
b98e1a04 | 580 | sock_set_flag(sk, SOCK_DEAD); |
833ef3b9 | 581 | spin_unlock_irqrestore(&net->mctp.keys_lock, flags); |
5f41ae6f JK |
582 | |
583 | /* Since there are no more tag allocations (we have removed all of the | |
584 | * keys), stop any pending expiry events. the timer cannot be re-queued | |
585 | * as the sk is no longer observable | |
586 | */ | |
587 | del_timer_sync(&msk->key_expiry); | |
833ef3b9 JK |
588 | } |
589 | ||
60bd1d90 JK |
590 | static void mctp_sk_destruct(struct sock *sk) |
591 | { | |
592 | skb_queue_purge(&sk->sk_receive_queue); | |
593 | } | |
594 | ||
8f601a1e JK |
595 | static struct proto mctp_proto = { |
596 | .name = "MCTP", | |
597 | .owner = THIS_MODULE, | |
598 | .obj_size = sizeof(struct mctp_sock), | |
833ef3b9 | 599 | .init = mctp_sk_init, |
8f601a1e | 600 | .close = mctp_sk_close, |
833ef3b9 JK |
601 | .hash = mctp_sk_hash, |
602 | .unhash = mctp_sk_unhash, | |
8f601a1e JK |
603 | }; |
604 | ||
605 | static int mctp_pf_create(struct net *net, struct socket *sock, | |
606 | int protocol, int kern) | |
607 | { | |
608 | const struct proto_ops *ops; | |
609 | struct proto *proto; | |
610 | struct sock *sk; | |
611 | int rc; | |
612 | ||
613 | if (protocol) | |
614 | return -EPROTONOSUPPORT; | |
615 | ||
616 | /* only datagram sockets are supported */ | |
617 | if (sock->type != SOCK_DGRAM) | |
618 | return -ESOCKTNOSUPPORT; | |
619 | ||
620 | proto = &mctp_proto; | |
621 | ops = &mctp_dgram_ops; | |
622 | ||
623 | sock->state = SS_UNCONNECTED; | |
624 | sock->ops = ops; | |
625 | ||
626 | sk = sk_alloc(net, PF_MCTP, GFP_KERNEL, proto, kern); | |
627 | if (!sk) | |
628 | return -ENOMEM; | |
629 | ||
630 | sock_init_data(sock, sk); | |
60bd1d90 | 631 | sk->sk_destruct = mctp_sk_destruct; |
8f601a1e JK |
632 | |
633 | rc = 0; | |
634 | if (sk->sk_prot->init) | |
635 | rc = sk->sk_prot->init(sk); | |
636 | ||
637 | if (rc) | |
638 | goto err_sk_put; | |
639 | ||
640 | return 0; | |
641 | ||
642 | err_sk_put: | |
643 | sock_orphan(sk); | |
644 | sock_put(sk); | |
645 | return rc; | |
646 | } | |
647 | ||
648 | static struct net_proto_family mctp_pf = { | |
649 | .family = PF_MCTP, | |
650 | .create = mctp_pf_create, | |
651 | .owner = THIS_MODULE, | |
652 | }; | |
653 | ||
654 | static __init int mctp_init(void) | |
655 | { | |
656 | int rc; | |
657 | ||
833ef3b9 JK |
658 | /* ensure our uapi tag definitions match the header format */ |
659 | BUILD_BUG_ON(MCTP_TAG_OWNER != MCTP_HDR_FLAG_TO); | |
660 | BUILD_BUG_ON(MCTP_TAG_MASK != MCTP_HDR_TAG_MASK); | |
661 | ||
8f601a1e JK |
662 | pr_info("mctp: management component transport protocol core\n"); |
663 | ||
664 | rc = sock_register(&mctp_pf); | |
665 | if (rc) | |
666 | return rc; | |
667 | ||
668 | rc = proto_register(&mctp_proto, 0); | |
669 | if (rc) | |
670 | goto err_unreg_sock; | |
671 | ||
889b7da2 JK |
672 | rc = mctp_routes_init(); |
673 | if (rc) | |
674 | goto err_unreg_proto; | |
675 | ||
4d8b9319 MJ |
676 | rc = mctp_neigh_init(); |
677 | if (rc) | |
d4072058 | 678 | goto err_unreg_routes; |
4d8b9319 | 679 | |
583be982 JK |
680 | mctp_device_init(); |
681 | ||
8f601a1e JK |
682 | return 0; |
683 | ||
d4072058 WY |
684 | err_unreg_routes: |
685 | mctp_routes_exit(); | |
889b7da2 JK |
686 | err_unreg_proto: |
687 | proto_unregister(&mctp_proto); | |
8f601a1e JK |
688 | err_unreg_sock: |
689 | sock_unregister(PF_MCTP); | |
690 | ||
691 | return rc; | |
692 | } | |
693 | ||
694 | static __exit void mctp_exit(void) | |
695 | { | |
583be982 | 696 | mctp_device_exit(); |
4d8b9319 | 697 | mctp_neigh_exit(); |
889b7da2 | 698 | mctp_routes_exit(); |
8f601a1e JK |
699 | proto_unregister(&mctp_proto); |
700 | sock_unregister(PF_MCTP); | |
701 | } | |
702 | ||
97f09abf | 703 | subsys_initcall(mctp_init); |
8f601a1e | 704 | module_exit(mctp_exit); |
bc49d816 JK |
705 | |
706 | MODULE_DESCRIPTION("MCTP core"); | |
bc49d816 | 707 | MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); |
8f601a1e JK |
708 | |
709 | MODULE_ALIAS_NETPROTO(PF_MCTP); |