没有IP_ORIGDSTADDR标头

时间:2016-11-07 15:24:15

标签: linux-kernel network-programming

我正在尝试使用IP_ORIGDSTADDR获取UDP数据包的原始目标。在我的旧内核上,它似乎工作(我正在运行当前的debian测试内核,4.7.0-1-amd64)。

#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define DEFAULT_ADDR "127.0.0.1"
#define DEFAULT_PORT 6666

int main(int ac, char **av)
{
    int sock;
    struct sockaddr_in sin;

    memset(&sin, 0, sizeof sin);
    sin.sin_family = AF_INET;
    if (inet_aton(ac >= 2 ? av[1] : DEFAULT_ADDR, &sin.sin_addr) < 0) {
        fprintf(stderr, "Invalid address\n");
        goto err;
    }
    sin.sin_port = htons(ac >= 3 ? atoi(av[2]) : DEFAULT_PORT);

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        goto err;
    }

    if (bind(sock, (struct sockaddr *) &sin, sizeof sin) < 0) {
        perror("bind");
        goto close_err;
    }
#define SOCK_OPT(l, n, v) do {                                     \
       int _v = v;                                                 \
       socklen_t _s;                                               \
       if (setsockopt(sock, l, n, &_v, sizeof _v) < 0) {           \
               perror("setsockopt "# l "/" # n);                   \
               goto close_err;                                     \
       }                                                           \
                                                                   \
       _s = sizeof (_v);                                           \
       if (getsockopt(sock, l, n, &_v, &_s) < 0) {                 \
               perror("getsockopt "# l "/" # n);                   \
               goto close_err;                                     \
       }                                                           \
                                                                   \
       if (_v != v) {                                              \
               fprintf(stderr, "Unexpected sockopt (expected %d, found %d)\n", v, _v); \
               goto close_err;                                     \
       }                                                           \
                                                                   \
       printf(#l "/" #n " is set to %d\n", _v);                    \
                                                                   \
    } while (0)

    SOCK_OPT(SOL_IP, IP_RECVORIGDSTADDR, 1);
    SOCK_OPT(SOL_IP, IP_RECVOPTS, 1);
    SOCK_OPT(SOL_IP, IP_PKTINFO, 1);
#undef SOCK_OPT

    printf("Reading on %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));

    for (;;) {
        ssize_t n;
        char buf[1024];
        char tmp[80];
        struct iovec iovec[] = {
            {
             .iov_base = buf,
             .iov_len = sizeof buf - 1,
             }
        };
        struct msghdr msghdr;
        struct cmsghdr *cmsg_ptr;
        struct sockaddr_storage from = { 0 };
        int port;
        union cmsg_data {
            struct sockaddr_in sin;
            struct in_pktinfo pktinfo;
        };
        char msg_control[CMSG_SPACE(sizeof(union cmsg_data)) * 10] = { 0 };
        int found;

        memset(&msghdr, 0, sizeof msghdr);

        msghdr.msg_name = &from;
        msghdr.msg_namelen = sizeof from;
        msghdr.msg_iov = iovec;
        msghdr.msg_iovlen = sizeof iovec / sizeof iovec[0];
        msghdr.msg_control = msg_control;
        msghdr.msg_controllen = sizeof msg_control;
        msghdr.msg_flags = MSG_EOR | MSG_TRUNC | MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE;

        n = recvmsg(sock, &msghdr, MSG_OOB);
        if (n < 0) {
            perror("recvmsg");
            continue;
        }
        if (buf[n - 1] == '\n')
            n--;
        buf[n] = 0;

        switch (from.ss_family) {
        default:
            tmp[0] = 0;
            break;

        case AF_INET:
            inet_ntop(AF_INET, &((struct sockaddr_in *) &from)->sin_addr, tmp, sizeof tmp);
            port = htons(((struct sockaddr_in *) &from)->sin_port);
            break;

        case AF_INET6:
            inet_ntop(AF_INET6, &((struct sockaddr_in6 *) &from)->sin6_addr, tmp, sizeof tmp);
            port = htons(((struct sockaddr_in6 *) &from)->sin6_port);
            break;
        }

        printf("%s:%d Rx %ldb: %.*s, msg_control = %zdb\n", tmp, port, n, (int) n, buf, sizeof msg_control);

        found = 0;
        for (cmsg_ptr = CMSG_FIRSTHDR(&msghdr); cmsg_ptr != NULL; cmsg_ptr = CMSG_NXTHDR(&msghdr, cmsg_ptr)) {
            union cmsg_data *cmsg_data = (union cmsg_data *) CMSG_DATA(cmsg_ptr);

            switch (cmsg_ptr->cmsg_level) {
            default:
                fprintf(stderr, "Unexecpted level : %d\n", cmsg_ptr->cmsg_level);
                break;

            case SOL_IP:
                switch (cmsg_ptr->cmsg_type) {
                default:
                    fprintf(stderr, "Unexecpted type : %d\n", cmsg_ptr->cmsg_type);
                    break;

                case IP_ORIGDSTADDR:
                    printf("IP_ORIGDSTADDR: sin_addr = %s, sin_port = %d\n", inet_ntoa(cmsg_data->sin.sin_addr), htons(cmsg_data->sin.sin_port));
                    found++;
                    break;

                case IP_PKTINFO:
                    snprintf(tmp, sizeof tmp, "%s", inet_ntoa(cmsg_data->pktinfo.ipi_spec_dst));
                    printf("IP_PKTINFO: ifindex = %u, spec_dst = %s, addr = %s\n", cmsg_data->pktinfo.ipi_ifindex, tmp, inet_ntoa(cmsg_data->pktinfo.ipi_addr));
                    break;
                }

            }
        }

        if (found != 1)
            fprintf(stderr, "*** Warning: No SOL_IP / IP_ORIGDSTADDR found\n");
    }


  close_err:
    close(sock);
  err:
    return 1;
}

当尝试这一堆代码时(例如使用netcat发送数据包),我没有任何IP_ORIGDSTADDR,但只有IP_PKTINFO:我需要有UDP端口,只有IP_ORIGDSTADDR可以提供它。

有没有人遇到过这种奇怪的行为?

0 个答案:

没有答案