我请求你的帮助,关于我已经经历了两个星期的错误。
我正在使用BCC在XDP中实现NAT64,节点之间的交换工作,我的发送方节点发送ping,接收方节点接收ping,在两个节点之间有另一个称为router
的节点,其中附加了XDP程序。在一个接口中,它从ipv6转换为ipv4,另一个接口将ipv4转换为ipv6。节点是命名空间。
问题发生在目的节点收到数据包时,我从tcpdump看到的IP校验和是错误的(IP校验和没有检查,所以我不知道它是否正确)。
我已经尝试了很多不同的方法来计算校验和,我发现分散在互联网上,但没有一个工作,我开始认为,也许错误可能是在其他地方?所以我一直在检查和重新检查代码这两个星期,但找不到任何错误。
下面是tcpdump -i veth1 -vvv
的输出:
16:10:45.883038 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.9.1 > archiz-linux: ICMP echo request, id 47244, seq 1, length 64 (wrong icmp cksum 3f72 (->6fc9)!)
字符串
我尝试禁用校验和卸载与:ethtool -K veth rx off tx off
为每个veth在每个命名空间,但我有相同的以前的输出.也许它不工作,因为这种方法不包括icmp校验和像这样post说.
校验和计算功能:
csum_fold_helper()
static inline __u16 csum_fold_helper(__u64 csum)
{
__u32 sum;
sum = (csum >> 16) + (csum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
型
这是我的实现,为了简单起见,我省略了一些部分:
int xdp_router_func(struct xdp_md *ctx){
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct bpf_fib_lookup fib_params = {0};
struct ethhdr eth_cpy;
struct ethhdr *eth = data;
struct iphdr *iph = {0};
__u64 nh_off = sizeof(*eth);
if (data + nh_off > data_end)
return XDP_DROP;
if (eth->h_proto == bpf_htons(ETH_P_IPV6))
{
memcpy(ð_cpy, eth, sizeof(eth_cpy));
struct ipv6hdr *ip6h = data + nh_off;
if (ip6h + 1 > data_end)
return XDP_DROP;
...
struct iphdr dst_hdr = {
.version = 4,
.ihl = 5,
.frag_off = bpf_htons(1 << 14),
};
__u32 assigned_ipv4 = 0;
// search inside the natting_table the ipv6_addr associated to the ipv4
...
dst_hdr.saddr = bpf_htonl((__be32)assigned_ipv4);
dst_hdr.daddr = ip6h->daddr.s6_addr32[3];
dst_hdr.protocol = ip6h->nexthdr;
dst_hdr.ttl = ip6h->hop_limit;
dst_hdr.tos = ip6h->priority << 4 | (ip6h->flow_lbl[0] >> 4);
dst_hdr.tot_len = bpf_htons(bpf_ntohs(ip6h->payload_len) + sizeof(dst_hdr));
// check if the packet is a icmpv6
if (dst_hdr.protocol == IPPROTO_ICMPV6)
{
struct icmp6hdr *icmp6h = (void *)ip6h + sizeof(*ip6h);
if (icmp6h + 1 > data_end)
return XDP_DROP;
// ready to parse the icmpv6 header in icmp
struct icmphdr tmp_icmp;
struct icmphdr *icmp;
// set the right type to icmp, id field and sequence field
if (write_icmp(&tmp_icmp, icmp6h) == -1)
{
bpf_trace_printk("[ERR]: error during icpmv6 parse in icmp");
return XDP_DROP;
}
if (bpf_xdp_adjust_head(ctx, (int)sizeof(*icmp6h) - (int)sizeof(tmp_icmp)))
return XDP_DROP;
// after the adjust head I have to reassign the pointers
data = (void *)(long)ctx->data;
data_end = (void *)(long)ctx->data_end;
icmp = (void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr));
if (icmp + 1 > data_end)
return XDP_DROP;
*icmp = tmp_icmp;
// set the checksum
icmp->checksum = 0x0000;
icmp->checksum = csum_fold_helper(bpf_csum_diff((__be32 *)icmp, 0, (__be32 *)icmp, sizeof(icmp), 0));
dst_hdr.protocol = IPPROTO_ICMP;
} // icmpv6
dst_hdr.check = csum_fold_helper(bpf_csum_diff((__be32 *)&dst_hdr, 0, (__be32 *)&dst_hdr, sizeof(dst_hdr), 0));
if (bpf_xdp_adjust_head(ctx, (int)sizeof(struct ipv6hdr) - (int)sizeof(struct iphdr)))
return XDP_DROP;
// after the adjust head I have to reassign the pointers
eth = (void *)(long)ctx->data;
data = (void *)(long)ctx->data;
data_end = (void *)(long)ctx->data_end;
if (eth + 1 > data_end)
return XDP_DROP;
memcpy(eth, ð_cpy, sizeof(*eth));
eth->h_proto = bpf_htons(ETH_P_IP);
iph = (void *)(data + sizeof(*eth));
if (iph + 1 > data_end)
{
bpf_trace_printk("iph out of boundary");
return XDP_DROP;
}
*iph = dst_hdr;
// start forwarding
// setting the fib_params
fib_params.family = AF_INET;
fib_params.tos = iph->tos;
fib_params.tot_len = bpf_ntohs(iph->tot_len);
fib_params.ipv4_dst = iph->daddr;
fib_params.ipv4_src = iph->saddr;
fib_params.ifindex = ctx->ingress_ifindex;
fib_params.sport = 0;
fib_params.dport = 0;
} // end ipv6
// forwarding
int rc;
rc = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), 0);
switch (rc)
{
case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */
memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
int action;
action = bpf_redirect(fib_params.ifindex, 0);
return action;
// other cases
...
}
return XDP_PASS;
}
型
1条答案
按热度按时间liwlm1x91#
Cilium实现了这样一个校验和计算,所以我会使用它(有适当的归属和copyleft许可证):
字符串
icmp_wsum_accumulate
是在源代码中定义的(我认为这需要属性和GPL兼容的许可)。icmp_payload
和icmp_payload_end
应该指向您的HTTP消息有效负载(通常是原始IP数据包)的开始和结束。