ICMP错误不会返回到连接的UDP套接字?

时间:2018-04-07 03:16:56

标签: c sockets unix networking icmp

RFC 1122 Internet主机要求 - 通信层表示在section 3.2.2.4

        An incoming Time Exceeded message MUST be passed to the
        transport layer.

以及(第3.2.2节)

    In those cases where the Internet layer is required to pass an
     ICMP error message to the transport layer, the IP protocol
     number MUST be extracted from the original header and used to
     select the appropriate transport protocol entity to handle the
     error.

对我来说,这意味着任何UNIX都必须将这些消息传递给传输层(我说UNIX,因为我关注UNIX网络编程,并且最初想知道它是否可能因UNIX风格而异)。换句话说,它是可移植的,我不需要使用Linux IP_RECVERRrecvmsg()

但是,this IETF thread表示在UDP传输的情况下,ICMP错误消息仅在连接的 UDP套接字的情况下传播。

在Mac OS X上,我编写了一个使用连接的UDP套接字的小程序(下面的代码),发送一个包含短TTL的数据包,但没有收到ICMP Time Exceeded消息。是什么给予​​了什么?

编辑:我还测试了Ubuntu 16.04 LTS和同样的问题。

我使用Wireshark来验证ICMP消息实际上是由我的计算机接收的。

    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <err.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <netinet/in.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <arpa/inet.h>

    #define MAXDATASIZE 1500

    /**
     * Prints a formatted message to stderr, prints a friendly version of errno, and then exits with error code 1.
     */
    void errorf(char *fmt, ...) {
      va_list args;
      va_start(args, fmt);
      vfprintf(stderr, fmt, args);
      va_end(args);
      perror(NULL);
      exit(1);
    }

    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa)
    {
      if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
      }

      return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }

    int main(int argc, char *argv[]) {
        int sockfd, numbytes;
        char buf[MAXDATASIZE];
        struct addrinfo hints, *servinfo, *p;
        int rv;
        char s[INET6_ADDRSTRLEN];
        int ttl;

        if (argc != 3) {
            fprintf(stderr,"usage: client hostname\n");
            exit(1);
        }

        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_DGRAM;

        if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            return 1;
        }

        // loop through all the results and connect to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next) {
            if ((sockfd = socket(p->ai_family, p->ai_socktype,
                    p->ai_protocol)) == -1) {
                continue;
            }
            printf("got a socket\n");

            if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
                close(sockfd);
                continue;
            }
            printf("connected\n");

            break;
        }

        if (p == NULL) {
            errorf("client: failed to connect\n");
        }

        inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
                s, sizeof s);
        printf("client: connecting to %s\n", s);

        freeaddrinfo(servinfo); // all done with this structure

        ttl = 1;
        if (setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
          errorf("can't set unicast time-to-live");
        }
        if ((numbytes = send(sockfd, "Hello, world!", 13, 0)) == -1) {
          errorf("send");
        } else {
          printf("send : %d\n", numbytes);
        }

        printf("receiving\n");
        // I expect this to fail if send() resulted in an ICMP error message
        // but instead it hangs.
        if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
            errorf("recv");
        }

        close(sockfd);

        return 0;
    }

0 个答案:

没有答案