summaryrefslogtreecommitdiff
path: root/net/ipv4/tcp_ipv4.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r--net/ipv4/tcp_ipv4.c129
1 files changed, 62 insertions, 67 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index bbc8668d80c3..c7962cb3ffd7 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_ipv4.c,v 1.222 2000/12/08 17:15:53 davem Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.228 2001/04/06 18:41:36 davem Exp $
*
* IPv4 specific functions
*
@@ -670,6 +670,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
}
__sk_dst_set(sk, &rt->u.dst);
+ sk->route_caps = rt->u.dst.dev->features;
if (!sk->protinfo.af_inet.opt || !sk->protinfo.af_inet.opt->srr)
daddr = rt->rt_dst;
@@ -717,6 +718,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
tp->ext_header_len = 0;
if (sk->protinfo.af_inet.opt)
tp->ext_header_len = sk->protinfo.af_inet.opt->optlen;
+ sk->protinfo.af_inet.id = tp->write_seq^jiffies;
tp->mss_clamp = 536;
@@ -726,6 +728,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
failure:
__sk_dst_reset(sk);
+ sk->route_caps = 0;
sk->dport = 0;
return err;
}
@@ -850,32 +853,21 @@ static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned
*
*/
-void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
+void tcp_v4_err(struct sk_buff *skb, u32 info)
{
- struct iphdr *iph = (struct iphdr*)dp;
- struct tcphdr *th;
+ struct iphdr *iph = (struct iphdr*)skb->data;
+ struct tcphdr *th = (struct tcphdr*)(skb->data+(iph->ihl<<2));
struct tcp_opt *tp;
int type = skb->h.icmph->type;
int code = skb->h.icmph->code;
-#if ICMP_MIN_LENGTH < 14
- int no_flags = 0;
-#else
-#define no_flags 0
-#endif
struct sock *sk;
__u32 seq;
int err;
- if (len < (iph->ihl << 2) + ICMP_MIN_LENGTH) {
+ if (skb->len < (iph->ihl << 2) + 8) {
ICMP_INC_STATS_BH(IcmpInErrors);
return;
}
-#if ICMP_MIN_LENGTH < 14
- if (len < (iph->ihl << 2) + 14)
- no_flags = 1;
-#endif
-
- th = (struct tcphdr*)(dp+(iph->ihl<<2));
sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, tcp_v4_iif(skb));
if (sk == NULL) {
@@ -921,7 +913,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
if (sk->lock.users == 0)
- do_pmtu_discovery(sk, iph, ntohs(skb->h.icmph->un.frag.mtu));
+ do_pmtu_discovery(sk, iph, info);
goto out;
}
@@ -940,15 +932,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
if (sk->lock.users != 0)
goto out;
- /* The final ACK of the handshake should be already
- * handled in the new socket context, not here.
- * Strictly speaking - an ICMP error for the final
- * ACK should set the opening flag, but that is too
- * complicated right now.
- */
- if (!no_flags && !th->syn && !th->ack)
- goto out;
-
req = tcp_v4_search_req(tp, iph, th, &prev);
if (!req)
goto out;
@@ -976,8 +959,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
case TCP_SYN_RECV: /* Cannot happen.
It can f.e. if SYNs crossed.
*/
- if (!no_flags && !th->syn)
- goto out;
if (sk->lock.users == 0) {
TCP_INC_STATS_BH(TcpAttemptFails);
sk->err = err;
@@ -1023,8 +1004,13 @@ out:
void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
struct sk_buff *skb)
{
- th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
- csum_partial((char *)th, th->doff<<2, skb->csum));
+ if (skb->ip_summed == CHECKSUM_HW) {
+ th->check = ~tcp_v4_check(th, len, sk->saddr, sk->daddr, 0);
+ skb->csum = offsetof(struct tcphdr, check);
+ } else {
+ th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+ csum_partial((char *)th, th->doff<<2, skb->csum));
+ }
}
/*
@@ -1445,6 +1431,7 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
goto exit;
newsk->dst_cache = dst;
+ newsk->route_caps = dst->dev->features;
newtp = &(newsk->tp_pinfo.af_tcp);
newsk->daddr = req->af.v4_req.rmt_addr;
@@ -1457,6 +1444,7 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newtp->ext_header_len = 0;
if (newsk->protinfo.af_inet.opt)
newtp->ext_header_len = newsk->protinfo.af_inet.opt->optlen;
+ newsk->protinfo.af_inet.id = newtp->write_seq^jiffies;
tcp_sync_mss(newsk, dst->pmtu);
newtp->advmss = dst->advmss;
@@ -1512,23 +1500,23 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb)
static int tcp_v4_checksum_init(struct sk_buff *skb)
{
if (skb->ip_summed == CHECKSUM_HW) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ if (!tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
+ skb->nh.iph->daddr,skb->csum))
+ return 0;
+
+ NETDEBUG(printk(KERN_DEBUG "hw tcp v4 csum failed\n"));
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+ if (skb->len <= 76) {
if (tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
- skb->nh.iph->daddr,skb->csum)) {
- NETDEBUG(printk(KERN_DEBUG "hw tcp v4 csum failed\n"));
+ skb->nh.iph->daddr,
+ skb_checksum(skb, 0, skb->len, 0)))
return -1;
- }
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else {
- if (skb->len <= 76) {
- if (tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
- skb->nh.iph->daddr,
- csum_partial((char *)skb->h.th, skb->len, 0)))
- return -1;
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- } else {
- skb->csum = ~tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
- skb->nh.iph->daddr,0);
- }
+ skb->csum = ~tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
+ skb->nh.iph->daddr,0);
}
return 0;
}
@@ -1601,7 +1589,7 @@ csum_err:
* From tcp_input.c
*/
-int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
+int tcp_v4_rcv(struct sk_buff *skb)
{
struct tcphdr *th;
struct sock *sk;
@@ -1610,31 +1598,35 @@ int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
if (skb->pkt_type!=PACKET_HOST)
goto discard_it;
- th = skb->h.th;
-
- /* Pull up the IP header. */
- __skb_pull(skb, skb->h.raw - skb->data);
-
/* Count it even if it's bad */
TCP_INC_STATS_BH(TcpInSegs);
+ if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+ goto discard_it;
+
+ th = skb->h.th;
+
+ if (th->doff < sizeof(struct tcphdr)/4)
+ goto bad_packet;
+ if (!pskb_may_pull(skb, th->doff*4))
+ goto discard_it;
+
/* An explanation is required here, I think.
* Packet length and doff are validated by header prediction,
* provided case of th->doff==0 is elimineted.
* So, we defer the checks. */
- if (th->doff < sizeof(struct tcphdr)/4 ||
- (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+ if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
tcp_v4_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 = skb->nh.iph->tos;
TCP_SKB_CB(skb)->sacked = 0;
- skb->used = 0;
sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
skb->nh.iph->daddr, ntohs(th->dest), tcp_v4_iif(skb));
@@ -1665,7 +1657,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 {
@@ -1682,7 +1674,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);
goto discard_and_relse;
}
@@ -1740,7 +1732,7 @@ static int tcp_v4_reselect_saddr(struct sock *sk)
return err;
__sk_dst_set(sk, &rt->u.dst);
- /* sk->route_caps = rt->u.dst.dev->features; */
+ sk->route_caps = rt->u.dst.dev->features;
new_saddr = rt->rt_src;
@@ -1788,20 +1780,19 @@ int tcp_v4_rebuild_header(struct sock *sk)
sk->bound_dev_if);
if (!err) {
__sk_dst_set(sk, &rt->u.dst);
- /* sk->route_caps = rt->u.dst.dev->features; */
+ sk->route_caps = rt->u.dst.dev->features;
return 0;
}
/* Routing failed... */
- /* sk->route_caps = 0; */
+ sk->route_caps = 0;
if (!sysctl_ip_dynaddr ||
sk->state != TCP_SYN_SENT ||
(sk->userlocks & SOCK_BINDADDR_LOCK) ||
- (err = tcp_v4_reselect_saddr(sk)) != 0) {
+ (err = tcp_v4_reselect_saddr(sk)) != 0)
sk->err_soft=-err;
- /* sk->error_report(sk); */
- }
+
return err;
}
@@ -1940,7 +1931,7 @@ static int tcp_v4_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 */
@@ -1950,6 +1941,10 @@ static int tcp_v4_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 0;
@@ -2076,7 +2071,7 @@ int tcp_get_info(char *buffer, char **start, off_t offset, int length)
if (pos >= offset) {
get_tcp_sock(sk, tmpbuf, num);
len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
- if (len >= length) {
+ if (pos >= offset + length) {
tcp_listen_unlock();
goto out_no_bh;
}
@@ -2097,7 +2092,7 @@ skip_listen:
continue;
get_openreq(sk, req, tmpbuf, num, uid);
len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
- if(len >= length) {
+ if (pos >= offset + length) {
read_unlock_bh(&tp->syn_wait_lock);
tcp_listen_unlock();
goto out_no_bh;
@@ -2129,7 +2124,7 @@ skip_listen:
continue;
get_tcp_sock(sk, tmpbuf, num);
len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
- if(len >= length) {
+ if (pos >= offset + length) {
read_unlock(&head->lock);
goto out;
}
@@ -2144,7 +2139,7 @@ skip_listen:
continue;
get_timewait_sock(tw, tmpbuf, num);
len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
- if(len >= length) {
+ if (pos >= offset + length) {
read_unlock(&head->lock);
goto out;
}
@@ -2159,7 +2154,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;