mdNoman21 / xdp-task

0 stars 0 forks source link

XDP Block Program #1

Open jameelkaisar opened 4 weeks ago

jameelkaisar commented 4 weeks ago

XDP Block

Compile bpf file

clang -O2 -g -Wall -target bpf -c xdp_block.c -o xdp_block.o

Compile loader file

clang -O2 -g -Wall xdp_loader.c -o xdp_loader.out -lbpf -lxdp

Execute loader file

sudo ./xdp_loader.out <domain-name>
sudo ./xdp_loader.out jameelkaisar.com

Open a website

curl https://<domain-name>
curl https://jameelkaisar.com

Read logs

Read logs (directly)

sudo cat /sys/kernel/debug/tracing/trace
sudo cat /sys/kernel/tracing/trace

Read logs (indefinitely)

# Compile log reader
clang -O2 -g -Wall ../common/log_reader.c -o ../common/log_reader.out

# Execute log reader
sudo ./../common/log_reader.out

xdp_block.c

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>
#include <linux/ip.h> // struct iphdr
#include <bpf/bpf_endian.h> // bpf_ntohs

// warning: declaration of 'struct bpf_map' will not be visible outside of this function [-Wvisibility]
// #include <bpf/libbpf.h>
struct bpf_map;

union ip_str {
    __u8 ip_arr[4];
    __u32 ip;
};

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_HASH);
    __type(key, union ip_str);
    __type(value, long);
    __uint(max_entries, 64);
} blocked_ips SEC(".maps");

struct callback_ctx {
    union ip_str *ip;
    int output;
};

static __u64 check_filter(struct bpf_map *map, __u32 *key, __u64 *val, struct callback_ctx *data) {
    if (data->ip->ip == *key) {
        data->output = XDP_DROP;
        __sync_fetch_and_add(val, 1);
    }
    return 0;
}

SEC("xdp")
int xdp_block_prog(struct xdp_md* ctx) {
    void* data = (void*)(long)ctx->data;
    void* data_end = (void*)(long)ctx->data_end;

    // Check if the packet is large enough
    if (data + sizeof(struct ethhdr) > data_end)
        return XDP_DROP;

    struct ethhdr *eth = data;

    // We only need IP Packets
    // eth->h_proto is 2 bytes
    // bpf_ntohs takes care of byte order
    if (bpf_ntohs(eth->h_proto) != ETH_P_IP)
        return XDP_PASS;

    // Check if the packet is large enough
    if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
        return XDP_DROP;

    struct iphdr *iph = data + sizeof(struct ethhdr);

    // We only need TCP Packets
    if (iph->protocol != IPPROTO_TCP)
        return XDP_PASS;

    // Get IP address
    union ip_str ip = {
        .ip = iph->saddr,
    };

    // Create a context
    struct callback_ctx ctx_data = {
        .ip = &ip,
        .output = XDP_PASS,
    };

    // Check if the IP address is blocked
    bpf_for_each_map_elem(&blocked_ips, check_filter, &ctx_data, 0);

    return ctx_data.output;
}

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

xdp_loader.c

#include <stdio.h>
#include <xdp/libxdp.h>
#include <unistd.h> // sleep
#include <stdlib.h> // exit
#include <signal.h> // SIG*
#include <net/if.h> // if_nametoindex
#include <arpa/inet.h>
#include <netdb.h>

#define BUFFER_SIZE 32

union ip_str {
    __u8 ip_arr[4];
    __u32 ip;
};

struct ip_list {
    int count;
    union ip_str* ips;
};

// Global variables
static int ifindex;
struct xdp_program *prog = NULL;
int ret = 1;

// Detach XDP program on exit
static void int_exit(int sig) {
    if (!ret) {
        xdp_program__detach(prog, ifindex, XDP_MODE_SKB, 0);
    }
    xdp_program__close(prog);
    printf("\n");
    exit(0);
}

void format_bytes(union ip_str* ip_obj, char* buffer) {
    snprintf(buffer, BUFFER_SIZE, "%u.%u.%u.%u", ip_obj->ip_arr[0], ip_obj->ip_arr[1], ip_obj->ip_arr[2], ip_obj->ip_arr[3]);
}

