Blame src/n-acd/src/n-acd-bpf.c

Packit Service dff8e4
/*
Packit Service dff8e4
 * eBPF filter for IPv4 Address Conflict Detection
Packit Service dff8e4
 *
Packit Service dff8e4
 * An eBPF map and an eBPF program are provided. The map contains all the
Packit Service dff8e4
 * addresses address conflict detection is performed on, and the program
Packit Service dff8e4
 * filters out all packets except exactly the packets relevant to the ACD
Packit Service dff8e4
 * protocol on the addresses currently in the map.
Packit Service dff8e4
 *
Packit Service dff8e4
 * Note that userspace still has to filter the incoming packets, as filter
Packit Service dff8e4
 * are applied when packets are queued on the socket, not when userspace
Packit Service dff8e4
 * receives them. It is therefore possible to receive packets about addresses
Packit Service dff8e4
 * that have already been removed.
Packit Service dff8e4
 */
Packit Service dff8e4
Packit Service dff8e4
#include <c-stdaux.h>
Packit Service dff8e4
#include <errno.h>
Packit Service dff8e4
#include <inttypes.h>
Packit Service dff8e4
#include <linux/bpf.h>
Packit Service dff8e4
#include <netinet/if_ether.h>
Packit Service dff8e4
#include <netinet/in.h>
Packit Service dff8e4
#include <stdlib.h>
Packit Service dff8e4
#include <string.h>
Packit Service dff8e4
#include <sys/resource.h>
Packit Service dff8e4
#include <sys/syscall.h>
Packit Service dff8e4
#include <unistd.h>
Packit Service dff8e4
#include "n-acd-private.h"
Packit Service dff8e4
Packit Service dff8e4
#define BPF_LD_ABS(SIZE, IMM)                                                   \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,            \
Packit Service dff8e4
                .dst_reg        = 0,                                            \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = IMM,                                          \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                                        \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,           \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = SRC,                                          \
Packit Service dff8e4
                .off            = OFF,                                          \
Packit Service dff8e4
                .imm            = 0,                                            \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_LD_MAP_FD(DST, MAP_FD)                                              \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_LD | BPF_DW | BPF_IMM,                    \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = BPF_PSEUDO_MAP_FD,                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = (__u32) (MAP_FD),                             \
Packit Service dff8e4
        }),                                                                     \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = 0, /* zero is reserved opcode */              \
Packit Service dff8e4
                .dst_reg        = 0,                                            \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = ((__u64) (MAP_FD)) >> 32,                     \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_ALU_REG(OP, DST, SRC)                                               \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_ALU64 | BPF_OP(OP) | BPF_X,               \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = SRC,                                          \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = 0,                                            \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_ALU_IMM(OP, DST, IMM)                                               \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_ALU64 | BPF_OP(OP) | BPF_K,               \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = IMM,                                          \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_MOV_REG(DST, SRC)                                                   \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_ALU64 | BPF_MOV | BPF_X,                  \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = SRC,                                          \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = 0,                                            \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_MOV_IMM(DST, IMM)                                                   \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_ALU64 | BPF_MOV | BPF_K,                  \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = IMM,                                          \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                                        \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,           \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = SRC,                                          \
Packit Service dff8e4
                .off            = OFF,                                          \
Packit Service dff8e4
                .imm            = 0,                                            \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_JMP_REG(OP, DST, SRC, OFF)                                          \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_JMP | BPF_OP(OP) | BPF_X,                 \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = SRC,                                          \
Packit Service dff8e4
                .off            = OFF,                                          \
Packit Service dff8e4
                .imm            = 0,                                            \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_JMP_IMM(OP, DST, IMM, OFF)                                          \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_JMP | BPF_OP(OP) | BPF_K,                 \
Packit Service dff8e4
                .dst_reg        = DST,                                          \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = OFF,                                          \
Packit Service dff8e4
                .imm            = IMM,                                          \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_EMIT_CALL(FUNC)                                                     \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_JMP | BPF_CALL,                           \
Packit Service dff8e4
                .dst_reg        = 0,                                            \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = FUNC,                                         \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
#define BPF_EXIT_INSN()                                                         \
Packit Service dff8e4
        ((struct bpf_insn) {                                                    \
Packit Service dff8e4
                .code           = BPF_JMP | BPF_EXIT,                           \
Packit Service dff8e4
                .dst_reg        = 0,                                            \
Packit Service dff8e4
                .src_reg        = 0,                                            \
Packit Service dff8e4
                .off            = 0,                                            \
Packit Service dff8e4
                .imm            = 0,                                            \
Packit Service dff8e4
        })
