【问题标题】:Recalculating TCP Checksum in Linux Kernel Module在 Linux 内核模块中重新计算 TCP 校验和
【发布时间】:2018-02-09 16:43:19
【问题描述】:

我正在构建一个 Netfilter 模块,该模块会修改发往特定端口的数据包的 TCP 有效负载。我既不修改 IP 标头也不修改 TCP 标头。收到数据包后,直接在 Netfilter 中的第一个点调用该模块 (NF_INET_PRE_ROUTING)。因此,我必须重新计算每个修改数据包中的 TCP 校验和字段。我已经在这里看到了一些帖子并使用了那里的方法来重新计算 TCP 校验和,但是这些方法都不起作用。以下是我使用的两种方法:

方法一:

tcplen = (skb->len - (iph->ihl << 2)); /* tcplen is the length of the 
                                        * skb - the ip-header length */
tcph->check = 0;
tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
                                tcplen, IPPROTO_TCP,
                                csum_partial(tcph, tcplen, 0));

skb->ip_summed = CHECKSUM_UNNECESSARY;

方法二:

tcph->check = ~(~tcph->check + ~new_field + new_field);
skb->ip_summed = CHECKSUM_UNNECESSARY;

在这两种情况下,我收到以下错误:

verif_dss_csum csum 错误:0x874a data_seq 2760801057 dss_csum_added 1 溢出 0 次迭代 1

这个问题有什么解决办法吗?我正在为 Linux Kernel 4.4.83 开发我的模块

【问题讨论】:

  • 我找不到任何错误。顺便说一句:是iph-&gt;ihl original ipheader->len(包括 TCP 标头+有效负载)?第二个想法是填充,但我想csum_partial() 会处理这个问题。
  • 我发现了错误。它与 TCP 校验和无关。它与 MP-TCP 校验和有关,所以我禁用了它。上面的语法是正确的

标签: c linux tcp linux-kernel netfilter


【解决方案1】:

**以上代码仅有助于计算伪标头的校验和** 这里是计算IP头校验和和TCP/UDP校验和的代码

void UpdateChecksum(char *prefix,struct sk_buff *skb){
struct iphdr *ip_header;

ip_header = ip_hdr(skb);
skb->ip_summed = CHECKSUM_NONE; //stop offloading
skb->csum_valid = 0;
ip_header->check = 0;
ip_header->check = ip_fast_csum((u8 *)ip_header, ip_header->ihl);


if ( (ip_header->protocol == IPPROTO_TCP) || (ip_header->protocol == IPPROTO_UDP) ) {

  if(skb_is_nonlinear(skb))
      skb_linearize(skb);  // very important.. You need this.

  if (ip_header->protocol == IPPROTO_TCP) {
     struct tcphdr *tcpHdr;
     unsigned int tcplen;

     tcpHdr = tcp_hdr(skb);
     skb->csum =0;
     tcplen = ntohs(ip_header->tot_len) - ip_header->ihl*4;
     tcpHdr->check = 0;
     tcpHdr->check = tcp_v4_check(tcplen, ip_header->saddr, ip_header->daddr, csum_partial((char *)tcpHdr, tcplen, 0));

     //printk(KERN_INFO "%s: TCP Len :%d, Computed TCP Checksum :%x : Network : %x\n",prefix,tcplen,tcpHdr->check,htons(tcpHdr->check));

  } else if (ip_header->protocol == IPPROTO_UDP) {
       struct udphdr *udpHdr;
       unsigned int udplen;

       udpHdr = udp_hdr(skb);
       skb->csum =0;
       udplen = ntohs(ip_header->tot_len) - ip_header->ihl*4;
       udpHdr->check = 0;
       udpHdr->check = udp_v4_check(udplen,ip_header->saddr, ip_header->daddr,csum_partial((char *)udpHdr, udplen, 0));;

       //printk(KERN_INFO "%s: UDP Len :%d, Computed UDP Checksum :%x : Network : %x\n",prefix,udplen,udpHdr->check,htons(udpHdr->check));
  }

}

}

【讨论】:

    【解决方案2】:

    如果只更改 tcp 有效负载,则无需重新计算 ipv4 标头校验和。 所以如果我理解正确的话,应该是:

    /* ... */
    struct iphdr *ip_header = ip_hdr(skb);
    if (ip_header->protocol == IPPROTO_TCP) {
        struct tcphdr *tcp_header = tcp_hdr(skb);
        if ((unsigned int)ntohs(tcp_header->dest) == some_port) {
            unsigned char *data = (char *)tcp_header + tcp_hdrlen(skb);
            /* altering TCP-payload */
            tcp_header->check = 0;
            tcp_header->check = tcp_v4_check(ntohs(ip_header->tot_len) - (ip_header->ihl << 2),
                                         ip_header->saddr, ip_header->daddr,
                                         csum_partial(tcp_header, tcp_header->doff << 2, 0));
    /* ... */
    /* e.g return NF_ACCEPT */
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-04
      • 1970-01-01
      • 2016-03-09
      • 1970-01-01
      • 1970-01-01
      • 2019-01-30
      相关资源
      最近更新 更多