OPENPPP2 sysnat loader implement / C/C++

openppp2_sysnat.h

cpp 复制代码
/*
 * Header for OpenPPP2 system NAT (eBPF TC based NAT).
 *
 * This API provides functions to attach/detach an eBPF program to the TC egress
 * hook of a network interface, and to manage NAT rules in a pinned BPF map.
 *
 * The library assumes single-process usage and does not include cross-process
 * locking or reference counting. It is the caller's responsibility to ensure
 * proper usage (e.g., not attaching to multiple interfaces simultaneously).
 */

#ifdef SYSNAT
#ifndef OPENPPP2_SYSNAT_H
#define OPENPPP2_SYSNAT_H

#include <stdint.h>
#include <netinet/in.h>

#ifdef __cplusplus
extern "C" {
#endif

    /* Error codes */
    #define ERR_IFINDEX       -1   /* Invalid interface name */
    #define ERR_BPF_OPEN      -2   /* Failed to open BPF object file */
    #define ERR_BPF_PROG      -3   /* Program not found in object */
    #define ERR_BPF_LOAD      -4   /* BPF object loading failed */
    #define ERR_MAP_PIN       -5   /* Failed to set map pin path */
    #define ERR_TC_CREATE     -6   /* Failed to create TC hook */
    #define ERR_TC_ATTACH     -7   /* Failed to attach TC program */
    #define ERR_TC_DETACH     -8   /* Failed to detach TC program */
    #define ERR_MAP_OPEN      -9   /* Failed to open pinned map */
    #define ERR_MAP_UPDATE    -10  /* Failed to update map element */
    #define ERR_MAP_DELETE    -11  /* Failed to delete map element */

    /*
     * NAT key structure (matches eBPF map key).
     * All IP addresses and ports are in network byte order.
     */
    struct openppp2_sysnat_key {
        uint32_t src_ip;      /* Source IP address */
        uint32_t dst_ip;      /* Destination IP address */
        uint16_t src_port;    /* Source port */
        uint16_t dst_port;    /* Destination port */
        uint16_t proto;       /* IP protocol (e.g., IPPROTO_TCP, IPPROTO_UDP) */
        uint16_t pad;         /* Padding to align to 8 bytes */
    } __attribute__((packed));

    /*
     * NAT value structure (matches eBPF map value).
     * All IP addresses and ports are in network byte order.
     */
    struct openppp2_sysnat_value {
        uint32_t new_src_addr;      /* New source IP address */
        uint16_t new_src_port;      /* New source port */
        uint32_t new_dst_addr;      /* New destination IP address */
        uint16_t new_dst_port;      /* New destination port */
        int32_t  redirect_ifindex;  /* 0 = no redirect, else interface index */
        uint8_t  pad[4];            /* Padding for alignment */
    } __attribute__((packed));

    /*
     * Mount the BPF filesystem if not already mounted.
     * This function is automatically called by attach(), but may be called
     * explicitly to ensure the BPF filesystem is available early.
     *
     * Returns:
     *   0 on success
     *   -1 on failure (errno set)
     */
    int openppp2_sysnat_mount(void);

    /*
     * Attach the eBPF TC egress NAT program to the given network interface.
     *
     * The BPF object file at `bpf_obj_path` must contain a program named
     * "tc_egress" and a map named "openppp2_sysnat_rules".
     *
     * If the map is already pinned at MAP_PIN_PATH, this function assumes the
     * program is already attached and returns success without reloading.
     *
     * Returns:
     *   0 on success
     *   one of the ERR_xxx codes on failure
     */
    int openppp2_sysnat_attach(const char* ifname, const char* bpf_obj_path);

    /*
     * Detach the eBPF TC egress NAT program from the network interface.
     *
     * This function removes the TC program, unpins the map, and cleans up the
     * TC hook. If the map is not present, it returns success.
     *
     * Returns:
     *   0 on success
     *   one of the ERR_xxx codes on failure
     */
    int openppp2_sysnat_detach(const char* ifname);

    /*
     * Check whether the NAT program is currently attached.
     *
     * This function checks the existence of the pinned map file.
     * It does not verify the actual TC attachment state.
     *
     * Returns:
     *   1 if attached (map exists)
     *   0 if not attached
     */
    int openppp2_sysnat_is_attached(void);

    /*
     * Add a NAT rule to the map.
     *
     * The map must be already pinned (i.e., the program attached).
     *
     * Returns:
     *   0 on success
     *   ERR_MAP_OPEN if the map is not found
     *   ERR_MAP_UPDATE if the update operation fails
     */
    int openppp2_sysnat_add_rule(const struct openppp2_sysnat_key* key, const struct openppp2_sysnat_value* val);

    /*
     * Delete a NAT rule from the map.
     *
     * Returns:
     *   0 on success
     *   ERR_MAP_OPEN if the map is not found
     *   ERR_MAP_DELETE if the deletion operation fails
     */
    int openppp2_sysnat_del_rule(const struct openppp2_sysnat_key* key);

#ifdef __cplusplus
}
#endif

