Adding timestamp to UDP packets using XDP eBPF on Linux

40 views Asked by At

I'm trying to write an XDP program that inserts a timestamp into the payload of an UDP packet, but I've no success.

I assume I'm doing something wrong with the pointers. Packet has no checksum and I'm completely fine with just overwriting parts of the payload to fit the timestamp.

The goal is to measure the latency of a packet between ingress and egress of OVS. I'm using CentOS Stream 9 with xdp-loader. Any tips on how to do this?

When loading the following code with sudo I get this:

Current rlimit 8388608 already >= minimum 1048576
Loading 1 files on interface 'ens16f0np0'.
XDP program 0: Run prio: 50. Chain call actions: XDP_PASS
Couldn't attach XDP program on iface 'ens16f0np0': Permission denied(-13)
#include <linux/bpf.h>
#include "bpf_helpers.h"
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>

#ifndef __bpf_htons
#define __bpf_htons(x) ((__be16)___constant_swab16((x)))
#endif

SEC("udp_timestamp_xdp")
int xdp_timestamp_func(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;
    struct iphdr *iph = (struct iphdr *)(eth + 1);
    struct udphdr *udph = (struct udphdr *)(iph + 1);
    __u64 timestamp = bpf_ktime_get_ns();

    // Check if the packet is UDP
    if (eth->h_proto != __bpf_htons(ETH_P_IP) || iph->protocol != IPPROTO_UDP)
        return XDP_PASS;

    // Calculate the pointer to the UDP payload
    __u8 *payload = (__u8 *)(udph + 1);

    // Copy the timestamp into the UDP payload
    __builtin_memcpy(payload, &timestamp, sizeof(__u64));

    // Adjust packet size if needed to accommodate the timestamp
    int ret = bpf_xdp_adjust_tail(ctx, sizeof(__u64));
    if (ret < 0) {
        // Adjusting tail failed, pass the packet without modification
        return XDP_PASS;
    }

    return XDP_PASS;
}

However, the problem is not related to sudo privileges since I'm able to load and use the following program that drops all packets:

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>

#ifndef __bpf_htons
#define __bpf_htons(x) ((__be16)___constant_swab16((x)))
#endif

SEC("udp_timestamp_xdp")
int xdp_timestamp_func(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;
    struct iphdr *iph = (struct iphdr *)(eth + 1);
    struct udphdr *udph = (struct udphdr *)(iph + 1);
    __u64 timestamp = bpf_ktime_get_ns();

    // Check if the packet contains enough data for UDP and timestamp
    if (udph + 1 > (struct udphdr *)data_end)
        return XDP_DROP;
    
    // Check if the packet is UDP
    if (eth->h_proto != __bpf_htons(ETH_P_IP) || iph->protocol != IPPROTO_UDP)
        return XDP_PASS;

    // Calculate the pointer to the UDP payload
    __u8 *payload = (__u8 *)(udph + 1);

    // Check if the payload length is sufficient
    if ((void *)(payload + sizeof(__u64)) > data_end)
        return XDP_DROP;

    // Copy the timestamp into the UDP payload
    __builtin_memcpy(payload, &timestamp, sizeof(__u64));

    return XDP_PASS;
}

What am I doing wrong?

0

There are 0 answers