xdp-project / xdp-tutorial

XDP tutorial
2.44k stars 576 forks source link

Map load does not work with snprintf or bpf_snprintf. When these lines are removed, map read/write continues. #412

Open samueljaydan opened 6 months ago

samueljaydan commented 6 months ago

This piece of XDP code is not working (I mean map is not woking for read and write) when snprintf or bpf_snprintf exists. At bpf side, is there any possibility to cast u64,u16,__u32 types to string(char array)

#include <stdbool.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/if_ether.h> // Include this for struct ethhdr
#include <linux/ip.h>       // Include this for struct iphdr
#include <linux/tcp.h>      // Include this for struct tcphdr
#include <stdint.h>
#include <arpa/inet.h>
#include <stdio.h>

#define MAX_ENTRIES_FLOW 65535

#define min(x, y) ((x) < (y) ? x : y)

char _license[] SEC("license") = "GPL";

struct pkt_trace_metadata {
    __u32 ifindex;
    __u32 rx_queue;
    __u16 pkt_len;
    __u16 cap_len;
    __u16 flags;
    __u16 prog_index;
    int action;
} __packed;

// Define the structure for BPF map
// A helper structure used by eBPF C program
// to describe map attributes to BPF program loader
struct bpf_map_def {
  __u32 map_type;
  __u32 key_size;
  __u32 value_size;
  __u32 max_entries;
  __u32 map_flags;
  // Array/Hash of maps use case: pointer to inner map template
  void *inner_map_def;
  // Define this to make map system wide ("object pinning")
  // path could be anything, like '/sys/fs/bpf/foo'
  // WARN: You must have BPF filesystem mounted on provided location
  const char *persistent_path;
};

#define BPF_MAP(_name, _type, _key_type, _value_type, _max_entries) \
    struct bpf_map_def SEC("maps") _name = {                        \
        .map_type = _type,                                          \
        .key_size = sizeof(_key_type),                              \
        .value_size = sizeof(_value_type),                          \
        .max_entries = _max_entries,                                \
    };

#define BPF_HASH(_name, _key_type, _value_type) \
    BPF_MAP(_name, BPF_MAP_TYPE_HASH, _key_type, _value_type, 10240);

#define BPF_PACKET_MAP(_name) \
    BPF_MAP(_name, BPF_MAP_TYPE_PERF_EVENT_ARRAY, int, __u32, MAX_ENTRIES_FLOW);

BPF_PACKET_MAP(packetmap)
BPF_HASH(blacklistmap,int,int)
// XDP program //
SEC("xdp")
int xdp_dump(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct pkt_trace_metadata metadata;
    //
    metadata.prog_index = 0;
    metadata.ifindex = ctx->ingress_ifindex;
    metadata.rx_queue = ctx->rx_queue_index;
    metadata.pkt_len = (__u16)(data_end - data);
    metadata.cap_len = min(metadata.pkt_len, 65535);
    metadata.action = 0;
    metadata.flags = 0;
    // L2
    struct ethhdr *ether = data;
    if (data + sizeof(*ether) > data_end) {
        return XDP_ABORTED;
    }
    // L3
    if (ether->h_proto != 0x08) { 
        // Non IPv4
        return XDP_PASS;
    }
    data += sizeof(*ether);
    struct iphdr *ip = data;
    if (data + sizeof(*ip) > data_end) {
        return XDP_ABORTED;
    }
    data += ip->ihl * 4;
    struct tcphdr *tcp = data;
    if (data + sizeof(*tcp) > data_end) {
        return XDP_ABORTED;
    }
    __u64 sourceport = (__u64)tcp->source;
    __u64 destport = (__u64)tcp->dest;
    __u64 saddr = (__u64)ip->saddr;
    __u64 daddr = (__u64)ip->daddr;

    char sourceport_str[6];
    char destport_str[6];
    char saddr_str[16];
    char daddr_str[16];

    sprintf(sourceport_str, "%hu", (unsigned short)sourceport);
    sprintf(destport_str, "%hu", (unsigned short)destport);
    sprintf(saddr_str, "%u", (unsigned int)saddr);
    sprintf(daddr_str, "%u", (unsigned int)daddr);

    //
    bpf_perf_event_output(ctx, &packetmap, ((__u64)metadata.cap_len << 32) | BPF_F_CURRENT_CPU, &metadata, sizeof(metadata));

    return XDP_PASS;
}
tohojo commented 6 months ago

samueljaydan @.***> writes:

This piece of XDP code is not working (I mean map is not woking for read and write) when snprintf or bpf_snprintf exists.

bpf_snprintf is quite limited compared to the regular C stdlib version, and does not have the same API. Read the API documentation for the helper for details, but you can't just drop it in as a replacement.

At bpf side, is there any possibility to cast u64,u16,__u32 types to string(char array)

It's all C so you can cast all you want, but not sure what you're trying to achieve with that.

samueljaydan commented 5 months ago

Thanks for the answer. I got it.