我喜欢用这段代码替换 linux 默认的 ipv6 路由。
我的问题是如果我设置
nl_request.r.rtm_flags = RTNH_F_ONLINK;
,那么路线不会改变。如果我删除nl_request.r.rtm_flags = RTNH_F_ONLINK;
然后路线更新没有onbline
attribue.
我怎样才能修复它以使其与 RTNH_F_ONLINK 一起工作?
#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/ioctl.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
/* Open netlink socket */
static int open_netlink(int af_family)
{
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));
return sock;
}
/* Helper structure for ip address data and attributes */
typedef struct {
uint8_t family;
uint8_t 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 */
static 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;
}
static int do_route(int sock, int cmd, int flags, _inet_addr *dst, _inet_addr *gw, int def_gw, int if_idx)
{
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[4096];
} nl_request;
/* Initialize request structure */
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_dst_len = dst->bitlen;
nl_request.r.rtm_family = dst->family;
nl_request.r.rtm_table = RT_TABLE_MAIN;
nl_request.r.rtm_protocol = RTPROT_BOOT;
nl_request.r.rtm_type = RTN_UNICAST;
/* Set additional flags if NOT deleting route */
if (cmd != RTM_DELROUTE) {
/* 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;
nl_request.r.rtm_flags = RTNH_F_ONLINK;
} else {
nl_request.r.rtm_scope = RT_SCOPE_LINK;
}
}
/* Set gateway */
if (gw->bitlen != 0) {
rtattr_add(&nl_request.n, sizeof(nl_request), RTA_GATEWAY, &gw->data, gw->bitlen / 8);
nl_request.r.rtm_scope = 0;
nl_request.r.rtm_family = gw->family;
/* RTA_PREF = ICMPV6_ROUTER_PREF_MEDIUM */
}
/* Set destination network */
rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &dst->data, dst->bitlen / 8);
if (!def_gw) {
/* Set interface */
rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof(int));
}
/* Send message to the netlink */
return send(sock, &nl_request, sizeof(nl_request), 0);
}
/* Simple parser of the string IP address
*/
static int read_addr(const 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);
}
int replace_default_gateway(const char* interface_name, const char* gateway_ip, int af_family, int* perr) {
int sock_fd, err;
struct ifreq ifr;
_inet_addr to_addr;
_inet_addr gw_addr;
int nl_cmd = RTM_NEWROUTE;
int nl_flags = NLM_F_CREATE | NLM_F_REPLACE;
memset(&to_addr, 0, sizeof(to_addr));
memset(&gw_addr, 0, sizeof(gw_addr));
to_addr.family = (char) af_family;
sock_fd = open_netlink(af_family);
if (sock_fd < 0) {
perr[0] = sock_fd;
return -1;
}
perr[0] = read_addr(gateway_ip, &gw_addr);
if( perr[0] != 1 ) {
err = -2;
goto L1;
}
if( gw_addr.family != af_family ) {
err = -3;
goto L1;
}
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, interface_name, IFNAMSIZ-1);
// Get the ifrindex of the interface
perr[0] = ioctl(sock_fd, SIOGIFINDEX, &ifr);
if ( perr[0] < 0 ) {
err = -2;
goto L1;
}
perr[0] = do_route(sock_fd, nl_cmd, nl_flags, &to_addr, &gw_addr, 1, ifr.ifr_ifindex);
err = 0;
L1:
close(sock_fd);
return err;
}
我想要像这个命令一样的结果:
ip -6 route replace default via $GW6 dev $IFACE proto static metric 1024 onlink pref medium