#endif /* OPENPPP2_SYSNAT_H */
#endif

openppp2_sysnat.c

cpp 复制代码
/*
 * Implementation of OpenPPP2 system NAT (eBPF TC based NAT).
 *
 * Provides functions to attach/detach an eBPF program to the TC egress hook
 * of a network interface, and to manage NAT rules stored in a pinned BPF map.
 *
 * The implementation is intended for single-process use and does not include
 * cross-process locking or reference counting.
 */

#ifdef SYSNAT
#include "openppp2_sysnat.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <mntent.h>
#include <net/if.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>

 /* Fixed pin path for the NAT rule map (also used as attach flag) */
#define MAP_PIN_PATH "/sys/fs/bpf/openppp2_sysnat_rules"

/* Check if a filesystem is mounted at the given path */
static int is_fs_mounted(const char* path, const char* fstype) {
    FILE* fp = setmntent("/proc/mounts", "r");
    if (!fp) {
        return 0;
    }

    struct mntent* mnt;
    while ((mnt = getmntent(fp)) != NULL) {
        if (strcmp(mnt->mnt_dir, path) == 0 && strcmp(mnt->mnt_type, fstype) == 0) {
            endmntent(fp);
            return 1;
        }
    }

    endmntent(fp);
    return 0;
}

/* Ensure a directory exists, create if missing */
static int ensure_dir(const char* path) {
    struct stat st;
    if (stat(path, &st) == 0) {
        if (S_ISDIR(st.st_mode)) {
            return 0;
        }

        errno = ENOTDIR;
        return -1;
    }

    if (errno == ENOENT) {
        if (mkdir(path, 0755) == 0) {
            return 0;
        }
    }
    return -1;
}

/* Mount BPF filesystem if not already mounted */
int openppp2_sysnat_mount(void) {
    const char* bpf_path = "/sys/fs/bpf";

    if (ensure_dir(bpf_path) != 0) {
        return -1;
    }

    if (!is_fs_mounted(bpf_path, "bpf")) {
        if (mount("none", bpf_path, "bpf", 0, NULL) != 0) {
            if (errno != EBUSY && errno != EEXIST) {
                return -1;
            }
        }
    }
    return 0;
}

/* Check if the pinned map exists */
static int map_exists(void) {
    int fd = bpf_obj_get(MAP_PIN_PATH);
    if (fd >= 0) {
        close(fd);
        return 1;
    }
    return 0;
}

/* Delete the pinned map file */
static void delete_map_pin(void) {
    unlink(MAP_PIN_PATH);
}

/* Remove the TC hook from the interface (best effort) */
static void delete_tc_hook(unsigned int ifindex) {
    struct bpf_tc_hook hook = {
        .sz = sizeof(hook),
        .ifindex = ifindex,
        .attach_point = BPF_TC_EGRESS,
    };
    (void)bpf_tc_hook_destroy(&hook);
}

