1

我正在尝试更改 netfilter 后路由挂钩中 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 计算伪标头而 csum_partial 计算有效负载和 tcp_header 的展开校验和是否正确?

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

有没有其他可行的方法?还是我错过了什么?

4

2 回答 2

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);
于 2013-06-23T17:32:03.523 回答
1

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

if (skb_is_nonlinear(skb)) {
    if (skb_linearize(skb) != 0) {
        return NF_DROP;
    }
    iph = ip_hdr(skb);
    tcph = (void *)iph + (iph->ihl << 2);
}
于 2013-05-17T16:17:33.387 回答