日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

TCP的核心系列 — SACK和DSACK的实现(三)

發布時間:2023/12/13 综合教程 23 生活家
生活随笔 收集整理的這篇文章主要介紹了 TCP的核心系列 — SACK和DSACK的实现(三) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

不論是18版,還是37版,一開始都會從TCP的控制塊中取出SACK選項的起始地址。

SACK選項的起始地址是保存在tcp_skb_cb結構的sacked項中的,那么這是在什么時候做的呢?

SACK塊并不是總是合法的,非法的SACK塊可能會引起處理錯誤,所以還需要進行SACK塊的合法性檢查。

本文主要內容:TCP首部中SACK選項的解析和地址的獲取,SACK塊的合法性檢查。

Author:zhangskd @ csdn

SACK選項的地址

TCP_SKB_CB(skb)->sacked is initialized to offset corresponding to the start of the SACK option in the

TCP header for the segment received.

處理時機為:

tcp_rcv_established(),進入慢速路徑時調用

| --> tcp_validate_incoming()

| --> tcp_fast_parse_options()

| --> tcp_parse_options()

在慢速路徑中,有可能只帶有TIMESTAMP選項,因此先用tcp_fast_parse_options()快速解析。

/* Fast parse options. This hopes to only see timestamps.
 * If it is wrong it falls back on tcp_parse_options().
 */
