diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-18 00:24:27 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-18 00:24:27 +0000 |
commit | b9558d5f86c471a125abf1fb3a3882fb053b1f8c (patch) | |
tree | 707b53ec64e740a7da87d5f36485e3cd9b1c794e /net/ipv6 | |
parent | b3ac367c7a3e6047abe74817db27e34e759f279f (diff) |
Merge with Linux 2.3.41.
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/addrconf.c | 24 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 19 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 18 | ||||
-rw-r--r-- | net/ipv6/ip6_fib.c | 6 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 6 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 11 | ||||
-rw-r--r-- | net/ipv6/raw.c | 8 | ||||
-rw-r--r-- | net/ipv6/route.c | 14 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 962 | ||||
-rw-r--r-- | net/ipv6/udp.c | 100 |
10 files changed, 560 insertions, 608 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8430729e5..11a435ab3 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6,7 +6,7 @@ * Pedro Roque <roque@di.fc.ul.pt> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * - * $Id: addrconf.c,v 1.55 1999/12/15 22:39:40 davem Exp $ + * $Id: addrconf.c,v 1.57 2000/01/18 08:24:21 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -682,6 +682,23 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) } return -1; } + +static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) +{ + int err = -1; + struct inet6_ifaddr *ifp; + + read_lock_bh(&idev->lock); + for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { + if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) { + memcpy(eui, ifp->addr.s6_addr+8, 8); + err = 0; + break; + } + } + read_unlock_bh(&idev->lock); + return err; +} #endif /* @@ -859,7 +876,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len) #ifdef CONFIG_IPV6_EUI64 if (pinfo->prefix_len == 64) { memcpy(&addr, &pinfo->prefix, 8); - if (ipv6_generate_eui64(addr.s6_addr + 8, dev)) { + if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && + ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { in6_dev_put(in6_dev); return; } @@ -1519,7 +1537,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) */ if (ifp->idev->cnf.forwarding == 0 && - (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) == 0 && + (dev->flags&IFF_LOOPBACK) == 0 && (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { struct in6_addr all_routers; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 68badee52..a8d396ba3 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.49 1999/12/15 22:39:43 davem Exp $ + * $Id: af_inet6.c,v 1.52 2000/01/18 08:24:21 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -85,13 +85,17 @@ extern void ipv6_sysctl_register(void); extern void ipv6_sysctl_unregister(void); #endif +#ifdef INET_REFCNT_DEBUG atomic_t inet6_sock_nr; +#endif static void inet6_sock_destruct(struct sock *sk) { inet_sock_destruct(sk); +#ifdef INET_REFCNT_DEBUG atomic_dec(&inet6_sock_nr); +#endif MOD_DEC_USE_COUNT; } @@ -140,9 +144,6 @@ static int inet6_create(struct socket *sock, int protocol) sk->prot = prot; sk->backlog_rcv = prot->backlog_rcv; - sk->timer.data = (unsigned long)sk; - sk->timer.function = &tcp_keepalive_timer; - sk->net_pinfo.af_inet6.hop_limit = -1; sk->net_pinfo.af_inet6.mcast_hops = -1; sk->net_pinfo.af_inet6.mc_loop = 1; @@ -158,8 +159,16 @@ static int inet6_create(struct socket *sock, int protocol) sk->protinfo.af_inet.mc_index = 0; sk->protinfo.af_inet.mc_list = NULL; + if (ipv4_config.no_pmtu_disc) + sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT; + else + sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_WANT; + + +#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr); atomic_inc(&inet_sock_nr); +#endif MOD_INC_USE_COUNT; if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW) @@ -421,7 +430,7 @@ struct proto_ops inet6_stream_ops = { sock_no_socketpair, /* a do nothing */ inet_accept, /* ok */ inet6_getname, - inet_poll, /* ok */ + tcp_poll, /* ok */ inet6_ioctl, /* must change */ inet_listen, /* ok */ inet_shutdown, /* ok */ diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index cfa18eee8..f1c211532 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.25 2000/01/09 02:19:54 davem Exp $ + * $Id: icmp.c,v 1.26 2000/01/19 04:06:19 davem Exp $ * * Based on net/ipv4/icmp.c * @@ -146,19 +146,19 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr, */ if (offset) { - csum = csum_partial_copy((void *) msg->data + - offset - sizeof(struct icmp6hdr), - buff, len, msg->csum); + csum = csum_partial_copy_nocheck((void *) msg->data + + offset - sizeof(struct icmp6hdr), + buff, len, msg->csum); msg->csum = csum; return 0; } - csum = csum_partial_copy((void *) &msg->icmph, buff, - sizeof(struct icmp6hdr), msg->csum); + csum = csum_partial_copy_nocheck((void *) &msg->icmph, buff, + sizeof(struct icmp6hdr), msg->csum); - csum = csum_partial_copy((void *) msg->data, - buff + sizeof(struct icmp6hdr), - len - sizeof(struct icmp6hdr), csum); + csum = csum_partial_copy_nocheck((void *) msg->data, + buff + sizeof(struct icmp6hdr), + len - sizeof(struct icmp6hdr), csum); icmph = (struct icmp6hdr *) buff; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 099953e53..d458adc93 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_fib.c,v 1.19 1999/08/31 07:04:00 davem Exp $ + * $Id: ip6_fib.c,v 1.20 2000/01/16 05:11:37 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -926,8 +926,8 @@ int fib6_del(struct rt6_info *rt) #if RT6_DEBUG >= 2 if (rt->u.dst.obsolete>0) { - BUG_TRAP(rt->u.dst.obsolete<=0); - return -EFAULT; + BUG_TRAP(fn==NULL || rt->u.dst.obsolete<=0); + return -ENOENT; } #endif if (fn == NULL || rt == &ip6_null_entry) diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index eddf935a0..873d22c3d 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.30 2000/01/09 02:19:49 davem Exp $ + * $Id: ipv6_sockglue.c,v 1.31 2000/01/16 05:11:38 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -192,7 +192,9 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, kfree_skb(pktopt); sk->destruct = inet_sock_destruct; +#ifdef INET_REFCNT_DEBUG atomic_dec(&inet6_sock_nr); +#endif MOD_DEC_USE_COUNT; retv = 0; break; @@ -271,7 +273,7 @@ update: if (sk->type == SOCK_STREAM) { if (opt) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - if ((tcp_connected(sk->state) || sk->state == TCP_SYN_SENT) + if (!((1<<sk->state)&(TCPF_LISTEN|TCPF_CLOSE)) && sk->daddr != LOOPBACK4_IPV6) { tp->ext_header_len = opt->opt_flen + opt->opt_nflen; tcp_sync_mss(sk, tp->pmtu_cookie); diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index ce9f17adc..412b0b5e6 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: mcast.c,v 1.28 2000/01/09 02:19:50 davem Exp $ + * $Id: mcast.c,v 1.29 2000/01/18 08:24:21 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * @@ -500,7 +500,8 @@ void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) if (dev->hard_header) { unsigned char ha[MAX_ADDR_LEN]; ndisc_mc_map(snd_addr, ha, dev, 1); - dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len); + if (dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len) < 0) + goto out; } if (ipv6_get_lladdr(dev, &addr_buf)) { @@ -508,7 +509,7 @@ void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) printk(KERN_DEBUG "igmp6: %s no linklocal address\n", dev->name); #endif - return; + goto out; } ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len); @@ -532,6 +533,10 @@ void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) else ICMP6_INC_STATS(Icmp6OutGroupMembResponses); ICMP6_INC_STATS(Icmp6OutMsgs); + return; + +out: + kfree_skb(skb); } static void igmp6_join_group(struct ifmcaddr6 *ma) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 04ecdea9c..574bc165c 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.31 2000/01/09 02:19:50 davem Exp $ + * $Id: raw.c,v 1.33 2000/01/18 08:24:22 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -763,10 +763,6 @@ struct proto rawv6_prot = { udpv6_connect, /* connect */ udp_disconnect, /* disconnect */ NULL, /* accept */ - NULL, /* retransmit */ - NULL, /* write_wakeup */ - NULL, /* read_wakeup */ - datagram_poll, /* poll */ NULL, /* ioctl */ rawv6_init_sk, /* init */ inet6_destroy_sock, /* destroy */ @@ -780,7 +776,5 @@ struct proto rawv6_prot = { raw_v6_hash, /* hash */ raw_v6_unhash, /* unhash */ NULL, /* get_port */ - 128, /* max_header */ - 0, /* retransmits */ "RAW", /* name */ }; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 668f61bfb..dc6020c33 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: route.c,v 1.44 2000/01/09 02:19:51 davem Exp $ + * $Id: route.c,v 1.45 2000/01/16 05:11:38 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -93,7 +93,7 @@ struct dst_ops ip6_dst_ops = { struct rt6_info ip6_null_entry = { {{NULL, ATOMIC_INIT(1), 1, &loopback_dev, - -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL, ip6_pkt_discard, ip6_pkt_discard, #ifdef CONFIG_NET_CLS_ROUTE @@ -296,6 +296,7 @@ static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr, rt->rt6i_dst.plen = 128; rt->rt6i_flags |= RTF_CACHE; + rt->u.dst.flags |= DST_HOST; #ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen && saddr) { @@ -687,6 +688,8 @@ int ip6_route_add(struct in6_rtmsg *rtmsg) ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst); rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len; + if (rt->rt6i_dst.plen == 128) + rt->u.dst.flags = DST_HOST; ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen); #ifdef CONFIG_IPV6_SUBTREES @@ -940,6 +943,7 @@ source_ok: ipv6_addr_copy(&nrt->rt6i_dst.addr, dest); nrt->rt6i_dst.plen = 128; + nrt->u.dst.flags |= DST_HOST; ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key); nrt->rt6i_nexthop = neigh_clone(neigh); @@ -1025,6 +1029,7 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, goto out; ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); nrt->rt6i_dst.plen = 128; + nrt->u.dst.flags |= DST_HOST; nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop); dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES; @@ -1045,7 +1050,7 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) struct rt6_info *rt; rt = dst_alloc(&ip6_dst_ops); - + if (rt) { rt->u.dst.input = ort->u.dst.input; rt->u.dst.output = ort->u.dst.output; @@ -1193,7 +1198,8 @@ int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev) rt = dst_alloc(&ip6_dst_ops); if (rt == NULL) return -ENOMEM; - + + rt->u.dst.flags = DST_HOST; rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; rt->rt6i_dev = dev_get_by_name("lo"); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index e87ef0c3e..420b81f4a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.116 2000/01/09 02:19:52 davem Exp $ + * $Id: tcp_ipv6.c,v 1.118 2000/01/18 08:24:22 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -46,11 +46,6 @@ #include <asm/uaccess.h> -extern int sysctl_max_syn_backlog; -extern int sysctl_tcp_tw_recycle; -extern __u32 sysctl_wmem_max; -extern __u32 sysctl_rmem_max; - static void tcp_v6_send_reset(struct sk_buff *skb); static void tcp_v6_or_send_ack(struct sk_buff *skb, struct open_request *req); static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, @@ -58,11 +53,6 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); static int tcp_v6_xmit(struct sk_buff *skb); -static struct open_request *tcp_v6_search_req(struct tcp_opt *tp, - struct ipv6hdr *ip6h, - struct tcphdr *th, - int iif, - struct open_request **prevp); static struct tcp_func ipv6_mapped; static struct tcp_func ipv6_specific; @@ -282,9 +272,10 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor * * The sockhash lock must be held as a reader here. */ -static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport, - struct in6_addr *daddr, u16 hnum, - int dif) + +static inline struct sock *__tcp_v6_lookup_established(struct in6_addr *saddr, u16 sport, + struct in6_addr *daddr, u16 hnum, + int dif) { struct tcp_ehash_bucket *head; struct sock *sk; @@ -314,8 +305,7 @@ static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport, } } read_unlock(&head->lock); - - return tcp_v6_lookup_listener(daddr, hnum, dif); + return NULL; hit: sock_hold(sk); @@ -323,6 +313,21 @@ hit: return sk; } + +static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport, + struct in6_addr *daddr, u16 hnum, + int dif) +{ + struct sock *sk; + + sk = __tcp_v6_lookup_established(saddr, sport, daddr, hnum, dif); + + if (sk) + return sk; + + return tcp_v6_lookup_listener(daddr, hnum, dif); +} + #define tcp_v6_lookup(sa, sp, da, dp, dif) \ ({ struct sock *___sk; \ local_bh_disable(); \ @@ -331,6 +336,46 @@ hit: ___sk; \ }) + +/* + * Open request hash tables. + */ + +static __inline__ unsigned tcp_v6_synq_hash(struct in6_addr *raddr, u16 rport) +{ + unsigned h = raddr->s6_addr32[3] ^ rport; + h ^= h>>16; + h ^= h>>8; + return h&(TCP_SYNQ_HSIZE-1); +} + +static struct open_request *tcp_v6_search_req(struct tcp_opt *tp, + struct ipv6hdr *ip6h, + struct tcphdr *th, + int iif, + struct open_request ***prevp) +{ + struct tcp_listen_opt *lopt = tp->listen_opt; + struct open_request *req, **prev; + __u16 rport = th->source; + + for (prev = &lopt->syn_table[tcp_v6_synq_hash(&ip6h->saddr, rport)]; + (req = *prev) != NULL; + prev = &req->dl_next) { + if (req->rmt_port == rport && + req->class->family == AF_INET6 && + !ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &ip6h->saddr) && + !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &ip6h->daddr) && + (!req->af.v6_req.iif || req->af.v6_req.iif == iif)) { + BUG_TRAP(req->sk == NULL); + *prevp = prev; + return req; + } + } + + return NULL; +} + static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len, struct in6_addr *saddr, struct in6_addr *daddr, @@ -375,10 +420,10 @@ static int tcp_v6_check_established(struct sock *sk) !ipv6_addr_cmp(&tw->v6_daddr, saddr) && !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) && sk2->bound_dev_if == sk->bound_dev_if) { -#ifdef CONFIG_TCP_TW_RECYCLE struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - if (sysctl_tcp_tw_recycle && tw->ts_recent_stamp) { + if (tw->substate == TCP_TIME_WAIT && + sysctl_tcp_tw_recycle && tw->ts_recent_stamp) { /* See comment in tcp_ipv4.c */ if ((tp->write_seq = tw->snd_nxt + 2) == 0) tp->write_seq = 1; @@ -388,8 +433,7 @@ static int tcp_v6_check_established(struct sock *sk) skp = &head->chain; goto unique; } else -#endif - goto not_unique; + goto not_unique; } } tw = NULL; @@ -399,9 +443,7 @@ static int tcp_v6_check_established(struct sock *sk) goto not_unique; } -#ifdef CONFIG_TCP_TW_RECYCLE unique: -#endif BUG_TRAP(sk->pprev==NULL); if ((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; @@ -411,17 +453,16 @@ unique: sock_prot_inc_use(sk->prot); write_unlock_bh(&head->lock); -#ifdef CONFIG_TCP_TW_RECYCLE if (tw) { /* Silly. Should hash-dance instead... */ local_bh_disable(); tcp_tw_deschedule(tw); tcp_timewait_kill(tw); + NET_INC_STATS_BH(TimeWaitRecycled); local_bh_enable(); tcp_tw_put(tw); } -#endif return 0; not_unique: @@ -467,9 +508,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_type; int err; - if (sk->state != TCP_CLOSE) - return(-EISCONN); - if (addr_len < sizeof(struct sockaddr_in6)) return(-EINVAL); @@ -501,18 +539,11 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if(addr_type & IPV6_ADDR_MULTICAST) return -ENETUNREACH; - /* We may need to bind the socket. */ - if (sk->num==0 && sk->prot->get_port(sk, 0)) - return -EAGAIN; - sk->sport = htons(sk->num); - -#ifdef CONFIG_TCP_TW_RECYCLE if (tp->ts_recent_stamp && ipv6_addr_cmp(&np->daddr, &usin->sin6_addr)) { tp->ts_recent = 0; tp->ts_recent_stamp = 0; tp->write_seq = 0; } -#endif ipv6_addr_copy(&np->daddr, &usin->sin6_addr); np->flow_label = fl.fl6_flowlabel; @@ -602,8 +633,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); err = -ENOBUFS; - buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header), - 0, GFP_KERNEL); + buff = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 0, GFP_KERNEL); if (buff == NULL) goto failure; @@ -629,46 +659,6 @@ failure: return err; } -static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len) -{ - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - int retval = -EINVAL; - - lock_sock(sk); - /* - * Do sanity checking for sendmsg/sendto/send - */ - - if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL)) - goto out; - if (msg->msg_name) { - struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name; - - if (msg->msg_namelen < sizeof(*addr)) - goto out; - - if (addr->sin6_family && addr->sin6_family != AF_INET6) - goto out; - retval = -ENOTCONN; - - if(sk->state == TCP_CLOSE) - goto out; - retval = -EISCONN; - if (addr->sin6_port != sk->dport) - goto out; - if (ipv6_addr_cmp(&addr->sin6_addr, &np->daddr)) - goto out; - if (np->sndflow && np->flow_label != (addr->sin6_flowinfo&IPV6_FLOWINFO_MASK)) - goto out; - } - - retval = tcp_do_sendmsg(sk, msg); - -out: - release_sock(sk); - return retval; -} - void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, struct inet6_skb_parm *opt, int type, int code, unsigned char *header, __u32 info) @@ -701,6 +691,9 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, if (sk->lock.users) NET_INC_STATS_BH(LockDroppedIcmps); + if (sk->state == TCP_CLOSE) + goto out; + tp = &sk->tp_pinfo.af_tcp; seq = ntohl(th->seq); if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) { @@ -719,7 +712,7 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, goto out; /* icmp should have updated the destination cache entry */ - dst = sk_dst_check(sk, np->dst_cookie); + dst = __sk_dst_check(sk, np->dst_cookie); if (dst == NULL) { struct flowi fl; @@ -736,7 +729,8 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, fl.uli_u.ports.sport = sk->sport; dst = ip6_route_output(sk, &fl); - } + } else + dst_clone(dst); if (dst->error) { sk->err_soft = -dst->error; @@ -752,7 +746,7 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, /* Might be for an open_request */ switch (sk->state) { - struct open_request *req, *prev; + struct open_request *req, **prev; struct ipv6hdr hd; case TCP_LISTEN: if (sk->lock.users) @@ -765,35 +759,19 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, if (!req) goto out; - if (req->sk) { - struct sock *nsk = req->sk; - - sock_hold(nsk); - bh_unlock_sock(sk); - sock_put(sk); - sk = nsk; - - BUG_TRAP(sk->lock.users==0); - - tp = &sk->tp_pinfo.af_tcp; - if (!between(seq, tp->snd_una, tp->snd_nxt)) { - NET_INC_STATS_BH(OutOfWindowIcmps); - goto out; - } - } else { - if (seq != req->snt_isn) { - NET_INC_STATS_BH(OutOfWindowIcmps); - goto out; - } + /* ICMPs are not backlogged, hence we cannot get + * an established socket here. + */ + BUG_TRAP(req->sk == NULL); - tp->syn_backlog--; - tcp_synq_unlink(tp, req, prev); - tcp_dec_slow_timer(TCP_SLT_SYNACK); - req->class->destructor(req); - tcp_openreq_free(req); + if (seq != req->snt_isn) { + NET_INC_STATS_BH(OutOfWindowIcmps); goto out; } - break; + + tcp_synq_drop(sk, req, prev); + goto out; + case TCP_SYN_SENT: case TCP_SYN_RECV: /* Cannot happen. It can, it SYNs are crossed. --ANK */ @@ -802,7 +780,6 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, sk->err = err; sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ - tcp_set_state(sk, TCP_CLOSE); tcp_done(sk); } else { sk->err_soft = err; @@ -823,12 +800,13 @@ out: } -static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) +static int tcp_v6_send_synack(struct sock *sk, struct open_request *req, + struct dst_entry *dst) { struct sk_buff * skb; - struct dst_entry *dst; struct ipv6_txoptions *opt = NULL; struct flowi fl; + int err = -1; fl.proto = IPPROTO_TCP; fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr; @@ -838,24 +816,26 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) fl.uli_u.ports.dport = req->rmt_port; fl.uli_u.ports.sport = sk->sport; - opt = sk->net_pinfo.af_inet6.opt; - if (opt == NULL && - sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 && - req->af.v6_req.pktopts) { - struct sk_buff *pktopts = req->af.v6_req.pktopts; - struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)pktopts->cb; - if (rxopt->srcrt) - opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(pktopts->nh.raw + rxopt->srcrt)); - } + if (dst == NULL) { + opt = sk->net_pinfo.af_inet6.opt; + if (opt == NULL && + sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 && + req->af.v6_req.pktopts) { + struct sk_buff *pktopts = req->af.v6_req.pktopts; + struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)pktopts->cb; + if (rxopt->srcrt) + opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(pktopts->nh.raw + rxopt->srcrt)); + } - if (opt && opt->srcrt) { - struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; - fl.nl_u.ip6_u.daddr = rt0->addr; - } + if (opt && opt->srcrt) { + struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; + fl.nl_u.ip6_u.daddr = rt0->addr; + } - dst = ip6_route_output(sk, &fl); - if (dst->error) - goto done; + dst = ip6_route_output(sk, &fl); + if (dst->error) + goto done; + } skb = tcp_make_synack(sk, dst, req); if (skb) { @@ -866,21 +846,22 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) csum_partial((char *)th, skb->len, skb->csum)); fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr; - ip6_xmit(sk, skb, &fl, opt); + err = ip6_xmit(sk, skb, &fl, opt); + if (err == NET_XMIT_CN) + err = 0; } done: dst_release(dst); if (opt && opt != sk->net_pinfo.af_inet6.opt) sock_kfree_s(sk, opt, opt->tot_len); + return err; } static void tcp_v6_or_free(struct open_request *req) { - if (req->af.v6_req.pktopts) { + if (req->af.v6_req.pktopts) kfree_skb(req->af.v6_req.pktopts); - req->af.v6_req.pktopts = NULL; - } } static struct or_calltable or_ipv6 = { @@ -907,8 +888,224 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb) } -#define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */ -#define BACKLOGMAX(sk) sysctl_max_syn_backlog +static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, + struct sk_buff *skb) +{ + struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + th->check = 0; + + th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, + csum_partial((char *)th, th->doff<<2, + skb->csum)); +} + + +static void tcp_v6_send_reset(struct sk_buff *skb) +{ + struct tcphdr *th = skb->h.th, *t1; + struct sk_buff *buff; + struct flowi fl; + + if (th->rst) + return; + + if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) + return; + + /* + * We need to grab some memory, and put together an RST, + * and then put it into the queue to be sent. + */ + + buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC); + if (buff == NULL) + return; + + skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr)); + + t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr)); + + /* Swap the send and the receive. */ + memset(t1, 0, sizeof(*t1)); + t1->dest = th->source; + t1->source = th->dest; + t1->doff = sizeof(*t1)/4; + t1->rst = 1; + + if(th->ack) { + t1->seq = th->ack_seq; + } else { + t1->ack = 1; + t1->ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin + + skb->len - (th->doff<<2)); + } + + buff->csum = csum_partial((char *)t1, sizeof(*t1), 0); + + fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr; + fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr; + fl.fl6_flowlabel = 0; + + t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr, + fl.nl_u.ip6_u.daddr, + sizeof(*t1), IPPROTO_TCP, + buff->csum); + + fl.proto = IPPROTO_TCP; + fl.oif = tcp_v6_iif(skb); + fl.uli_u.ports.dport = t1->dest; + fl.uli_u.ports.sport = t1->source; + + /* sk = NULL, but it is safe for now. RST socket required. */ + buff->dst = ip6_route_output(NULL, &fl); + + if (buff->dst->error == 0) { + ip6_xmit(NULL, buff, &fl, NULL); + TCP_INC_STATS_BH(TcpOutSegs); + TCP_INC_STATS_BH(TcpOutRsts); + return; + } + + kfree_skb(buff); +} + +static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts) +{ + struct tcphdr *th = skb->h.th, *t1; + struct sk_buff *buff; + struct flowi fl; + int tot_len = sizeof(struct tcphdr); + + buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC); + if (buff == NULL) + return; + + skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr)); + + if (ts) + tot_len += 3*4; + + t1 = (struct tcphdr *) skb_push(buff,tot_len); + + /* Swap the send and the receive. */ + memset(t1, 0, sizeof(*t1)); + t1->dest = th->source; + t1->source = th->dest; + t1->doff = tot_len/4; + t1->seq = htonl(seq); + t1->ack_seq = htonl(ack); + t1->ack = 1; + t1->window = htons(win); + + if (ts) { + u32 *ptr = (u32*)(t1 + 1); + *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | + TCPOLEN_TIMESTAMP); + *ptr++ = htonl(tcp_time_stamp); + *ptr = htonl(ts); + } + + buff->csum = csum_partial((char *)t1, tot_len, 0); + + fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr; + fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr; + fl.fl6_flowlabel = 0; + + t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr, + fl.nl_u.ip6_u.daddr, + tot_len, IPPROTO_TCP, + buff->csum); + + fl.proto = IPPROTO_TCP; + fl.oif = tcp_v6_iif(skb); + fl.uli_u.ports.dport = t1->dest; + fl.uli_u.ports.sport = t1->source; + + buff->dst = ip6_route_output(NULL, &fl); + + if (buff->dst->error == 0) { + ip6_xmit(NULL, buff, &fl, NULL); + TCP_INC_STATS_BH(TcpOutSegs); + return; + } + + kfree_skb(buff); +} + +static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; + + tcp_v6_send_ack(skb, tw->snd_nxt, tw->rcv_nxt, + tw->rcv_wnd>>tw->rcv_wscale, tw->ts_recent); + + tcp_tw_put(tw); +} + +static void tcp_v6_or_send_ack(struct sk_buff *skb, struct open_request *req) +{ + tcp_v6_send_ack(skb, req->snt_isn+1, req->rcv_isn+1, req->rcv_wnd, req->ts_recent); +} + + +static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) +{ + struct open_request *req, **prev; + struct tcphdr *th = skb->h.th; + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + + /* Find possible connection requests. */ + req = tcp_v6_search_req(tp, skb->nh.ipv6h, th, tcp_v6_iif(skb), &prev); + if (req) + return tcp_check_req(sk, skb, req, prev); + + if (tp->accept_queue) { + struct sock *nsk; + + nsk = __tcp_v6_lookup_established(&skb->nh.ipv6h->saddr, + th->source, + &skb->nh.ipv6h->daddr, + ntohs(th->dest), + tcp_v6_iif(skb)); + + if (nsk) { + if (nsk->state != TCP_TIME_WAIT) { + bh_lock_sock(nsk); + return nsk; + } + tcp_tw_put((struct tcp_tw_bucket*)sk); + return NULL; + } + } + +#if 0 /*def CONFIG_SYN_COOKIES*/ + if (!th->rst && (th->syn || th->ack)) + sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt)); +#endif + return sk; +} + +static void tcp_v6_synq_add(struct sock *sk, struct open_request *req) +{ + struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + struct tcp_listen_opt *lopt = tp->listen_opt; + unsigned h = tcp_v6_synq_hash(&req->af.v6_req.rmt_addr, req->rmt_port); + + req->sk = NULL; + req->expires = jiffies + TCP_TIMEOUT_INIT; + req->retrans = 0; + req->index = h; + req->dl_next = lopt->syn_table[h]; + + write_lock(&tp->syn_wait_lock); + lopt->syn_table[h] = req; + write_unlock(&tp->syn_wait_lock); + + tcp_synq_added(sk); +} + /* FIXME: this is substantially similar to the ipv4 code. * Can some kind of merge be done? -- erics @@ -916,7 +1113,7 @@ static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb) static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { struct tcp_opt tp; - struct open_request *req; + struct open_request *req = NULL; __u32 isn = TCP_SKB_CB(skb)->when; if (skb->protocol == __constant_htons(ETH_P_IP)) @@ -926,44 +1123,31 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) goto drop; - if (isn == 0) - isn = tcp_v6_init_sequence(sk,skb); - /* * There are no SYN attacks on IPv6, yet... */ - if (BACKLOG(sk) >= BACKLOGMAX(sk)) { - (void)(net_ratelimit() && - printk(KERN_INFO "droping syn ack:%d max:%d\n", - BACKLOG(sk), BACKLOGMAX(sk))); + if (tcp_synq_is_full(sk) && !isn) { + if (net_ratelimit()) + printk(KERN_INFO "TCPv6: dropping request, synflood is possible\n"); goto drop; } - req = tcp_openreq_alloc(); - if (req == NULL) { + if (tcp_acceptq_is_full(sk) && tcp_synq_young(sk) > 1) goto drop; - } - - BACKLOG(sk)++; - req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ + req = tcp_openreq_alloc(); + if (req == NULL) + goto drop; - req->rcv_isn = TCP_SKB_CB(skb)->seq; - req->snt_isn = isn; tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; - tp.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); tp.user_mss = sk->tp_pinfo.af_tcp.user_mss; tcp_parse_options(NULL, skb->h.th, &tp, 0); - req->mss = tp.mss_clamp; - req->ts_recent = tp.saw_tstamp ? tp.rcv_tsval : 0; - req->tstamp_ok = tp.tstamp_ok; - req->sack_ok = tp.sack_ok; - req->snd_wscale = tp.snd_wscale; - req->wscale_ok = tp.wscale_ok; - req->rmt_port = skb->h.th->source; + tcp_openreq_init(req, &tp, skb); + + req->class = &or_ipv6; ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr); ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr); req->af.v6_req.pktopts = NULL; @@ -979,34 +1163,26 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (!sk->bound_dev_if && ipv6_addr_type(&req->af.v6_req.rmt_addr)&IPV6_ADDR_LINKLOCAL) req->af.v6_req.iif = tcp_v6_iif(skb); - req->class = &or_ipv6; - req->retrans = 0; - req->sk = NULL; + if (isn == 0) + isn = tcp_v6_init_sequence(sk,skb); - tcp_v6_send_synack(sk, req); + req->snt_isn = isn; - req->expires = jiffies + TCP_TIMEOUT_INIT; - tcp_inc_slow_timer(TCP_SLT_SYNACK); - tcp_synq_queue(&sk->tp_pinfo.af_tcp, req); + if (tcp_v6_send_synack(sk, req, NULL)) + goto drop; + + tcp_v6_synq_add(sk, req); return 0; drop: + if (req) + tcp_openreq_free(req); + TCP_INC_STATS_BH(TcpAttemptFails); return 0; /* don't send reset */ } -static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, - struct sk_buff *skb) -{ - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - th->check = 0; - - th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, - csum_partial((char *)th, th->doff<<2, - skb->csum)); -} - static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, struct dst_entry *dst) @@ -1047,7 +1223,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, /* Charge newly allocated IPv6 socket. Though it is mapped, * it is IPv6 yet. */ +#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr); +#endif MOD_INC_USE_COUNT; /* It is tricky place. Until this moment IPv4 tcp @@ -1061,8 +1239,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, opt = sk->net_pinfo.af_inet6.opt; - if (sk->ack_backlog > sk->max_ack_backlog) - goto out; + if (tcp_acceptq_is_full(sk)) + goto out_overflow; if (sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 && opt == NULL && req->af.v6_req.pktopts) { @@ -1090,15 +1268,14 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, if (dst->error) goto out; - sk->tp_pinfo.af_tcp.syn_backlog--; - sk->ack_backlog++; - newsk = tcp_create_openreq_child(sk, req, skb); if (newsk == NULL) goto out; /* Charge newly allocated IPv6 socket */ +#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr); +#endif MOD_INC_USE_COUNT; ip6_dst_store(newsk, dst, NULL); @@ -1124,6 +1301,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, np->pktoptions = NULL; if (req->af.v6_req.pktopts) { np->pktoptions = skb_clone(req->af.v6_req.pktopts, GFP_ATOMIC); + kfree_skb(req->af.v6_req.pktopts); + req->af.v6_req.pktopts = NULL; if (np->pktoptions) skb_set_owner_r(np->pktoptions, newsk); } @@ -1149,250 +1328,49 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_sync_mss(newsk, dst->pmtu); tcp_initialize_rcv_mss(newsk); + newtp->advmss = dst->advmss; - if (newsk->rcvbuf < (3 * (dst->advmss+60+MAX_HEADER+15))) - newsk->rcvbuf = min ((3 * (dst->advmss+60+MAX_HEADER+15)), sysctl_rmem_max); - if (newsk->sndbuf < (3 * (newtp->mss_clamp+60+MAX_HEADER+15))) - newsk->sndbuf = min ((3 * (newtp->mss_clamp+60+MAX_HEADER+15)), sysctl_wmem_max); + tcp_init_buffer_space(newsk); newsk->daddr = LOOPBACK4_IPV6; newsk->saddr = LOOPBACK4_IPV6; newsk->rcv_saddr= LOOPBACK4_IPV6; - bh_lock_sock(newsk); - __tcp_v6_hash(newsk); tcp_inherit_port(sk, newsk); return newsk; +out_overflow: + NET_INC_STATS_BH(ListenOverflows); out: + NET_INC_STATS_BH(ListenDrops); if (opt && opt != sk->net_pinfo.af_inet6.opt) sock_kfree_s(sk, opt, opt->tot_len); dst_release(dst); return NULL; } -static void tcp_v6_send_reset(struct sk_buff *skb) -{ - struct tcphdr *th = skb->h.th, *t1; - struct sk_buff *buff; - struct flowi fl; - - if (th->rst) - return; - - if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) - return; - - /* - * We need to grab some memory, and put together an RST, - * and then put it into the queue to be sent. - */ - - buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC); - if (buff == NULL) - return; - - skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr)); - - t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr)); - - /* Swap the send and the receive. */ - memset(t1, 0, sizeof(*t1)); - t1->dest = th->source; - t1->source = th->dest; - t1->doff = sizeof(*t1)/4; - t1->rst = 1; - - if(th->ack) { - t1->seq = th->ack_seq; - } else { - t1->ack = 1; - t1->ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin - + skb->len - (th->doff<<2)); - } - - buff->csum = csum_partial((char *)t1, sizeof(*t1), 0); - - fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr; - fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr; - fl.fl6_flowlabel = 0; - - t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr, - fl.nl_u.ip6_u.daddr, - sizeof(*t1), IPPROTO_TCP, - buff->csum); - - fl.proto = IPPROTO_TCP; - fl.oif = tcp_v6_iif(skb); - fl.uli_u.ports.dport = t1->dest; - fl.uli_u.ports.sport = t1->source; - - /* sk = NULL, but it is safe for now. RST socket required. */ - buff->dst = ip6_route_output(NULL, &fl); - - if (buff->dst->error == 0) { - ip6_xmit(NULL, buff, &fl, NULL); - TCP_INC_STATS_BH(TcpOutSegs); - TCP_INC_STATS_BH(TcpOutRsts); - return; - } - - kfree_skb(buff); -} - -static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts) -{ - struct tcphdr *th = skb->h.th, *t1; - struct sk_buff *buff; - struct flowi fl; - int tot_len = sizeof(struct tcphdr); - - buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC); - if (buff == NULL) - return; - - skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr)); - - if (ts) - tot_len += 3*4; - - t1 = (struct tcphdr *) skb_push(buff,tot_len); - - /* Swap the send and the receive. */ - memset(t1, 0, sizeof(*t1)); - t1->dest = th->source; - t1->source = th->dest; - t1->doff = tot_len/4; - t1->seq = htonl(seq); - t1->ack_seq = htonl(ack); - t1->ack = 1; - t1->window = htons(win); - - if (ts) { - u32 *ptr = (u32*)(t1 + 1); - *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_TIMESTAMP << 8) | - TCPOLEN_TIMESTAMP); - *ptr++ = htonl(tcp_time_stamp); - *ptr = htonl(ts); - } - - buff->csum = csum_partial((char *)t1, tot_len, 0); - - fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr; - fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr; - fl.fl6_flowlabel = 0; - - t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr, - fl.nl_u.ip6_u.daddr, - tot_len, IPPROTO_TCP, - buff->csum); - - fl.proto = IPPROTO_TCP; - fl.oif = tcp_v6_iif(skb); - fl.uli_u.ports.dport = t1->dest; - fl.uli_u.ports.sport = t1->source; - - buff->dst = ip6_route_output(NULL, &fl); - - if (buff->dst->error == 0) { - ip6_xmit(NULL, buff, &fl, NULL); - TCP_INC_STATS_BH(TcpOutSegs); - return; - } - - kfree_skb(buff); -} - -static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; - - tcp_v6_send_ack(skb, tw->snd_nxt, tw->rcv_nxt, 0, tw->ts_recent); - - tcp_tw_put(tw); -} - -static void tcp_v6_or_send_ack(struct sk_buff *skb, struct open_request *req) -{ - tcp_v6_send_ack(skb, req->snt_isn+1, req->rcv_isn+1, req->rcv_wnd, req->ts_recent); -} - -static struct open_request *tcp_v6_search_req(struct tcp_opt *tp, - struct ipv6hdr *ip6h, - struct tcphdr *th, - int iif, - struct open_request **prevp) +static int tcp_v6_checksum_init(struct sk_buff *skb) { - struct open_request *req, *prev; - __u16 rport = th->source; - - /* assumption: the socket is not in use. - * as we checked the user count on tcp_rcv and we're - * running from a soft interrupt. - */ - prev = (struct open_request *) (&tp->syn_wait_queue); - for (req = prev->dl_next; req; req = req->dl_next) { - if (req->rmt_port == rport && - req->class->family == AF_INET6 && - !ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &ip6h->saddr) && - !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &ip6h->daddr) && - (!req->af.v6_req.iif || req->af.v6_req.iif == iif)) { - if (req->sk) { - bh_lock_sock(req->sk); - BUG_TRAP(req->sk->lock.users==0); - if (req->sk->state == TCP_CLOSE) { - bh_unlock_sock(req->sk); - prev = req; - continue; - } - } - *prevp = prev; - return req; - } - prev = req; - } - return NULL; -} - - -static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) -{ - struct open_request *req, *prev; - struct tcphdr *th = skb->h.th; - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - - /* Find possible connection requests. */ - req = tcp_v6_search_req(tp, skb->nh.ipv6h, th, tcp_v6_iif(skb), &prev); - if (req) - return tcp_check_req(sk, skb, req, prev); - -#if 0 /*def CONFIG_SYN_COOKIES*/ - if (!th->rst && (th->syn || th->ack)) - sk = cookie_v6_check(sk, skb, &(IPCB(skb)->opt)); -#endif - return sk; -} - - -static int tcp_v6_csum_verify(struct sk_buff *skb) -{ - switch (skb->ip_summed) { - case CHECKSUM_NONE: - skb->csum = csum_partial((char *)skb->h.th, skb->len, 0); - case CHECKSUM_HW: + if (skb->ip_summed == CHECKSUM_HW) { if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,skb->csum)) { - printk(KERN_DEBUG "tcp v6 csum failed\n"); - return 1; + NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n")); + return -1; } skb->ip_summed = CHECKSUM_UNNECESSARY; - default: - /* CHECKSUM_UNNECESSARY */ - }; + } else if (skb->ip_summed != CHECKSUM_UNNECESSARY) { + if (skb->len <= 68) { + if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, + &skb->nh.ipv6h->daddr,csum_partial((char *)skb->h.th, skb->len, 0))) + return -1; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, + &skb->nh.ipv6h->daddr,0); + } + } return 0; } @@ -1435,13 +1413,6 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) IP6_INC_STATS_BH(Ip6InDelivers); - /* - * This doesn't check if the socket has enough room for the packet. - * Either process the packet _without_ queueing it and then free it, - * or do the check later. - */ - skb_set_owner_r(skb, sk); - /* Do Stevens' IPV6_PKTOPTIONS. Yes, guys, it is the only place in our code, where we @@ -1461,23 +1432,20 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) } if (sk->state == TCP_ESTABLISHED) { /* Fast path */ - /* Ready to move deeper ... */ - if (tcp_v6_csum_verify(skb)) - goto csum_err; + TCP_CHECK_TIMER(sk); if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) goto reset; + TCP_CHECK_TIMER(sk); if (users) goto ipv6_pktoptions; return 0; } - if (tcp_v6_csum_verify(skb)) + if (tcp_checksum_complete(skb)) goto csum_err; if (sk->state == TCP_LISTEN) { - struct sock *nsk; - - nsk = tcp_v6_hnd_req(sk, skb); + struct sock *nsk = tcp_v6_hnd_req(sk, skb); if (!nsk) goto discard; @@ -1486,21 +1454,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) * otherwise we just shortcircuit this and continue with * the new socket.. */ - if(nsk != sk) { - int ret; - int state = nsk->state; - - skb_orphan(skb); - BUG_TRAP(nsk->lock.users == 0); - skb_set_owner_r(skb, nsk); - ret = tcp_rcv_state_process(nsk, skb, skb->h.th, skb->len); - - /* Wakeup parent, send SIGIO */ - if (state == TCP_SYN_RECV && nsk->state != state) - sk->data_ready(sk, 0); - bh_unlock_sock(nsk); - - if (ret) + if(nsk != sk) { + if (tcp_child_process(sk, nsk, skb)) goto reset; if (users) kfree_skb(skb); @@ -1508,8 +1463,10 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) } } + TCP_CHECK_TIMER(sk); if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) goto reset; + TCP_CHECK_TIMER(sk); if (users) goto ipv6_pktoptions; return 0; @@ -1588,6 +1545,9 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len) if (len < sizeof(struct tcphdr)) goto bad_packet; + if (tcp_v6_checksum_init(skb) < 0) + goto bad_packet; + TCP_SKB_CB(skb)->seq = ntohl(th->seq); TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + len - th->doff*4); @@ -1608,9 +1568,10 @@ process: bh_lock_sock(sk); ret = 0; - if (!sk->lock.users) - ret = tcp_v6_do_rcv(sk, skb); - else + if (!sk->lock.users) { + if (!tcp_prequeue(sk, skb)) + ret = tcp_v6_do_rcv(sk, skb); + } else sk_add_backlog(sk, skb); bh_unlock_sock(sk); @@ -1618,7 +1579,7 @@ process: return ret; no_tcp_socket: - if (tcp_v6_csum_verify(skb)) { + if (tcp_checksum_complete(skb)) { bad_packet: TCP_INC_STATS_BH(TcpInErrs); } else { @@ -1639,7 +1600,7 @@ discard_and_relse: goto discard_it; do_time_wait: - if (tcp_v6_csum_verify(skb)) { + if (tcp_checksum_complete(skb)) { TCP_INC_STATS_BH(TcpInErrs); sock_put(sk); goto discard_it; @@ -1677,7 +1638,7 @@ static int tcp_v6_rebuild_header(struct sock *sk) struct dst_entry *dst; struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - dst = sk_dst_check(sk, np->dst_cookie); + dst = __sk_dst_check(sk, np->dst_cookie); if (dst == NULL) { struct flowi fl; @@ -1704,12 +1665,9 @@ static int tcp_v6_rebuild_header(struct sock *sk) } ip6_dst_store(sk, dst, NULL); - return 0; } - err = dst->error; - dst_release(dst); - return err; + return 0; } static int tcp_v6_xmit(struct sk_buff *skb) @@ -1732,7 +1690,7 @@ static int tcp_v6_xmit(struct sk_buff *skb) fl.nl_u.ip6_u.daddr = rt0->addr; } - dst = sk_dst_check(sk, np->dst_cookie); + dst = __sk_dst_check(sk, np->dst_cookie); if (dst == NULL) { dst = ip6_route_output(sk, &fl); @@ -1743,11 +1701,10 @@ static int tcp_v6_xmit(struct sk_buff *skb) return -sk->err_soft; } - dst_clone(dst); ip6_dst_store(sk, dst, NULL); } - skb->dst = dst; + skb->dst = dst_clone(dst); /* Restore final destination back after routing done */ fl.nl_u.ip6_u.daddr = &np->daddr; @@ -1767,6 +1724,12 @@ static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) sin6->sin6_flowinfo = 0; } +static int tcp_v6_remember_stamp(struct sock *sk) +{ + /* Alas, not yet... */ + return 0; +} + static struct tcp_func ipv6_specific = { tcp_v6_xmit, tcp_v6_send_check, @@ -1774,6 +1737,7 @@ static struct tcp_func ipv6_specific = { tcp_v6_conn_request, tcp_v6_syn_recv_sock, tcp_v6_hash_connecting, + tcp_v6_remember_stamp, sizeof(struct ipv6hdr), ipv6_setsockopt, @@ -1793,6 +1757,7 @@ static struct tcp_func ipv6_mapped = { tcp_v6_conn_request, tcp_v6_syn_recv_sock, tcp_v4_hash_connecting, + tcp_v4_remember_stamp, sizeof(struct iphdr), ipv6_setsockopt, @@ -1812,6 +1777,7 @@ static int tcp_v6_init_sock(struct sock *sk) skb_queue_head_init(&tp->out_of_order_queue); tcp_init_xmit_timers(sk); + tcp_prequeue_init(tp); tp->rto = TCP_TIMEOUT_INIT; tp->mdev = TCP_TIMEOUT_INIT; @@ -1826,16 +1792,11 @@ static int tcp_v6_init_sock(struct sock *sk) /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. */ - tp->snd_cwnd_cnt = 0; tp->snd_ssthresh = 0x7fffffff; tp->snd_cwnd_clamp = ~0; tp->mss_cache = 536; sk->state = TCP_CLOSE; - sk->max_ack_backlog = SOMAXCONN; - - /* Init SYN queue. */ - tcp_synq_init(tp); sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific; @@ -1847,27 +1808,19 @@ static int tcp_v6_init_sock(struct sock *sk) static int tcp_v6_destroy_sock(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - struct sk_buff *skb; tcp_clear_xmit_timers(sk); - /* - * Cleanup up the write buffer. - */ - - while((skb = __skb_dequeue(&sk->write_queue)) != NULL) - kfree_skb(skb); + /* Cleanup up the write buffer. */ + __skb_queue_purge(&sk->write_queue); - /* - * Cleans up our, hopefuly empty, out_of_order_queue - */ + /* Cleans up our, hopefuly empty, out_of_order_queue. */ + __skb_queue_purge(&tp->out_of_order_queue); - while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL) - kfree_skb(skb); + /* Clean prequeue, it must be empty really */ + __skb_queue_purge(&tp->ucopy.prequeue); - /* Clean up a locked TCP bind bucket, this only happens if a - * port is allocated for a socket, but it never fully connects. - */ + /* Clean up a referenced TCP bind bucket. */ if(sk->prev != NULL) tcp_put_port(sk); @@ -1878,12 +1831,16 @@ static int tcp_v6_destroy_sock(struct sock *sk) static void get_openreq6(struct sock *sk, struct open_request *req, char *tmpbuf, int i) { struct in6_addr *dest, *src; + int ttd = req->expires - jiffies; + + if (ttd < 0) + ttd = 0; src = &req->af.v6_req.loc_addr; dest = &req->af.v6_req.rmt_addr; sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p", + "%02X %08X:%08X %02X:%08X %08X %5d %8d %d %d %p", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], @@ -1894,7 +1851,7 @@ static void get_openreq6(struct sock *sk, struct open_request *req, char *tmpbuf TCP_SYN_RECV, 0,0, /* could print option size, but that is af dependent. */ 1, /* timers active (only the expire timer) */ - (unsigned long)(req->expires - jiffies), + ttd, req->retrans, sk->socket ? sk->socket->inode->i_uid : 0, 0, /* non standard timer */ @@ -1906,7 +1863,7 @@ static void get_tcp6_sock(struct sock *sp, char *tmpbuf, int i) { struct in6_addr *dest, *src; __u16 destp, srcp; - int timer_active, timer_active1, timer_active2; + int timer_active; unsigned long timer_expires; struct tcp_opt *tp = &sp->tp_pinfo.af_tcp; @@ -1914,15 +1871,16 @@ static void get_tcp6_sock(struct sock *sp, char *tmpbuf, int i) src = &sp->net_pinfo.af_inet6.rcv_saddr; destp = ntohs(sp->dport); srcp = ntohs(sp->sport); - timer_active1 = tp->retransmit_timer.prev != NULL; - timer_active2 = sp->timer.prev != NULL; timer_active = 0; timer_expires = (unsigned) -1; - if (timer_active1 && tp->retransmit_timer.expires < timer_expires) { + if (tp->retransmit_timer.prev != NULL && tp->retransmit_timer.expires < timer_expires) { timer_active = 1; timer_expires = tp->retransmit_timer.expires; + } else if (tp->probe_timer.prev != NULL && tp->probe_timer.expires < timer_expires) { + timer_active = 4; + timer_expires = tp->probe_timer.expires; } - if (timer_active2 && sp->timer.expires < timer_expires) { + if (sp->timer.prev != NULL && sp->timer.expires < timer_expires) { timer_active = 2; timer_expires = sp->timer.expires; } @@ -1931,7 +1889,7 @@ static void get_tcp6_sock(struct sock *sp, char *tmpbuf, int i) sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld %d %p", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld %d %p %u %u %u %u", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, @@ -1942,28 +1900,27 @@ static void get_tcp6_sock(struct sock *sp, char *tmpbuf, int i) timer_active, timer_expires-jiffies, tp->retransmits, sp->socket ? sp->socket->inode->i_uid : 0, - 0, + tp->probes_out, sp->socket ? sp->socket->inode->i_ino : 0, - atomic_read(&sp->refcnt), sp); + atomic_read(&sp->refcnt), sp, + tp->rto, tp->ack.ato, tp->ack.quick, tp->ack.pingpong + ); } static void get_timewait6_sock(struct tcp_tw_bucket *tw, char *tmpbuf, int i) { struct in6_addr *dest, *src; __u16 destp, srcp; - int slot_dist; + int ttd = tw->ttd - jiffies; + + if (ttd < 0) + ttd = 0; dest = &tw->v6_daddr; src = &tw->v6_rcv_saddr; destp = ntohs(tw->dport); srcp = ntohs(tw->sport); - slot_dist = tw->death_slot; - if(slot_dist > tcp_tw_death_row_slot) - slot_dist = (TCP_TWKILL_SLOTS - slot_dist) + tcp_tw_death_row_slot; - else - slot_dist = tcp_tw_death_row_slot - slot_dist; - sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " "%02X %08X:%08X %02X:%08X %08X %5d %8d %d %d %p", @@ -1972,8 +1929,8 @@ static void get_timewait6_sock(struct tcp_tw_bucket *tw, char *tmpbuf, int i) src->s6_addr32[2], src->s6_addr32[3], srcp, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, - TCP_TIME_WAIT, 0, 0, - 3, slot_dist * TCP_TWKILL_PERIOD, 0, 0, 0, 0, + tw->substate, 0, 0, + 3, ttd, 0, 0, 0, 0, atomic_read(&tw->refcnt), tw); } @@ -2002,6 +1959,8 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) tcp_listen_lock(); for(i = 0; i < TCP_LHTABLE_SIZE; i++) { struct sock *sk = tcp_listening_hash[i]; + struct tcp_listen_opt *lopt; + int k; for (sk = tcp_listening_hash[i]; sk; sk = sk->next, num++) { struct open_request *req; @@ -2019,24 +1978,29 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) } } - lock_sock(sk); - for (req = tp->syn_wait_queue; req; req = req->dl_next, num++) { - if (req->sk) - continue; - if (req->class->family != PF_INET6) - continue; - pos += LINE_LEN+1; - if (pos < offset) - continue; - get_openreq6(sk, req, tmpbuf, num); - len += sprintf(buffer+len, LINE_FMT, tmpbuf); - if(len >= length) { - release_sock(sk); - tcp_listen_unlock(); - goto out_no_bh; + read_lock_bh(&tp->syn_wait_lock); + lopt = tp->listen_opt; + if (lopt && lopt->qlen != 0) { + for (k=0; k<TCP_SYNQ_HSIZE; k++) { + for (req = lopt->syn_table[k]; req; req = req->dl_next, num++) { + if (req->class->family != PF_INET6) + continue; + pos += LINE_LEN+1; + if (pos < offset) + continue; + get_openreq6(sk, req, tmpbuf, num); + len += sprintf(buffer+len, LINE_FMT, tmpbuf); + if(len >= length) { + read_unlock_bh(&tp->syn_wait_lock); + tcp_listen_unlock(); + goto out_no_bh; + } + } } } - release_sock(sk); + read_unlock_bh(&tp->syn_wait_lock); + + /* Completed requests are in normal socket hash table */ } } tcp_listen_unlock(); @@ -2100,25 +2064,19 @@ struct proto tcpv6_prot = { tcp_v6_connect, /* connect */ tcp_disconnect, /* disconnect */ tcp_accept, /* accept */ - NULL, /* retransmit */ - tcp_write_wakeup, /* write_wakeup */ - tcp_read_wakeup, /* read_wakeup */ - tcp_poll, /* poll */ tcp_ioctl, /* ioctl */ tcp_v6_init_sock, /* init */ tcp_v6_destroy_sock, /* destroy */ tcp_shutdown, /* shutdown */ tcp_setsockopt, /* setsockopt */ tcp_getsockopt, /* getsockopt */ - tcp_v6_sendmsg, /* sendmsg */ + tcp_sendmsg, /* sendmsg */ tcp_recvmsg, /* recvmsg */ NULL, /* bind */ tcp_v6_do_rcv, /* backlog_rcv */ tcp_v6_hash, /* hash */ tcp_unhash, /* unhash */ tcp_v6_get_port, /* get_port */ - 128, /* max_header */ - 0, /* retransmits */ "TCPv6", /* name */ }; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 3ecc55030..a5984354b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.48 2000/01/09 02:19:53 davem Exp $ + * $Id: udp.c,v 1.50 2000/01/18 08:24:24 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -366,54 +366,19 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, msg->msg_flags |= MSG_TRUNC; } -#ifndef CONFIG_UDP_DELAY_CSUM - err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov, copied); -#else if (skb->ip_summed==CHECKSUM_UNNECESSARY) { err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); - } else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) { - if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) { - /* Clear queue. */ - if (flags&MSG_PEEK) { - int clear = 0; - spin_lock_irq(&sk->receive_queue.lock); - if (skb == skb_peek(&sk->receive_queue)) { - __skb_unlink(skb, &sk->receive_queue); - clear = 1; - } - spin_unlock_irq(&sk->receive_queue.lock); - if (clear) - kfree_skb(skb); - } - - /* Error for blocking case is chosen to masquerade - as some normal condition. - */ - err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; - udp_stats_in6.UdpInErrors++; - goto out_free; - } + } else if (msg->msg_flags&MSG_TRUNC) { + if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) + goto csum_copy_err; err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); } else { - unsigned int csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum); - - err = 0; - csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err); + err = copy_and_csum_toiovec(msg->msg_iov, skb, sizeof(struct udphdr)); if (err) - goto out_free; - if ((unsigned short)csum_fold(csum)) { - /* Error for blocking case is chosen to masquerade - as some normal condition. - */ - err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; - udp_stats_in6.UdpInErrors++; - goto out_free; - } + goto csum_copy_err; } -#endif if (err) goto out_free; @@ -447,6 +412,27 @@ out_free: skb_free_datagram(sk, skb); out: return err; + +csum_copy_err: + /* Clear queue. */ + if (flags&MSG_PEEK) { + int clear = 0; + spin_lock_irq(&sk->receive_queue.lock); + if (skb == skb_peek(&sk->receive_queue)) { + __skb_unlink(skb, &sk->receive_queue); + clear = 1; + } + spin_unlock_irq(&sk->receive_queue.lock); + if (clear) + kfree_skb(skb); + } + + /* Error for blocking case is chosen to masquerade + as some normal condition. + */ + err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; + UDP6_INC_STATS_USER(UdpInErrors); + goto out_free; } void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr, @@ -474,7 +460,7 @@ void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr, !sk->net_pinfo.af_inet6.recverr) goto out; - if (sk->bsdism && sk->state!=TCP_ESTABLISHED && + if (sk->state!=TCP_ESTABLISHED && !sk->net_pinfo.af_inet6.recverr) goto out; @@ -489,7 +475,7 @@ out: static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) { -#if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM) +#if defined(CONFIG_FILTER) if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) { if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) { UDP6_INC_STATS_BH(UdpInErrors); @@ -621,24 +607,12 @@ int udpv6_rcv(struct sk_buff *skb, unsigned long len) skb_trim(skb, ulen); -#ifndef CONFIG_UDP_DELAY_CSUM - switch (skb->ip_summed) { - case CHECKSUM_NONE: - skb->csum = csum_partial((char*)uh, ulen, 0); - case CHECKSUM_HW: - if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) { - printk(KERN_DEBUG "IPv6: udp checksum error\n"); - goto discard; - } - }; -#else if (skb->ip_summed==CHECKSUM_HW) { if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) goto discard; skb->ip_summed = CHECKSUM_UNNECESSARY; } else if (skb->ip_summed != CHECKSUM_UNNECESSARY) skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0); -#endif len = ulen; @@ -651,7 +625,7 @@ int udpv6_rcv(struct sk_buff *skb, unsigned long len) } /* Unicast */ - + /* * check socket cache ... must talk to Alan about his plans * for sock caches... i'll skip this for now. @@ -660,11 +634,9 @@ int udpv6_rcv(struct sk_buff *skb, unsigned long len) sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest, dev->ifindex); if (sk == NULL) { -#ifdef CONFIG_UDP_DELAY_CSUM if (skb->ip_summed != CHECKSUM_UNNECESSARY && (unsigned short)csum_fold(csum_partial((char*)uh, len, skb->csum))) goto discard; -#endif UDP6_INC_STATS_BH(UdpNoPorts); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev); @@ -672,12 +644,6 @@ int udpv6_rcv(struct sk_buff *skb, unsigned long len) kfree_skb(skb); return(0); } - if (0/*sk->user_callback && - sk->user_callback(sk->user_data, skb) == 0*/) { - UDP6_INC_STATS_BH(UdpInDatagrams); - sock_put(sk); - return(0); - } /* deliver */ @@ -980,10 +946,6 @@ struct proto udpv6_prot = { udpv6_connect, /* connect */ udp_disconnect, /* disconnect */ NULL, /* accept */ - NULL, /* retransmit */ - NULL, /* write_wakeup */ - NULL, /* read_wakeup */ - datagram_poll, /* poll */ udp_ioctl, /* ioctl */ NULL, /* init */ inet6_destroy_sock, /* destroy */ @@ -997,8 +959,6 @@ struct proto udpv6_prot = { udp_v6_hash, /* hash */ udp_v6_unhash, /* unhash */ udp_v6_get_port, /* get_port */ - 128, /* max_header */ - 0, /* retransmits */ "UDP", /* name */ }; |