Commit e8b06d94 authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji Committed by David S. Miller

[IPV6]: Use real storage for cork'd packets, else MSG_MORE corrupts UDP packets.

parent 63d6889d
...@@ -174,6 +174,7 @@ enum { ...@@ -174,6 +174,7 @@ enum {
#include <net/if_inet6.h> /* struct ipv6_mc_socklist */ #include <net/if_inet6.h> /* struct ipv6_mc_socklist */
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <net/flow.h>
/* /*
This structure contains results of exthdrs parsing This structure contains results of exthdrs parsing
...@@ -234,7 +235,7 @@ struct ipv6_pinfo { ...@@ -234,7 +235,7 @@ struct ipv6_pinfo {
struct { struct {
struct ipv6_txoptions *opt; struct ipv6_txoptions *opt;
struct rt6_info *rt; struct rt6_info *rt;
struct flowi *fl; struct flowi fl;
int hop_limit; int hop_limit;
} cork; } cork;
}; };
......
...@@ -1239,7 +1239,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse ...@@ -1239,7 +1239,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
} }
dst_hold(&rt->u.dst); dst_hold(&rt->u.dst);
np->cork.rt = rt; np->cork.rt = rt;
np->cork.fl = fl; np->cork.fl = *fl;
np->cork.hop_limit = hlimit; np->cork.hop_limit = hlimit;
inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst); inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst);
inet->cork.length = 0; inet->cork.length = 0;
...@@ -1423,7 +1423,7 @@ int ip6_push_pending_frames(struct sock *sk) ...@@ -1423,7 +1423,7 @@ int ip6_push_pending_frames(struct sock *sk)
struct ipv6hdr *hdr; struct ipv6hdr *hdr;
struct ipv6_txoptions *opt = np->cork.opt; struct ipv6_txoptions *opt = np->cork.opt;
struct rt6_info *rt = np->cork.rt; struct rt6_info *rt = np->cork.rt;
struct flowi *fl = np->cork.fl; struct flowi *fl = &np->cork.fl;
unsigned char proto = fl->proto; unsigned char proto = fl->proto;
int err = 0; int err = 0;
...@@ -1487,9 +1487,7 @@ int ip6_push_pending_frames(struct sock *sk) ...@@ -1487,9 +1487,7 @@ int ip6_push_pending_frames(struct sock *sk)
dst_release(&np->cork.rt->u.dst); dst_release(&np->cork.rt->u.dst);
np->cork.rt = NULL; np->cork.rt = NULL;
} }
if (np->cork.fl) { memset(&np->cork.fl, 0, sizeof(np->cork.fl));
np->cork.fl = NULL;
}
return err; return err;
error: error:
goto out; goto out;
...@@ -1514,7 +1512,5 @@ void ip6_flush_pending_frames(struct sock *sk) ...@@ -1514,7 +1512,5 @@ void ip6_flush_pending_frames(struct sock *sk)
dst_release(&np->cork.rt->u.dst); dst_release(&np->cork.rt->u.dst);
np->cork.rt = NULL; np->cork.rt = NULL;
} }
if (np->cork.fl) { memset(&np->cork.fl, 0, sizeof(np->cork.fl));
np->cork.fl = NULL;
}
} }
...@@ -721,7 +721,7 @@ static int udp_v6_push_pending_frames(struct sock *sk, struct udp_opt *up) ...@@ -721,7 +721,7 @@ static int udp_v6_push_pending_frames(struct sock *sk, struct udp_opt *up)
struct sk_buff *skb; struct sk_buff *skb;
struct udphdr *uh; struct udphdr *uh;
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct flowi *fl = np->cork.fl; struct flowi *fl = &np->cork.fl;
int err = 0; int err = 0;
/* Grab the skbuff where UDP header space exists. */ /* Grab the skbuff where UDP header space exists. */
...@@ -783,7 +783,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -783,7 +783,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
struct in6_addr *daddr; struct in6_addr *daddr;
struct ipv6_txoptions *opt = NULL; struct ipv6_txoptions *opt = NULL;
struct ip6_flowlabel *flowlabel = NULL; struct ip6_flowlabel *flowlabel = NULL;
struct flowi fl; struct flowi *fl = &np->cork.fl;
struct dst_entry *dst; struct dst_entry *dst;
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
int ulen = len; int ulen = len;
...@@ -812,7 +812,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -812,7 +812,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
} }
ulen += sizeof(struct udphdr); ulen += sizeof(struct udphdr);
memset(&fl, 0, sizeof(fl)); memset(fl, 0, sizeof(*fl));
if (sin6) { if (sin6) {
if (sin6->sin6_family == AF_INET) { if (sin6->sin6_family == AF_INET) {
...@@ -834,9 +834,9 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -834,9 +834,9 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
daddr = &sin6->sin6_addr; daddr = &sin6->sin6_addr;
if (np->sndflow) { if (np->sndflow) {
fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; fl->fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) { if (fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel);
if (flowlabel == NULL) if (flowlabel == NULL)
return -EINVAL; return -EINVAL;
daddr = &flowlabel->dst; daddr = &flowlabel->dst;
...@@ -854,14 +854,14 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -854,14 +854,14 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (addr_len >= sizeof(struct sockaddr_in6) && if (addr_len >= sizeof(struct sockaddr_in6) &&
sin6->sin6_scope_id && sin6->sin6_scope_id &&
ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
fl.oif = sin6->sin6_scope_id; fl->oif = sin6->sin6_scope_id;
} else { } else {
if (sk->sk_state != TCP_ESTABLISHED) if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ; return -EDESTADDRREQ;
up->dport = inet->dport; up->dport = inet->dport;
daddr = &np->daddr; daddr = &np->daddr;
fl.fl6_flowlabel = np->flow_label; fl->fl6_flowlabel = np->flow_label;
} }
addr_type = ipv6_addr_type(daddr); addr_type = ipv6_addr_type(daddr);
...@@ -882,20 +882,20 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -882,20 +882,20 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
return udp_sendmsg(iocb, sk, msg, len); return udp_sendmsg(iocb, sk, msg, len);
} }
if (!fl.oif) if (!fl->oif)
fl.oif = sk->sk_bound_dev_if; fl->oif = sk->sk_bound_dev_if;
if (msg->msg_controllen) { if (msg->msg_controllen) {
opt = &opt_space; opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_txoptions)); memset(opt, 0, sizeof(struct ipv6_txoptions));
err = datagram_send_ctl(msg, &fl, opt, &hlimit); err = datagram_send_ctl(msg, fl, opt, &hlimit);
if (err < 0) { if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
} }
if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { if ((fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel);
if (flowlabel == NULL) if (flowlabel == NULL)
return -EINVAL; return -EINVAL;
} }
...@@ -907,28 +907,28 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -907,28 +907,28 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (flowlabel) if (flowlabel)
opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = fl6_merge_options(&opt_space, flowlabel, opt);
fl.proto = IPPROTO_UDP; fl->proto = IPPROTO_UDP;
ipv6_addr_copy(&fl.fl6_dst, daddr); ipv6_addr_copy(&fl->fl6_dst, daddr);
if (ipv6_addr_any(&fl.fl6_src) && !ipv6_addr_any(&np->saddr)) if (ipv6_addr_any(&fl->fl6_src) && !ipv6_addr_any(&np->saddr))
ipv6_addr_copy(&fl.fl6_src, &np->saddr); ipv6_addr_copy(&fl->fl6_src, &np->saddr);
fl.fl_ip_dport = up->dport; fl->fl_ip_dport = up->dport;
fl.fl_ip_sport = inet->sport; fl->fl_ip_sport = inet->sport;
/* merge ip6_build_xmit from ip6_output */ /* merge ip6_build_xmit from ip6_output */
if (opt && opt->srcrt) { if (opt && opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
ipv6_addr_copy(&fl.fl6_dst, rt0->addr); ipv6_addr_copy(&fl->fl6_dst, rt0->addr);
} }
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;
err = ip6_dst_lookup(sk, &dst, &fl); err = ip6_dst_lookup(sk, &dst, fl);
if (err) if (err)
goto out; goto out;
if (hlimit < 0) { if (hlimit < 0) {
if (ipv6_addr_is_multicast(&fl.fl6_dst)) if (ipv6_addr_is_multicast(&fl->fl6_dst))
hlimit = np->mcast_hops; hlimit = np->mcast_hops;
else else
hlimit = np->hop_limit; hlimit = np->hop_limit;
...@@ -956,7 +956,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -956,7 +956,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
do_append_data: do_append_data:
up->len += ulen; up->len += ulen;
err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr), err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr),
hlimit, opt, &fl, (struct rt6_info*)dst, hlimit, opt, fl, (struct rt6_info*)dst,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
if (err) if (err)
udp_v6_flush_pending_frames(sk); udp_v6_flush_pending_frames(sk);
...@@ -965,7 +965,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg ...@@ -965,7 +965,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
if (dst) if (dst)
ip6_dst_store(sk, dst, ip6_dst_store(sk, dst,
!ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ? !ipv6_addr_cmp(&fl->fl6_dst, &np->daddr) ?
&np->daddr : NULL); &np->daddr : NULL);
if (err > 0) if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0; err = np->recverr ? net_xmit_errno(err) : 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