我将代码从 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
此 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