Packit Service dff8e4
Packit Service dff8e4
static int n_acd_syscall_bpf(int cmd, union bpf_attr *attr, unsigned int size) {
Packit Service dff8e4
        return (int)syscall(__NR_bpf, cmd, attr, size);
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
int n_acd_bpf_map_create(int *mapfdp, size_t max_entries) {
Packit Service dff8e4
        union bpf_attr attr;
Packit Service dff8e4
        int mapfd;
Packit Service dff8e4
Packit Service dff8e4
        memset(&attr, 0, sizeof(attr));
Packit Service dff8e4
        attr = (union bpf_attr){
Packit Service dff8e4
                .map_type    = BPF_MAP_TYPE_HASH,
Packit Service dff8e4
                .key_size    = sizeof(uint32_t),
Packit Service dff8e4
                .value_size  = sizeof(uint8_t), /* values are never used, but must be set */
Packit Service dff8e4
                .max_entries = max_entries,
Packit Service dff8e4
        };
Packit Service dff8e4
Packit Service dff8e4
        mapfd = n_acd_syscall_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
Packit Service dff8e4
        if (mapfd < 0)
Packit Service dff8e4
                return -errno;
Packit Service dff8e4
Packit Service dff8e4
        *mapfdp = mapfd;
Packit Service dff8e4
        return 0;
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
int n_acd_bpf_map_add(int mapfd, struct in_addr *addrp) {
Packit Service dff8e4
        union bpf_attr attr;
Packit Service dff8e4
        uint32_t addr = be32toh(addrp->s_addr);
Packit Service dff8e4
        uint8_t _dummy = 0;
Packit Service dff8e4
        int r;
Packit Service dff8e4
Packit Service dff8e4
        memset(&attr, 0, sizeof(attr));
Packit Service dff8e4
        attr = (union bpf_attr){
Packit Service dff8e4
                .map_fd = mapfd,
Packit Service dff8e4
                .key    = (uint64_t)(unsigned long)&addr,
Packit Service dff8e4
                .value  = (uint64_t)(unsigned long)&_dummy,
Packit Service dff8e4
                .flags  = BPF_NOEXIST,
Packit Service dff8e4
        };
Packit Service dff8e4
Packit Service dff8e4
        r = n_acd_syscall_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
Packit Service dff8e4
        if (r < 0)
Packit Service dff8e4
                return -errno;
Packit Service dff8e4
Packit Service dff8e4
        return 0;
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
int n_acd_bpf_map_remove(int mapfd, struct in_addr *addrp) {
Packit Service dff8e4
        uint32_t addr = be32toh(addrp->s_addr);
Packit Service dff8e4
        union bpf_attr attr;
Packit Service dff8e4
        int r;
Packit Service dff8e4
Packit Service dff8e4
        memset(&attr, 0, sizeof(attr));
Packit Service dff8e4
        attr = (union bpf_attr){
Packit Service dff8e4
                .map_fd = mapfd,
Packit Service dff8e4
                .key    = (uint64_t)(unsigned long)&addr,
Packit Service dff8e4
        };
Packit Service dff8e4
Packit Service dff8e4
        r = n_acd_syscall_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
Packit Service dff8e4
        if (r < 0)
Packit Service dff8e4
                return -errno;
Packit Service dff8e4
Packit Service dff8e4
        return 0;
Packit Service dff8e4
}
Packit Service dff8e4
Packit Service dff8e4
int n_acd_bpf_compile(int *progfdp, int mapfd, struct ether_addr *macp) {
Packit Service dff8e4
        const union {
Packit Service dff8e4
                uint8_t u8[6];
Packit Service dff8e4
                uint16_t u16[3];
Packit Service dff8e4
                uint32_t u32[1];
Packit Service dff8e4
        } mac = {
Packit Service dff8e4
                .u8 = {
Packit Service dff8e4
                        macp->ether_addr_octet[0],
Packit Service dff8e4
                        macp->ether_addr_octet[1],
Packit Service dff8e4
                        macp->ether_addr_octet[2],
Packit Service dff8e4
                        macp->ether_addr_octet[3],
Packit Service dff8e4
                        macp->ether_addr_octet[4],
Packit Service dff8e4
                        macp->ether_addr_octet[5],
Packit Service dff8e4
                },
Packit Service dff8e4
        };
Packit Service dff8e4
        struct bpf_insn prog[] = {
Packit Service dff8e4
                /* for using BPF_LD_ABS r6 must point to the skb, currently in r1 */
Packit Service dff8e4
                BPF_MOV_REG(6, 1),                                              /* r6 = r1 */
Packit Service dff8e4
Packit Service dff8e4
                /* drop the packet if it is too short */
Packit Service dff8e4
                BPF_LDX_MEM(BPF_W, 0, 6, offsetof(struct __sk_buff, len)),      /* r0 = skb->len */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JGE, 0, sizeof(struct ether_arp), 2),           /* if (r0 >= sizeof(ether_arp)) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                /* drop the packet if the header is not as expected */
Packit Service dff8e4
                BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_hrd)),         /* r0 = header type */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, ARPHRD_ETHER, 2),                       /* if (r0 == ethernet) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_pro)),         /* r0 = protocol */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, ETHERTYPE_IP, 2),                       /* if (r0 == IP) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                BPF_LD_ABS(BPF_B, offsetof(struct ether_arp, arp_hln)),         /* r0 = hw addr length */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, sizeof(struct ether_addr), 2),          /* if (r0 == sizeof(ether_addr)) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                BPF_LD_ABS(BPF_B, offsetof(struct ether_arp, arp_pln)),         /* r0 = protocol addr length */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, sizeof(struct in_addr), 2),             /* if (r0 == sizeof(in_addr)) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                /* drop packets from our own mac address */
