1

私はBSDソケットを使用して、root権限(tracepathなど)を必要としない高度なtracerouteプログラムを構築しています。UDPとバインドされたソケットを使用して、次のように呼び出します。

recvmsg(socket, header, MSG_ERRQUEUE)

以前に送信されたパケットがトリガーされたと想定されるICMP通知に関する情報を受け取ります。ICMPペイロード(以前に送信されたパケットである必要があります)にアクセスできるかどうか知っていますか?

私はrecvmsgのマニュアルページから読みました:

[..] The payload of the original packet that caused the error
is passed as normal data via msg_iovec. [..] 

しかし、そこには有用なものが見つかりません。ランダムなデータのようです(wiresharkを使用してデータをクロスチェックしました)。

4

1 に答える 1

7

このサンプル コードを使用して、取得した ICMP エラーを確認して処理できます (サンプルにはいくつかのコメントとリンクが含まれています)。

#define BUFFER_MAX_SIZE 1024
int on = 1;
/* Set the option, so we can receive errors */
setsockopt(socket, SOL_IP, IP_RECVERR,(char*)&on, sizeof(on));
/* Other code here */
/* .... */
/* Handle receving ICMP Errors */
int return_status;
char buffer[BUFFER_MAX_SIZE];
struct iovec iov;                       /* Data array */
struct msghdr msg;                      /* Message header */
struct cmsghdr *cmsg;                   /* Control related data */
struct sock_extended_err *sock_err;     /* Struct describing the error */ 
struct icmphdr icmph;                   /* ICMP header */
struct sockaddr_in remote;              /* Our socket */

for (;;)
{
    iov.iov_base = &icmph;
    iov.iov_len = sizeof(icmph);
    msg.msg_name = (void*)&remote;
    msg.msg_namelen = sizeof(remote);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_flags = 0;
    msg.msg_control = buffer;
    msg.msg_controllen = sizeof(buffer);
    /* Receiving errors flog is set */
    return_status = recvmsg(socket, &msg, MSG_ERRQUEUE);
    if (return_status < 0)
        continue;
    /* Control messages are always accessed via some macros 
     * http://www.kernel.org/doc/man-pages/online/pages/man3/cmsg.3.html
     */
    for (cmsg = CMSG_FIRSTHDR(&msg);cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) 
    {
        /* Ip level */
        if (cmsg->cmsg_level == SOL_IP)
        {
            /* We received an error */
            if (cmsg->cmsg_type == IP_RECVERR)
            {
                fprintf(stderror, "We got IP_RECVERR message\n");
                sock_err = (struct sock_extended_err*)CMSG_DATA(cmsg); 
                if (sock_err)
                {
                    /* We are interested in ICMP errors */
                    if (sock_err->ee_origin == SO_EE_ORIGIN_ICMP && sock_err->ee_type == ICMP_DEST_UNREACH) 
                    {
                        /* Handle ICMP destination unreachable error codes */
                        switch (sock_err->ee_code) 
                        {
                            case ICMP_NET_UNREACH:
                                /* Handle this error */
                                fprintf(stderror, "Network Unreachable Error\n");
                                break;
                            case ICMP_HOST_UNREACH:
                                /* Handle this error */
                                fprintf(stderror, "Host Unreachable Error\n");
                                break;
                            /* Handle all other cases. Find more errors :
                             * http://lxr.linux.no/linux+v3.5/include/linux/icmp.h#L39
                             */

                        }
                    }
                }
            }
        } 
    }
} 
于 2012-08-12T00:22:28.823 に答える