static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th, struct tcp_sock *tp, u8 **hvpp)
{
    /* In the spirit of fast parsing, compare doff directly to constant values.
     * Because equality is used, short doff can be ignored here.
     */
    if (th->doff == (sizeof(*th) / 4)) { /* 沒有帶選項 */
        tp->rx_opt.saw_tstamp = 0;
        return 0;

    } else if (tp->rx_opt.tstamp_ok &&
        th->doff == ((sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4)) { /* 只帶有時間戳選項 */
        if (tcp_parse_aligned_timestamp(tp, th))
            return 1;
    }

    /* 如果以上的快速解析失敗,則進行全面解析 */
    tcp_parse_options(skb, &tp->rx_opt, hvpp, 1);

    return 1;
}
static int tcp_parse_aligned_timestamp(struct tcp_sock *tp, struct tcphdr *th)
{
    __be32 *ptr = (__be32 *) (th + 1); /* 指向選項部分 */
 
    /* 如果選項部分的前4個字節分別為:0x 01 01 08 0A */
    if (*ptr == htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
         | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {

        tp->rx_opt.saw_tstamp = 1;
        ++ptr;

        tp->rx_opt.rcv_tsval = ntohl(*ptr); /* 提取接收包的時間戳*/
        ++ptr;

        tp->rx_opt.rcv_tsecr = ntohl(*ptr); /* 提取接收包的回顯值*/
        return 1;
    }

    return 0;
}

在慢速路徑中,如果tcp_fast_parse_options()失敗,則調用tcp_parse_options()全面解析TCP選項。

/* Look for tcp options. Normally only called on SYN and SYNACK packets.
 * But, this can also be called on packets in the established flow when the fast version
 * below fails.
 */
void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx, u8 **hvpp, int estab)
{
    unsigned char *ptr;
    struct tcphdr *th = tcp_hdr(skb);
    int length = (th->doff * 4) - sizeof(struct tcphdr); /* 選項總長度 */

    ptr = (unsigned char *) (th + 1); /* 選項起始地址 */
    opt_rx->saw_tstamp = 0; /* 此ACK有沒有帶時間戳接下來才知道 */

    while (length > 0) {
        int opcode = *ptr++; /* 選項kind */
        int opsize;

        switch (opcode) {
            case TCPOPT_EOL: /* 結束選項,不常見到 */
                return;

            case TCPOPT_NOP: /* 填充選項 */
                length--; /* 此選項只占一個字節 */
                continue;

            default:
                opsize = *ptr++; /* 此選項長度 */

                if (opsize < 2) /* "silly options" */
                    return; /* 選項長度過小 */

                if (opsize > length)
                    return; /* don't parse partial options */

                switch (opcode) {
                    ...
                    case TCPOPT_SACK_PERM: 
                        if (opsize == TCPOLEN_SACK_PERM && th->syn && 
                             !estab && sysctl_tcp_sack) {

                            opt_rx->sack_ok = 1; /* SYN包中顯示支持SACK */
                             tcp_sack_reset(opt_rx); /* 清空dsack和num_sacks */
                        }
                        break;

                        case TCPOPT_SACK:
                            if ((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
                               !((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK) &&
                               opt_rx->sack_ok) {
                                
                                /*保存SACK選項的起始地址偏移*/
                                TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *) th; 
                            }
                            break;
                        ...
                }
        }
    }
}
/* TCP options */
#define TCPOPT_NOP 1 /* Padding */
#define TCPOPT_EOL 0 /* End of options */
#define TCPOPT_MSS 2 /* Segment size negotiating */
#define TCPOPT_WINDOW 3 /* Window Scaling */
#define TCPOPT_SACK_PERM 4 /* SACK Permitted */
#define TCPOPT_SACK 5 /* SACK Block */
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */

static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
{
    rx_opt->dsack = 0;
    rx_opt->num_sacks = 0;
}

/* This is the max number of SACKS that we'll generate and process.
 * It's safe to increase this, although since:
 * size = TCPOLEN_SACK_BASE_ALIGNED(4) + n * TCPOLEN_SACK_PERBLOCK(8)
 * only four options will fit in a standard TCP header
 */
#define TCP_NUM_SACKS 4 /* SACK塊數最多為4 */

SACK塊合法性檢查

檢查SACK塊或者DSACK塊是否合法。

2.6.24之前的版本沒有檢查SACK塊的合法性,而某些非法的SACK塊可能會觸發空指針的引用。

在3.1版本之前有一個小bug,處理DSACK時會產生問題,修復非常簡單:

@if (! after(end_seq, tp->snd_una)),把非去掉。

符合以下任一條件的SACK塊是合法的:

1. sack塊和dsack塊:snd_una < start_seq < end_seq <= snd_nxt

2. dsack塊:undo_marker <= start_seq < end_seq <= snd_una

3. dsack塊:start_seq < undo_marker < end_seq <= snd_una 且 end_seq - start_seq <= max_window

/* SACK block range validation checks that the received SACK block fits to the 
 * expected sequence limits, i.e., it is between SND.UNA and SND.NXT.
 */
static int tcp_is_sackblock_valid(struct tcp_sock *tp, int is_dsack, u32 start_seq, u32 end_seq)
{
    /* Too far in future, or reversed (interpretation is ambiguous)
     * end_seq超過了snd_nxt,或者start_seq >= end_seq,那么不合法
     */
    if (after(end_seq, tp->snd_nxt) || ! before(start_seq, end_seq))
        return 0;

    /* Nasty start_seq wrap-around check (see comments above) */
     * start_seq超過了snd_nxt
     */
    if (! before(start_seq, tp->snd_nxt))
        return 0;

    /* In outstanding window? This is valid exit for D-SACKs too.
     * start_seq == snd_una is non-sensical (see comments above)
     */
    if (after(start_seq, tp->snd_una))
        return 1; /* 合法 */

    if (! is_dsack || ! tp->undo_marker)
        return 0;

    /* Then it's D-SACK, and must reside below snd_una completely.
     * 注意在3.1以前這里是:! after(end_seq, tp->snd_una),是一個bug
     */
    if (after(end_seq, tp->snd_una))
        return 0; 

    if (! before(start_seq, tp->undo_marker))
        return 1; /* dsack塊合法 */

    /* Too old,DSACK塊太舊了*/
    if (! after(end_seq, tp->undo_marker))
        return 0;

    /* Undo_marker boundary crossing */
    return !before(start_seq, end_seq - tp->max_window);
}

總結

以上是生活随笔為你收集整理的TCP的核心系列 — SACK和DSACK的实现(三)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。