xdp-project / xdp-tutorial

XDP tutorial
2.43k stars 574 forks source link

advanced03-AF_XDP - adapting for UDP #74

Closed sevagh closed 5 years ago

sevagh commented 5 years ago

Hello. I'm having a hard time adapting the advanced03 AF_XDP code to do a similar action to UDP packets - unless I'm really fundamentally misunderstanding the direction of the rx/tx flow.

I have a socat listener on the external ip of my veth-adv03:

sevagh:~ $ socat - UDP6-LISTEN:1234,bind=[fc00:dead:cafe:1::1],fork
hello world 2
hello world 3
hello world 8
hello world 9

I send hello world 1-10 with socat via the internal ip, after running testenv.sh enter:

[root@localhost sevagh]# for x in 1 2 3 4 5 6 7 8 9 10; do echo "hello world ${x}" | socat - UDP6-SENDTO:[fc00:dead:cafe:1::1]:1234; done

Finally, I have XDP code redirecting half the packets to AF_XDP, where I simply want to do a random sleep, and send them, to scramble the packets and have them arrive out of order.

My desired result is:

sevagh:~ $ socat - UDP6-LISTEN:1234,bind=[fc00:dead:cafe:1::1],fork
hello world 2 // XDP_PASS
hello world 3 // XDP_PASS
hello world 8 // XDP_PASS
hello world 9 // XDP_PASS
hello world 4 // AF_XDP + random sleep
hello world 1 // AF_XDP + random sleep
hello world 10 // AF_XDP + random sleep
...

However, in my AF_XDP code, the packet doesn't go anywhere:

static bool process_packet(struct xsk_socket_info *xsk, uint64_t addr,
               uint32_t len, int udp4_out, int udp6_out)
{
    uint8_t *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);

    int ret;
    uint32_t tx_idx = 0;

    // sleep randomly to scramble
    dawdle();

    ret = xsk_ring_prod__reserve(&xsk->tx, 1, &tx_idx);
    if (ret != 1) {
        /* No more transmit slots, drop the packet */
        return false;
    }

    xsk_ring_prod__tx_desc(&xsk->tx, tx_idx)->addr = addr;
    xsk_ring_prod__tx_desc(&xsk->tx, tx_idx)->len = len;
    xsk_ring_prod__submit(&xsk->tx, 1);
    xsk->outstanding_tx++;

    return true;
}

As a workaround, I can send those AF_XDP rx packets above, on a different, regular AF_INET6 UDP socket, to achieve my desired goal:

static bool process_packet(struct xsk_socket_info *xsk, uint64_t addr,
               uint32_t len, int udp4_out, int udp6_out)
{
    uint8_t *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);

        ....

        ssize_t sent_bytes;
        struct sockaddr_in6 sin;
        sin.sin6_family = AF_INET6;
        sin.sin6_port = udphdr->dest;
        inet_pton(AF_INET6, "::1", &sin.sin6_addr);
        sent_bytes = sendto(udp6_out, (void *)nh.pos, len, 0,
                                   (struct sockaddr *)&sin, sizeof(sin))

Is using a regular UDP socket the only way to achieve what I need, or should it be possible to do so with AF_XDP?

chaudron commented 5 years ago

This code will send the packet back over the interface it's received upon, not back to the kernel. So TX means send it out of the interface, and RX is the receive path on the interface. The example below uses the same ingress and egress interface.

So TX does not mean the same as XDP_PASS, XDP_PASS continue the packet where TX does not. AF_XDP has no built-in feature to re-insert the packet back into the kernel.

sevagh commented 5 years ago

Thank you.