亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1168 | 回復: 0
打印 上一主題 下一主題

內核中的TCP的追蹤分析-18-TCP(IPV4)的客戶端與服務器端socket連接過程-5 [復制鏈接]

論壇徽章:
0
跳轉到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2008-11-24 09:13 |只看該作者 |倒序瀏覽
我們上一節(jié)講到了客戶端請求到達服務器中的tcp_v4_conn_request()函數(shù)中時,服務器會給客戶端發(fā)送一個ack,“第二次握手”的內容我們在上節(jié)中提到放在本文中完成,我們在tcp_v4_conn_request()函數(shù)中看到“第二次握手”是由__tcp_v4_send_synack()函數(shù)完成的
static int __tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
                struct dst_entry *dst)
{
    const struct inet_request_sock *ireq = inet_rsk(req);
    int err = -1;
    struct sk_buff * skb;
    /* First, grab a route. */
    if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)
        return -1;
    skb = tcp_make_synack(sk, dst, req);
    if (skb) {
        struct tcphdr *th = tcp_hdr(skb);
        th->check = tcp_v4_check(skb->len,
                     ireq->loc_addr,
                     ireq->rmt_addr,
                     csum_partial((char *)th, skb->len,
                         skb->csum));
        err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
                     ireq->rmt_addr,
                     ireq->opt);
        err = net_xmit_eval(err);
    }
    dst_release(dst);
    return err;
}
我們看到根據(jù)我們客戶端的requst_sock結構得到inet_request_sock結構指針變量ireq,這個結構體我們在上一節(jié)
http://blog.chinaunix.net/u2/64681/showart.php?id=1657954
中看到了。我們在上一節(jié)的tcp_v4_conn_request()函數(shù)中看到了取得路由的相關信息
dst = inet_csk_route_req(sk, req),在那個函數(shù)中會調用ip_route_output_flow()來查找路由表并且得到路由的信息,ip_route_output_flow()函數(shù)我們在第8節(jié)
http://blog.chinaunix.net/u2/64681/showart.php?id=1408613
的文章末尾處看到了,在那里我們還并沒有深入探討路由的一些細節(jié),但是在將來我們會再次具體的分析這里。所以這里還是圍繞著主線繼續(xù)往下看,我們看到在上邊的__tcp_v4_send_synack()函數(shù)中再次看一下路由信息是否已經找到了,如果沒有的話再次調用inet_csk_route_req()查找,如果還是沒有找到就不能進行下去了。接下來要調用tcp_make_synack()函數(shù)準備一個SYN的ack包
struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
                struct request_sock *req)
{
    struct inet_request_sock *ireq = inet_rsk(req);
    struct tcp_sock *tp = tcp_sk(sk);
    struct tcphdr *th;
    int tcp_header_size;
    struct sk_buff *skb;
#ifdef CONFIG_TCP_MD5SIG
    struct tcp_md5sig_key *md5;
    __u8 *md5_hash_location;
#endif
    skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC);
    if (skb == NULL)
        return NULL;
    /* Reserve space for headers. */
    skb_reserve(skb, MAX_TCP_HEADER);
    skb->dst = dst_clone(dst);
    tcp_header_size = (sizeof(struct tcphdr) + TCPOLEN_MSS +
             (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) +
             (ireq->wscale_ok ? TCPOLEN_WSCALE_ALIGNED : 0) +
             /* SACK_PERM is in the place of NOP NOP of TS */
             ((ireq->sack_ok && !ireq->tstamp_ok) ? TCPOLEN_SACKPERM_ALIGNED : 0));
#ifdef CONFIG_TCP_MD5SIG
    /* Are we doing MD5 on this segment? If so - make room for it */
    md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
    if (md5)
        tcp_header_size += TCPOLEN_MD5SIG_ALIGNED;
#endif
    skb_push(skb, tcp_header_size);
    skb_reset_transport_header(skb);
    th = tcp_hdr(skb);
    memset(th, 0, sizeof(struct tcphdr));
    th->syn = 1;
    th->ack = 1;
    TCP_ECN_make_synack(req, th);
    th->source = inet_sk(sk)->sport;
    th->dest = ireq->rmt_port;
    /* Setting of flags are superfluous here for callers (and ECE is
     * not even correctly set)
     */
    tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
             TCPCB_FLAG_SYN | TCPCB_FLAG_ACK);
    th->seq = htonl(TCP_SKB_CB(skb)->seq);
    th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1);
    if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
        __u8 rcv_wscale;
        /* Set this up on the first call only */
        req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
        /* tcp_full_space because it is guaranteed to be the first packet */
        tcp_select_initial_window(tcp_full_space(sk),
            dst_metric(dst, RTAX_ADVMSS) - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
            &req->rcv_wnd,
            &req->window_clamp,
            ireq->wscale_ok,
            &rcv_wscale);
        ireq->rcv_wscale = rcv_wscale;
    }
    /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
    th->window = htons(min(req->rcv_wnd, 65535U));
#ifdef CONFIG_SYN_COOKIES
    if (unlikely(req->cookie_ts))
        TCP_SKB_CB(skb)->when = cookie_init_timestamp(req);
    else
#endif
    TCP_SKB_CB(skb)->when = tcp_time_stamp;
    tcp_syn_build_options((__be32 *)(th + 1), dst_metric(dst, RTAX_ADVMSS), ireq->tstamp_ok,
             ireq->sack_ok, ireq->wscale_ok, ireq->rcv_wscale,
             TCP_SKB_CB(skb)->when,
             req->ts_recent,
             (
#ifdef CONFIG_TCP_MD5SIG
             md5 ? &md5_hash_location :
#endif
             NULL)
             );
    th->doff = (tcp_header_size >> 2);
    TCP_INC_STATS(TCP_MIB_OUTSEGS);
