Commit 0e7caa2a authored by Ville Nuorvala's avatar Ville Nuorvala Committed by David S. Miller

[IPV6]: Fix ip6_dst_lookup() route corruption.

parent f8d1f5ed
...@@ -353,7 +353,9 @@ extern int ip6_push_pending_frames(struct sock *sk); ...@@ -353,7 +353,9 @@ extern int ip6_push_pending_frames(struct sock *sk);
extern void ip6_flush_pending_frames(struct sock *sk); extern void ip6_flush_pending_frames(struct sock *sk);
extern struct dst_entry * ip6_dst_lookup(struct sock *sk, struct flowi *fl); extern int ip6_dst_lookup(struct sock *sk,
struct dst_entry **dst,
struct flowi *fl);
/* /*
* skb processing functions * skb processing functions
......
...@@ -355,8 +355,8 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -355,8 +355,8 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
fl.oif = np->mcast_oif; fl.oif = np->mcast_oif;
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if (dst->error) goto out; if (err) goto out;
if (hlimit < 0) { if (hlimit < 0) {
if (ipv6_addr_is_multicast(&fl.fl6_dst)) if (ipv6_addr_is_multicast(&fl.fl6_dst))
...@@ -434,9 +434,9 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -434,9 +434,9 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
fl.oif = np->mcast_oif; fl.oif = np->mcast_oif;
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if (dst->error) goto out; if (err) goto out;
if (hlimit < 0) { if (hlimit < 0) {
if (ipv6_addr_is_multicast(&fl.fl6_dst)) if (ipv6_addr_is_multicast(&fl.fl6_dst))
......
...@@ -1136,17 +1136,16 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) ...@@ -1136,17 +1136,16 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
return err; return err;
} }
struct dst_entry *ip6_dst_lookup(struct sock *sk, struct flowi *fl) int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
{ {
struct dst_entry *dst = NULL;
int err = 0; int err = 0;
if (sk) { if (sk) {
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
dst = __sk_dst_check(sk, np->dst_cookie); *dst = __sk_dst_check(sk, np->dst_cookie);
if (dst) { if (*dst) {
struct rt6_info *rt = (struct rt6_info*)dst; struct rt6_info *rt = (struct rt6_info*)*dst;
/* Yes, checking route validity in not connected /* Yes, checking route validity in not connected
case is not very simple. Take into account, case is not very simple. Take into account,
...@@ -1170,39 +1169,41 @@ struct dst_entry *ip6_dst_lookup(struct sock *sk, struct flowi *fl) ...@@ -1170,39 +1169,41 @@ struct dst_entry *ip6_dst_lookup(struct sock *sk, struct flowi *fl)
ipv6_addr_cmp(&fl->fl6_dst, &rt->rt6i_dst.addr)) ipv6_addr_cmp(&fl->fl6_dst, &rt->rt6i_dst.addr))
&& (np->daddr_cache == NULL || && (np->daddr_cache == NULL ||
ipv6_addr_cmp(&fl->fl6_dst, np->daddr_cache))) ipv6_addr_cmp(&fl->fl6_dst, np->daddr_cache)))
|| (fl->oif && fl->oif != dst->dev->ifindex)) { || (fl->oif && fl->oif != (*dst)->dev->ifindex)) {
dst = NULL; *dst = NULL;
} else } else
dst_hold(dst); dst_hold(*dst);
} }
} }
if (dst == NULL) if (*dst == NULL)
dst = ip6_route_output(sk, fl); *dst = ip6_route_output(sk, fl);
if (dst->error) if ((err = (*dst)->error))
return dst; goto out_err_release;
if (ipv6_addr_any(&fl->fl6_src)) { if (ipv6_addr_any(&fl->fl6_src)) {
err = ipv6_get_saddr(dst, &fl->fl6_dst, &fl->fl6_src); err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
if (err) { if (err) {
#if IP6_DEBUG >= 2 #if IP6_DEBUG >= 2
printk(KERN_DEBUG "ip6_build_xmit: " printk(KERN_DEBUG "ip6_dst_lookup: "
"no available source address\n"); "no available source address\n");
#endif #endif
dst->error = err; goto out_err_release;
return dst;
}
} }
if (dst) {
if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) {
dst->error = -ENETUNREACH;
} }
if ((err = xfrm_lookup(dst, fl, sk, 0)) < 0) {
err = -ENETUNREACH;
goto out_err_release;
} }
return dst; return 0;
out_err_release:
dst_release(*dst);
*dst = NULL;
return err;
} }
int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb),
......
...@@ -658,8 +658,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -658,8 +658,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
fl.oif = np->mcast_oif; fl.oif = np->mcast_oif;
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if ((err = dst->error)) if (err)
goto out; goto out;
if (hlimit < 0) { if (hlimit < 0) {
......
...@@ -663,19 +663,12 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -663,19 +663,12 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
ipv6_addr_copy(&fl.fl6_dst, rt0->addr); ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
} }
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if ((err = dst->error) != 0) { if (err)
dst_release(dst);
goto failure; goto failure;
}
if (saddr == NULL) { if (saddr == NULL) {
err = ipv6_get_saddr(dst, &np->daddr, &fl.fl6_src);
if (err) {
dst_release(dst);
goto failure;
}
saddr = &fl.fl6_src; saddr = &fl.fl6_src;
ipv6_addr_copy(&np->rcv_saddr, saddr); ipv6_addr_copy(&np->rcv_saddr, saddr);
} }
...@@ -790,13 +783,14 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -790,13 +783,14 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
fl.fl_ip_dport = inet->dport; fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport; fl.fl_ip_sport = inet->sport;
dst = ip6_dst_lookup(sk, &fl); if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
sk->sk_err_soft = -err;
goto out;
}
} else } else
dst_hold(dst); dst_hold(dst);
if (dst->error) { if (tp->pmtu_cookie > dst_pmtu(dst)) {
sk->sk_err_soft = -dst->error;
} else if (tp->pmtu_cookie > dst_pmtu(dst)) {
tcp_sync_mss(sk, dst_pmtu(dst)); tcp_sync_mss(sk, dst_pmtu(dst));
tcp_simple_retransmit(sk); tcp_simple_retransmit(sk);
} /* else let the usual retransmit timer handle it */ } /* else let the usual retransmit timer handle it */
...@@ -891,8 +885,8 @@ static int tcp_v6_send_synack(struct sock *sk, struct open_request *req, ...@@ -891,8 +885,8 @@ static int tcp_v6_send_synack(struct sock *sk, struct open_request *req,
ipv6_addr_copy(&fl.fl6_dst, rt0->addr); ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
} }
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if (dst->error) if (err)
goto done; goto done;
} }
...@@ -1020,9 +1014,7 @@ static void tcp_v6_send_reset(struct sk_buff *skb) ...@@ -1020,9 +1014,7 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
fl.fl_ip_sport = t1->source; fl.fl_ip_sport = t1->source;
/* sk = NULL, but it is safe for now. RST socket required. */ /* sk = NULL, but it is safe for now. RST socket required. */
buff->dst = ip6_dst_lookup(NULL, &fl); if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
if (buff->dst->error == 0) {
ip6_xmit(NULL, buff, &fl, NULL, 0); ip6_xmit(NULL, buff, &fl, NULL, 0);
TCP_INC_STATS_BH(TcpOutSegs); TCP_INC_STATS_BH(TcpOutSegs);
TCP_INC_STATS_BH(TcpOutRsts); TCP_INC_STATS_BH(TcpOutRsts);
...@@ -1083,9 +1075,7 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ...@@ -1083,9 +1075,7 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
fl.fl_ip_dport = t1->dest; fl.fl_ip_dport = t1->dest;
fl.fl_ip_sport = t1->source; fl.fl_ip_sport = t1->source;
buff->dst = ip6_dst_lookup(NULL, &fl); if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
if (buff->dst->error == 0) {
ip6_xmit(NULL, buff, &fl, NULL, 0); ip6_xmit(NULL, buff, &fl, NULL, 0);
TCP_INC_STATS_BH(TcpOutSegs); TCP_INC_STATS_BH(TcpOutSegs);
return; return;
...@@ -1331,11 +1321,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ...@@ -1331,11 +1321,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
fl.fl_ip_dport = req->rmt_port; fl.fl_ip_dport = req->rmt_port;
fl.fl_ip_sport = inet_sk(sk)->sport; fl.fl_ip_sport = inet_sk(sk)->sport;
dst = ip6_dst_lookup(sk, &fl); if (ip6_dst_lookup(sk, &dst, &fl))
}
if (dst->error)
goto out; goto out;
}
newsk = tcp_create_openreq_child(sk, req, skb); newsk = tcp_create_openreq_child(sk, req, skb);
if (newsk == NULL) if (newsk == NULL)
...@@ -1730,11 +1718,9 @@ static int tcp_v6_rebuild_header(struct sock *sk) ...@@ -1730,11 +1718,9 @@ static int tcp_v6_rebuild_header(struct sock *sk)
ipv6_addr_copy(&fl.fl6_dst, rt0->addr); ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
} }
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if (dst->error) { if (err) {
err = dst->error;
dst_release(dst);
sk->sk_route_caps = 0; sk->sk_route_caps = 0;
return err; return err;
} }
...@@ -1774,12 +1760,11 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok) ...@@ -1774,12 +1760,11 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok)
dst = __sk_dst_check(sk, np->dst_cookie); dst = __sk_dst_check(sk, np->dst_cookie);
if (dst == NULL) { if (dst == NULL) {
dst = ip6_dst_lookup(sk, &fl); int err = ip6_dst_lookup(sk, &dst, &fl);
if (dst->error) { if (err) {
sk->sk_err_soft = -dst->error; sk->sk_err_soft = -err;
dst_release(dst); return err;
return -sk->sk_err_soft;
} }
ip6_dst_store(sk, dst, NULL); ip6_dst_store(sk, dst, NULL);
......
...@@ -330,21 +330,11 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -330,21 +330,11 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
ipv6_addr_copy(&fl.fl6_dst, rt0->addr); ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
} }
dst = ip6_route_output(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
if ((err = dst->error) != 0) {
dst_release(dst);
goto out; goto out;
}
/* get the source address used in the appropriate device */
err = ipv6_get_saddr(dst, daddr, &fl.fl6_src); /* source address lookup done in ip6_dst_lookup */
if (err) {
dst_release(dst);
goto out;
}
if (ipv6_addr_any(&np->saddr)) if (ipv6_addr_any(&np->saddr))
ipv6_addr_copy(&np->saddr, &fl.fl6_src); ipv6_addr_copy(&np->saddr, &fl.fl6_src);
...@@ -930,8 +920,8 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -930,8 +920,8 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))
fl.oif = np->mcast_oif; fl.oif = np->mcast_oif;
dst = ip6_dst_lookup(sk, &fl); err = ip6_dst_lookup(sk, &dst, &fl);
if ((err = dst->error)) if (err)
goto out; goto out;
if (hlimit < 0) { if (hlimit < 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment