📄 tcp_ipv4.c
字号:
* Send an ACK for a socket less packet (needed for time wait) * * FIXME: Does not echo timestamps yet. * * Assumes that the caller did basic address and flag checks. */static void tcp_v4_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack, __u16 window){ struct tcphdr *th = skb->h.th; struct tcphdr rth; struct ip_reply_arg arg; /* Swap the send and the receive. */ memset(&rth, 0, sizeof(struct tcphdr)); rth.dest = th->source; rth.source = th->dest; rth.doff = sizeof(struct tcphdr)/4; rth.seq = seq; rth.ack_seq = ack; rth.ack = 1; rth.window = htons(window); memset(&arg, 0, sizeof arg); arg.iov[0].iov_base = (unsigned char *)&rth; arg.iov[0].iov_len = sizeof rth; arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr, skb->nh.iph->saddr, /*XXX*/ sizeof(struct tcphdr), IPPROTO_TCP, 0); arg.n_iov = 1; arg.csumoffset = offsetof(struct tcphdr, check) / 2; ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth); tcp_statistics.TcpOutSegs++;}#ifdef CONFIG_IP_TRANSPARENT_PROXY/* Seems, I never wrote nothing more stupid. I hope Gods will forgive me, but I cannot forgive myself 8) --ANK (981001) */static struct sock *tcp_v4_search_proxy_openreq(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4); struct sock *sk; int i; for (i=0; i<TCP_LHTABLE_SIZE; i++) { for(sk = tcp_listening_hash[i]; sk; sk = sk->next) { struct open_request *dummy; if (tcp_v4_search_req(&sk->tp_pinfo.af_tcp, iph, th, &dummy) && (!sk->bound_dev_if || sk->bound_dev_if == skb->dev->ifindex)) return sk; } } return NULL;}/* * Check whether a received TCP packet might be for one of our * connections. */int tcp_chkaddr(struct sk_buff *skb){ struct iphdr *iph = skb->nh.iph; struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4); struct sock *sk; sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest, skb->dev->ifindex); if (!sk) return tcp_v4_search_proxy_openreq(skb) != NULL; if (sk->state == TCP_LISTEN) { struct open_request *dummy; if (tcp_v4_search_req(&sk->tp_pinfo.af_tcp, skb->nh.iph, th, &dummy) && (!sk->bound_dev_if || sk->bound_dev_if == skb->dev->ifindex)) return 1; } /* 0 means accept all LOCAL addresses here, not all the world... */ if (sk->rcv_saddr == 0) return 0; return 1;}#endif/* * Send a SYN-ACK after having received an ACK. * This still operates on a open_request only, not on a big * socket. */ static void tcp_v4_send_synack(struct sock *sk, struct open_request *req){ struct rtable *rt; struct ip_options *opt; struct sk_buff * skb; int mss; /* First, grab a route. */ opt = req->af.v4_req.opt; if(ip_route_output(&rt, ((opt && opt->srr) ? opt->faddr : req->af.v4_req.rmt_addr), req->af.v4_req.loc_addr, RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute, sk->bound_dev_if)) { ip_statistics.IpOutNoRoutes++; return; } if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) { ip_rt_put(rt); ip_statistics.IpOutNoRoutes++; return; } mss = rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr); skb = tcp_make_synack(sk, &rt->u.dst, req, mss); if (skb) { struct tcphdr *th = skb->h.th;#ifdef CONFIG_IP_TRANSPARENT_PROXY th->source = req->lcl_port; /* LVE */#endif th->check = tcp_v4_check(th, skb->len, req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr, csum_partial((char *)th, skb->len, skb->csum)); ip_build_and_send_pkt(skb, sk, req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr, req->af.v4_req.opt); } ip_rt_put(rt);}/* * IPv4 open_request destructor. */ static void tcp_v4_or_free(struct open_request *req){ if(!req->sk && req->af.v4_req.opt) kfree_s(req->af.v4_req.opt, optlength(req->af.v4_req.opt));}static inline void syn_flood_warning(struct sk_buff *skb){ static unsigned long warntime; if (jiffies - warntime > HZ*60) { warntime = jiffies; printk(KERN_INFO "possible SYN flooding on port %d. Sending cookies.\n", ntohs(skb->h.th->dest)); }}/* * Save and compile IPv4 options into the open_request if needed. */static inline struct ip_options * tcp_v4_save_options(struct sock *sk, struct sk_buff *skb){ struct ip_options *opt = &(IPCB(skb)->opt); struct ip_options *dopt = NULL; if (opt && opt->optlen) { int opt_size = optlength(opt); dopt = kmalloc(opt_size, GFP_ATOMIC); if (dopt) { if (ip_options_echo(dopt, skb)) { kfree_s(dopt, opt_size); dopt = NULL; } } } return dopt;}/* * Maximum number of SYN_RECV sockets in queue per LISTEN socket. * One SYN_RECV socket costs about 80bytes on a 32bit machine. * It would be better to replace it with a global counter for all sockets * but then some measure against one socket starving all other sockets * would be needed. */int sysctl_max_syn_backlog = 128; struct or_calltable or_ipv4 = { tcp_v4_send_synack, tcp_v4_or_free, tcp_v4_send_reset};#define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */#define BACKLOGMAX(sk) sysctl_max_syn_backlogint tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, __u32 isn){ struct tcp_opt tp; struct open_request *req; struct tcphdr *th = skb->h.th; __u32 saddr = skb->nh.iph->saddr; __u32 daddr = skb->nh.iph->daddr;#ifdef CONFIG_SYN_COOKIES int want_cookie = 0;#else#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */#endif /* If the socket is dead, don't accept the connection. */ if (sk->dead) goto dead; /* Never answer to SYNs send to broadcast or multicast */ if (((struct rtable *)skb->dst)->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) goto drop; /* XXX: Check against a global syn pool counter. */ if (BACKLOG(sk) > BACKLOGMAX(sk)) {#ifdef CONFIG_SYN_COOKIES if (sysctl_tcp_syncookies) { syn_flood_warning(skb); want_cookie = 1; } else#endif goto drop; } else { if (isn == 0) isn = tcp_v4_init_sequence(sk, skb); BACKLOG(sk)++; } req = tcp_openreq_alloc(); if (req == NULL) { goto dropbacklog; } req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->rcv_isn = TCP_SKB_CB(skb)->seq; tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.mss_clamp = 65535; tcp_parse_options(NULL, th, &tp, want_cookie); if (tp.mss_clamp == 65535) tp.mss_clamp = 576 - sizeof(struct iphdr) - sizeof(struct iphdr); if (sk->tp_pinfo.af_tcp.user_mss && sk->tp_pinfo.af_tcp.user_mss < tp.mss_clamp) tp.mss_clamp = sk->tp_pinfo.af_tcp.user_mss; req->mss = tp.mss_clamp; if (tp.saw_tstamp) req->ts_recent = tp.rcv_tsval; 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 = th->source;#ifdef CONFIG_IP_TRANSPARENT_PROXY req->lcl_port = th->dest ; /* LVE */#endif req->af.v4_req.loc_addr = daddr; req->af.v4_req.rmt_addr = saddr; /* Note that we ignore the isn passed from the TIME_WAIT * state here. That's the price we pay for cookies. */ if (want_cookie) isn = cookie_v4_init_sequence(sk, skb, &req->mss); req->snt_isn = isn; req->af.v4_req.opt = tcp_v4_save_options(sk, skb); req->class = &or_ipv4; req->retrans = 0; req->sk = NULL; tcp_v4_send_synack(sk, req); if (want_cookie) { if (req->af.v4_req.opt) kfree(req->af.v4_req.opt); tcp_v4_or_free(req); tcp_openreq_free(req); } else { req->expires = jiffies + TCP_TIMEOUT_INIT; tcp_inc_slow_timer(TCP_SLT_SYNACK); tcp_synq_queue(&sk->tp_pinfo.af_tcp, req); } return 0;dead: SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk); tcp_statistics.TcpAttemptFails++; return -ENOTCONN; /* send reset */dropbacklog: if (!want_cookie) BACKLOG(sk)--;drop: tcp_statistics.TcpAttemptFails++; return 0;}/* This is not only more efficient than what we used to do, it eliminates * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM * * This function wants to be moved to a common for IPv[46] file. --ANK */struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb){ struct sock *newsk = sk_alloc(PF_INET, GFP_ATOMIC, 0); if(newsk != NULL) { struct tcp_opt *newtp;#ifdef CONFIG_FILTER struct sk_filter *filter;#endif memcpy(newsk, sk, sizeof(*newsk)); newsk->sklist_next = NULL; newsk->state = TCP_SYN_RECV; /* Clone the TCP header template */ newsk->dport = req->rmt_port; atomic_set(&newsk->sock_readers, 0); atomic_set(&newsk->rmem_alloc, 0); skb_queue_head_init(&newsk->receive_queue); atomic_set(&newsk->wmem_alloc, 0); skb_queue_head_init(&newsk->write_queue); atomic_set(&newsk->omem_alloc, 0); newsk->done = 0; newsk->proc = 0; skb_queue_head_init(&newsk->back_log); skb_queue_head_init(&newsk->error_queue);#ifdef CONFIG_FILTER if ((filter = newsk->filter) != NULL) sk_filter_charge(newsk, filter);#endif /* Now setup tcp_opt */ newtp = &(newsk->tp_pinfo.af_tcp); newtp->pred_flags = 0; newtp->rcv_nxt = req->rcv_isn + 1; newtp->snd_nxt = req->snt_isn + 1; newtp->snd_una = req->snt_isn + 1; newtp->srtt = 0; newtp->ato = 0; newtp->snd_wl1 = req->rcv_isn; newtp->snd_wl2 = req->snt_isn; /* RFC1323: The window in SYN & SYN/ACK segments * is never scaled. */ newtp->snd_wnd = ntohs(skb->h.th->window); newtp->max_window = newtp->snd_wnd; newtp->pending = 0; newtp->retransmits = 0; newtp->last_ack_sent = req->rcv_isn + 1; newtp->backoff = 0; newtp->mdev = TCP_TIMEOUT_INIT; /* So many TCP implementations out there (incorrectly) count the * initial SYN frame in their delayed-ACK and congestion control * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */ newtp->snd_cwnd = 2; newtp->rto = TCP_TIMEOUT_INIT; newtp->packets_out = 0; newtp->fackets_out = 0; newtp->retrans_out = 0; newtp->high_seq = 0; newtp->snd_ssthresh = 0x7fffffff; newtp->snd_cwnd_cnt = 0; newtp->dup_acks = 0; newtp->delayed_acks = 0; init_timer(&newtp->retransmit_timer); newtp->retransmit_timer.function = &tcp_retransmit_timer; newtp->retransmit_timer.data = (unsigned long) newsk; init_timer(&newtp->delack_timer); newtp->delack_timer.function = &tcp_delack_timer; newtp->delack_timer.data = (unsigned long) newsk; skb_queue_head_init(&newtp->out_of_order_queue); newtp->send_head = newtp->retrans_head = NULL; newtp->rcv_wup = req->rcv_isn + 1; newtp->write_seq = req->snt_isn + 1; newtp->copied_seq = req->rcv_isn + 1; newtp->saw_tstamp = 0; newtp->mss_clamp = req->mss; init_timer(&newtp->probe_timer); newtp->probe_timer.function = &tcp_probe_timer; newtp->probe_timer.data = (unsigned long) newsk; newtp->probes_out = 0; newtp->syn_seq = req->rcv_isn; newtp->fin_seq = req->rcv_isn; newtp->urg_data = 0; tcp_synq_init(newtp); newtp->syn_backlog = 0; if (skb->len >= 536) newtp->last_seg_size = skb->len; /* Back to base struct sock members. */ newsk->err = 0; newsk->ack_backlog = 0; newsk->max_ack_backlog = SOMAXCONN; newsk->priority = 0; /* IP layer stuff */ newsk->timeout = 0; init_timer(&newsk->timer); newsk->timer.function = &net_timer; newsk->timer.data = (unsigned long) newsk; newsk->socket = NULL; newtp->tstamp_ok = req->tstamp_ok; if((newtp->sack_ok = req->sack_ok) != 0) newtp->num_sacks = 0; newtp->window_clamp = req->window_clamp; newtp->rcv_wnd = req->rcv_wnd; newtp->wscale_ok = req->wscale_ok; if (newtp->wscale_ok) { newtp->snd_wscale = req->snd_wscale; newtp->rcv_wscale = req->rcv_wscale; } else { newtp->snd_wscale = newtp->rcv_wscale = 0; newtp->window_clamp = min(newtp->window_clamp,65535); } if (newtp->tstamp_ok) { newtp->ts_recent = req->ts_recent; newtp->ts_recent_stamp = tcp_time_stamp; newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; } else { newtp->tcp_header_len = sizeof(struct tcphdr); } } return newsk;}/* * The three way handshake has completed - we got a valid synack - * now create the new socket. */struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, struct dst_entry *dst){ struct ip_options *opt = req->af.v4_req.opt; struct tcp_opt *newtp; struct sock *newsk; if (sk->ack_backlog > sk->max_ack_backlog) goto exit; /* head drop */ if (dst == NULL) { struct rtable *rt; if (ip_route_output(&rt, opt && opt->srr ? opt->faddr : req->af.v4_req.rmt_addr, req->af.v4_req.loc_addr, sk->ip_tos|RTO_CONN, 0)) return NULL; dst = &rt->u.dst; }#ifdef CONFIG_IP_TRANSPARENT_PROXY /* The new socket created for transparent proxy may fall * into a non-existed bind bucket because sk->num != newsk->num. * Ensure existance of the bucket now. The placement of the check * later will require to destroy just created newsk in the case of fail. * 1998/04/22 Andrey V. Savochkin <saw@msu.ru> */ if (__tcp_bucket_check(ntohs(skb->h.th->dest))) goto exit;#endif newsk = tcp_create_openreq_child(sk, req, skb); if (!newsk) goto exit; sk->tp_pinfo.af_tcp.syn_backlog--; sk->ack_backlog++; newsk->dst_cache = dst; newtp = &(newsk->tp_pinfo.af_tcp); newsk->daddr = req->af.v4_req.rmt_addr; newsk->saddr = req->af.v4_req.loc_addr; newsk->rcv_saddr = req->af.v4_req.loc_addr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -