xdp-project / xdp-tools

Utilities and example programs for use with XDP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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, &eth);
        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...