我将代码从 FreeBSD 移植到 Linux 但它没有提取目标地址

I ported code from FreeBSD to Linux but it doesn't extract destination address

此 C 代码从套接字中提取 FreeBSD 系统上的源和目标 IPv4 地址。我将它移植到 Linux 并且它只能部分工作。它正确地打印了 pk 的源地址,但没有打印 pk 的目标地址(这将是我机器的 IP 地址)。我总是得到 0.0.0.0.

的目标地址

如何更改代码以使其也 extract/print 目标地址?

static void* start_controlpackets_demuxer_ipv4(void* arg) {
    int r;

    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE;

    struct addrinfo* sorter_addr;
    r = getaddrinfo(NULL, LISP_CONTROL_PORT, &hints, &sorter_addr);
    if (r != 0) {
        fatalr("Unable to get westbound IPv4 listener address", r);
    }

    struct addrinfo* curr_addr;
    int s;
    for (curr_addr = sorter_addr; curr_addr != NULL; curr_addr = curr_addr->ai_next) {
        s = socket(curr_addr->ai_family, curr_addr->ai_socktype, curr_addr->ai_protocol);
        if (s == -1) {
            continue;
        }

        r = bind(s, curr_addr->ai_addr, curr_addr->ai_addrlen);
        if (r == -1) {
            continue;
        }

        freeaddrinfo(sorter_addr);
        break;
    }

    if (curr_addr == NULL) {
        fatal("Unable to bind westbound IPv4 listener");
    }

    ipv4_controlpackets_socket = s;

    debug_printf("IPv4 westbound server is listening");

    /*
     * The datagram is kept in the stack space. Should the processing be offloaded to a pool of worker threads,
     * it will be necessary to move it to the heap.
     */
    uint8_t buf[IP_MAXLEN];
    char control_buf[SOCK_MSG_CONTROL_LEN];

    int opt = 1;
    setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));

    while (1) {
        ipv4_datagram datagram;
        datagram.payload = buf;

        struct msghdr raw_msg;
        struct iovec iov;

        raw_msg.msg_name = &(datagram.source);
        raw_msg.msg_namelen = sizeof(datagram.source);
        raw_msg.msg_iov = &iov;
        raw_msg.msg_iovlen = 1;
        raw_msg.msg_iov->iov_base = datagram.payload;
        raw_msg.msg_iov->iov_len = IP_MAXLEN;
        raw_msg.msg_control = (caddr_t) &control_buf;
        raw_msg.msg_controllen = SOCK_MSG_CONTROL_LEN;
        raw_msg.msg_flags = 0;

        datagram.payload_len = recvmsg(s, &raw_msg, 0);

        if (datagram.payload_len < 0) {
            fatal("Error reading from westbound IPv4 socket");
        }

        for (struct cmsghdr *c = CMSG_FIRSTHDR(&raw_msg); c != NULL; c = CMSG_NXTHDR(&raw_msg, c)) {
            if (c->cmsg_level != IPPROTO_IP || c->cmsg_type != IP_RECVDSTADDR) {
                continue;
            }

            struct in_addr* tmp_destination = (struct in_addr*) CMSG_DATA(c);

            memset(&(datagram.destination), 0, sizeof(datagram.destination));
#ifndef LINUX_OS
            datagram.destination.sin_len = sizeof(datagram.destination);
#endif
            datagram.destination.sin_family = AF_INET;
            datagram.destination.sin_addr = *tmp_destination;
        }

        char src[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(datagram.source.sin_addr), src, INET_ADDRSTRLEN);
        char dst[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(datagram.destination.sin_addr), dst, INET_ADDRSTRLEN);
        debug_printf("Processing UDPv4 datagram from %s to %s", src, dst);

        process_ipv4_datagram(&datagram);
    }

    pthread_exit(0);
}

检查 cmsg_len 字段。根据 recv(2) 手册页,它没有 return 一个地址以防出现本地错误。

在Linux中,socket选项为IP_RECVORIGDSTADDR。 (您的代码也无法初始化 iov。)

这是一个在 Linux 中工作的 lightly-tested 示例(如果使用 2.6.29、3.x 或更高版本的内核)。

#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>


typedef struct {
    struct sockaddr_in  source;
    struct sockaddr_in  destination;
    uint8_t            *payload;
    int                 payload_len;        
} ipv4_datagram;

void process_ipv4_datagram(ipv4_datagram *const datagram)
{
    char  src_host[128], src_port[32];
    char  dst_host[128], dst_port[32];
    int   result, i;

    src_host[0] = src_port[0] = '[=10=]';
    dst_host[0] = dst_port[0] = '[=10=]';

    result = getnameinfo((const struct sockaddr *)(&datagram->source),
                         sizeof (struct sockaddr_in),
                         src_host, sizeof src_host,
                         src_port, sizeof src_port,
                         NI_NUMERICHOST | NI_NUMERICSERV);
    if (result) {
        fprintf(stderr, "Warning: Cannot translate source address: %s.\n", gai_strerror(result));
        fflush(stderr);
    }

    result = getnameinfo((const struct sockaddr *)(&datagram->destination),
                         sizeof (struct sockaddr_in),
                         dst_host, sizeof dst_host,
                         dst_port, sizeof dst_port,
                         NI_NUMERICHOST | NI_NUMERICSERV);
    if (result) {
        fprintf(stderr, "Warning: Cannot translate destination address: %s.\n", gai_strerror(result));
        fflush(stderr);
    }

    printf("Received %d bytes,\n", datagram->payload_len);
    printf("  From %s port %s\n", src_host, src_port);
    printf("    To %s port %s", dst_host, dst_port);
    for (i = 0; i < datagram->payload_len; i++)
        if (i & 15)
            printf(" %02x", datagram->payload[i]);
        else
            printf("\n\t%02x", datagram->payload[i]);
    printf("\n");
    fflush(stdout);
}

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    __sync_bool_compare_and_swap(&done, (sig_atomic_t)0, (sig_atomic_t)signum);
}

