我正在通过IP过滤器拦截,修改和重新注入传出的IPv4 TCP数据包。 问题是,在我改变数据包并设置IP和TCP校验和之后,当我用Wireshark分析结果数据包时,IP校验和等于0(我正在计算的校验和似乎是正确的,因为它等于Wireshark的建议一个)。
以下是我正在遵循的程序,我希望有人可以发现错误或建议更好的处理方式:
static int handle_packet(mbuf_t* data, int ip_len, int dir, ipf_pktopts_t options)
{
errno_t result = 0;
unsigned char packet[1500];
struct tcphdr *tcp;
struct ip *ip;
mbuf_t old_packet = *data, new_packet;
uint32_t mbufs = 0, packet_bytes = 0;
// zero packet
bzero(packet, sizeof(packet));
// "finalize" the packet so that it is safe to modify it
mbuf_outbound_finalize(*data, AF_INET, 0);
// get length of mbuf chain
do
{
mbufs++;
packet_bytes += mbuf_len(old_packet);
old_packet = mbuf_next(old_packet);
} while (old_packet != NULL);
// copy data to local buffer
if (0 != (result = mbuf_copydata(*data, 0, packet_bytes, packet))) {
printf("mbuf_copydata returned %d", result);
return 0;
}
// pointer to start IP header
ip = (struct ip*)packet;
tcp = (struct tcphdr*)((u_int32_t*)ip + ip->ip_hl);
// only consider SYN packet
if (!(tcp->th_flags & TH_SYN))
return KERN_SUCCESS;
if (0 != (result = mbuf_dup(*data, MBUF_DONTWAIT, &new_packet)))
{
printf("ERROR - mbuf_dup: unable to duplicate mbuf, %d", result);
return 0;
}
/**
… I’m modifying the packet and recalculating ip and tcp’s checksums here
(by previously setting them to 0, so to avoid that the previous values
are considered in the calculation) …
*/
/*
* Copy buffer back to mbuf
*/
if (0 != (result = mbuf_copyback(new_packet, 0, ntohs(ip->ip_len), packet, MBUF_DONTWAIT)))
{
mbuf_freem(new_packet);
switch (result) {
case EINVAL:
printf("ERROR - handle_packet: mbuf_copyback returned EINVAL");
return 0;
break;
case ENOBUFS:
printf("ERROR - handle_packet: mbuf_copyback returned ENOBUFS");
return 0;
break;
default:
break;
}
}
// recompute any checksums invalidated by data changes
// mbuf_outbound_finalize(new_packet, AF_INET, 0); // -> PANIC(m->m_flags & M_PKTHDR)
// is this necessary?
mbuf_set_csum_performed(new_packet, MBUF_CSUM_DID_IP | MBUF_CSUM_IP_GOOD | MBUF_CSUM_DID_DATA | MBUF_CSUM_PSEUDO_HDR, checksum_ip(ip));
result = ipf_inject_output(new_packet, ip_filter_ref, options);
return result == 0 ? EJUSTRETURN : result;
}
static errno_t ip_filter_output(void* cookie, mbuf_t *data, ipf_pktopts_t options)
{
struct ip *ip;
char src[32], dst[32];
int ip_len;
// pointer to start IP header
ip = (struct ip*)mbuf_data(*data);
ip_len = ntohs(ip->ip_len);
bzero(src, sizeof(src));
bzero(dst, sizeof(dst));
// converts the network address structure into a character string
inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
// avoid congestion and filter only packets from/to tcpcrypt website
if (ip->ip_p == IPPROTO_TCP
&& mbuf_flags(*data) == MBUF_PKTHDR) {
return handle_packet(data, ip_len, DIRECTION_OUT /* 1 */, options);
}
// continue with normal processing of the packet
return KERN_SUCCESS;
}
我怀疑获得零是在整个标题上重新计算校验和的预期结果,即它会导致计算出的总和自行取消。
无论如何,我真的不明白为什么会这样。
有人知道答案还是可以提供帮助?
非常感谢您提前,
罗密欧
答案 0 :(得分:3)
首先,这个
do
{
mbufs++;
packet_bytes += mbuf_len(old_packet);
old_packet = mbuf_next(old_packet);
} while (old_packet != NULL);
是获得mbuf链长度的可怕方式。正确的方法是
size_t totalLength = (
mbuf_flags(mbuf) & MBUF_PKTHDR ?
mbuf_pkthdr_len(mbuf) : mbuf_len(mbuf)
);
要么它是一个mbuf链,但是那个链的第一个mbuf应该有一个paket头,这个paket头包含整个mbuf链的大小(没有这样一个头的链被定义打破)或者它只是一个mbuf,但只要问这个mbuf的大小就足够了。在整个内核代码中,检索mbuf链的大小,如上面的代码所示。
然后请理解mbuf_outbound_finalize()
的作用。从该方法的文档:
此功能将"最终确定"数据包允许您的代码检查最终数据包。
在硬件中执行许多操作,例如计算校验和。此功能将在软件中执行计划在硬件中完成的各种操作。
因此,如果将校验和计算卸载到硬件上并不重要,一旦调用该函数,数据包应该具有正确的校验和。如果您随后修改了数据包,则由您决定再次修复校验和。您可以通过从头开始重新计算校验和,完成修改后,或通过修复"现有的(例如,如果您知道哪些旧数据变成了哪些新数据,那么可以修复"旧校验和以匹配新数据,而不是从头开始计算所有数据,它& #39;有点棘手,但计算速度更快。)
但所有这一切都不能通过最后再次调用mbuf_outbound_finalize()
来完成。您可以在mbuf或mbuf链上调用此函数一次,之后由您决定,这意味着您的代码可以保持校验和的正确性。如果校验和计算尚未完成并且计划在硬件中完成,则mbuf_outbound_finalize()
仅执行某些操作。如果您的硬件不支持卸载,那么一旦您的过滤器捕获它,该数据包就已经有了正确的校验和,然后mbuf_outbound_finalize()
将无法执行任何操作,因为没有任何内容可以执行。
最后一期:您不应该致电mbuf_set_csum_performed()
。请参阅该功能的文档:
驱动程序使用它来向堆栈指示在硬件中执行了哪些校验和操作。
"由司机"而你不是司机;尽管此函数用于传入而不是用于传出数据包。
答案 1 :(得分:0)
如果您没有运行程序,是否看到非零校验和?
如果没有,你的NIC可能会进行校验和卸载,这使得驱动程序实际上不会将任何校验和插入到数据包中(因为网络接口会处理它)。
请参阅WireShark文档的section 7.10.2. (Checksum offloading)。