2016-01-13 60 views
0

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

如何更改代码以使其提取/打印目标地址?

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); 
} 
+0

你从哪里得到'IP_RECVDSTADDR'? AFAIK,它是一个BSD /达尔文功能。 ['IP_RECVORIGDSTADDR'](http://man7.org/linux/man-pages/man7/ip.7.html)是Linux中使用的。如果你能验证这些代码在FreeBSD中也能工作,我可以掀起一个Linux例子。 –

+0

您的权利,IP_RECVDSTADDR适用于BSD。我发布的代码的确在FreeBSD上工作,但我需要在Linux上工作。所以改为IP_RECVORIGDSTADDR,因为你建议,但它仍然无法正常工作。 – Pheonix7

+0

在Linux中,套接字选项是'IP_RECVORIGDSTADDR',辅助消息类型是'IP_ORIGDSTADDR',而不是'IP_RECVDSTADDR'。你也应该使用'CMSG _()'宏。在我的答案中看到我的(轻度测试工作)示例代码。 –

回答

3

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

这是一个在Linux(如果使用的是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] = '\0'; 
    dst_host[0] = dst_port[0] = '\0'; 

    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] == '\0' || !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 
+0

这正是我需要的。有用。非常感谢! – Pheonix7

+0

只是最后一个问题。我需要传递一个具有以下结构的数据报: typedef struct { \t struct sockaddr_in source; \t struct sockaddr_in destination;有效载荷; \t uint8_t * payload; \t int payload_len; } ipv4_datagram; - 我怎么能通过我刚提取到的ipv4_datagram数据报的信息(谢谢你)?因为会有另外一个处理ipv4_datagrams的函数。它在我的原始代码的末尾被调用:process_ipv4_datagram(&datagram); – Pheonix7

+0

@ Pheonix7:'payload'成员需要指向数据缓冲区'iov'引用,'payload_len'成员是'recvmsg()','msg.name'和'msg的返回值。name_len'应指向'ipv4_datagram'结构中的'source',并且'destination'从'CMSG()'for循环中的辅助缓冲区复制。我修改了上面的示例代码,使这些显式。 –

0

检查cmsg_len字段。根据recv(2)手册页,如果发生本地错误,它不会返回地址。

+0

你能告诉我一个代码snipet来检查并打印错误吗? – Pheonix7

+0

'if(!cmsg-> cmsg_len){/ * handle error here ... * /}' 查看手册页recv(2)以获取更多详细信息。 – Bernhard