static int install_done(int signum)
{
    struct sigaction act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;
    return 0;
}


int main(int argc, char *argv[])
{
    int socketfd = -1;

    /* Verify command line parameters, and print usage if necessary.
    */
    if (argc < 2 || argc > 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s [ ADDRESS/HOST ] PORT\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "This listens on IPv4 UDP connections, and reports on them.\n");
        fprintf(stderr, "Send INT (Ctrl+C), HUP, or TERM signal to exit.\n");
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    /* Install INT, TERM, and HUP signal handlers.
     * They all set the 'done' flag if caught.
    */
    if (install_done(SIGINT) ||
        install_done(SIGTERM) ||
        install_done(SIGHUP)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Open the listening socket. */
    {
        struct addrinfo *list = NULL, *curr, hints; 
        const char *node, *serv;
        int result;

        /* Empty or "-" or "*" address is the wildcard address. */
        if (argc == 3) {
            node = argv[1];
            serv = argv[2];
            if (node[0] == '[=10=]' || !strcmp(node, "-") || !strcmp(node, "*"))
                node = NULL;
        } else {
            node = NULL;
            serv = argv[1];
        }

        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_DGRAM;
        hints.ai_flags = AI_PASSIVE;
        hints.ai_protocol = 0;
        hints.ai_canonname = NULL;
        hints.ai_addr = NULL;
        hints.ai_next = NULL;
        result = getaddrinfo(node, serv, &hints, &list);
        if (result) {
            if (node)
                fprintf(stderr, "%s %s: %s.\n", node, serv, gai_strerror(result));
            else
                fprintf(stderr, "%s: %s.\n", serv, gai_strerror(result));
            return EXIT_FAILURE;
        }

        result = 0;
        socketfd = -1;
        for (curr = list; curr != NULL; curr = curr->ai_next) {
            socketfd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
            if (socketfd == -1)
                continue;

            if (bind(socketfd, curr->ai_addr, curr->ai_addrlen) == 0)
                break;

            result = errno;
            close(socketfd);
            socketfd = -1;
        }

        freeaddrinfo(list);

        if (socketfd == -1) {
            if (result)
                fprintf(stderr, "%s.\n", strerror(result));
            else
                fprintf(stderr, "Cannot bind to socket.\n");
            return EXIT_FAILURE;
        }
    }

    /* Enable the IP_RECVORIGDSTADDR socket option. */
    {
        int flag = 1;
        if (setsockopt(socketfd, IPPROTO_IP, IP_RECVORIGDSTADDR, &flag, sizeof flag) == -1) {
            fprintf(stderr, "IP_RECVORIGDSTADDR not supported: %s.\n", strerror(errno));
            close(socketfd);
            return EXIT_FAILURE;
        }
    }

    /* Receive datagram messages, until signaled.
     * Note that signal delivery causes recvmsg() to return with -1, errno == EINTR.
    */
    while (!done) {
        char    data_buffer[1024];
        char    ancillary_buffer[1024];
        ssize_t data_bytes;

        ipv4_datagram   dgram;
        struct msghdr   msg;
        struct iovec    iov;
        struct cmsghdr *cmsg;

        iov.iov_base = data_buffer;
        iov.iov_len = sizeof data_buffer;

        msg.msg_name = &(dgram.source);
        msg.msg_namelen = sizeof dgram.source;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = ancillary_buffer;
        msg.msg_controllen = sizeof ancillary_buffer;
        msg.msg_flags = 0;

        memset(&(dgram.source), 0, sizeof dgram.source);
        memset(&(dgram.destination), 0, sizeof dgram.destination);

        data_bytes = recvmsg(socketfd, &msg, 0);
        if (data_bytes == (ssize_t)-1) {

            /* Interrupted by a signal? */
            if (errno == EINTR)
                continue;

            /* Other errors we can ignore? */
            if (errno == EAGAIN || errno == EWOULDBLOCK ||
                errno == ECONNREFUSED)
                continue;

            fprintf(stderr, "Error receiving data: %s.\n", strerror(errno));
            fflush(stderr);
            break;
        }

        dgram.payload = (void *)data_buffer;
        dgram.payload_len = data_bytes;

        /* Find IP_ORIGDSTADDR ancillary message. */
        for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
            if (cmsg->cmsg_level == IPPROTO_IP &&
                cmsg->cmsg_type == IP_ORIGDSTADDR)
                memmove(&dgram.destination, CMSG_DATA(cmsg), sizeof dgram.destination);

        process_ipv4_datagram(&dgram);
    }

    close(socketfd);
    return EXIT_SUCCESS;
}

这是一个编辑版本,用于说明所需 ipv4_datagram 结构和 process_ipv4_datagram() 函数的用法。

使用例如

编译它
gcc -Wall -Wextra -O2 example.c -o example

如果你运行例如

./example host port

你可以使用Ctrl+C命令它干净退出。 使用例如netcat 使用例如

测试连接(来自同一本地网络上的其他机器)
date | nc -q 1 -u host port