.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * net/dccp/proto.c |
---|
3 | 4 | * |
---|
4 | 5 | * An implementation of the DCCP protocol |
---|
5 | 6 | * Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify it |
---|
8 | | - * under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | 7 | */ |
---|
11 | 8 | |
---|
12 | 9 | #include <linux/dccp.h> |
---|
.. | .. |
---|
104 | 101 | if (inet_csk(sk)->icsk_bind_hash != NULL && |
---|
105 | 102 | !(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) |
---|
106 | 103 | inet_put_port(sk); |
---|
107 | | - /* fall through */ |
---|
| 104 | + fallthrough; |
---|
108 | 105 | default: |
---|
109 | 106 | if (oldstate == DCCP_OPEN) |
---|
110 | 107 | DCCP_DEC_STATS(DCCP_MIB_CURRESTAB); |
---|
.. | .. |
---|
174 | 171 | |
---|
175 | 172 | EXPORT_SYMBOL_GPL(dccp_packet_name); |
---|
176 | 173 | |
---|
177 | | -static void dccp_sk_destruct(struct sock *sk) |
---|
| 174 | +void dccp_destruct_common(struct sock *sk) |
---|
178 | 175 | { |
---|
179 | 176 | struct dccp_sock *dp = dccp_sk(sk); |
---|
180 | 177 | |
---|
181 | 178 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); |
---|
182 | 179 | dp->dccps_hc_tx_ccid = NULL; |
---|
| 180 | +} |
---|
| 181 | +EXPORT_SYMBOL_GPL(dccp_destruct_common); |
---|
| 182 | + |
---|
| 183 | +static void dccp_sk_destruct(struct sock *sk) |
---|
| 184 | +{ |
---|
| 185 | + dccp_destruct_common(sk); |
---|
183 | 186 | inet_sock_destruct(sk); |
---|
184 | 187 | } |
---|
185 | 188 | |
---|
.. | .. |
---|
263 | 266 | struct inet_connection_sock *icsk = inet_csk(sk); |
---|
264 | 267 | struct inet_sock *inet = inet_sk(sk); |
---|
265 | 268 | struct dccp_sock *dp = dccp_sk(sk); |
---|
266 | | - int err = 0; |
---|
267 | 269 | const int old_state = sk->sk_state; |
---|
268 | 270 | |
---|
269 | 271 | if (old_state != DCCP_CLOSED) |
---|
.. | .. |
---|
307 | 309 | WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); |
---|
308 | 310 | |
---|
309 | 311 | sk->sk_error_report(sk); |
---|
310 | | - return err; |
---|
| 312 | + return 0; |
---|
311 | 313 | } |
---|
312 | 314 | |
---|
313 | 315 | EXPORT_SYMBOL_GPL(dccp_disconnect); |
---|
.. | .. |
---|
322 | 324 | __poll_t dccp_poll(struct file *file, struct socket *sock, |
---|
323 | 325 | poll_table *wait) |
---|
324 | 326 | { |
---|
325 | | - __poll_t mask; |
---|
326 | 327 | struct sock *sk = sock->sk; |
---|
| 328 | + __poll_t mask; |
---|
| 329 | + u8 shutdown; |
---|
| 330 | + int state; |
---|
327 | 331 | |
---|
328 | 332 | sock_poll_wait(file, sock, wait); |
---|
329 | | - if (sk->sk_state == DCCP_LISTEN) |
---|
| 333 | + |
---|
| 334 | + state = inet_sk_state_load(sk); |
---|
| 335 | + if (state == DCCP_LISTEN) |
---|
330 | 336 | return inet_csk_listen_poll(sk); |
---|
331 | 337 | |
---|
332 | 338 | /* Socket is not locked. We are protected from async events |
---|
.. | .. |
---|
335 | 341 | */ |
---|
336 | 342 | |
---|
337 | 343 | mask = 0; |
---|
338 | | - if (sk->sk_err) |
---|
| 344 | + if (READ_ONCE(sk->sk_err)) |
---|
339 | 345 | mask = EPOLLERR; |
---|
| 346 | + shutdown = READ_ONCE(sk->sk_shutdown); |
---|
340 | 347 | |
---|
341 | | - if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED) |
---|
| 348 | + if (shutdown == SHUTDOWN_MASK || state == DCCP_CLOSED) |
---|
342 | 349 | mask |= EPOLLHUP; |
---|
343 | | - if (sk->sk_shutdown & RCV_SHUTDOWN) |
---|
| 350 | + if (shutdown & RCV_SHUTDOWN) |
---|
344 | 351 | mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; |
---|
345 | 352 | |
---|
346 | 353 | /* Connected? */ |
---|
347 | | - if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { |
---|
| 354 | + if ((1 << state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { |
---|
348 | 355 | if (atomic_read(&sk->sk_rmem_alloc) > 0) |
---|
349 | 356 | mask |= EPOLLIN | EPOLLRDNORM; |
---|
350 | 357 | |
---|
351 | | - if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { |
---|
| 358 | + if (!(shutdown & SEND_SHUTDOWN)) { |
---|
352 | 359 | if (sk_stream_is_writeable(sk)) { |
---|
353 | 360 | mask |= EPOLLOUT | EPOLLWRNORM; |
---|
354 | 361 | } else { /* send SIGIO later */ |
---|
.. | .. |
---|
366 | 373 | } |
---|
367 | 374 | return mask; |
---|
368 | 375 | } |
---|
369 | | - |
---|
370 | 376 | EXPORT_SYMBOL_GPL(dccp_poll); |
---|
371 | 377 | |
---|
372 | 378 | int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) |
---|
.. | .. |
---|
379 | 385 | goto out; |
---|
380 | 386 | |
---|
381 | 387 | switch (cmd) { |
---|
| 388 | + case SIOCOUTQ: { |
---|
| 389 | + int amount = sk_wmem_alloc_get(sk); |
---|
| 390 | + /* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and |
---|
| 391 | + * always 0, comparably to UDP. |
---|
| 392 | + */ |
---|
| 393 | + |
---|
| 394 | + rc = put_user(amount, (int __user *)arg); |
---|
| 395 | + } |
---|
| 396 | + break; |
---|
382 | 397 | case SIOCINQ: { |
---|
383 | 398 | struct sk_buff *skb; |
---|
384 | 399 | unsigned long amount = 0; |
---|
.. | .. |
---|
406 | 421 | EXPORT_SYMBOL_GPL(dccp_ioctl); |
---|
407 | 422 | |
---|
408 | 423 | static int dccp_setsockopt_service(struct sock *sk, const __be32 service, |
---|
409 | | - char __user *optval, unsigned int optlen) |
---|
| 424 | + sockptr_t optval, unsigned int optlen) |
---|
410 | 425 | { |
---|
411 | 426 | struct dccp_sock *dp = dccp_sk(sk); |
---|
412 | 427 | struct dccp_service_list *sl = NULL; |
---|
.. | .. |
---|
421 | 436 | return -ENOMEM; |
---|
422 | 437 | |
---|
423 | 438 | sl->dccpsl_nr = optlen / sizeof(u32) - 1; |
---|
424 | | - if (copy_from_user(sl->dccpsl_list, |
---|
425 | | - optval + sizeof(service), |
---|
426 | | - optlen - sizeof(service)) || |
---|
| 439 | + if (copy_from_sockptr_offset(sl->dccpsl_list, optval, |
---|
| 440 | + sizeof(service), optlen - sizeof(service)) || |
---|
427 | 441 | dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) { |
---|
428 | 442 | kfree(sl); |
---|
429 | 443 | return -EFAULT; |
---|
.. | .. |
---|
477 | 491 | } |
---|
478 | 492 | |
---|
479 | 493 | static int dccp_setsockopt_ccid(struct sock *sk, int type, |
---|
480 | | - char __user *optval, unsigned int optlen) |
---|
| 494 | + sockptr_t optval, unsigned int optlen) |
---|
481 | 495 | { |
---|
482 | 496 | u8 *val; |
---|
483 | 497 | int rc = 0; |
---|
.. | .. |
---|
485 | 499 | if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) |
---|
486 | 500 | return -EINVAL; |
---|
487 | 501 | |
---|
488 | | - val = memdup_user(optval, optlen); |
---|
| 502 | + val = memdup_sockptr(optval, optlen); |
---|
489 | 503 | if (IS_ERR(val)) |
---|
490 | 504 | return PTR_ERR(val); |
---|
491 | 505 | |
---|
.. | .. |
---|
502 | 516 | } |
---|
503 | 517 | |
---|
504 | 518 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
---|
505 | | - char __user *optval, unsigned int optlen) |
---|
| 519 | + sockptr_t optval, unsigned int optlen) |
---|
506 | 520 | { |
---|
507 | 521 | struct dccp_sock *dp = dccp_sk(sk); |
---|
508 | 522 | int val, err = 0; |
---|
.. | .. |
---|
524 | 538 | if (optlen < (int)sizeof(int)) |
---|
525 | 539 | return -EINVAL; |
---|
526 | 540 | |
---|
527 | | - if (get_user(val, (int __user *)optval)) |
---|
| 541 | + if (copy_from_sockptr(&val, optval, sizeof(int))) |
---|
528 | 542 | return -EFAULT; |
---|
529 | 543 | |
---|
530 | 544 | if (optname == DCCP_SOCKOPT_SERVICE) |
---|
.. | .. |
---|
567 | 581 | return err; |
---|
568 | 582 | } |
---|
569 | 583 | |
---|
570 | | -int dccp_setsockopt(struct sock *sk, int level, int optname, |
---|
571 | | - char __user *optval, unsigned int optlen) |
---|
| 584 | +int dccp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, |
---|
| 585 | + unsigned int optlen) |
---|
572 | 586 | { |
---|
573 | 587 | if (level != SOL_DCCP) |
---|
574 | 588 | return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, |
---|
.. | .. |
---|
578 | 592 | } |
---|
579 | 593 | |
---|
580 | 594 | EXPORT_SYMBOL_GPL(dccp_setsockopt); |
---|
581 | | - |
---|
582 | | -#ifdef CONFIG_COMPAT |
---|
583 | | -int compat_dccp_setsockopt(struct sock *sk, int level, int optname, |
---|
584 | | - char __user *optval, unsigned int optlen) |
---|
585 | | -{ |
---|
586 | | - if (level != SOL_DCCP) |
---|
587 | | - return inet_csk_compat_setsockopt(sk, level, optname, |
---|
588 | | - optval, optlen); |
---|
589 | | - return do_dccp_setsockopt(sk, level, optname, optval, optlen); |
---|
590 | | -} |
---|
591 | | - |
---|
592 | | -EXPORT_SYMBOL_GPL(compat_dccp_setsockopt); |
---|
593 | | -#endif |
---|
594 | 595 | |
---|
595 | 596 | static int dccp_getsockopt_service(struct sock *sk, int len, |
---|
596 | 597 | __be32 __user *optval, |
---|
.. | .. |
---|
642 | 643 | return dccp_getsockopt_service(sk, len, |
---|
643 | 644 | (__be32 __user *)optval, optlen); |
---|
644 | 645 | case DCCP_SOCKOPT_GET_CUR_MPS: |
---|
645 | | - val = dp->dccps_mss_cache; |
---|
| 646 | + val = READ_ONCE(dp->dccps_mss_cache); |
---|
646 | 647 | break; |
---|
647 | 648 | case DCCP_SOCKOPT_AVAILABLE_CCIDS: |
---|
648 | 649 | return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); |
---|
.. | .. |
---|
700 | 701 | |
---|
701 | 702 | EXPORT_SYMBOL_GPL(dccp_getsockopt); |
---|
702 | 703 | |
---|
703 | | -#ifdef CONFIG_COMPAT |
---|
704 | | -int compat_dccp_getsockopt(struct sock *sk, int level, int optname, |
---|
705 | | - char __user *optval, int __user *optlen) |
---|
706 | | -{ |
---|
707 | | - if (level != SOL_DCCP) |
---|
708 | | - return inet_csk_compat_getsockopt(sk, level, optname, |
---|
709 | | - optval, optlen); |
---|
710 | | - return do_dccp_getsockopt(sk, level, optname, optval, optlen); |
---|
711 | | -} |
---|
712 | | - |
---|
713 | | -EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); |
---|
714 | | -#endif |
---|
715 | | - |
---|
716 | 704 | static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) |
---|
717 | 705 | { |
---|
718 | 706 | struct cmsghdr *cmsg; |
---|
.. | .. |
---|
764 | 752 | |
---|
765 | 753 | trace_dccp_probe(sk, len); |
---|
766 | 754 | |
---|
767 | | - if (len > dp->dccps_mss_cache) |
---|
| 755 | + if (len > READ_ONCE(dp->dccps_mss_cache)) |
---|
768 | 756 | return -EMSGSIZE; |
---|
769 | 757 | |
---|
770 | 758 | lock_sock(sk); |
---|
771 | | - |
---|
772 | | - if (dccp_qpolicy_full(sk)) { |
---|
773 | | - rc = -EAGAIN; |
---|
774 | | - goto out_release; |
---|
775 | | - } |
---|
776 | 759 | |
---|
777 | 760 | timeo = sock_sndtimeo(sk, noblock); |
---|
778 | 761 | |
---|
.. | .. |
---|
792 | 775 | if (skb == NULL) |
---|
793 | 776 | goto out_release; |
---|
794 | 777 | |
---|
| 778 | + if (dccp_qpolicy_full(sk)) { |
---|
| 779 | + rc = -EAGAIN; |
---|
| 780 | + goto out_discard; |
---|
| 781 | + } |
---|
| 782 | + |
---|
795 | 783 | if (sk->sk_state == DCCP_CLOSED) { |
---|
796 | 784 | rc = -ENOTCONN; |
---|
| 785 | + goto out_discard; |
---|
| 786 | + } |
---|
| 787 | + |
---|
| 788 | + /* We need to check dccps_mss_cache after socket is locked. */ |
---|
| 789 | + if (len > dp->dccps_mss_cache) { |
---|
| 790 | + rc = -EMSGSIZE; |
---|
797 | 791 | goto out_discard; |
---|
798 | 792 | } |
---|
799 | 793 | |
---|
.. | .. |
---|
856 | 850 | case DCCP_PKT_CLOSEREQ: |
---|
857 | 851 | if (!(flags & MSG_PEEK)) |
---|
858 | 852 | dccp_finish_passive_close(sk); |
---|
859 | | - /* fall through */ |
---|
| 853 | + fallthrough; |
---|
860 | 854 | case DCCP_PKT_RESET: |
---|
861 | 855 | dccp_pr_debug("found fin (%s) ok!\n", |
---|
862 | 856 | dccp_packet_name(dh->dccph_type)); |
---|
.. | .. |
---|
948 | 942 | if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) |
---|
949 | 943 | goto out; |
---|
950 | 944 | |
---|
| 945 | + WRITE_ONCE(sk->sk_max_ack_backlog, backlog); |
---|
951 | 946 | /* Really, if the socket is already in listen state |
---|
952 | 947 | * we can only allow the backlog to be adjusted. |
---|
953 | 948 | */ |
---|
.. | .. |
---|
960 | 955 | if (err) |
---|
961 | 956 | goto out; |
---|
962 | 957 | } |
---|
963 | | - sk->sk_max_ack_backlog = backlog; |
---|
964 | 958 | err = 0; |
---|
965 | 959 | |
---|
966 | 960 | out: |
---|
.. | .. |
---|
982 | 976 | case DCCP_PARTOPEN: |
---|
983 | 977 | dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk); |
---|
984 | 978 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); |
---|
985 | | - /* fall through */ |
---|
| 979 | + fallthrough; |
---|
986 | 980 | case DCCP_OPEN: |
---|
987 | 981 | dccp_send_close(sk, 1); |
---|
988 | 982 | |
---|
.. | .. |
---|
991 | 985 | next_state = DCCP_ACTIVE_CLOSEREQ; |
---|
992 | 986 | else |
---|
993 | 987 | next_state = DCCP_CLOSING; |
---|
994 | | - /* fall through */ |
---|
| 988 | + fallthrough; |
---|
995 | 989 | default: |
---|
996 | 990 | dccp_set_state(sk, next_state); |
---|
997 | 991 | } |
---|
.. | .. |
---|
1131 | 1125 | static int __init dccp_init(void) |
---|
1132 | 1126 | { |
---|
1133 | 1127 | unsigned long goal; |
---|
| 1128 | + unsigned long nr_pages = totalram_pages(); |
---|
1134 | 1129 | int ehash_order, bhash_order, i; |
---|
1135 | 1130 | int rc; |
---|
1136 | 1131 | |
---|
1137 | 1132 | BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > |
---|
1138 | | - FIELD_SIZEOF(struct sk_buff, cb)); |
---|
| 1133 | + sizeof_field(struct sk_buff, cb)); |
---|
1139 | 1134 | rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL); |
---|
1140 | 1135 | if (rc) |
---|
1141 | 1136 | goto out_fail; |
---|
1142 | | - rc = -ENOBUFS; |
---|
1143 | 1137 | inet_hashinfo_init(&dccp_hashinfo); |
---|
| 1138 | + rc = inet_hashinfo2_init_mod(&dccp_hashinfo); |
---|
| 1139 | + if (rc) |
---|
| 1140 | + goto out_free_percpu; |
---|
| 1141 | + rc = -ENOBUFS; |
---|
1144 | 1142 | dccp_hashinfo.bind_bucket_cachep = |
---|
1145 | 1143 | kmem_cache_create("dccp_bind_bucket", |
---|
1146 | 1144 | sizeof(struct inet_bind_bucket), 0, |
---|
1147 | 1145 | SLAB_HWCACHE_ALIGN, NULL); |
---|
1148 | 1146 | if (!dccp_hashinfo.bind_bucket_cachep) |
---|
1149 | | - goto out_free_percpu; |
---|
| 1147 | + goto out_free_hashinfo2; |
---|
1150 | 1148 | |
---|
1151 | 1149 | /* |
---|
1152 | 1150 | * Size and allocate the main established and bind bucket |
---|
.. | .. |
---|
1154 | 1152 | * |
---|
1155 | 1153 | * The methodology is similar to that of the buffer cache. |
---|
1156 | 1154 | */ |
---|
1157 | | - if (totalram_pages >= (128 * 1024)) |
---|
1158 | | - goal = totalram_pages >> (21 - PAGE_SHIFT); |
---|
| 1155 | + if (nr_pages >= (128 * 1024)) |
---|
| 1156 | + goal = nr_pages >> (21 - PAGE_SHIFT); |
---|
1159 | 1157 | else |
---|
1160 | | - goal = totalram_pages >> (23 - PAGE_SHIFT); |
---|
| 1158 | + goal = nr_pages >> (23 - PAGE_SHIFT); |
---|
1161 | 1159 | |
---|
1162 | 1160 | if (thash_entries) |
---|
1163 | 1161 | goal = (thash_entries * |
---|
.. | .. |
---|
1242 | 1240 | free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); |
---|
1243 | 1241 | out_free_bind_bucket_cachep: |
---|
1244 | 1242 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
---|
| 1243 | +out_free_hashinfo2: |
---|
| 1244 | + inet_hashinfo2_free_mod(&dccp_hashinfo); |
---|
1245 | 1245 | out_free_percpu: |
---|
1246 | 1246 | percpu_counter_destroy(&dccp_orphan_count); |
---|
1247 | 1247 | out_fail: |
---|
.. | .. |
---|
1265 | 1265 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
---|
1266 | 1266 | dccp_ackvec_exit(); |
---|
1267 | 1267 | dccp_sysctl_exit(); |
---|
| 1268 | + inet_hashinfo2_free_mod(&dccp_hashinfo); |
---|
1268 | 1269 | percpu_counter_destroy(&dccp_orphan_count); |
---|
1269 | 1270 | } |
---|
1270 | 1271 | |
---|