我正在尝试在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的展开校验和是否正确?
我真的想避免自己编写函数,因为内核会更快,因为底层函数使用汇编进行计算。
是否有可行的替代方法?或者有什么我错过了吗?
答案 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);
}