serious bug discovered for filtering IP after port modification.
hiqsociety opened this issue · comments
i would like to filter the port first then only ip. basically it's just simple copy and paste below
so i changed from line 196 here
https://github.com/xdp-project/xdp-tools/blob/master/xdp-filter/xdpfilt_prog.h#L196
it can "make" but not loaded onto the interface.
i get error of below, how do i resolve this?
invalid access to packet, off=50 size=4, R6(id=0,off=50,r=34)
R6 offset is outside of the packet
this is a very obvious bug of ipv6. coz there's no way to do ip filtering AFTER port.
; } else if (eth_type == bpf_htons(ETH_P_IPV6)) {
306: (55) if r8 != 0xdd86 goto pc-174
R0=map_value(id=0,off=0,ks=4,vs=8,imm=0) R1_w=inv1 R2=inv(id=0,umax_value=9,var_off=(0x0; 0x9)) R6=pkt(id=0,off=14,r=34,imm=0) R7=pkt_end(id=0,off=0,imm=0) R8_w=inv56710 R9=pkt(id=8,off=14,r=22,umax_value=60,var_off=(0x0; 0x3c)) R10=fp0 fp-16=??mmmmmm fp-24=ctx
; addr = ipv6hdr->daddr;
307: (61) r1 = *(u32 *)(r6 +36)
invalid access to packet, off=50 size=4, R6(id=0,off=50,r=34)
R6 offset is outside of the packet
processed 195 insns (limit 1000000) max_states_per_insn 0 total_states 14 peak_states 14 mark_read 6
-- END PROG LOAD LOG --
libbpf: prog 'xdpfilt_dny_all': failed to load: -13
libbpf: unpinned map 'xdp_stats_map' from '/sys/fs/bpf/xdp-filter/xdp_stats_map'
libbpf: unpinned map 'filter_ports' from '/sys/fs/bpf/xdp-filter/filter_ports'
libbpf: unpinned map 'filter_ipv4' from '/sys/fs/bpf/xdp-filter/filter_ipv4'
libbpf: unpinned map 'filter_ipv6' from '/sys/fs/bpf/xdp-filter/filter_ipv6'
libbpf: unpinned map 'filter_ethernet' from '/sys/fs/bpf/xdp-filter/filter_ethernet'
libbpf: failed to load object './xdpfilt_dny_all.o'
libxdp: Failed to load program xdpfilt_dny_all: Permission denied
Couldn't attach XDP program on iface 'enp3s0': Permission denied(-13)
code modified below
SEC("xdp")
int FUNCNAME(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
__u32 action = VERDICT_MISS; /* Default action */
struct hdr_cursor nh;
struct ethhdr *eth;
int eth_type;
nh.pos = data;
eth_type = parse_ethhdr(&nh, data_end, ð);
CHECK_RET(eth_type);
CHECK_VERDICT_ETHERNET(eth);
#if defined(FILT_MODE_IPV4) || defined(FILT_MODE_IPV6) || \
defined(FILT_MODE_TCP) || defined(FILT_MODE_UDP)
struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr;
int ip_type;
if (eth_type == bpf_htons(ETH_P_IP)) {
ip_type = parse_iphdr(&nh, data_end, &iphdr);
// CHECK_RET(ip_type);
// CHECK_VERDICT_IPV4(iphdr);
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
// CHECK_RET(ip_type);
// CHECK_VERDICT_IPV6(ipv6hdr);
} else {
goto out;
}
#ifdef FILT_MODE_UDP
struct udphdr *udphdr;
if (ip_type == IPPROTO_UDP) {
CHECK_RET(parse_udphdr(&nh, data_end, &udphdr));
CHECK_VERDICT(udp, udphdr);
}
#endif /* FILT_MODE_UDP */
#ifdef FILT_MODE_TCP
struct tcphdr *tcphdr;
if (ip_type == IPPROTO_TCP) {
CHECK_RET(parse_tcphdr(&nh, data_end, &tcphdr));
CHECK_VERDICT(tcp, tcphdr);
}
#endif /* FILT_MODE_TCP*/
if (eth_type == bpf_htons(ETH_P_IP)) {
// ip_type = parse_iphdr(&nh, data_end, &iphdr);
CHECK_RET(ip_type);
CHECK_VERDICT_IPV4(iphdr);
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
// ip_type = parse_ip6hdr(&nh, data_end, &ipv6hdr);
CHECK_RET(ip_type);
CHECK_VERDICT_IPV6(ipv6hdr);
// } else {
// goto out;
}
#endif /* FILT_MODE_{IPV4,IPV6,TCP,UDP} */
out:
return xdp_stats_record_action(ctx, action);
}
The CHECK_RET bits are important, that's to appease the verifier; you can't just get rid of these.
And no, this is not a "serious bug": the xdp-filter application is deliberately simple and cannot replace a full-features firewall. Closing this as WONTFIX...