TCP 연결 구축 시리즈 - 클 라 이언 트 SYN 세그먼트 발송
커 널 버 전: 3.15.2
나의 블 로그:http://blog.csdn.net/zhangskd
connect 의 TCP 계층 구현
SOCK_STREAM 클래스 socket 의 TCP 계층 작업 함수 집합 인 스 턴 스 는 tcpprot, 그 중 클 라 이언 트 는 tcp 를 사용 합 니 다.v4_connect () 에서 SYN 단 을 보 냅 니 다.
struct proto tcp_prot = {
.name = "TCP",
...
.connect = tcp_v4_connect,
...
.h.hashinfo = &tcp_hashinfo,
...
};
tcp_v4_connect () 는 주로 다음 과 같은 일 을 했다.
1. socket 의 주소 길이 와 사용 하 는 프로 토 콜 족 을 확인 합 니 다.
2. 루트 캐 시 찾기.
3. 이 엔 드 의 IP 를 설정 합 니 다.
4. 전송 제어 블록 이 이미 사용 되 었 다 면 관련 변 수 를 다시 초기 화 합 니 다.
5. 서버 쪽 의 IP 와 포트 를 기록 합 니 다.
6. 연결 상 태 를 TCP 로 업데이트SYN_SENT。
7. 로 컬 포트 를 선택 하면 사용 되 지 않 은 포트 일 수도 있 고 다시 사용 할 수 있 는 포트 일 수도 있 습 니 다.
이 편 은 좀 복잡 해서 다음 편 에 서 는 상세 하 게 소개 할 것 이다.
8. sock 체인 을 로 컬 포트 의 사용자 해시 대기 열 에 넣 고 sock 체인 을 ehash 해시 표 에 넣 습 니 다.
9. 원본 포트 나 목적 포트 가 바 뀌 면 경 로 를 다시 찾 아야 합 니 다.
10. 4 원 그룹 에 따라 이 엔 드 의 초기 시리 얼 번 호 를 설정 합 니 다.
11. 초기 번호 와 현재 시간 에 따라 IP 첫 번 째 ID 필드 값 을 설정 합 니 다.
12. SYN 단 을 만들어 서 보 냅 니 다.
/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
struct inet_sock *inet = inet_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
__be16 orig_sport, orig_dport;
__be32 daddr, nexthop;
struct flowi4 *fl4;
struc rtable *rt;
int err;
struct ip_options_rcu *inet_opt;
/* Socket */
if (addr_len < sizeof(struct sockaddr_in))
return -EINVAL;
/* Socket */
if (usin->sin_family != AF_INET)
return -EAFNOSUPPORT;
nexthop = daddr = usin->sin_addr.s_addr; /* IP */
inet_opt = rcu_dereference_protected(inet->inet_opt, sock_owned_by_user(sk));
if (inet_opt && inet_opt->opt.srr) { /* */
if (! daddr)
return -EINVAL;
nexthop = inet_opt->opt.faddr; /* */
}
orig_sport = inet->inet_sport; /* , , */
orig_dport = usin->sin_port; /* */
fl4 = &inet->cork.fl.u.ip4;
/* */
rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk),
sk->sk_bound_dev_if, IPPROTO_TCP, orig_sport, orig_dport, sk);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
if (err == -ENETUREACH)
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
return err;
}
/* */
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
ip_rt_put(rt);
return -ENETUNREACH;
}
/* */
if (! inet_opt || ! inet_opt->opt.srr)
daddr = fl4->daddr;
/* IP */
if (! inet->inet_saddr)
inet->inet_saddr = fl4->saddr;
inet->inet_rcv_saddr = inet->inet_saddr;
/* , */
if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {
/* Reset inherited state */
tp->rx_opt.ts_recent = 0;
tp->rx_opt.ts_recent_stamp = 0;
/* TCP_REPAIR , */
if (likely(! tp->repair))
tp->write_seq = 0; /* Tail +1 of data held in tcp send buffer */
}
/* tcp_tw_recycle, */
if (tcp_death_row.sysctl_tw_recycle && ! tp->rx_opt.ts_recent_stamp &&
fl4->daddr == daddr)
tcp_fetch_timewait_stamp(sk, &rt->dst);
inet->inet_dport = usin->sin_port; /* */
inet->inet_daddr = daddr; /* IP */
inet_csk(sk)->icsk_ext_hdr_len = 0; /* IP */
if (inet_opt)
inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; /* IP */
tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT; /* MSS, */
/* Socket identity is still unknown (sport may be zero).
* However we set state to SYN-SENT and not releasing socket lock
* select source port, enter ourselves into the hash tables complete
* initialization after this.
*/
tcp_set_state(sk, TCP_SYN_SENT); /* TCP_SYN_SENT */
/* , sk , sk ehash */
err = inet_hash_connect(&tcp_death_row, sk);
if (err)
goto failure;
/* , */
rt = ip_route_newports(fl4, rt, orig_sport, orig_dport, inet->inet_sport,
inet->inet_dport, sk);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
rt = NULL;
goto failure;
}
/* OK, now commit destination to socket. */
sk->sk_gso_type = SKB_GSO_TCPV4;
sk_setup_caps(sk, &rt->dst); /* , */
/* , , 。
* TCP_REPAIR , 。
*/
if (! tp->write_seq && likely(! tp->repair))
tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr, inet->inet_daddr,
inet->inet_sport, usin->sin_port);
inet->inet_id = tp->write_seq ^ jiffies; /* , IP ID */
err = tcp_connect(sk); /* SYN , */
rt = NULL;
if (err)
goto failure;
return 0;
failure:
/* This unhashes the socket and releases the local port, if necessary. */
tcp_set_state(sk, TCP_CLOSE);
ip_rt_put(rt);
sk->sk_route_caps = 0;
inet->inet_dport = 0;
return err;
}
SYN 단의 구조 와 발송
tcp_connect () 는 SYN 단 을 구성 하고 발송 하 는 데 사용 되 며 주로 다음 과 같은 일 을 했 습 니 다.
1. 전송 제어 블록 과 TCP 층 또는 연결 에 관 한 변 수 를 초기 화 합 니 다.
2. 제어 필드 의 초기 화 를 위해 skb 를 신청 합 니 다.
3. 발송 대기 열의 끝 에 skb 를 삽입 하고 관련 변 수 를 업데이트 합 니 다.
4. 발송 함 수 를 호출 하여 skb 를 IP 층 에 전달 합 니 다.
5. 시간 초과 리 셋 타 이 머 를 켜 면 SYN 세그먼트 의 초기 시간 초과 시간 은 1s 입 니 다.
/* Build a SYN and send it off. */
int tcp_connect(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *buff;
int err;
/* TCP */
tcp_connect_init(sk);
/* TCP_REPAIR , SYN SYNACK, */
if (unlikely(tp->repair)) {
tcp_finsih_connect(sk, NULL);
}
buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
if (unlikely(buff == NULL))
return -ENOBUFS; /* No buffer space available */
/* Reserve space for headers. */
skb_reserve(buff, MAX_TCP_HEADER);
/* skb */
tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
tp->retrans_stamp = TCP_SKB_CB(buff)->when = tcp_time_stamp; /* SYN */
tcp_connect_queue_skb(sk, buff); /* skb , */
TCP_ECN_send_syn(sk, buff); /* ECN */
/* TCP Fast Open , SYN */
err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
if (err = -ECONNREFUSED) /* Connection refused */
return err;
/* We change tp->snd_nxt after the tcp_transmit_skb() call in order to
* make this packet get counted in tcpOutSegs.
*/
tp->snd_nxt = tp->write_seq;
tp->pushed_seq = tp->write_seq; /* Last pushed seq, required to talk to windows */
TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);
/* Timer for repeating the SYN until an answer.
* ,SYN 1s。
*/
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
}
대 tcpsock 와 inetconnection_sock 의 일부 변 수 를 초기 화 합 니 다.
/* Do all connect socket setups that can be done AF independent. */
static void tcp_connect_init(struct sock *sk)
{
const struct dst_entry *dst = __sk_dst_get(sk); /* */
struct tcp_sock *tp = tcp_sk(sk);
__u8 rcv_wscale;
/* We'll fix this up when we get a response from the other end.
* See tcp_input.c: tcp_rcv_state_process case TCP_SYN_SENT.
*/
tp->tcp_header_len = sizeof(struct tcphdr) +
(sysctl_tcp_timestamp ? TCPOLEN_TSTAMP_ALIGNED : 0); /* TCP , */
#ifdef CONFIG_TCP_MD5SIG
if (tp->af_specific->md5_lookup(sk, sk) != NULL)
tp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED; /* MD5SIG */
#endif
/* If user gave his TCP_MAXSEG, record it to clamp */
/* TCP_MAXSEG MSS */
if (tp->rx_opt.user_mss)
tp->rx_opt.mss_clamp = tp->rx_opt.user_mss; /* MSS */
tp->max_window = 0; /* */
tcp_mtup_init(sk); /* TCP PMTU */
tcp_sync_mss(sk, dst_mtu(dst)); /* MSS */
if (! tp->window_clamp) /* */
tp->window_clamp = dst_metric(dst, RTAX_WINDOW);
tp->advmss = dst_metric_advmss(dst); /* MSS */
if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < tp->advmss)
tp->advmss = tp->rx_opt.user_mss; /* MSS */
tcp_initialize_rcv_mss(sk); /* MSS */
/* SO_RCVBUF 。
* limit the window selection if the user enforce a smaller rx buffer.
*/
if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
(tp->window_clamp > tcp_full_space(sk) || tp->window_clamp == 0))
tp->window_clamp = tcp_full_space(sk); /* 3/4 sk->sk_rcvbuf */
/* 、 */
tcp_select_initial_window(tcp_full_space(sk),
tp->advmss - (tp->rx_opt.ts_recent_stamp? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
&tp->rcv_wnd,
&tp->window_clamp,
sysctl_tcp_window_scaling,
&rcv_wscale,
dst_metric(dst, RTAX_INITRWND));
tp->rx_opt.rcv_wscale = rcv_wscale; /* */
tp->rcv_ssthresh = tp->rcv_wnd;
sk->sk_err = 0;
sock_reset_flag(sk, SOCK_DONE);
tp->snd_wnd = 0;
tcp_init_wl(tp, 0); /* tp->snd_wl1 ACK */
tp->snd_una = tp->write_seq;
tp->snd_sml = tp->write_seq;
tp->snd_up = tp->write_seq;
tp->snd_nxt = tp->write_seq;
if (likely(! tp->repair)) /* TCP Repair */
tp->rcv_nxt = 0;
else
tp->rcv_tstamp = tcp_time_stamp;
tp->rcv_wup = tp->rcv_nxt;
tp->copied_seq = tp->rcv_nxt;
inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; /* RTO 1s */
inet_csk(sk)->icsk_retransmits = 0;
tcp_clear_retrans(tp);
}
발송 대기 열의 끝 에 skb 를 추가 하고 관련 변 수 를 업데이트 합 니 다.
static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
tcb->end_seq += skb->len;
skb_header_release(skb); /* skb */
__tcp_add_write_queue_tail(sk, skb); /* skb */
/* sk_wmem_queued , skb 、
* sk_buff、sk_shared_info , 。
*/
sk->sk_wmem_queued += skb->truesize; /* */
sk_mem_charge(sk, skb->truesize); /* */
tp->write_seq += tcb->end_seq; /* (+1) */
tp->packets_out += tcp_skb_pcount(skb); /* */
}
/* skb_header_release - release reference to header
* @skb: buffer to operate on
*
* Drop a reference to the header part of the buffer.
* This is done by acquiring a payload reference.
* You must not read from the header part of skb->data after this.
*/
static inline void skb_header_release(struct sk_buff *skb)
{
BUG_ON(skb->nohdr);
skb->nohdr = 1; /* skb */
/* skb */
atomic_add(1 << SKB_DATAREF_SHIFT, &skb_shinfo(skb)->dataref);
}
/* We divide dataref into two halves. The higher 16 bits hold references to
* the payload part of skb->data. The lower 16 bits hold references to the
* entire skb->data. A clone of a headerless skb holds the length of the header
* in skb->hdr_len.
*
* All users must obey the rule that the skb->data reference count must be greater
* than or equal to the payload reference count.
*
* Holding a reference to the payload part means that the user does not care about
* modifications to the header part of skb->data.
*/
#define SKB_DATAREF_SHIFT 16
#define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT - 1)
SYN 세그먼트 ECN 로고 설정.
/* Packet ECN state for a SYN. */
static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
tp->ecn_flags = 0;
/* ECN */
if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1) {
TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR;
tp->ecn_flags = TCP_ECN_OK;
}
}
다음으로 전송:https://www.cnblogs.com/aiwz/p/6333243.html
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.