summaryrefslogtreecommitdiff
path: root/net/ipv6/tcp_ipv6.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
-rw-r--r--net/ipv6/tcp_ipv6.c106
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;