不能通过ioctl设置ipv6路由在线属性

问题描述 投票:0回答:0

我喜欢用这段代码替换 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
linux ipv6 ioctl
© www.soinside.com 2019 - 2024. All rights reserved.