3

我的路由表上有以下条目:

tecik:ihsan $ netstat -rn -f inet6 | grep 2400:3700:61:4::/64
2400:3700:61:4::/64               2400:3700:60:4::2             UG       gif104

我通过路由套接字做了一个 RTM_GET 请求,我设法正确地获取了 RTA_DST 信息,但是 RTA_NETMASK 总是返回 0,尽管路由表说它是 /64。

我如何获得正确的网络掩码条目。

编码:

 /* fill in request header */
pid = getpid();
buf = calloc(0, sizeof(char) * buflen);
rtm = (struct rt_msghdr *) buf;
rtm->rtm_version = RTM_VERSION;
rtm->rtm_flags = RTF_UP | RTF_GATEWAY;
rtm->rtm_addrs = RTA_DST | RTA_NETMASK;
rtm->rtm_type  = RTM_GET;
rtm->rtm_pid   = pid;
rtm->rtm_seq   = seq;

/* take the dst AFI and assume the rest are of the same AFI */
if (dst->sa_family == AF_INET6) {
    sin6 = (struct sockaddr_in6 *) (rtm + 1);
    memcpy(sin6, dst, sizeof(struct sockaddr_in6));
    sin6 = (struct sockaddr_in6 *) ((char *) sin6 + alignsa(sizeof(struct sockaddr_in6)));
    memcpy(sin6, mask, sizeof(struct sockaddr_in6));
    sin6 = (struct sockaddr_in6 *) ((char *) sin6 + alignsa(sizeof(struct sockaddr_in6)));
    rtm->rtm_msglen = (char *) sin6 - buf;
}

/* write to routing socket */
write(s, rtm, rtm->rtm_msglen);

/* now read the reply */
do {
    n = read(s, rtm, buflen);
} while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != seq || rtm->rtm_pid != pid);

close(s);
/* cycle through all routing replies, checking for expected sockaddr */
rtm = (struct rt_msghdr *) buf;
sin6 = (struct sockaddr_in6 *) (rtm + 1);
for (rtax = 0; rtax < RTAX_MAX; rtax++) {
    if (rtm->rtm_addrs & (1 << rtax)) {
        sa = (struct sockaddr *) sin6;

        if (rtax == RTAX_DST) {
            al->rt = malloc(sizeof(char) * MAX_ADDRSTR);
            getaddrstr(al->rt, sa);
        }
        else if (rtax == RTAX_NETMASK) {
            sa->sa_family = AF_INET6;
            al->rtplen    = getcidr(sa);
        }

        sin6 = (struct sockaddr_in6 *) (char *) sin6 + alignsa(sizeof(struct sockaddr_in6));
    }
}

if (al->rtplen == 0)
    def = 1;

if (def)
    debug("%s has default route, ignoring..", al->ifname);
else
    debug("%s has route %s/%u", al->ifname, al->rt, al->rtplen);

free(buf);

alignsa() 是:

size_t
alignsa(size_t s)
{
    return (1 + (((s) - 1) | (sizeof(size_t) - 1)));
}

getcidr() 是

/* all zeros netmask */
if (sa->sa_len == 0)
    return(plen);

switch (sa->sa_family) {
    case AF_INET:

        break;

    case AF_INET6:
        s = (uint8_t *) &((struct sockaddr_in6 *)sa)->sin6_addr;

        if (*s == 0)
            break;

        for (i = 0; ((i < 16) && (*s == 0xff)); i++, s++)
            plen += 8;

        break;

    default:
            return(-1);
}

return(plen);

如果我打印出 al->rt 的值,它会显示为 2400:3700:61:4:: 但 rtplen 始终为 0。

如果我打印出网络掩码的 sockaddr_in6,它的输出与 ffff:ffff:ffff:ffff:: 或 /64 完全相同,所以我认为对于 RTA_NETMASK,sin6_len 为 0,但我不明白为什么。

4

2 回答 2

2

编辑:这个答案是错误的,因为它只适用于 netlink RTM_GETROUTE。操作员询问路由套接字。

我会假设你的意思是 RTM_GETROUTE,即使你的主题是 RTM_GET。RTM_GETROUTE 不返回由 netstat -6 -rn 显示的相同信息。该命令可以返回缓存的路由。也许更令人惊讶的是,RTM_GETROUTE 可以在路由缓存中创建路由。

这来自 iproute2 文档(在我的 Ubuntu 上,它在man 8 ip-route中列出):

   Note that this operation is not equivalent to ip route show.  show
   shows existing routes.  get resolves them and creates new clones if
   necessary.  Essentially, get is equivalent to sending a packet along
   this path.  If the iif argument is not given, the kernel creates a
   route to output packets towards the requested destination.  This is
   equivalent to pinging the destination with a subsequent ip route ls
   cache, however, no packets are actually sent.  With the iif argument,
   the kernel pretends that a packet arrived from this interface and
   searches for a path to forward the packet.

在您的情况下,您可以比较ip -6 route show table allip -6 route get 2400:3700:60:4::123的输出。它们可能会有点不同,ip route get版本包含最后缓存的单词。

我目前在一个没有 IPv6 连接的位置,但是这条本地路线说明了我的意思:

thuovila@glx:~$ ip -6 rl table all |grep ff00::/8

ff00::/8 dev eth1 表本地度量 256

thuovila@glx:~$ ip r get ff00::/8

ff00:: from :: via ff00:: dev eth1 src fe80::21b:b1ff:fe48:1a75 metric 0 cache

正如您所注意到的,缓存的路由不包含目标地址长度(网络掩码)。

为了解决您的问题,(如何获取网络掩码)我建议研究过滤网络链接消息。您将不得不转储符合您的条件的路由并过滤克隆的路由,而不是单个 RTM_GET 请求。iproute2 工具在例如带有函数的iproute.c模块中执行此操作static int filter_nlmsg。除其他内容外,它还包含一条说明if (filter.cloned == !(r->rtm_flags&RTM_F_CLONED))删除克隆(即源自缓存)路由的行。调用它route_print()来限制显示哪些路线。在ip route list命令中,路由是通过调用 libnetlink function 转储的rtnl_dump_filter()

有关如何使用 netlink 的更多提示,请检查 iproute2 的来源,尤其是ip/iproute.clib/libnetlink.c模块。

于 2013-02-28T12:24:19.523 回答
1

我已经设法解决了这个问题,事实证明我没有正确地将缓冲区转换回主函数上的 sockaddr_in6 :

sin6 = (struct sockaddr_in6 *) (char *) sin6 + alignsa(sizeof(struct sockaddr_in6));

虽然它应该是:

sin6 = (struct sockaddr_in6 *) ((char *) sin6 + alignsa(sizeof(struct sockaddr_in6)));

通过将所有内容投射到第一个 sockaddr_in6 投射的右侧,并从 rt_msghdr 结构中删除 RTA_NETMASK 请求,我设法返回了正确的网络掩码信息。

所以现在一切都很好。

于 2013-02-28T13:48:15.970 回答