diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 106 |
1 files changed, 57 insertions, 49 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3bbfcca17c0a..19ee97acc76d 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.128 2000/12/08 17:15:54 davem Exp $ + * $Id: tcp_ipv6.c,v 1.135 2001/04/06 18:41:36 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -635,6 +635,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, } ip6_dst_store(sk, dst, NULL); + sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; if (saddr == NULL) { err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf); @@ -678,25 +679,23 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, failure: __sk_dst_reset(sk); sk->dport = 0; + sk->route_caps = 0; return err; } -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) +void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) { + struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; struct in6_addr *saddr = &hdr->saddr; struct in6_addr *daddr = &hdr->daddr; - struct tcphdr *th = (struct tcphdr *)header; + struct tcphdr *th = (struct tcphdr *)(skb->data+offset); struct ipv6_pinfo *np; struct sock *sk; int err; struct tcp_opt *tp; __u32 seq; - if (header + 8 > skb->tail) - return; - sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex); if (sk == NULL) { @@ -914,10 +913,15 @@ 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 = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, - csum_partial((char *)th, th->doff<<2, - skb->csum)); + + if (skb->ip_summed == CHECKSUM_HW) { + th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); + skb->csum = offsetof(struct tcphdr, check); + } else { + th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, + csum_partial((char *)th, th->doff<<2, + skb->csum)); + } } @@ -1298,6 +1302,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, MOD_INC_USE_COUNT; ip6_dst_store(newsk, dst, NULL); + sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; newtp = &(newsk->tp_pinfo.af_tcp); @@ -1371,22 +1376,20 @@ out: static int tcp_v6_checksum_init(struct sk_buff *skb) { if (skb->ip_summed == CHECKSUM_HW) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, + &skb->nh.ipv6h->daddr,skb->csum)) + return 0; + NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n")); + } + if (skb->len <= 76) { if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr,skb->csum)) { - NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n")); + &skb->nh.ipv6h->daddr,skb_checksum(skb, 0, skb->len, 0))) return -1; - } skb->ip_summed = CHECKSUM_UNNECESSARY; } else { - if (skb->len <= 76) { - 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); - } + skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr, + &skb->nh.ipv6h->daddr,0); } return 0; } @@ -1526,46 +1529,45 @@ ipv6_pktoptions: return 0; } -int tcp_v6_rcv(struct sk_buff *skb, unsigned long len) +int tcp_v6_rcv(struct sk_buff *skb) { struct tcphdr *th; struct sock *sk; - struct in6_addr *saddr = &skb->nh.ipv6h->saddr; - struct in6_addr *daddr = &skb->nh.ipv6h->daddr; int ret; - th = skb->h.th; - if (skb->pkt_type != PACKET_HOST) goto discard_it; /* - * Pull up the IP header. + * Count it even if it's bad. */ + TCP_INC_STATS_BH(TcpInSegs); - __skb_pull(skb, skb->h.raw - skb->data); + if (!pskb_may_pull(skb, sizeof(struct tcphdr))) + goto discard_it; - /* - * Count it even if it's bad. - */ + th = skb->h.th; - TCP_INC_STATS_BH(TcpInSegs); + if (th->doff < sizeof(struct tcphdr)/4) + goto bad_packet; + if (!pskb_may_pull(skb, th->doff*4)) + goto discard_it; - if (th->doff < sizeof(struct tcphdr)/4 || - (skb->ip_summed != CHECKSUM_UNNECESSARY && + if ((skb->ip_summed != CHECKSUM_UNNECESSARY && tcp_v6_checksum_init(skb) < 0)) goto bad_packet; + th = skb->h.th; 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); + skb->len - th->doff*4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); TCP_SKB_CB(skb)->when = 0; TCP_SKB_CB(skb)->flags = ip6_get_dsfield(skb->nh.ipv6h); TCP_SKB_CB(skb)->sacked = 0; - skb->used = 0; - sk = __tcp_v6_lookup(saddr, th->source, daddr, ntohs(th->dest), tcp_v6_iif(skb)); + sk = __tcp_v6_lookup(&skb->nh.ipv6h->saddr, th->source, + &skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb)); if (!sk) goto no_tcp_socket; @@ -1591,7 +1593,7 @@ process: return ret; no_tcp_socket: - if (len < (th->doff<<2) || tcp_checksum_complete(skb)) { + if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { bad_packet: TCP_INC_STATS_BH(TcpInErrs); } else { @@ -1612,7 +1614,7 @@ discard_and_relse: goto discard_it; do_time_wait: - if (len < (th->doff<<2) || tcp_checksum_complete(skb)) { + if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { TCP_INC_STATS_BH(TcpInErrs); sock_put(sk); goto discard_it; @@ -1673,10 +1675,12 @@ static int tcp_v6_rebuild_header(struct sock *sk) if (dst->error) { err = dst->error; dst_release(dst); + sk->route_caps = 0; return err; } ip6_dst_store(sk, dst, NULL); + sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; } return 0; @@ -1837,7 +1841,7 @@ static int tcp_v6_destroy_sock(struct sock *sk) /* Cleanup up the write buffer. */ tcp_writequeue_purge(sk); - /* Cleans up our, hopefuly empty, out_of_order_queue. */ + /* Cleans up our, hopefully empty, out_of_order_queue. */ __skb_queue_purge(&tp->out_of_order_queue); /* Clean prequeue, it must be empty really */ @@ -1847,6 +1851,10 @@ static int tcp_v6_destroy_sock(struct sock *sk) if(sk->prev != NULL) tcp_put_port(sk); + /* If sendmsg cached page exists, toss it. */ + if (tp->sndmsg_page != NULL) + __free_page(tp->sndmsg_page); + atomic_dec(&tcp_sockets_allocated); return inet6_destroy_sock(sk); @@ -1967,7 +1975,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) off_t begin, pos = 0; char tmpbuf[LINE_LEN+2]; - if(offset < LINE_LEN+1) + if (offset < LINE_LEN+1) len += sprintf(buffer, LINE_FMT, " sl " /* 6 */ "local_address " /* 38 */ @@ -1997,7 +2005,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) if (pos >= offset) { get_tcp6_sock(sk, tmpbuf, num); len += sprintf(buffer+len, LINE_FMT, tmpbuf); - if (len >= length) { + if (pos >= offset + length) { tcp_listen_unlock(); goto out_no_bh; } @@ -2016,7 +2024,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) continue; get_openreq6(sk, req, tmpbuf, num, uid); len += sprintf(buffer+len, LINE_FMT, tmpbuf); - if(len >= length) { + if (pos >= offset + length) { read_unlock_bh(&tp->syn_wait_lock); tcp_listen_unlock(); goto out_no_bh; @@ -2048,7 +2056,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) continue; get_tcp6_sock(sk, tmpbuf, num); len += sprintf(buffer+len, LINE_FMT, tmpbuf); - if(len >= length) { + if (pos >= offset + length) { read_unlock(&head->lock); goto out; } @@ -2063,7 +2071,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) continue; get_timewait6_sock(tw, tmpbuf, num); len += sprintf(buffer+len, LINE_FMT, tmpbuf); - if(len >= length) { + if (pos >= offset + length) { read_unlock(&head->lock); goto out; } @@ -2078,7 +2086,7 @@ out_no_bh: begin = len - (pos - offset); *start = buffer + begin; len -= begin; - if(len > length) + if (len > length) len = length; if (len < 0) len = 0; |
