计算netfilter模块中的TCP校验和

时间:2013-05-17 14:02:24

标签: c linux tcp kernel netfilter

我正在尝试在netfilter postrouting钩子中更改IP和TCP头中的某些字段,但是我似乎无法使内核TCP校验和功能正常工作以便以后修改它。

在TCP握手期间,校验和很好,但只要数据包有任何有效负载,校验和就错了。

我已经通过挖掘TCP源来将这个校验和代码拉到一起。我相当确定tcplen是正确的,匹配预期的TCP标头+有效负载大小。

static unsigned int posthook_fn(
    unsigned int hooknum, 
    struct sk_buff *skb,
    const struct net_device *in, 
    const struct net_device *out,
    int (*okfn)(struct sk_buff *))
{
  struct iphdr *iph;
  struct tcphdr *tcph;
  iph = ip_hdr(skb);
  tcph = (struct tcphdr *)(skb->data + iph->ihl * 4);

  tcph->source = port;
  iph->saddr = addr;

  tcplen = (skb->len - (ip_header->ihl << 2));
  tcph->check = 0; 
  tcph->check = tcp_v4_check(tcph, tcplen, 
        iph->saddr, 
        iph->daddr, 
        csum_partial((char *)tcph, tcplen, 0)); 
  skb->ip_summed = CHECKSUM_NONE; //stop offloading

  ip_header->check = ip_fast_csum((u8 *)iph, iph->ihl);         

  return NF_ACCEPT;
}

我认为tcp_v4_check计算psuedo标头并且csum_partial计算有效负载和tcp_header的展开校验和是否正确?

我真的想避免自己编写函数,因为内核会更快,因为底层函数使用汇编进行计算。

是否有可行的替代方法?或者有什么我错过了吗?

2 个答案:

答案 0 :(得分:3)

不需要额外调用skb_is_nonlinear(),因为include / linux / skbuff.h:

static inline int skb_linearize(struct sk_buff *skb)
{
         return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0;
}

另外,你必须:

ip_header->check = 0

之前:

ip_header->check = ip_fast_csum((u8 *)iph, iph->ihl);

答案 1 :(得分:1)

这需要一段时间才能到达,但问题似乎是套接字缓冲区并不总是线性的,以下代码确保它在计算校验和之前。

if (skb_is_nonlinear(skb)) {
    if (skb_linearize(skb) != 0) {
        return NF_DROP;
    }
    iph = ip_hdr(skb);
    tcph = (void *)iph + (iph->ihl << 2);
}