You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改RTNETLINK的C程序以支持from方向Linux路由规则?

实现支持"to"和"from"方向的RTNETLINK路由规则管理程序

要让程序支持"from"方向的路由规则,需要修改参数解析逻辑和Netlink请求构造逻辑,核心是区分源地址(RTA_SRC)和目标地址(RTA_DST)的属性类型,并正确设置rtmsg结构体的对应字段。

关键修改点

  • 新增命令行参数解析,识别from选项,标记路由规则的方向
  • 在do_rule函数中根据方向选择对应的RTA属性(RTA_SRC或RTA_DST)
  • 针对from方向,设置rtmsg的rtm_src_len字段,而非rtm_dst_len
  • 修复原open_netlink函数未绑定套接字的问题(Netlink套接字必须绑定本地地址才能正常通信)
  • 增加参数合法性检查,避免未指定命令或地址时程序崩溃

修改后的完整代码

/*
 * 支持to/from方向的路由规则管理程序
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>

/* Open netlink socket */
int open_netlink()
{
    struct sockaddr_nl saddr;

    int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

    if (sock < 0) {
        perror("Failed to open netlink socket");
        return -1;
    }

    memset(&saddr, 0, sizeof(saddr));
    saddr.nl_family = AF_NETLINK;
    saddr.nl_pid = getpid();
    saddr.nl_groups = 0;

    if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
        perror("Failed to bind netlink socket");
        close(sock);
        return -1;
    }

    return sock;
}

/* Helper structure for ip address data and attributes */
typedef struct {
    char family;
    char bitlen;
    unsigned char data[sizeof(struct in6_addr)];
} _inet_addr;

#define NLMSG_TAIL(nmsg) \
    ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))

/* Add new data to rtattr */
int rtattr_add(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
{
    int len = RTA_LENGTH(alen);
    struct rtattr *rta;

    if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
        fprintf(stderr, "rtattr_add error: message exceeded bound of %d\n", maxlen);
        return -1;
    }

    rta = NLMSG_TAIL(n);
    rta->rta_type = type;
    rta->rta_len = len; 

    if (alen) {
        memcpy(RTA_DATA(rta), data, alen);
    }

    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);

    return 0;
}

// 新增规则方向枚举
typedef enum {
    RULE_DIR_TO,
    RULE_DIR_FROM
} rule_direction_t;

int do_rule(int sock, int cmd, int flags, _inet_addr *address, rule_direction_t dir, int if_idx)
{
    struct {
        struct nlmsghdr n;
        struct rtmsg r;
        char buf[4096];
    } nl_request;

    /* Initialize request structure */
    memset(&nl_request, 0, sizeof(nl_request));
    nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags;
    nl_request.n.nlmsg_type = cmd;
    nl_request.r.rtm_family = address->family;
    nl_request.r.rtm_table = 1; // 固定使用表1,可根据需求修改

    /* Set additional flags if NOT deleting route */
    if (cmd != RTM_DELRULE) {
        nl_request.r.rtm_protocol = RTPROT_BOOT;
        nl_request.r.rtm_type = RTN_UNICAST;
    }

    /* 根据方向设置对应的长度字段 */
    if (dir == RULE_DIR_TO) {
        nl_request.r.rtm_dst_len = address->bitlen;
    } else {
        nl_request.r.rtm_src_len = address->bitlen;
    }

    /* Select scope, for simplicity we supports here only IPv6 and IPv4 */
    if (nl_request.r.rtm_family == AF_INET6) {
        nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE;
    } else {
        nl_request.r.rtm_scope = RT_SCOPE_LINK;
    }

    /* 根据方向添加对应的RTA属性 */
    int rta_type = (dir == RULE_DIR_TO) ? RTA_DST : RTA_SRC;
    rtattr_add(&nl_request.n, sizeof(nl_request), rta_type, &address->data, address->bitlen / 8);

    /* 可选:如果指定了接口,添加RTA_OIF属性 */
    if (if_idx > 0) {
        rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof(if_idx));
    }

    /* Send message to the netlink */
    return send(sock, &nl_request, nl_request.n.nlmsg_len, 0);
}

/* Simple parser of the string IP address */
int read_addr(char *addr, _inet_addr *res)
{
    if (strchr(addr, ':')) {
        res->family = AF_INET6;
        res->bitlen = 128;
    } else {
        res->family = AF_INET;
        res->bitlen = 32;
    }

    return inet_pton(res->family, addr, res->data);
}

#define NEXT_CMD_ARG() do { argv++; if (--argc <= 0) exit(-1); } while(0)

int main(int argc, char **argv)
{
    int if_idx = 0;
    int nl_sock;
    _inet_addr address = { 0 };
    rule_direction_t rule_dir = RULE_DIR_TO; // 默认to方向

    int nl_cmd = 0;
    int nl_flags = 0;

    /* Parse command line arguments */
    while (argc > 0) {
        if (strcmp(*argv, "add") == 0) {
            nl_cmd = RTM_NEWRULE;
            nl_flags = NLM_F_CREATE | NLM_F_EXCL;
        } else if (strcmp(*argv, "del") == 0) {
            nl_cmd = RTM_DELRULE;
            nl_flags = 0;
        } else if (strcmp(*argv, "to") == 0) {
            rule_dir = RULE_DIR_TO;
            NEXT_CMD_ARG(); /* skip "to" and jump to the actual destination addr */
            if (read_addr(*argv, &address) != 1) {
                fprintf(stderr, "Failed to parse destination network %s\n", *argv);
                exit(-1);
            }
        } else if (strcmp(*argv, "from") == 0) {
            rule_dir = RULE_DIR_FROM;
            NEXT_CMD_ARG(); /* skip "from" and jump to the actual source addr */
            if (read_addr(*argv, &address) != 1) {
                fprintf(stderr, "Failed to parse source network %s\n", *argv);
                exit(-1);
            }
        } else if (strcmp(*argv, "dev") == 0) {
            NEXT_CMD_ARG(); /* skip "dev" */
            if_idx = if_nametoindex(*argv);
            if (if_idx == 0) {
                fprintf(stderr, "Invalid interface name %s\n", *argv);
                exit(-1);
            }
        }
        
        argc--; argv++;
    }

    // 检查必要参数是否齐全
    if (nl_cmd == 0) {
        fprintf(stderr, "Missing add/del command\n");
        exit(-1);
    }
    if (address.family == 0) {
        fprintf(stderr, "Missing to/from address\n");
        exit(-1);
    }

    nl_sock = open_netlink();
    if (nl_sock < 0) {
        exit(-1);
    }

    do_rule(nl_sock, nl_cmd, nl_flags, &address, rule_dir, if_idx);

    close(nl_sock);
    return 0;
}

使用示例

  • 添加to方向规则:./program add to 192.168.1.0/24 dev eth0
  • 添加from方向规则:./program add from 10.0.0.0/8 dev eth1
  • 删除from方向规则:./program del from 10.0.0.0/8

内容的提问来源于stack exchange,提问作者Ogy89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 14:50:23