| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * DCCP over IPv6 |
|---|
| 3 | 4 | * Linux INET6 implementation |
|---|
| .. | .. |
|---|
| 5 | 6 | * Based on net/dccp6/ipv6.c |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Arnaldo Carvalho de Melo <acme@ghostprotocols.net> |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or |
|---|
| 10 | | - * modify it under the terms of the GNU General Public License |
|---|
| 11 | | - * as published by the Free Software Foundation; either version |
|---|
| 12 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 13 | 9 | */ |
|---|
| 14 | 10 | |
|---|
| 15 | 11 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 68 | 64 | |
|---|
| 69 | 65 | } |
|---|
| 70 | 66 | |
|---|
| 71 | | -static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
|---|
| 67 | +static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
|---|
| 72 | 68 | u8 type, u8 code, int offset, __be32 info) |
|---|
| 73 | 69 | { |
|---|
| 74 | | - const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; |
|---|
| 70 | + const struct ipv6hdr *hdr; |
|---|
| 75 | 71 | const struct dccp_hdr *dh; |
|---|
| 76 | 72 | struct dccp_sock *dp; |
|---|
| 77 | 73 | struct ipv6_pinfo *np; |
|---|
| .. | .. |
|---|
| 80 | 76 | __u64 seq; |
|---|
| 81 | 77 | struct net *net = dev_net(skb->dev); |
|---|
| 82 | 78 | |
|---|
| 83 | | - /* Only need dccph_dport & dccph_sport which are the first |
|---|
| 84 | | - * 4 bytes in dccp header. |
|---|
| 85 | | - * Our caller (icmpv6_notify()) already pulled 8 bytes for us. |
|---|
| 86 | | - */ |
|---|
| 87 | | - BUILD_BUG_ON(offsetofend(struct dccp_hdr, dccph_sport) > 8); |
|---|
| 88 | | - BUILD_BUG_ON(offsetofend(struct dccp_hdr, dccph_dport) > 8); |
|---|
| 79 | + if (!pskb_may_pull(skb, offset + sizeof(*dh))) |
|---|
| 80 | + return -EINVAL; |
|---|
| 81 | + dh = (struct dccp_hdr *)(skb->data + offset); |
|---|
| 82 | + if (!pskb_may_pull(skb, offset + __dccp_basic_hdr_len(dh))) |
|---|
| 83 | + return -EINVAL; |
|---|
| 84 | + hdr = (const struct ipv6hdr *)skb->data; |
|---|
| 89 | 85 | dh = (struct dccp_hdr *)(skb->data + offset); |
|---|
| 90 | 86 | |
|---|
| 91 | 87 | sk = __inet6_lookup_established(net, &dccp_hashinfo, |
|---|
| .. | .. |
|---|
| 96 | 92 | if (!sk) { |
|---|
| 97 | 93 | __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), |
|---|
| 98 | 94 | ICMP6_MIB_INERRORS); |
|---|
| 99 | | - return; |
|---|
| 95 | + return -ENOENT; |
|---|
| 100 | 96 | } |
|---|
| 101 | 97 | |
|---|
| 102 | 98 | if (sk->sk_state == DCCP_TIME_WAIT) { |
|---|
| 103 | 99 | inet_twsk_put(inet_twsk(sk)); |
|---|
| 104 | | - return; |
|---|
| 100 | + return 0; |
|---|
| 105 | 101 | } |
|---|
| 106 | 102 | seq = dccp_hdr_seq(dh); |
|---|
| 107 | | - if (sk->sk_state == DCCP_NEW_SYN_RECV) |
|---|
| 108 | | - return dccp_req_err(sk, seq); |
|---|
| 103 | + if (sk->sk_state == DCCP_NEW_SYN_RECV) { |
|---|
| 104 | + dccp_req_err(sk, seq); |
|---|
| 105 | + return 0; |
|---|
| 106 | + } |
|---|
| 109 | 107 | |
|---|
| 110 | 108 | bh_lock_sock(sk); |
|---|
| 111 | 109 | if (sock_owned_by_user(sk)) |
|---|
| .. | .. |
|---|
| 183 | 181 | out: |
|---|
| 184 | 182 | bh_unlock_sock(sk); |
|---|
| 185 | 183 | sock_put(sk); |
|---|
| 184 | + return 0; |
|---|
| 186 | 185 | } |
|---|
| 187 | 186 | |
|---|
| 188 | 187 | |
|---|
| .. | .. |
|---|
| 204 | 203 | fl6.flowi6_oif = ireq->ir_iif; |
|---|
| 205 | 204 | fl6.fl6_dport = ireq->ir_rmt_port; |
|---|
| 206 | 205 | fl6.fl6_sport = htons(ireq->ir_num); |
|---|
| 207 | | - security_req_classify_flow(req, flowi6_to_flowi(&fl6)); |
|---|
| 206 | + security_req_classify_flow(req, flowi6_to_flowi_common(&fl6)); |
|---|
| 208 | 207 | |
|---|
| 209 | 208 | |
|---|
| 210 | 209 | rcu_read_lock(); |
|---|
| .. | .. |
|---|
| 231 | 230 | opt = ireq->ipv6_opt; |
|---|
| 232 | 231 | if (!opt) |
|---|
| 233 | 232 | opt = rcu_dereference(np->opt); |
|---|
| 234 | | - err = ip6_xmit(sk, skb, &fl6, sk->sk_mark, opt, np->tclass); |
|---|
| 233 | + err = ip6_xmit(sk, skb, &fl6, sk->sk_mark, opt, np->tclass, |
|---|
| 234 | + sk->sk_priority); |
|---|
| 235 | 235 | rcu_read_unlock(); |
|---|
| 236 | 236 | err = net_xmit_eval(err); |
|---|
| 237 | 237 | } |
|---|
| .. | .. |
|---|
| 279 | 279 | fl6.flowi6_oif = inet6_iif(rxskb); |
|---|
| 280 | 280 | fl6.fl6_dport = dccp_hdr(skb)->dccph_dport; |
|---|
| 281 | 281 | fl6.fl6_sport = dccp_hdr(skb)->dccph_sport; |
|---|
| 282 | | - security_skb_classify_flow(rxskb, flowi6_to_flowi(&fl6)); |
|---|
| 282 | + security_skb_classify_flow(rxskb, flowi6_to_flowi_common(&fl6)); |
|---|
| 283 | 283 | |
|---|
| 284 | 284 | /* sk = NULL, but it is safe for now. RST socket required. */ |
|---|
| 285 | 285 | dst = ip6_dst_lookup_flow(sock_net(ctl_sk), ctl_sk, &fl6, NULL); |
|---|
| 286 | 286 | if (!IS_ERR(dst)) { |
|---|
| 287 | 287 | skb_dst_set(skb, dst); |
|---|
| 288 | | - ip6_xmit(ctl_sk, skb, &fl6, 0, NULL, 0); |
|---|
| 288 | + ip6_xmit(ctl_sk, skb, &fl6, 0, NULL, 0, 0); |
|---|
| 289 | 289 | DCCP_INC_STATS(DCCP_MIB_OUTSEGS); |
|---|
| 290 | 290 | DCCP_INC_STATS(DCCP_MIB_OUTRSTS); |
|---|
| 291 | 291 | return; |
|---|
| .. | .. |
|---|
| 538 | 538 | dccp_done(newsk); |
|---|
| 539 | 539 | goto out; |
|---|
| 540 | 540 | } |
|---|
| 541 | | - *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash)); |
|---|
| 541 | + *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash), NULL); |
|---|
| 542 | 542 | /* Clone pktoptions received with SYN, if we own the req */ |
|---|
| 543 | 543 | if (*own_req && ireq->pktopts) { |
|---|
| 544 | | - newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC); |
|---|
| 544 | + newnp->pktoptions = skb_clone_and_charge_r(ireq->pktopts, newsk); |
|---|
| 545 | 545 | consume_skb(ireq->pktopts); |
|---|
| 546 | 546 | ireq->pktopts = NULL; |
|---|
| 547 | | - if (newnp->pktoptions) |
|---|
| 548 | | - skb_set_owner_r(newnp->pktoptions, newsk); |
|---|
| 549 | 547 | } |
|---|
| 550 | 548 | |
|---|
| 551 | 549 | return newsk; |
|---|
| .. | .. |
|---|
| 605 | 603 | --ANK (980728) |
|---|
| 606 | 604 | */ |
|---|
| 607 | 605 | if (np->rxopt.all) |
|---|
| 608 | | - opt_skb = skb_clone(skb, GFP_ATOMIC); |
|---|
| 606 | + opt_skb = skb_clone_and_charge_r(skb, sk); |
|---|
| 609 | 607 | |
|---|
| 610 | 608 | if (sk->sk_state == DCCP_OPEN) { /* Fast path */ |
|---|
| 611 | 609 | if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len)) |
|---|
| .. | .. |
|---|
| 669 | 667 | np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb)); |
|---|
| 670 | 668 | if (ipv6_opt_accepted(sk, opt_skb, |
|---|
| 671 | 669 | &DCCP_SKB_CB(opt_skb)->header.h6)) { |
|---|
| 672 | | - skb_set_owner_r(opt_skb, sk); |
|---|
| 673 | 670 | memmove(IP6CB(opt_skb), |
|---|
| 674 | 671 | &DCCP_SKB_CB(opt_skb)->header.h6, |
|---|
| 675 | 672 | sizeof(struct inet6_skb_parm)); |
|---|
| .. | .. |
|---|
| 836 | 833 | if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) { |
|---|
| 837 | 834 | struct ip6_flowlabel *flowlabel; |
|---|
| 838 | 835 | flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); |
|---|
| 839 | | - if (flowlabel == NULL) |
|---|
| 836 | + if (IS_ERR(flowlabel)) |
|---|
| 840 | 837 | return -EINVAL; |
|---|
| 841 | 838 | fl6_sock_release(flowlabel); |
|---|
| 842 | 839 | } |
|---|
| .. | .. |
|---|
| 912 | 909 | fl6.flowi6_oif = sk->sk_bound_dev_if; |
|---|
| 913 | 910 | fl6.fl6_dport = usin->sin6_port; |
|---|
| 914 | 911 | fl6.fl6_sport = inet->inet_sport; |
|---|
| 915 | | - security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); |
|---|
| 912 | + security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); |
|---|
| 916 | 913 | |
|---|
| 917 | 914 | opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk)); |
|---|
| 918 | 915 | final_p = fl6_update_dst(&fl6, opt, &final); |
|---|
| .. | .. |
|---|
| 957 | 954 | |
|---|
| 958 | 955 | late_failure: |
|---|
| 959 | 956 | dccp_set_state(sk, DCCP_CLOSED); |
|---|
| 957 | + if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) |
|---|
| 958 | + inet_reset_saddr(sk); |
|---|
| 960 | 959 | __sk_dst_reset(sk); |
|---|
| 961 | 960 | failure: |
|---|
| 962 | 961 | inet->inet_dport = 0; |
|---|
| .. | .. |
|---|
| 975 | 974 | .getsockopt = ipv6_getsockopt, |
|---|
| 976 | 975 | .addr2sockaddr = inet6_csk_addr2sockaddr, |
|---|
| 977 | 976 | .sockaddr_len = sizeof(struct sockaddr_in6), |
|---|
| 978 | | -#ifdef CONFIG_COMPAT |
|---|
| 979 | | - .compat_setsockopt = compat_ipv6_setsockopt, |
|---|
| 980 | | - .compat_getsockopt = compat_ipv6_getsockopt, |
|---|
| 981 | | -#endif |
|---|
| 982 | 977 | }; |
|---|
| 983 | 978 | |
|---|
| 984 | 979 | /* |
|---|
| .. | .. |
|---|
| 995 | 990 | .getsockopt = ipv6_getsockopt, |
|---|
| 996 | 991 | .addr2sockaddr = inet6_csk_addr2sockaddr, |
|---|
| 997 | 992 | .sockaddr_len = sizeof(struct sockaddr_in6), |
|---|
| 998 | | -#ifdef CONFIG_COMPAT |
|---|
| 999 | | - .compat_setsockopt = compat_ipv6_setsockopt, |
|---|
| 1000 | | - .compat_getsockopt = compat_ipv6_getsockopt, |
|---|
| 1001 | | -#endif |
|---|
| 1002 | 993 | }; |
|---|
| 994 | + |
|---|
| 995 | +static void dccp_v6_sk_destruct(struct sock *sk) |
|---|
| 996 | +{ |
|---|
| 997 | + dccp_destruct_common(sk); |
|---|
| 998 | + inet6_sock_destruct(sk); |
|---|
| 999 | +} |
|---|
| 1003 | 1000 | |
|---|
| 1004 | 1001 | /* NOTE: A lot of things set to zero explicitly by call to |
|---|
| 1005 | 1002 | * sk_alloc() so need not be done here. |
|---|
| .. | .. |
|---|
| 1013 | 1010 | if (unlikely(!dccp_v6_ctl_sock_initialized)) |
|---|
| 1014 | 1011 | dccp_v6_ctl_sock_initialized = 1; |
|---|
| 1015 | 1012 | inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops; |
|---|
| 1013 | + sk->sk_destruct = dccp_v6_sk_destruct; |
|---|
| 1016 | 1014 | } |
|---|
| 1017 | 1015 | |
|---|
| 1018 | 1016 | return err; |
|---|
| 1019 | | -} |
|---|
| 1020 | | - |
|---|
| 1021 | | -static void dccp_v6_destroy_sock(struct sock *sk) |
|---|
| 1022 | | -{ |
|---|
| 1023 | | - dccp_destroy_sock(sk); |
|---|
| 1024 | | - inet6_destroy_sock(sk); |
|---|
| 1025 | 1017 | } |
|---|
| 1026 | 1018 | |
|---|
| 1027 | 1019 | static struct timewait_sock_ops dccp6_timewait_sock_ops = { |
|---|
| .. | .. |
|---|
| 1046 | 1038 | .accept = inet_csk_accept, |
|---|
| 1047 | 1039 | .get_port = inet_csk_get_port, |
|---|
| 1048 | 1040 | .shutdown = dccp_shutdown, |
|---|
| 1049 | | - .destroy = dccp_v6_destroy_sock, |
|---|
| 1041 | + .destroy = dccp_destroy_sock, |
|---|
| 1050 | 1042 | .orphan_count = &dccp_orphan_count, |
|---|
| 1051 | 1043 | .max_header = MAX_DCCP_HEADER, |
|---|
| 1052 | 1044 | .obj_size = sizeof(struct dccp6_sock), |
|---|
| .. | .. |
|---|
| 1054 | 1046 | .rsk_prot = &dccp6_request_sock_ops, |
|---|
| 1055 | 1047 | .twsk_prot = &dccp6_timewait_sock_ops, |
|---|
| 1056 | 1048 | .h.hashinfo = &dccp_hashinfo, |
|---|
| 1057 | | -#ifdef CONFIG_COMPAT |
|---|
| 1058 | | - .compat_setsockopt = compat_dccp_setsockopt, |
|---|
| 1059 | | - .compat_getsockopt = compat_dccp_getsockopt, |
|---|
| 1060 | | -#endif |
|---|
| 1061 | 1049 | }; |
|---|
| 1062 | 1050 | |
|---|
| 1063 | 1051 | static const struct inet6_protocol dccp_v6_protocol = { |
|---|
| .. | .. |
|---|
| 1077 | 1065 | .getname = inet6_getname, |
|---|
| 1078 | 1066 | .poll = dccp_poll, |
|---|
| 1079 | 1067 | .ioctl = inet6_ioctl, |
|---|
| 1068 | + .gettstamp = sock_gettstamp, |
|---|
| 1080 | 1069 | .listen = inet_dccp_listen, |
|---|
| 1081 | 1070 | .shutdown = inet_shutdown, |
|---|
| 1082 | 1071 | .setsockopt = sock_common_setsockopt, |
|---|
| .. | .. |
|---|
| 1086 | 1075 | .mmap = sock_no_mmap, |
|---|
| 1087 | 1076 | .sendpage = sock_no_sendpage, |
|---|
| 1088 | 1077 | #ifdef CONFIG_COMPAT |
|---|
| 1089 | | - .compat_setsockopt = compat_sock_common_setsockopt, |
|---|
| 1090 | | - .compat_getsockopt = compat_sock_common_getsockopt, |
|---|
| 1078 | + .compat_ioctl = inet6_compat_ioctl, |
|---|
| 1091 | 1079 | #endif |
|---|
| 1092 | 1080 | }; |
|---|
| 1093 | 1081 | |
|---|