我正在构建一个NetFilter模块,该模块修改了定于特定端口的数据包的TCP有效负载。我没有修改IP标头和TCP标头。接收到数据包后,在NetFilter的第一点调用该模块(NF_INET_PRE_ROUTING
)。因此,我必须重新计算每个修改后数据包中的TCP校验和字段。我已经在这里看到了一些帖子,并使用了那里的方法来重新计算TCP校验和,但是这些方法中的没有奏效。以下是我使用的两种方法:
方法1:
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;
方法2:
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内核开发4.4.83
**上述代码仅有助于计算伪标头的校验和这是用于计算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 : %xn",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 : %xn",prefix,udplen,udpHdr->check,htons(udpHdr->check));
}
}
}
如果仅更改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 */