aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorNeal Cardwell <ncardwell@google.com>2012-06-28 08:34:21 -0400
committerDavid S. Miller <davem@davemloft.net>2012-06-28 20:54:03 -0400
commit9f10d3f6f966ef6f6a8d025a4b1d341923d04607 (patch)
tree1f47abac17996fdbe45114dd6bbf65f451f1075d /net/ipv6
parent9494218fbae2f88bd3f9b887714734abfdf38bab (diff)
tcp: plug dst leak in tcp_v6_conn_request()
The code in tcp_v6_conn_request() was implicitly assuming that tcp_v6_send_synack() would take care of dst_release(), much as tcp_v4_send_synack() already does. This resulted in tcp_v6_conn_request() leaking a dst if sysctl_tw_recycle is enabled. This commit restructures tcp_v6_send_synack() so that it accepts a dst pointer and takes care of releasing the dst that is passed in, to plug the leak and avoid future surprises by bringing the IPv6 behavior in line with the IPv4 side. Signed-off-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/tcp_ipv6.c19
1 files changed, 10 insertions, 9 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index d1db0caefdc..9c06eafaf69 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -477,7 +477,8 @@ out:
477} 477}
478 478
479 479
480static int tcp_v6_send_synack(struct sock *sk, 480static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
481 struct flowi6 *fl6,
481 struct request_sock *req, 482 struct request_sock *req,
482 struct request_values *rvp, 483 struct request_values *rvp,
483 u16 queue_mapping) 484 u16 queue_mapping)
@@ -486,12 +487,10 @@ static int tcp_v6_send_synack(struct sock *sk,
486 struct ipv6_pinfo *np = inet6_sk(sk); 487 struct ipv6_pinfo *np = inet6_sk(sk);
487 struct sk_buff * skb; 488 struct sk_buff * skb;
488 struct ipv6_txoptions *opt = np->opt; 489 struct ipv6_txoptions *opt = np->opt;
489 struct flowi6 fl6;
490 struct dst_entry *dst;
491 int err = -ENOMEM; 490 int err = -ENOMEM;
492 491
493 dst = inet6_csk_route_req(sk, &fl6, req); 492 /* First, grab a route. */
494 if (!dst) 493 if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL)
495 goto done; 494 goto done;
496 495
497 skb = tcp_make_synack(sk, dst, req, rvp); 496 skb = tcp_make_synack(sk, dst, req, rvp);
@@ -499,9 +498,9 @@ static int tcp_v6_send_synack(struct sock *sk,
499 if (skb) { 498 if (skb) {
500 __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); 499 __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr);
501 500
502 fl6.daddr = treq->rmt_addr; 501 fl6->daddr = treq->rmt_addr;
503 skb_set_queue_mapping(skb, queue_mapping); 502 skb_set_queue_mapping(skb, queue_mapping);
504 err = ip6_xmit(sk, skb, &fl6, opt, np->tclass); 503 err = ip6_xmit(sk, skb, fl6, opt, np->tclass);
505 err = net_xmit_eval(err); 504 err = net_xmit_eval(err);
506 } 505 }
507 506
@@ -514,8 +513,10 @@ done:
514static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req, 513static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req,
515 struct request_values *rvp) 514 struct request_values *rvp)
516{ 515{
516 struct flowi6 fl6;
517
517 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); 518 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
518 return tcp_v6_send_synack(sk, req, rvp, 0); 519 return tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0);
519} 520}
520 521
521static void tcp_v6_reqsk_destructor(struct request_sock *req) 522static void tcp_v6_reqsk_destructor(struct request_sock *req)
@@ -1201,7 +1202,7 @@ have_isn:
1201 if (security_inet_conn_request(sk, skb, req)) 1202 if (security_inet_conn_request(sk, skb, req))
1202 goto drop_and_release; 1203 goto drop_and_release;
1203 1204
1204 if (tcp_v6_send_synack(sk, req, 1205 if (tcp_v6_send_synack(sk, dst, &fl6, req,
1205 (struct request_values *)&tmp_ext, 1206 (struct request_values *)&tmp_ext,
1206 skb_get_queue_mapping(skb)) || 1207 skb_get_queue_mapping(skb)) ||
1207 want_cookie) 1208 want_cookie)