| .. | .. |
|---|
| 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); |
|---|
| .. | .. |
|---|
| 263 | 260 | struct inet_connection_sock *icsk = inet_csk(sk); |
|---|
| 264 | 261 | struct inet_sock *inet = inet_sk(sk); |
|---|
| 265 | 262 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 266 | | - int err = 0; |
|---|
| 267 | 263 | const int old_state = sk->sk_state; |
|---|
| 268 | 264 | |
|---|
| 269 | 265 | if (old_state != DCCP_CLOSED) |
|---|
| .. | .. |
|---|
| 307 | 303 | WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); |
|---|
| 308 | 304 | |
|---|
| 309 | 305 | sk->sk_error_report(sk); |
|---|
| 310 | | - return err; |
|---|
| 306 | + return 0; |
|---|
| 311 | 307 | } |
|---|
| 312 | 308 | |
|---|
| 313 | 309 | EXPORT_SYMBOL_GPL(dccp_disconnect); |
|---|
| .. | .. |
|---|
| 379 | 375 | goto out; |
|---|
| 380 | 376 | |
|---|
| 381 | 377 | switch (cmd) { |
|---|
| 378 | + case SIOCOUTQ: { |
|---|
| 379 | + int amount = sk_wmem_alloc_get(sk); |
|---|
| 380 | + /* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and |
|---|
| 381 | + * always 0, comparably to UDP. |
|---|
| 382 | + */ |
|---|
| 383 | + |
|---|
| 384 | + rc = put_user(amount, (int __user *)arg); |
|---|
| 385 | + } |
|---|
| 386 | + break; |
|---|
| 382 | 387 | case SIOCINQ: { |
|---|
| 383 | 388 | struct sk_buff *skb; |
|---|
| 384 | 389 | unsigned long amount = 0; |
|---|
| .. | .. |
|---|
| 406 | 411 | EXPORT_SYMBOL_GPL(dccp_ioctl); |
|---|
| 407 | 412 | |
|---|
| 408 | 413 | static int dccp_setsockopt_service(struct sock *sk, const __be32 service, |
|---|
| 409 | | - char __user *optval, unsigned int optlen) |
|---|
| 414 | + sockptr_t optval, unsigned int optlen) |
|---|
| 410 | 415 | { |
|---|
| 411 | 416 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 412 | 417 | struct dccp_service_list *sl = NULL; |
|---|
| .. | .. |
|---|
| 421 | 426 | return -ENOMEM; |
|---|
| 422 | 427 | |
|---|
| 423 | 428 | sl->dccpsl_nr = optlen / sizeof(u32) - 1; |
|---|
| 424 | | - if (copy_from_user(sl->dccpsl_list, |
|---|
| 425 | | - optval + sizeof(service), |
|---|
| 426 | | - optlen - sizeof(service)) || |
|---|
| 429 | + if (copy_from_sockptr_offset(sl->dccpsl_list, optval, |
|---|
| 430 | + sizeof(service), optlen - sizeof(service)) || |
|---|
| 427 | 431 | dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) { |
|---|
| 428 | 432 | kfree(sl); |
|---|
| 429 | 433 | return -EFAULT; |
|---|
| .. | .. |
|---|
| 477 | 481 | } |
|---|
| 478 | 482 | |
|---|
| 479 | 483 | static int dccp_setsockopt_ccid(struct sock *sk, int type, |
|---|
| 480 | | - char __user *optval, unsigned int optlen) |
|---|
| 484 | + sockptr_t optval, unsigned int optlen) |
|---|
| 481 | 485 | { |
|---|
| 482 | 486 | u8 *val; |
|---|
| 483 | 487 | int rc = 0; |
|---|
| .. | .. |
|---|
| 485 | 489 | if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) |
|---|
| 486 | 490 | return -EINVAL; |
|---|
| 487 | 491 | |
|---|
| 488 | | - val = memdup_user(optval, optlen); |
|---|
| 492 | + val = memdup_sockptr(optval, optlen); |
|---|
| 489 | 493 | if (IS_ERR(val)) |
|---|
| 490 | 494 | return PTR_ERR(val); |
|---|
| 491 | 495 | |
|---|
| .. | .. |
|---|
| 502 | 506 | } |
|---|
| 503 | 507 | |
|---|
| 504 | 508 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
|---|
| 505 | | - char __user *optval, unsigned int optlen) |
|---|
| 509 | + sockptr_t optval, unsigned int optlen) |
|---|
| 506 | 510 | { |
|---|
| 507 | 511 | struct dccp_sock *dp = dccp_sk(sk); |
|---|
| 508 | 512 | int val, err = 0; |
|---|
| .. | .. |
|---|
| 524 | 528 | if (optlen < (int)sizeof(int)) |
|---|
| 525 | 529 | return -EINVAL; |
|---|
| 526 | 530 | |
|---|
| 527 | | - if (get_user(val, (int __user *)optval)) |
|---|
| 531 | + if (copy_from_sockptr(&val, optval, sizeof(int))) |
|---|
| 528 | 532 | return -EFAULT; |
|---|
| 529 | 533 | |
|---|
| 530 | 534 | if (optname == DCCP_SOCKOPT_SERVICE) |
|---|
| .. | .. |
|---|
| 567 | 571 | return err; |
|---|
| 568 | 572 | } |
|---|
| 569 | 573 | |
|---|
| 570 | | -int dccp_setsockopt(struct sock *sk, int level, int optname, |
|---|
| 571 | | - char __user *optval, unsigned int optlen) |
|---|
| 574 | +int dccp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, |
|---|
| 575 | + unsigned int optlen) |
|---|
| 572 | 576 | { |
|---|
| 573 | 577 | if (level != SOL_DCCP) |
|---|
| 574 | 578 | return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, |
|---|
| .. | .. |
|---|
| 578 | 582 | } |
|---|
| 579 | 583 | |
|---|
| 580 | 584 | 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 | 585 | |
|---|
| 595 | 586 | static int dccp_getsockopt_service(struct sock *sk, int len, |
|---|
| 596 | 587 | __be32 __user *optval, |
|---|
| .. | .. |
|---|
| 700 | 691 | |
|---|
| 701 | 692 | EXPORT_SYMBOL_GPL(dccp_getsockopt); |
|---|
| 702 | 693 | |
|---|
| 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 | 694 | static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) |
|---|
| 717 | 695 | { |
|---|
| 718 | 696 | struct cmsghdr *cmsg; |
|---|
| .. | .. |
|---|
| 769 | 747 | |
|---|
| 770 | 748 | lock_sock(sk); |
|---|
| 771 | 749 | |
|---|
| 772 | | - if (dccp_qpolicy_full(sk)) { |
|---|
| 773 | | - rc = -EAGAIN; |
|---|
| 774 | | - goto out_release; |
|---|
| 775 | | - } |
|---|
| 776 | | - |
|---|
| 777 | 750 | timeo = sock_sndtimeo(sk, noblock); |
|---|
| 778 | 751 | |
|---|
| 779 | 752 | /* |
|---|
| .. | .. |
|---|
| 791 | 764 | lock_sock(sk); |
|---|
| 792 | 765 | if (skb == NULL) |
|---|
| 793 | 766 | goto out_release; |
|---|
| 767 | + |
|---|
| 768 | + if (dccp_qpolicy_full(sk)) { |
|---|
| 769 | + rc = -EAGAIN; |
|---|
| 770 | + goto out_discard; |
|---|
| 771 | + } |
|---|
| 794 | 772 | |
|---|
| 795 | 773 | if (sk->sk_state == DCCP_CLOSED) { |
|---|
| 796 | 774 | rc = -ENOTCONN; |
|---|
| .. | .. |
|---|
| 856 | 834 | case DCCP_PKT_CLOSEREQ: |
|---|
| 857 | 835 | if (!(flags & MSG_PEEK)) |
|---|
| 858 | 836 | dccp_finish_passive_close(sk); |
|---|
| 859 | | - /* fall through */ |
|---|
| 837 | + fallthrough; |
|---|
| 860 | 838 | case DCCP_PKT_RESET: |
|---|
| 861 | 839 | dccp_pr_debug("found fin (%s) ok!\n", |
|---|
| 862 | 840 | dccp_packet_name(dh->dccph_type)); |
|---|
| .. | .. |
|---|
| 948 | 926 | if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) |
|---|
| 949 | 927 | goto out; |
|---|
| 950 | 928 | |
|---|
| 929 | + WRITE_ONCE(sk->sk_max_ack_backlog, backlog); |
|---|
| 951 | 930 | /* Really, if the socket is already in listen state |
|---|
| 952 | 931 | * we can only allow the backlog to be adjusted. |
|---|
| 953 | 932 | */ |
|---|
| .. | .. |
|---|
| 960 | 939 | if (err) |
|---|
| 961 | 940 | goto out; |
|---|
| 962 | 941 | } |
|---|
| 963 | | - sk->sk_max_ack_backlog = backlog; |
|---|
| 964 | 942 | err = 0; |
|---|
| 965 | 943 | |
|---|
| 966 | 944 | out: |
|---|
| .. | .. |
|---|
| 982 | 960 | case DCCP_PARTOPEN: |
|---|
| 983 | 961 | dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk); |
|---|
| 984 | 962 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); |
|---|
| 985 | | - /* fall through */ |
|---|
| 963 | + fallthrough; |
|---|
| 986 | 964 | case DCCP_OPEN: |
|---|
| 987 | 965 | dccp_send_close(sk, 1); |
|---|
| 988 | 966 | |
|---|
| .. | .. |
|---|
| 991 | 969 | next_state = DCCP_ACTIVE_CLOSEREQ; |
|---|
| 992 | 970 | else |
|---|
| 993 | 971 | next_state = DCCP_CLOSING; |
|---|
| 994 | | - /* fall through */ |
|---|
| 972 | + fallthrough; |
|---|
| 995 | 973 | default: |
|---|
| 996 | 974 | dccp_set_state(sk, next_state); |
|---|
| 997 | 975 | } |
|---|
| .. | .. |
|---|
| 1131 | 1109 | static int __init dccp_init(void) |
|---|
| 1132 | 1110 | { |
|---|
| 1133 | 1111 | unsigned long goal; |
|---|
| 1112 | + unsigned long nr_pages = totalram_pages(); |
|---|
| 1134 | 1113 | int ehash_order, bhash_order, i; |
|---|
| 1135 | 1114 | int rc; |
|---|
| 1136 | 1115 | |
|---|
| 1137 | 1116 | BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > |
|---|
| 1138 | | - FIELD_SIZEOF(struct sk_buff, cb)); |
|---|
| 1117 | + sizeof_field(struct sk_buff, cb)); |
|---|
| 1139 | 1118 | rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL); |
|---|
| 1140 | 1119 | if (rc) |
|---|
| 1141 | 1120 | goto out_fail; |
|---|
| 1142 | | - rc = -ENOBUFS; |
|---|
| 1143 | 1121 | inet_hashinfo_init(&dccp_hashinfo); |
|---|
| 1122 | + rc = inet_hashinfo2_init_mod(&dccp_hashinfo); |
|---|
| 1123 | + if (rc) |
|---|
| 1124 | + goto out_free_percpu; |
|---|
| 1125 | + rc = -ENOBUFS; |
|---|
| 1144 | 1126 | dccp_hashinfo.bind_bucket_cachep = |
|---|
| 1145 | 1127 | kmem_cache_create("dccp_bind_bucket", |
|---|
| 1146 | 1128 | sizeof(struct inet_bind_bucket), 0, |
|---|
| 1147 | 1129 | SLAB_HWCACHE_ALIGN, NULL); |
|---|
| 1148 | 1130 | if (!dccp_hashinfo.bind_bucket_cachep) |
|---|
| 1149 | | - goto out_free_percpu; |
|---|
| 1131 | + goto out_free_hashinfo2; |
|---|
| 1150 | 1132 | |
|---|
| 1151 | 1133 | /* |
|---|
| 1152 | 1134 | * Size and allocate the main established and bind bucket |
|---|
| .. | .. |
|---|
| 1154 | 1136 | * |
|---|
| 1155 | 1137 | * The methodology is similar to that of the buffer cache. |
|---|
| 1156 | 1138 | */ |
|---|
| 1157 | | - if (totalram_pages >= (128 * 1024)) |
|---|
| 1158 | | - goal = totalram_pages >> (21 - PAGE_SHIFT); |
|---|
| 1139 | + if (nr_pages >= (128 * 1024)) |
|---|
| 1140 | + goal = nr_pages >> (21 - PAGE_SHIFT); |
|---|
| 1159 | 1141 | else |
|---|
| 1160 | | - goal = totalram_pages >> (23 - PAGE_SHIFT); |
|---|
| 1142 | + goal = nr_pages >> (23 - PAGE_SHIFT); |
|---|
| 1161 | 1143 | |
|---|
| 1162 | 1144 | if (thash_entries) |
|---|
| 1163 | 1145 | goal = (thash_entries * |
|---|
| .. | .. |
|---|
| 1242 | 1224 | free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); |
|---|
| 1243 | 1225 | out_free_bind_bucket_cachep: |
|---|
| 1244 | 1226 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
|---|
| 1227 | +out_free_hashinfo2: |
|---|
| 1228 | + inet_hashinfo2_free_mod(&dccp_hashinfo); |
|---|
| 1245 | 1229 | out_free_percpu: |
|---|
| 1246 | 1230 | percpu_counter_destroy(&dccp_orphan_count); |
|---|
| 1247 | 1231 | out_fail: |
|---|
| .. | .. |
|---|
| 1265 | 1249 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
|---|
| 1266 | 1250 | dccp_ackvec_exit(); |
|---|
| 1267 | 1251 | dccp_sysctl_exit(); |
|---|
| 1252 | + inet_hashinfo2_free_mod(&dccp_hashinfo); |
|---|
| 1268 | 1253 | percpu_counter_destroy(&dccp_orphan_count); |
|---|
| 1269 | 1254 | } |
|---|
| 1270 | 1255 | |
|---|