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情報を正しく取得できましたが、ルーティングテーブルに/ 64と表示されていても、RTA_NETMASKは常に0を返します。

正しいネットマスクエントリを取得するにはどうすればよいですか。

コード:

 /* 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 にのみ適用されるため、この回答は間違っています。op はソケットのルーティングについて尋ねていました。

あなたのトピックはRTM_GETと言っていますが、RTM_GETROUTEを意味していると思います。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 versionで、おそらく少し異なります。

私は現在、IPv6 接続のない場所にいますが、このローカル ルートは私が何を意味するかを示しています。

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

ff00::/8 dev eth1 テーブル ローカル メトリック 256

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

ff00:: から :: 経由で ff00:: dev eth1 src fe80::21b:b1ff:fe48:1a75 メトリック 0 キャッシュ

お気づきのように、キャッシュされたルートには宛先アドレスの長さ (ネットマスク) が含まれていません。

あなたの問題 (ネットマスクの取得方法) を解決するには、ネットリンク メッセージのフィルタリングを検討することをお勧めします。単一の RTM_GET リクエストの代わりに、基準に一致するルートをダンプし、複製されたルートをフィルタリングする必要があります。iproute2 ツールは、たとえば関数iproute.cモジュールでこれを行いますstatic int filter_nlmsgif (filter.cloned == !(r->rtm_flags&RTM_F_CLONED))これには、特に、複製された (つまり、キャッシュから発信された) ルートを削除するという行が含まれています。route_print()表示されるルートを制限するためにによって呼び出されます。ルートは、ip route listコマンドで libnetlink function を呼び出してダンプされますrtnl_dump_filter()

netlink の使用方法に関するその他のヒントについては、iproute2 のソース、特にip/iproute.cおよびlib/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 に答える