如何修改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
相关产品推荐
相关产品推荐

