Search code examples
ebpfxdp-bpf

Failed to forward the packet using xdp. Procedure


I wanted to use xdp to forward traffic from 127.0.0.1:9999 on the lo network card to 127.0.0.1:8080 to access the golang web service I started, but the web service never connected.

cat /sys/kernel/debug/tracing/trace_pipe

bpf_trace_printk: source3879 target 36895

//+build ignore
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_endian.h>
#define MAX_CPU 128
#define ETH_P_IP        0x0800

struct pkt_meta {
    union {
        __be32 src;
        __be32 srcv6[4];
    };
    union {
        __be32 dst;
        __be32 dstv6[4];
    };
    __u16 port16[2];
    __u16 l3_proto;
    __u16 l4_proto;
    __u16 data_len;
    __u16 pkt_len;
    __u32 seq;
};

static __always_inline bool parse_tcp(struct tcphdr *tcp,
                      struct pkt_meta *pkt)
{
    pkt->port16[0] = tcp->source;
    pkt->port16[1] = tcp->dest;
    pkt->seq = tcp->seq;
    return true;
}

static __always_inline bool parse_ip4(struct iphdr *iph,
                      struct pkt_meta *pkt)
{
    if (iph->ihl != 5)
        return false;
    pkt->src = iph->saddr;
    pkt->dst = iph->daddr;
    pkt->l4_proto = iph->protocol;
    return true;
}

SEC("xdp")
int process_packet(struct xdp_md *ctx)
{
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;
    struct tcphdr *tcp;
    struct iphdr *iph;
    struct pkt_meta pkt = {};
    __u32 off;

    /* parse packet for IP Addresses and Ports */
    off = sizeof(struct ethhdr);
    if (data + off > data_end)
        return XDP_PASS;

    pkt.l3_proto = bpf_htons(eth->h_proto);

    if (pkt.l3_proto == ETH_P_IP) {
        iph = data + off;
        if ((void *)(iph + 1) > data_end)
            return false;
        if (!parse_ip4(iph, &pkt))
            return XDP_PASS;
        off += sizeof(struct iphdr);
    }
    if (data + off > data_end)
        return XDP_PASS;

    if (pkt.l4_proto == IPPROTO_TCP) {
        tcp = data + off;
        if ((void *)(tcp + 1) > data_end)
            return false;
        off += sizeof(struct tcphdr);

        if (pkt.port16[1] == bpf_htons(9999) && pkt.dst == bpf_htonl(0x7f000001)) {
            tcp->dest = bpf_htons(8080);
            iph->daddr = bpf_htonl(0x7f000001);
            bpf_printk("source%d target %d\n", pkt.port16[1], tcp->dest);
            return XDP_TX;
        }
    }
    return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";

Is there something wrong with my code or with my logic?


Solution

  • In case of appeal, the client resets the [syn,ack] packet on the server when the tcp connection is established, so you also need to change the source port 8080 to 9999 at the client receiving network card to succeed. example:

     if (tcp->source == bpf_htons(8080) && iph->saddr == bpf_htonl(0x7f000001)) {
            tcp->source = bpf_htons(9999);
            iph->saddr = bpf_htonl(0x7f000001);
            return XDP_PASS;
        }