首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >路由套接字RTM_GET RTA_NETMASK与路由表条目不匹配

路由套接字RTM_GET RTA_NETMASK与路由表条目不匹配
EN

Stack Overflow用户
提问于 2013-02-28 11:11:11
回答 2查看 1.1K关注 0票数 3

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

代码语言:javascript
复制
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。

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

代码:

代码语言:javascript
复制
 /* 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()为:

代码语言:javascript
复制
size_t
alignsa(size_t s)
{
    return (1 + (((s) - 1) | (sizeof(size_t) - 1)));
}

getcidr()是

代码语言:javascript
复制
/* 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::或/64,所以不知何故我认为对于RTA_NETMASK,sin6_len是0,但我不明白为什么。

EN

回答 2

Stack Overflow用户

发布于 2013-02-28 20:24:20

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

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

这来自iproute2文档(在我的Ubuntu上,它列在man8ip-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 -6route show table all和ip -6route get 2400:3700:60:4::123的输出。它们可能会略有不同,因为ip route get版本在末尾包含cached一词。

我当前所在的位置没有IPv6连接,但此本地路由说明了我的意思:

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

ff00::/8设备eth1表本地指标256

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

ff00::from ::via ff00::dev eth1源fe80::21b:b1ff:fe48:1a75公制0高速缓存

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

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

有关如何使用netlink的更多提示,请查看iproute2的源代码,特别是ip/iproute.c和lib/libnetlink.c模块。

票数 2
EN

Stack Overflow用户

发布于 2013-02-28 21:48:16

我已经设法解决了这个问题,结果是我没有正确地将缓冲区转换回main函数上的sockaddr_in6:

代码语言:javascript
复制
sin6 = (struct sockaddr_in6 *) (char *) sin6 + alignsa(sizeof(struct sockaddr_in6));

而它本应该是:

代码语言:javascript
复制
sin6 = (struct sockaddr_in6 *) ((char *) sin6 + alignsa(sizeof(struct sockaddr_in6)));

通过将所有内容转换为第一个sockaddr_in6类型转换的右侧,并从rt_msghdr结构中删除RTA_NETMASK请求,我成功地返回了正确的网络掩码信息。

所以现在一切都好了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15126757

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档