/* Attach the eBPF TC egress program to the interface */
int openppp2_sysnat_attach(const char* ifname, const char* bpf_obj_path) {
    struct bpf_tc_hook hook = { .sz = sizeof(hook) };
    struct bpf_tc_opts opts = { .sz = sizeof(opts) };
    struct bpf_object* obj = NULL;
    struct bpf_program* prog = NULL;
    struct bpf_map* map = NULL;
    unsigned int ifindex;
    int err = 0;
    int map_pinned = 0;

    if (openppp2_sysnat_mount() != 0) {
        return ERR_BPF_OPEN;
    }

    ifindex = if_nametoindex(ifname);
    if (ifindex == 0) {
        return ERR_IFINDEX;
    }

    /* Remove any stale map file from previous crashes */
    delete_map_pin();

    /* Load BPF object */
    obj = bpf_object__open_file(bpf_obj_path, NULL);
    if (libbpf_get_error(obj)) {
        err = ERR_BPF_OPEN;
        goto cleanup;
    }

    prog = bpf_object__find_program_by_name(obj, "tc_egress");
    if (!prog) {
        err = ERR_BPF_PROG;
        goto cleanup;
    }

    bpf_program__set_type(prog, BPF_PROG_TYPE_SCHED_CLS);

    map = bpf_object__find_map_by_name(obj, "openppp2_sysnat_rules");
    if (!map) {
        err = ERR_MAP_PIN;
        goto cleanup;
    }

    if (bpf_map__set_pin_path(map, MAP_PIN_PATH) != 0) {
        err = ERR_MAP_PIN;
        goto cleanup;
    }

    if (bpf_object__load(obj) != 0) {
        err = ERR_BPF_LOAD;
        goto cleanup;
    }

    map_pinned = 1;

    /* Create TC hook */
    hook.ifindex = ifindex;
    hook.attach_point = BPF_TC_EGRESS;

    int ret = bpf_tc_hook_create(&hook);
    if (ret != 0 && ret != -EEXIST) {
        err = ERR_TC_CREATE;
        goto cleanup;
    }

    /* Attach program */
    opts.prog_fd = bpf_program__fd(prog);
    opts.handle = 1;
    opts.priority = 1;
    opts.flags = BPF_TC_F_REPLACE;

    if (bpf_tc_attach(&hook, &opts) != 0) {
        err = ERR_TC_ATTACH;
        goto cleanup;
    }

    err = 0;

cleanup:
    if (err != 0) {
        if (map_pinned) {
            delete_map_pin();
        }

        if (ifindex) {
            delete_tc_hook(ifindex);
        }
    }


    if (obj)
        bpf_object__close(obj);

    return err;
}

/* Detach the eBPF TC egress program from the interface */
int openppp2_sysnat_detach(const char* ifname) {
    struct bpf_tc_hook hook = { .sz = sizeof(hook) };
    struct bpf_tc_opts opts = { .sz = sizeof(opts) };
    unsigned int ifindex;

    ifindex = if_nametoindex(ifname);
    if (ifindex == 0) {
        return ERR_IFINDEX;
    }

    if (!map_exists()) {
        return 0;
    }

    hook.ifindex = ifindex;
    hook.attach_point = BPF_TC_EGRESS;
    opts.prog_fd = 0;
    opts.handle = 1;
    opts.priority = 1;

    int rc = bpf_tc_detach(&hook, &opts);
    if (rc != 0 && rc != -ENOENT) {
        return ERR_TC_DETACH;
    }

    delete_map_pin();
    delete_tc_hook(ifindex);

    return 0;
}

/* Check if attached (by map existence) */
int openppp2_sysnat_is_attached(void) {
    return map_exists() ? 1 : 0;
}

/* Add a NAT rule to the pinned map */
int openppp2_sysnat_add_rule(const struct openppp2_sysnat_key* key, const struct openppp2_sysnat_value* val) {
    int map_fd = bpf_obj_get(MAP_PIN_PATH);
    if (map_fd < 0) {
        return ERR_MAP_OPEN;
    }

    int ret = bpf_map_update_elem(map_fd, key, val, BPF_ANY);
    close(map_fd);

    return (ret == 0) ? 0 : ERR_MAP_UPDATE;
}

/* Delete a NAT rule from the pinned map */
int openppp2_sysnat_del_rule(const struct openppp2_sysnat_key* key) {
    int map_fd = bpf_obj_get(MAP_PIN_PATH);
    if (map_fd < 0) {
        return ERR_MAP_OPEN;
    }

    int ret = bpf_map_delete_elem(map_fd, key);
    close(map_fd);

    return (ret == 0) ? 0 : ERR_MAP_DELETE;
}
#endif
相关推荐
float_com2 小时前
【java进阶】------ Lambda表达式
java·开发语言
垫脚摸太阳2 小时前
第 36 场 蓝桥·算法挑战赛·百校联赛---赛后复盘
数据结构·c++·算法
木木em哈哈2 小时前
记一次在线编辑器的探索
linux·服务器·网络
码云数智-大飞2 小时前
Java接口与抽象类:从本质区别到架构选型
开发语言
小碗羊肉2 小时前
【从零开始学Java | 第二十三篇】泛型(Generics)
java·开发语言·新手入门
一个有温度的技术博主2 小时前
网安实验系列一:Burp Suite探测敏感信息路径
网络·安全
Aaswk2 小时前
刷题笔记(回溯算法)
数据结构·c++·笔记·算法·leetcode·深度优先·剪枝
m0_750580302 小时前
Java并发—Java线程
java·开发语言
zhooyu2 小时前
GLM中lerp实现线性插值
c++·opengl
我要成为嵌入式大佬2 小时前
正点原子MP157--问题详解--二(NFS挂载根文件系统双网卡设置)
linux·服务器·网络