#ifdef CONFIG_TCP_MD5SIG
    /* Okay, we have all we need - do the md5 hash if needed */
    if (md5) {
        tp->af_specific->calc_md5_hash(md5_hash_location,
                     md5,
                     NULL, dst, req,
                     tcp_hdr(skb), sk->sk_protocol,
                     skb->len);
    }
#endif
    return skb;
}
這個函數(shù)我們就不細加分析了,總體來說是為“應答”的第二次握手準備一個skb數(shù)據(jù)結構,然后設置其相應的tcp頭信息,我們看到其tcp的頭設置了
         th->syn = 1;
         th->ack = 1;
當然還有路由信息也進行了設置,我們不細看了,繼續(xù)往下看__tcp_v4_send_synack()函數(shù)中的代碼,通過tcp_v4_check()計算并設置檢驗和后進入了ip_build_and_send_pkt()來建立ip頭并發(fā)送數(shù)據(jù)包
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
             __be32 saddr, __be32 daddr, struct ip_options *opt)
{
    struct inet_sock *inet = inet_sk(sk);
    struct rtable *rt = skb->rtable;
    struct iphdr *iph;
    /* Build the IP header. */
    skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
    skb_reset_network_header(skb);
    iph = ip_hdr(skb);
    iph->version = 4;
    iph->ihl = 5;
    iph->tos = inet->tos;
    if (ip_dont_fragment(sk, &rt->u.dst))
        iph->frag_off = htons(IP_DF);
    else
        iph->frag_off = 0;
    iph->ttl = ip_select_ttl(inet, &rt->u.dst);
    iph->daddr = rt->rt_dst;
    iph->saddr = rt->rt_src;
    iph->protocol = sk->sk_protocol;
    ip_select_ident(iph, &rt->u.dst, sk);
    if (opt && opt->optlen) {
        iph->ihl += opt->optlen>>2;
        ip_options_build(skb, opt, daddr, rt, 0);
    }
    skb->priority = sk->sk_priority;
    skb->mark = sk->sk_mark;
    /* Send it out. */
    return ip_local_out(skb);
}
關于ip頭的具體設置請朋友們自己閱讀,函數(shù)最后ip_local_out()這個函數(shù)我們在第12節(jié)
http://blog.chinaunix.net/u2/64681/showart.php?id=1420186
,具體過程就象客戶端開始向服務器端發(fā)送的過程是一樣的,一樣會從硬件網卡發(fā)向客戶端的網卡,客戶端的過程類似于我們上面看到的服務器的sock過程一樣,同樣會在客戶端的sock過程中進入tcp_v4_do_rcv()函數(shù),具體的進入過程完全與我們的前面四節(jié),從第14節(jié)
http://blog.chinaunix.net/u2/64681/showart.php?id=1432417
開始分析的客戶端到服務器端的過程相似,只不過這次是從服務器端的應答ack數(shù)據(jù)包到客戶端的過程,我們不再具體描述如何進入tcp_v4_do_rcv()的函數(shù)過程,所以請朋友們參考前面四節(jié)的過程,只不過這次的運行過程在客戶端的機器上,而不是服務器本身了。所以我們下邊的分析僅限于客戶端的linux內核的過程,請朋友們注意,我們假設客戶端和服務器端都是一樣的內核版本,即2.6.26版本。
在tcp_v4_do_rcv()函數(shù)中會調用tcp_rcv_state_process()來處理接收到的數(shù)據(jù)包,我們假設客戶端已經接收到了服務器的ack數(shù)據(jù)包,我們只關心與我們的過程相關部分代碼
int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
             struct tcphdr *th, unsigned len)
{
。。。。。。
case TCP_SYN_SENT:
        queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
        if (queued >= 0)
        if (queued >= 0)
            return queued;
        /* Do step6 onward by hand. */
        tcp_urg(sk, skb, th);
        __kfree_skb(skb);
        tcp_data_snd_check(sk);
        return 0;
。。。。。。
}
在第9節(jié)
http://blog.chinaunix.net/u2/64681/showart.php?id=1411408
tcp_v4_connect()函數(shù)的代碼中我們曾經看到
tcp_set_state(sk, TCP_SYN_SENT);
將客戶端的sock設置為了TCP_SYN_SENT的發(fā)送狀態(tài),所以客戶端在接收到服務器端的ack包時會進入tcp_rcv_synsent_state_process()函數(shù),函數(shù)的代碼非常的長,我們不列出了,在這個函數(shù)中客戶端會重新準備一個第三次握手的數(shù)據(jù)包然后調用tcp_send_ack()函數(shù)向服務器端發(fā)出“第三次握手”, tcp_send_ack()函數(shù)最終會調用tcp_transmit_skb()函數(shù)發(fā)送出去,而tcp_transmit_skb()函數(shù)我們在第11節(jié)
http://blog.chinaunix.net/u2/64681/showart.php?id=1415963
那篇中分析過了。這里我們就不詳細探討過程,先請朋友們對其有一個總體的了解,我們在將來的還會再詳細的探討這段過程。


本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u2/64681/showart_1662181.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術有限公司. 版權所有 京ICP備16024965號-6 北京市公安局海淀分局網監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉載本站內容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP