📄 tcp.c
字号:
(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1| TCPF_FIN_WAIT2|TCPF_SYN_RECV));}int tcp_disconnect(struct sock *sk, int flags){ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; int old_state; int err = 0; old_state = sk->state; if (old_state != TCP_CLOSE) tcp_set_state(sk, TCP_CLOSE); /* ABORT function of RFC793 */ if (old_state == TCP_LISTEN) { tcp_listen_stop(sk); } else if (tcp_need_reset(old_state) || (tp->snd_nxt != tp->write_seq && (1<<old_state)&(TCPF_CLOSING|TCPF_LAST_ACK))) { /* The last check adjusts for discrepance of Linux wrt. RFC * states */ tcp_send_active_reset(sk, gfp_any()); sk->err = ECONNRESET; } else if (old_state == TCP_SYN_SENT) sk->err = ECONNRESET; tcp_clear_xmit_timers(sk); __skb_queue_purge(&sk->receive_queue); tcp_writequeue_purge(sk); __skb_queue_purge(&tp->out_of_order_queue); sk->dport = 0; if (!(sk->userlocks&SOCK_BINDADDR_LOCK)) { sk->rcv_saddr = 0; sk->saddr = 0;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) memset(&sk->net_pinfo.af_inet6.saddr, 0, 16); memset(&sk->net_pinfo.af_inet6.rcv_saddr, 0, 16);#endif } sk->shutdown = 0; sk->done = 0; tp->srtt = 0; if ((tp->write_seq += tp->max_window+2) == 0) tp->write_seq = 1; tp->backoff = 0; tp->snd_cwnd = 2; tp->probes_out = 0; tp->packets_out = 0; tp->snd_ssthresh = 0x7fffffff; tp->snd_cwnd_cnt = 0; tp->ca_state = TCP_CA_Open; tcp_clear_retrans(tp); tcp_delack_init(tp); tp->send_head = NULL; tp->saw_tstamp = 0; tcp_sack_reset(tp); __sk_dst_reset(sk); BUG_TRAP(!sk->num || sk->prev); sk->error_report(sk); return err;}/* * Wait for an incoming connection, avoid race * conditions. This must be called with the socket locked. */static int wait_for_connect(struct sock * sk, long timeo){ DECLARE_WAITQUEUE(wait, current); int err; /* * True wake-one mechanism for incoming connections: only * one process gets woken up, not the 'whole herd'. * Since we do not 'race & poll' for established sockets * anymore, the common case will execute the loop only once. * * Subtle issue: "add_wait_queue_exclusive()" will be added * after any current non-exclusive waiters, and we know that * it will always _stay_ after any new non-exclusive waiters * because all non-exclusive waiters are added at the * beginning of the wait-queue. As such, it's ok to "drop" * our exclusiveness temporarily when we get woken up without * having to remove and re-insert us on the wait queue. */ add_wait_queue_exclusive(sk->sleep, &wait); for (;;) { current->state = TASK_INTERRUPTIBLE; release_sock(sk); if (sk->tp_pinfo.af_tcp.accept_queue == NULL) timeo = schedule_timeout(timeo); lock_sock(sk); err = 0; if (sk->tp_pinfo.af_tcp.accept_queue) break; err = -EINVAL; if (sk->state != TCP_LISTEN) break; err = sock_intr_errno(timeo); if (signal_pending(current)) break; err = -EAGAIN; if (!timeo) break; } current->state = TASK_RUNNING; remove_wait_queue(sk->sleep, &wait); return err;}/* * This will accept the next outstanding connection. */struct sock *tcp_accept(struct sock *sk, int flags, int *err){ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; struct open_request *req; struct sock *newsk; int error; lock_sock(sk); /* We need to make sure that this socket is listening, * and that it has something pending. */ error = -EINVAL; if (sk->state != TCP_LISTEN) goto out; /* Find already established connection */ if (!tp->accept_queue) { long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); /* If this is a non blocking socket don't sleep */ error = -EAGAIN; if (!timeo) goto out; error = wait_for_connect(sk, timeo); if (error) goto out; } req = tp->accept_queue; if ((tp->accept_queue = req->dl_next) == NULL) tp->accept_queue_tail = NULL; newsk = req->sk; tcp_acceptq_removed(sk); tcp_openreq_fastfree(req); BUG_TRAP(newsk->state != TCP_SYN_RECV); release_sock(sk); return newsk;out: release_sock(sk); *err = error; return NULL;}/* * Socket option code for TCP. */ int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen){ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); int val; int err = 0; if (level != SOL_TCP) return tp->af_specific->setsockopt(sk, level, optname, optval, optlen); if(optlen<sizeof(int)) return -EINVAL; if (get_user(val, (int *)optval)) return -EFAULT; lock_sock(sk); switch(optname) { case TCP_MAXSEG: /* values greater than interface MTU won't take effect. however at * the point when this call is done we typically don't yet know * which interface is going to be used */ if(val < 8 || val > MAX_TCP_WINDOW) { err = -EINVAL; break; } tp->user_mss = val; break; case TCP_NODELAY: /* You cannot try to use this and TCP_CORK in * tandem, so let the user know. */ if (tp->nonagle == 2) { err = -EINVAL; break; } tp->nonagle = (val == 0) ? 0 : 1; if (val) tcp_push_pending_frames(sk, tp); break; case TCP_CORK: /* When set indicates to always queue non-full frames. * Later the user clears this option and we transmit * any pending partial frames in the queue. This is * meant to be used alongside sendfile() to get properly * filled frames when the user (for example) must write * out headers with a write() call first and then use * sendfile to send out the data parts. * * You cannot try to use TCP_NODELAY and this mechanism * at the same time, so let the user know. */ if (tp->nonagle == 1) { err = -EINVAL; break; } if (val != 0) { tp->nonagle = 2; } else { tp->nonagle = 0; tcp_push_pending_frames(sk, tp); } break; case TCP_KEEPIDLE: if (val < 1 || val > MAX_TCP_KEEPIDLE) err = -EINVAL; else { tp->keepalive_time = val * HZ; if (sk->keepopen && !((1<<sk->state)&(TCPF_CLOSE|TCPF_LISTEN))) { __u32 elapsed = tcp_time_stamp - tp->rcv_tstamp; if (tp->keepalive_time > elapsed) elapsed = tp->keepalive_time - elapsed; else elapsed = 0; tcp_reset_keepalive_timer(sk, elapsed); } } break; case TCP_KEEPINTVL: if (val < 1 || val > MAX_TCP_KEEPINTVL) err = -EINVAL; else tp->keepalive_intvl = val * HZ; break; case TCP_KEEPCNT: if (val < 1 || val > MAX_TCP_KEEPCNT) err = -EINVAL; else tp->keepalive_probes = val; break; case TCP_SYNCNT: if (val < 1 || val > MAX_TCP_SYNCNT) err = -EINVAL; else tp->syn_retries = val; break; case TCP_LINGER2: if (val < 0) tp->linger2 = -1; else if (val > sysctl_tcp_fin_timeout/HZ) tp->linger2 = 0; else tp->linger2 = val*HZ; break; case TCP_DEFER_ACCEPT: tp->defer_accept = 0; if (val > 0) { /* Translate value in seconds to number of retransmits */ while (tp->defer_accept < 32 && val > ((TCP_TIMEOUT_INIT/HZ)<<tp->defer_accept)) tp->defer_accept++; tp->defer_accept++; } break; case TCP_WINDOW_CLAMP: if (val==0) { if (sk->state != TCP_CLOSE) { err = -EINVAL; break; } tp->window_clamp = 0; } else { tp->window_clamp = val<SOCK_MIN_RCVBUF/2 ? SOCK_MIN_RCVBUF/2 : val; } break; case TCP_QUICKACK: if (!val) { tp->ack.pingpong = 1; } else { tp->ack.pingpong = 0; if ((1<<sk->state)&(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT) && tcp_ack_scheduled(tp)) { tp->ack.pending |= TCP_ACK_PUSHED; cleanup_rbuf(sk, 1); if (!(val & 1)) tp->ack.pingpong = 1; } } break; default: err = -ENOPROTOOPT; break; }; release_sock(sk); return err;}int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen){ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); int val, len; if(level != SOL_TCP) return tp->af_specific->getsockopt(sk, level, optname, optval, optlen); if(get_user(len,optlen)) return -EFAULT; len = min_t(unsigned int, len, sizeof(int)); if(len < 0) return -EINVAL; switch(optname) { case TCP_MAXSEG: val = tp->mss_cache; if (val == 0 && ((1<<sk->state)&(TCPF_CLOSE|TCPF_LISTEN))) val = tp->user_mss; break; case TCP_NODELAY: val = (tp->nonagle == 1); break; case TCP_CORK: val = (tp->nonagle == 2); break; case TCP_KEEPIDLE: val = (tp->keepalive_time ? : sysctl_tcp_keepalive_time)/HZ; break; case TCP_KEEPINTVL: val = (tp->keepalive_intvl ? : sysctl_tcp_keepalive_intvl)/HZ; break; case TCP_KEEPCNT: val = tp->keepalive_probes ? : sysctl_tcp_keepalive_probes; break; case TCP_SYNCNT: val = tp->syn_retries ? : sysctl_tcp_syn_retries; break; case TCP_LINGER2: val = tp->linger2; if (val >= 0) val = (val ? : sysctl_tcp_fin_timeout)/HZ; break; case TCP_DEFER_ACCEPT: val = tp->defer_accept == 0 ? 0 : ((TCP_TIMEOUT_INIT/HZ)<<(tp->defer_accept-1)); break; case TCP_WINDOW_CLAMP: val = tp->window_clamp; break; case TCP_INFO: { struct tcp_info info; u32 now = tcp_time_stamp; if(get_user(len,optlen)) return -EFAULT; info.tcpi_state = sk->state; info.tcpi_ca_state = tp->ca_state; info.tcpi_retransmits = tp->retransmits; info.tcpi_probes = tp->probes_out; info.tcpi_backoff = tp->backoff; info.tcpi_options = 0; if (tp->tstamp_ok) info.tcpi_options |= TCPI_OPT_TIMESTAMPS; if (tp->sack_ok) info.tcpi_options |= TCPI_OPT_SACK; if (tp->wscale_ok) { info.tcpi_options |= TCPI_OPT_WSCALE; info.tcpi_snd_wscale = tp->snd_wscale; info.tcpi_rcv_wscale = tp->rcv_wscale; } else { info.tcpi_snd_wscale = 0; info.tcpi_rcv_wscale = 0; } if (tp->ecn_flags&TCP_ECN_OK) info.tcpi_options |= TCPI_OPT_ECN; info.tcpi_rto = (1000000*tp->rto)/HZ; info.tcpi_ato = (1000000*tp->ack.ato)/HZ; info.tcpi_snd_mss = tp->mss_cache; info.tcpi_rcv_mss = tp->ack.rcv_mss; info.tcpi_unacked = tp->packets_out; info.tcpi_sacked = tp->sacked_out; info.tcpi_lost = tp->lost_out; info.tcpi_retrans = tp->retrans_out; info.tcpi_fackets = tp->fackets_out; info.tcpi_last_data_sent = ((now - tp->lsndtime)*1000)/HZ; info.tcpi_last_ack_sent = 0; info.tcpi_last_data_recv = ((now - tp->ack.lrcvtime)*1000)/HZ; info.tcpi_last_ack_recv = ((now - tp->rcv_tstamp)*1000)/HZ; info.tcpi_pmtu = tp->pmtu_cookie; info.tcpi_rcv_ssthresh = tp->rcv_ssthresh; info.tcpi_rtt = ((1000000*tp->srtt)/HZ)>>3; info.tcpi_rttvar = ((1000000*tp->mdev)/HZ)>>2; info.tcpi_snd_ssthresh = tp->snd_ssthresh; info.tcpi_snd_cwnd = tp->snd_cwnd; info.tcpi_advmss = tp->advmss; info.tcpi_reordering = tp->reordering; len = min_t(unsigned int, len, sizeof(info)); if(put_user(len, optlen)) return -EFAULT; if(copy_to_user(optval, &info,len)) return -EFAULT; return 0; } case TCP_QUICKACK: val = !tp->ack.pingpong; break; default: return -ENOPROTOOPT; }; if(put_user(len, optlen)) return -EFAULT; if(copy_to_user(optval, &val,len)) return -EFAULT; return 0;}extern void __skb_cb_too_small_for_tcp(int, int);extern void tcpdiag_init(void);void __init tcp_init(void){ struct sk_buff *skb = NULL; unsigned long goal; int order, i; if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)) __skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb), sizeof(skb->cb)); tcp_openreq_cachep = kmem_cache_create("tcp_open_request", sizeof(struct open_request), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!tcp_openreq_cachep) panic("tcp_init: Cannot alloc open_request cache."); tcp_bucket_cachep = kmem_cache_create("tcp_bind_bucket", sizeof(struct tcp_bind_bucket), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!tcp_bucket_cachep) panic("tcp_init: Cannot alloc tcp_bind_bucket cache."); tcp_timewait_cachep = kmem_cache_create("tcp_tw_bucket", sizeof(struct tcp_tw_bucket), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!tcp_timewait_cachep) panic("tcp_init: Cannot alloc tcp_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -