diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 54 |
1 files changed, 32 insertions, 22 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c5429a636f1a..db9f1c318afc 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -93,10 +93,9 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) | |||
93 | { | 93 | { |
94 | struct dst_entry *dst = skb_dst(skb); | 94 | struct dst_entry *dst = skb_dst(skb); |
95 | 95 | ||
96 | if (dst) { | 96 | if (dst && dst_hold_safe(dst)) { |
97 | const struct rt6_info *rt = (const struct rt6_info *)dst; | 97 | const struct rt6_info *rt = (const struct rt6_info *)dst; |
98 | 98 | ||
99 | dst_hold(dst); | ||
100 | sk->sk_rx_dst = dst; | 99 | sk->sk_rx_dst = dst; |
101 | inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; | 100 | inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; |
102 | inet6_sk(sk)->rx_dst_cookie = rt6_get_cookie(rt); | 101 | inet6_sk(sk)->rx_dst_cookie = rt6_get_cookie(rt); |
@@ -120,6 +119,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
120 | struct ipv6_pinfo *np = inet6_sk(sk); | 119 | struct ipv6_pinfo *np = inet6_sk(sk); |
121 | struct tcp_sock *tp = tcp_sk(sk); | 120 | struct tcp_sock *tp = tcp_sk(sk); |
122 | struct in6_addr *saddr = NULL, *final_p, final; | 121 | struct in6_addr *saddr = NULL, *final_p, final; |
122 | struct ipv6_txoptions *opt; | ||
123 | struct flowi6 fl6; | 123 | struct flowi6 fl6; |
124 | struct dst_entry *dst; | 124 | struct dst_entry *dst; |
125 | int addr_type; | 125 | int addr_type; |
@@ -235,7 +235,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
235 | fl6.fl6_dport = usin->sin6_port; | 235 | fl6.fl6_dport = usin->sin6_port; |
236 | fl6.fl6_sport = inet->inet_sport; | 236 | fl6.fl6_sport = inet->inet_sport; |
237 | 237 | ||
238 | final_p = fl6_update_dst(&fl6, np->opt, &final); | 238 | opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); |
239 | final_p = fl6_update_dst(&fl6, opt, &final); | ||
239 | 240 | ||
240 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); | 241 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); |
241 | 242 | ||
@@ -255,7 +256,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
255 | inet->inet_rcv_saddr = LOOPBACK4_IPV6; | 256 | inet->inet_rcv_saddr = LOOPBACK4_IPV6; |
256 | 257 | ||
257 | sk->sk_gso_type = SKB_GSO_TCPV6; | 258 | sk->sk_gso_type = SKB_GSO_TCPV6; |
258 | __ip6_dst_store(sk, dst, NULL, NULL); | 259 | ip6_dst_store(sk, dst, NULL, NULL); |
259 | 260 | ||
260 | if (tcp_death_row.sysctl_tw_recycle && | 261 | if (tcp_death_row.sysctl_tw_recycle && |
261 | !tp->rx_opt.ts_recent_stamp && | 262 | !tp->rx_opt.ts_recent_stamp && |
@@ -263,9 +264,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | |||
263 | tcp_fetch_timewait_stamp(sk, dst); | 264 | tcp_fetch_timewait_stamp(sk, dst); |
264 | 265 | ||
265 | icsk->icsk_ext_hdr_len = 0; | 266 | icsk->icsk_ext_hdr_len = 0; |
266 | if (np->opt) | 267 | if (opt) |
267 | icsk->icsk_ext_hdr_len = (np->opt->opt_flen + | 268 | icsk->icsk_ext_hdr_len = opt->opt_flen + |
268 | np->opt->opt_nflen); | 269 | opt->opt_nflen; |
269 | 270 | ||
270 | tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); | 271 | tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); |
271 | 272 | ||
@@ -461,7 +462,10 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, | |||
461 | if (np->repflow && ireq->pktopts) | 462 | if (np->repflow && ireq->pktopts) |
462 | fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); | 463 | fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); |
463 | 464 | ||
464 | err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); | 465 | rcu_read_lock(); |
466 | err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), | ||
467 | np->tclass); | ||
468 | rcu_read_unlock(); | ||
465 | err = net_xmit_eval(err); | 469 | err = net_xmit_eval(err); |
466 | } | 470 | } |
467 | 471 | ||
@@ -852,7 +856,9 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) | |||
852 | 856 | ||
853 | #ifdef CONFIG_TCP_MD5SIG | 857 | #ifdef CONFIG_TCP_MD5SIG |
854 | hash_location = tcp_parse_md5sig_option(th); | 858 | hash_location = tcp_parse_md5sig_option(th); |
855 | if (!sk && hash_location) { | 859 | if (sk && sk_fullsock(sk)) { |
860 | key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr); | ||
861 | } else if (hash_location) { | ||
856 | /* | 862 | /* |
857 | * active side is lost. Try to find listening socket through | 863 | * active side is lost. Try to find listening socket through |
858 | * source port, and then find md5 key through listening socket. | 864 | * source port, and then find md5 key through listening socket. |
@@ -875,8 +881,6 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) | |||
875 | genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb); | 881 | genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb); |
876 | if (genhash || memcmp(hash_location, newhash, 16) != 0) | 882 | if (genhash || memcmp(hash_location, newhash, 16) != 0) |
877 | goto release_sk1; | 883 | goto release_sk1; |
878 | } else { | ||
879 | key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL; | ||
880 | } | 884 | } |
881 | #endif | 885 | #endif |
882 | 886 | ||
@@ -972,6 +976,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
972 | struct inet_request_sock *ireq; | 976 | struct inet_request_sock *ireq; |
973 | struct ipv6_pinfo *newnp; | 977 | struct ipv6_pinfo *newnp; |
974 | const struct ipv6_pinfo *np = inet6_sk(sk); | 978 | const struct ipv6_pinfo *np = inet6_sk(sk); |
979 | struct ipv6_txoptions *opt; | ||
975 | struct tcp6_sock *newtcp6sk; | 980 | struct tcp6_sock *newtcp6sk; |
976 | struct inet_sock *newinet; | 981 | struct inet_sock *newinet; |
977 | struct tcp_sock *newtp; | 982 | struct tcp_sock *newtp; |
@@ -1056,7 +1061,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
1056 | */ | 1061 | */ |
1057 | 1062 | ||
1058 | newsk->sk_gso_type = SKB_GSO_TCPV6; | 1063 | newsk->sk_gso_type = SKB_GSO_TCPV6; |
1059 | __ip6_dst_store(newsk, dst, NULL, NULL); | 1064 | ip6_dst_store(newsk, dst, NULL, NULL); |
1060 | inet6_sk_rx_dst_set(newsk, skb); | 1065 | inet6_sk_rx_dst_set(newsk, skb); |
1061 | 1066 | ||
1062 | newtcp6sk = (struct tcp6_sock *)newsk; | 1067 | newtcp6sk = (struct tcp6_sock *)newsk; |
@@ -1098,13 +1103,15 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
1098 | but we make one more one thing there: reattach optmem | 1103 | but we make one more one thing there: reattach optmem |
1099 | to newsk. | 1104 | to newsk. |
1100 | */ | 1105 | */ |
1101 | if (np->opt) | 1106 | opt = rcu_dereference(np->opt); |
1102 | newnp->opt = ipv6_dup_options(newsk, np->opt); | 1107 | if (opt) { |
1103 | 1108 | opt = ipv6_dup_options(newsk, opt); | |
1109 | RCU_INIT_POINTER(newnp->opt, opt); | ||
1110 | } | ||
1104 | inet_csk(newsk)->icsk_ext_hdr_len = 0; | 1111 | inet_csk(newsk)->icsk_ext_hdr_len = 0; |
1105 | if (newnp->opt) | 1112 | if (opt) |
1106 | inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + | 1113 | inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + |
1107 | newnp->opt->opt_flen); | 1114 | opt->opt_flen; |
1108 | 1115 | ||
1109 | tcp_ca_openreq_child(newsk, dst); | 1116 | tcp_ca_openreq_child(newsk, dst); |
1110 | 1117 | ||
@@ -1130,7 +1137,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
1130 | */ | 1137 | */ |
1131 | tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, | 1138 | tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, |
1132 | AF_INET6, key->key, key->keylen, | 1139 | AF_INET6, key->key, key->keylen, |
1133 | sk_gfp_atomic(sk, GFP_ATOMIC)); | 1140 | sk_gfp_mask(sk, GFP_ATOMIC)); |
1134 | } | 1141 | } |
1135 | #endif | 1142 | #endif |
1136 | 1143 | ||
@@ -1146,7 +1153,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * | |||
1146 | /* Clone pktoptions received with SYN, if we own the req */ | 1153 | /* Clone pktoptions received with SYN, if we own the req */ |
1147 | if (ireq->pktopts) { | 1154 | if (ireq->pktopts) { |
1148 | newnp->pktoptions = skb_clone(ireq->pktopts, | 1155 | newnp->pktoptions = skb_clone(ireq->pktopts, |
1149 | sk_gfp_atomic(sk, GFP_ATOMIC)); | 1156 | sk_gfp_mask(sk, GFP_ATOMIC)); |
1150 | consume_skb(ireq->pktopts); | 1157 | consume_skb(ireq->pktopts); |
1151 | ireq->pktopts = NULL; | 1158 | ireq->pktopts = NULL; |
1152 | if (newnp->pktoptions) | 1159 | if (newnp->pktoptions) |
@@ -1212,7 +1219,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
1212 | --ANK (980728) | 1219 | --ANK (980728) |
1213 | */ | 1220 | */ |
1214 | if (np->rxopt.all) | 1221 | if (np->rxopt.all) |
1215 | opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); | 1222 | opt_skb = skb_clone(skb, sk_gfp_mask(sk, GFP_ATOMIC)); |
1216 | 1223 | ||
1217 | if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ | 1224 | if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ |
1218 | struct dst_entry *dst = sk->sk_rx_dst; | 1225 | struct dst_entry *dst = sk->sk_rx_dst; |
@@ -1511,7 +1518,9 @@ do_time_wait: | |||
1511 | break; | 1518 | break; |
1512 | case TCP_TW_RST: | 1519 | case TCP_TW_RST: |
1513 | tcp_v6_restore_cb(skb); | 1520 | tcp_v6_restore_cb(skb); |
1514 | goto no_tcp_socket; | 1521 | tcp_v6_send_reset(sk, skb); |
1522 | inet_twsk_deschedule_put(inet_twsk(sk)); | ||
1523 | goto discard_it; | ||
1515 | case TCP_TW_SUCCESS: | 1524 | case TCP_TW_SUCCESS: |
1516 | ; | 1525 | ; |
1517 | } | 1526 | } |
@@ -1884,6 +1893,7 @@ struct proto tcpv6_prot = { | |||
1884 | .proto_cgroup = tcp_proto_cgroup, | 1893 | .proto_cgroup = tcp_proto_cgroup, |
1885 | #endif | 1894 | #endif |
1886 | .clear_sk = tcp_v6_clear_sk, | 1895 | .clear_sk = tcp_v6_clear_sk, |
1896 | .diag_destroy = tcp_abort, | ||
1887 | }; | 1897 | }; |
1888 | 1898 | ||
1889 | static const struct inet6_protocol tcpv6_protocol = { | 1899 | static const struct inet6_protocol tcpv6_protocol = { |