今天调试bug时, 忘了将原始的check_sum值reset,导致发包-抓包后发现。check-sum 错误。

来看一看check-sum:简单讲就是对要计算的数据,以16bit为单元进行累加,然后取反

  在内核中构造数据包的时候,我们需要关注三个校验和:分别是sk_buf中的csum,ip_summed,ip头部中的check和udp或者tcp头部中的check

用于计算校验和的API:L3校验和的计算比L4的校验和要快得多,因为它只包含IP报头。校验和的API都在checksum.h中。

checksum在收包和发包时意义不一样

/*
 *    @csum: Checksum (must include start/offset pair)
 *    @csum_start: Offset from skb->head where checksumming should start
 *    @csum_offset: Offset from csum_start where checksum should be stored
 *    @ip_summed: Driver fed us an IP checksum
 */
struct sk_buff {
    union {
        __wsum        csum;
        struct {
            __u16    csum_start;
            __u16    csum_offset;
        };
    };

    __u8    ip_summed:2,
            

IP/TCP/UDP checsum

 

csum_start: Offset from skb->head where checksumming should start

csum_offset: Offset from csum_start where checksum should be store

 

/* Don't change this without changing skb_csum_unnecessary! */
#define CHECKSUM_NONE        0
#define CHECKSUM_UNNECESSARY    1
#define CHECKSUM_COMPLETE    2
#define CHECKSUM_PARTIAL    3 

 

TCP收包时:

  • CHECKSUM_UNNECESSARY

    CHECKSUM_UNNECESSARY表示底层硬件或者协议栈已经计算了CSUM,也就是计算了tcp udp的伪头;所以TCP层在收到包后,发现skb->ip_summedCHECKSUM_UNNECESSARY就不会再检查checksum;还有一种情况就是回环,因为在回环中错误发生的概率太低了,因此就不需要计算校验来节省cpu事件。

  • CHECKSUM_NONE

    csum中的校验和无效,需要L4层自己校验payload和伪头;可能有以下几种原因:设备不支持硬件校验和计算;设备计算了硬件校验和,但发现该数据帧已经损坏。部分驱动不会丢弃,而是将ip_summed设置为CHECKSUM_NONE,然后交给上层协议栈重新计算并处理这种错误。

  • CHECKSUM_COMPLETE

  网卡已经计算了L4层报头和payload的校验和,并且skb->csum已经被赋值,此时L4层的接收者只需要加伪头并验证校验结果。

 

1) 在L4层发现skb->ip_summed==CHECKSUM_UNNECESSARY,或者skb的csum_valid字段有效, 则放行该报文。skb->ip_summed==CHECKSUM_PARTIAL,但是checksum_start_offset存在,也放行。

2)   如果skb->ip_summed为CHECKSUM_COMPLETE,则把skb->csum加上伪头进行校验,成功则将skb->ip_summed设为CHECKSUM_UNNECESSARY,同时设置 skb->csum_valid=1 并 放行该数据包。

3)   如果skb->ip_summed是CHECKSUM_NONE且 skb->csum_bad已经置位,则不能放行-丢弃。

 

4)    如是为CHECKSUM_NONE且 csum_bad==0 ;则需要将数据报文的payload加上skb->csum进行checksum计算,成功将设为CHECKSUM_COMPLETE并放行,失败则丢弃。

 

skb->csum:存放硬件或者软件计算的payload的checksum不包括伪头,或者是只有伪头,但是是否有意义由skb->ip_summed的值决定,同时不同版本内核代码其值也不一样

 

int tcp_v4_rcv(struct sk_buff *skb)
{
------------------------------

    if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
        goto csum_error;
--------------------------
}

   skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo) 函数实质上调用的是:__skb_checksum_validate(skb, IPPROTO_TCP, false, false, 0, inet_compute_pseudo)

__skb_checksum_validate(skb, IPPROTO_TCP, false, false, 0, inet_compute_pseudo)
({                                    \
    __sum16 __ret = 0;                        \
    skb->csum_valid = 0;                        \
    if (__skb_checksum_validate_needed(skb, false, 0))    \
        __ret = __skb_checksum_validate_complete(skb,        \
                complete, inet_compute_pseudo(skb, proto));    \
    __ret;                                \
})



static inline void __skb_decr_checksum_unnecessary(struct sk_buff *skb)
{
    if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
        if (skb->csum_level == 0)
            skb->ip_summed = CHECKSUM_NONE;
        else
            skb->csum_level--;
    }
}

static inline int skb_csum_unnecessary(const struct sk_buff *skb)
{
    return ((skb->ip_summed == CHECKSUM_UNNECESSARY) ||
        skb->csum_valid ||
        (skb->ip_summed == CHECKSUM_PARTIAL &&
         skb_checksum_start_offset(skb) >= 0));
}

static inline bool __skb_checksum_validate_needed(struct sk_buff *skb,
                          bool zero_okay,
                          __sum16 check)
{
    if (skb_csum_unnecessary(skb) || (zero_okay && !check)) {
        skb->csum_valid = 1;
        __skb_decr_checksum_unnecessary(skb);
        return false;
    }

    return true;
}

static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto)
{
    return csum_tcpudp_nofold(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
                  skb->len, proto, 0);
}



/* Validate (init) checksum based on checksum complete.
 *
 * Return values:
 *   0: checksum is validated or try to in skb_checksum_complete. In the latter
 *    case the ip_summed will not be CHECKSUM_UNNECESSARY and the pseudo
 *    checksum is stored in skb->csum for use in __skb_checksum_complete
 *   non-zero: value of invalid checksum
 *
 */
static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb,
                               bool complete,
                               __wsum psum)
{
    if (skb->ip_summed == CHECKSUM_COMPLETE) {
        if (!csum_fold(csum_add(psum, skb->csum))) {
            skb->csum_valid = 1;
            return 0;
        }
    } else if (skb->csum_bad) {
        /* ip_summed == CHECKSUM_NONE in this case */
        return (__force __sum16)1;
    }

    skb->csum = psum;

    if (complete || skb->len <= CHECKSUM_BREAK) {
        __sum16 csum;

        csum = __skb_checksum_complete(skb);
        skb->csum_valid = !csum;
        return csum;
    }

    return 0;
}

__sum16 __skb_checksum_complete(struct sk_buff *skb)
{
    __wsum csum;
    __sum16 sum;

    csum = skb_checksum(skb, 0, skb->len, 0);

    /* skb->csum holds pseudo checksum */
    sum = csum_fold(csum_add(skb->csum, csum));
    if (likely(!sum)) {
        if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
            !skb->csum_complete_sw)
            netdev_rx_csum_fault(skb->dev);
    }

    if (!skb_shared(skb)) {
        /* Save full packet checksum */
        skb->csum = csum;
        skb->ip_summed = CHECKSUM_COMPLETE;
        skb->csum_complete_sw = 1;
        skb->csum_valid = !sum;
    }

    return sum;
}
View Code

相关文章:

  • 2021-05-29
  • 2021-12-06
  • 2021-12-22
  • 2021-05-27
  • 2021-09-14
  • 2021-12-03
  • 2021-05-12
  • 2021-07-06
猜你喜欢
  • 2021-12-26
  • 2021-10-02
  • 2022-01-19
  • 2021-09-24
  • 2021-07-03
  • 2022-01-09
相关资源
相似解决方案