Packit Service dff8e4
                BPF_LD_ABS(BPF_W, offsetof(struct ether_arp, arp_sha)),         /* r0 = first four bytes of packet mac address */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JNE, 0, be32toh(mac.u32[0]), 4),                /* if (r0 != first four bytes of our mac address) skip 4 */
Packit Service dff8e4
                BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_sha) + 4),     /* r0 = last two bytes of packet mac address */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JNE, 0, be16toh(mac.u16[2]), 2),                /* if (r0 != last two bytes of our mac address) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                /*
Packit Service dff8e4
                 * We listen for two kinds of packets:
Packit Service dff8e4
                 *  Conflicts)
Packit Service dff8e4
                 *    These are requests or replies with the sender address not set to INADDR_ANY. The
Packit Service dff8e4
                 *    conflicted address is the sender address, remember this in r7.
Packit Service dff8e4
                 *  Probes)
Packit Service dff8e4
                 *    These are requests with the sender address set to INADDR_ANY. The probed address
Packit Service dff8e4
                 *    is the target address, remember this in r7.
Packit Service dff8e4
                 *  Any other packets are dropped.
Packit Service dff8e4
                 */
Packit Service dff8e4
                BPF_LD_ABS(BPF_W, offsetof(struct ether_arp, arp_spa)),         /* r0 = sender ip address */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, 0, 7),                                  /* if (r0 == 0) skip 7 */
Packit Service dff8e4
                BPF_MOV_REG(7, 0),                                              /* r7 = r0 */
Packit Service dff8e4
                BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_op)),          /* r0 = operation */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, ARPOP_REQUEST, 3),                      /* if (r0 == request) skip 3 */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, ARPOP_REPLY, 2),                        /* if (r0 == reply) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JA, 0, 0, 6),                                   /* skip 6 */
Packit Service dff8e4
                BPF_LD_ABS(BPF_W, offsetof(struct ether_arp, arp_tpa)),         /* r0 = target ip address */
Packit Service dff8e4
                BPF_MOV_REG(7, 0),                                              /* r7 = r0 */
Packit Service dff8e4
                BPF_LD_ABS(BPF_H, offsetof(struct ether_arp, arp_op)),          /* r0 = operation */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JEQ, 0, ARPOP_REQUEST, 2),                      /* if (r0 == request) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                /* check if the probe or conflict is for an address we are monitoring */
Packit Service dff8e4
                BPF_STX_MEM(BPF_W, 10, 7, -4),                                  /* *(uint32_t*)fp - 4 = r7 */
Packit Service dff8e4
                BPF_MOV_REG(2, 10),                                             /* r2 = fp */
Packit Service dff8e4
                BPF_ALU_IMM(BPF_ADD, 2, -4),                                    /* r2 -= 4 */
Packit Service dff8e4
                BPF_LD_MAP_FD(1, mapfd),                                        /* r1 = mapfd */
Packit Service dff8e4
                BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),                        /* r0 = map_lookup_elem(r1, r2) */
Packit Service dff8e4
                BPF_JMP_IMM(BPF_JNE, 0, 0, 2),                                  /* if (r0 != NULL) skip 2 */
Packit Service dff8e4
                BPF_MOV_IMM(0, 0),                                              /* r0 = 0 */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
Packit Service dff8e4
                /* return exactly the packet length*/
Packit Service dff8e4
                BPF_MOV_IMM(0, sizeof(struct ether_arp)),                       /* r0 = sizeof(struct ether_arp) */
Packit Service dff8e4
                BPF_EXIT_INSN(),                                                /* return */
Packit Service dff8e4
        };
Packit Service dff8e4
        union bpf_attr attr;
Packit Service dff8e4
        int progfd;
Packit Service dff8e4
Packit Service dff8e4
        memset(&attr, 0, sizeof(attr));
Packit Service dff8e4
        attr = (union bpf_attr){
Packit Service dff8e4
                .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
Packit Service dff8e4
                .insns     = (uint64_t)(unsigned long)prog,
Packit Service dff8e4
                .insn_cnt  = sizeof(prog) / sizeof(*prog),
Packit Service dff8e4
                .license   = (uint64_t)(unsigned long)"ASL",
Packit Service dff8e4
        };
Packit Service dff8e4
Packit Service dff8e4
        progfd = n_acd_syscall_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
Packit Service dff8e4
        if (progfd < 0)
Packit Service dff8e4
                return -errno;
Packit Service dff8e4
Packit Service dff8e4
        *progfdp = progfd;
Packit Service dff8e4
        return 0;
Packit Service dff8e4
}