【问题标题】:route socket RTM_GET RTA_NETMASK does not match routing table entry路由套接字 RTM_GET RTA_NETMASK 与路由表条目不匹配
【发布时间】:2013-02-28 03:11:10
【问题描述】:

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

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,但我不明白为什么。

【问题讨论】:

  • 我想更正一下,当我打印出网络掩码值时,它显示为 0,而不是 /64。
  • 我实际上在 FreeBSD 上,所以我不知道 iproute 设施。
  • 我也想通过 IPv6 路由从内核读取信息,但在 write 调用时我得到了 EINVAL。

标签: c sockets ipv6


【解决方案1】:

编辑:这个答案是错误的,因为它只适用于 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 版本的末尾包含 cached 这个词。

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

thuovila@glx:~$ ip -6 r l 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 缓存

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

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

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

【讨论】:

    【解决方案2】:

    我已经设法解决了这个问题,事实证明我没有正确地将缓冲区转换回主函数上的 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 请求,我设法返回了正确的网络掩码信息。

    所以现在一切都很好。

    【讨论】:

      猜你喜欢
      • 2021-12-09
      • 2014-09-27
      • 2017-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-13
      • 2019-11-20
      • 1970-01-01
      相关资源
      最近更新 更多