struct ip_list* get_ip_address(char* domain_name) {
    // Get host information using gethostbyname
    struct hostent *hostinfo = gethostbyname(domain_name);
    if (hostinfo == NULL) {
        herror("gethostbyname");
        exit(1);
    }

    // Check for multiple IP addresses (not always guaranteed)
    if (hostinfo->h_addr_list[0] == NULL) {
        fprintf(stderr, "No IP address found for %s\n", domain_name);
        exit(1);
    }

    // Calculate the number of IP addresses
    int count = 0;
    for (int i = 0; hostinfo->h_addr_list[i] != NULL; i++) count++;

    // Store all IP addresses
    union ip_str* my_ips = (union ip_str*)malloc(count * sizeof(union ip_str));
    for (int i = 0; i < count; i++) {
        struct in_addr *address = (struct in_addr *)hostinfo->h_addr_list[i];
        my_ips[i].ip = address->s_addr;
    }

    // Return the IP addresses list
    struct ip_list* my_ip_list = (struct ip_list*)malloc(sizeof(struct ip_list));
    my_ip_list->count = count;
    my_ip_list->ips = my_ips;

    return my_ip_list;
}

static void poll_stats(int map_fd, int interval, struct ip_list* my_ip_list) {
    int count = my_ip_list->count;
    union ip_str *ips = my_ip_list->ips;

    int ncpus = libbpf_num_possible_cpus();
    if (ncpus < 0) {
        printf("Failed to get possible cpus\n");
        return;
    }

    while (1) {
        sleep(interval);
        printf("\033[H\033[J");
        for (int i = 0; i < count; i++) {
            long values[ncpus];
            if (bpf_map_lookup_elem(map_fd, &ips[i], values) == 0) {
                long num = 0;
                for (int j = 0; j < ncpus; j++) {
                    num += values[j];
                }
                char buff[BUFFER_SIZE];
                format_bytes(&ips[i], buff);
                printf("IP: %s, Count: %ld\n", buff, num);
            }
        }
        fflush(stdout);
    }
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <domain-name>\n", argv[0]);
        exit(1);
    }

    // Get IP addresses
    struct ip_list* my_ip_list = get_ip_address(argv[1]);
    int count = my_ip_list->count;
    union ip_str *ips = my_ip_list->ips;

    const char* filename = "xdp_block.o";
    const char* secname = "xdp"; // section name

    // Interface name
    // Command: ip a
    const char* ifname = "eth0";

    // Interface name to index
    ifindex = if_nametoindex(ifname);
    if (!ifindex) {
        printf("Failed to get interface index from interface name\n");
        return 1;
    }

    // Load XDP program
    prog = xdp_program__open_file(filename, secname, NULL);
    if (!prog) {
        printf("Failed to load XDP program\n");
        return 1;
    }

    // Attach XDP program to interface with SKB mode
    // Please set ulimit if you got an -EPERM error
    ret = xdp_program__attach(prog, ifindex, XDP_MODE_SKB, 0);
    if (ret) {
        printf("Failed to attach XDP program to %d interface\n", ifindex);
        return ret;
    }

    int map_fd;
    struct bpf_object *bpf_obj;
    bpf_obj = xdp_program__bpf_obj(prog);
    map_fd = bpf_object__find_map_fd_by_name(bpf_obj, "blocked_ips");
    if (map_fd < 0) {
        printf("Error, get map fd from bpf obj failed\n");
        return map_fd;
    }

    // Add blocked IPs to the map
    for (int i = 0; i < count; i++) {
        long values[2] = {0, 0};
        if (bpf_map_update_elem(map_fd, &ips[i], values, BPF_ANY) < 0) {
            printf("Failed to add IP to the map\n");
            return 1;
        }
    }

    // Detach XDP program when it is interrupted or killed
    signal(SIGINT, int_exit);
    signal(SIGTERM, int_exit);

    poll_stats(map_fd, 1, my_ip_list);

    // Sleep indefinitely
    while (1) sleep(1);

    return 0;
}
mdNoman21 commented 4 weeks ago

great