38#include <sys/socket.h>
39#include <netinet/in.h>
52#define UDP_REDIRECT_VERSION "2.2.0"
57#define STATISTICS_DELAY_SECONDS 60
62#define NETWORK_BUFFER_SIZE 65535
77#define ERRNO_IGNORE_INIT(X) memset((X), 0, MAX_ERRNO * sizeof(unsigned char))
82#define ERRNO_IGNORE_SET(X, Y) if ((Y) >= 0 && (Y) < MAX_ERRNO) (X)[(Y)] = 1
87#define ERRNO_IGNORE_CHECK(X, Y) ((Y) >= 0 && (Y) < MAX_ERRNO && (X)[(Y)] == 1)
104#define PARSE_PORT(src, dest, label) \
109 _val = strtol((src), &_endptr, 10); \
110 if (errno != EOK || _endptr == (src) || *_endptr != '\0' || _val < 0 || _val > 65535) { \
111 DEBUG(debug_level, DEBUG_LEVEL_ERROR, "Invalid %s: %s", (label), (src)); \
112 exit(EXIT_FAILURE); \
114 (dest) = (int)_val; \
123#define DEBUG(debug_level_local, debug_level_message, fmt, ...) \
125 if ((debug_level_local) >= (debug_level_message)) { \
126 fprintf(stderr, "%s:%d:%lld:%s(): " fmt "\n", __FILE__, \
127 __LINE__, (long long)(time(NULL)), __func__, ##__VA_ARGS__); \
135 {
"verbose", no_argument, NULL,
'v' },
136 {
"debug", no_argument, NULL,
'd' },
138 {
"listen-address", required_argument, NULL,
'a' },
139 {
"listen-port", required_argument, NULL,
'b' },
140 {
"listen-interface", required_argument, NULL,
'c' },
142 {
"connect-address", required_argument, NULL,
'g' },
143 {
"connect-host", required_argument, NULL,
'h' },
144 {
"connect-port", required_argument, NULL,
'i' },
146 {
"send-address", required_argument, NULL,
'm' },
147 {
"send-port", required_argument, NULL,
'n' },
148 {
"send-interface", required_argument, NULL,
'o' },
150 {
"listen-address-strict", no_argument, NULL,
'x' },
151 {
"connect-address-strict",no_argument, NULL,
'y' },
153 {
"listen-sender-address", required_argument, NULL,
'k' },
154 {
"listen-sender-port", required_argument, NULL,
'l' },
156 {
"ignore-errors", no_argument, NULL,
'r' },
157 {
"stop-errors", no_argument, NULL,
's' },
159 {
"stats", no_argument, NULL,
'q' },
161 {
"version", no_argument, NULL,
'z' },
231static int socket_setup(
const int debug_level,
const char *desc,
const char *xaddr,
const int xport,
const char *xif,
int xfamily,
struct sockaddr_storage *xsock_name);
232static char *
resolve_host(
int debug_level,
const char *host);
234static int parse_addr(
const char *str,
int port,
struct sockaddr_storage *out);
235static const char *
addr_tostring(
const struct sockaddr_storage *sa,
char *buf,
size_t len);
236static int addr_port(
const struct sockaddr_storage *sa);
237static socklen_t
addr_len(
const struct sockaddr_storage *sa);
239static int addr_equal(
const struct sockaddr_storage *a,
const struct sockaddr_storage *b);
242void usage(
const char *argv0,
const char *message);
257int main(
int argc,
char *argv[]) {
260 char *argv0 = argv[0];
274 struct sockaddr_storage lsock_name;
275 struct sockaddr_storage ssock_name;
277 struct sockaddr_storage caddr;
280 char print_buffer1[INET6_ADDRSTRLEN];
281 char print_buffer2[INET6_ADDRSTRLEN];
285 struct pollfd ufds[2];
287 struct sockaddr_storage endpoint;
288 struct sockaddr_storage previous_endpoint;
295 while ((ch = getopt_long(argc, argv,
"",
longopts, NULL)) != -1) {
301 debug_level = debug_level + 1;
387 usage(argv0,
"Unknown argument");
391 usage(argv0,
"Listen port not specified");
395 usage(argv0,
"Connect host or address not specified");
399 usage(argv0,
"Connect port not specified");
404 usage(argv0,
"Options --listen-sender-port and --list-sender-address must either both be specified or none");
413 char *caddr_alloc = NULL;
414 if (s.
chost != NULL) {
416 s.
caddr = caddr_alloc;
425 if (s.
chost != NULL) {
429 if (s.
caddr != NULL) {
462 if (caddr_alloc != NULL) {
467 if (caddr_alloc != NULL) {
476 int lsock_family = (int)caddr.ss_family;
477 if (s.
laddr != NULL) {
478 struct sockaddr_storage tmp;
479 memset(&tmp, 0,
sizeof(tmp));
486 if (lsock_family != (
int)caddr.ss_family) {
488 (lsock_family == AF_INET6) ?
"IPv6" :
"IPv4",
489 ((
int)caddr.ss_family == AF_INET6) ?
"IPv6" :
"IPv4");
493 if (s.
saddr != NULL) {
494 struct sockaddr_storage tmp;
495 memset(&tmp, 0,
sizeof(tmp));
497 if (f != -1 && f != (
int)caddr.ss_family) {
499 "Send address family (%s) does not match connect address family (%s)",
500 (f == AF_INET6) ?
"IPv6" :
"IPv4",
501 ((
int)caddr.ss_family == AF_INET6) ?
"IPv6" :
"IPv4");
509 memset(&endpoint, 0,
sizeof(endpoint));
512 memset(&previous_endpoint, 0,
sizeof(previous_endpoint));
521 socklen_t endpoint_len =
sizeof(endpoint);
543 ssize_t recvfrom_retval;
544 ssize_t sendto_retval;
548 ufds[0].fd = lsock; ufds[0].events = POLLIN | POLLPRI; ufds[0].revents = 0;
549 ufds[1].fd = ssock; ufds[1].events = POLLIN | POLLPRI; ufds[1].revents = 0;
558 if ((poll_retval = poll(ufds, 2, 1000)) == -1) {
559 if (errno == EINTR) {
568 if (poll_retval == 0) {
574 if (ufds[0].revents & POLLIN || ufds[0].revents & POLLPRI) {
575 endpoint_len =
sizeof(endpoint);
576 if ((recvfrom_retval = recvfrom(lsock, network_buffer,
sizeof(network_buffer), 0,
577 (
struct sockaddr *)&endpoint, &endpoint_len)) == -1) {
585 if (recvfrom_retval >= 0) {
603 if (!
addr_equal(&previous_endpoint, &endpoint)) {
607 memcpy(&previous_endpoint, &endpoint,
sizeof(endpoint));
610 if ((sendto_retval = sendto(ssock, network_buffer, recvfrom_retval, 0,
611 (
struct sockaddr *)&caddr,
addr_len(&caddr))) == -1) {
624 "SEND (%s, %d) -> (%s, %d) (SEND PORT): %zd bytes (%s WRITE %zd bytes)",
628 (sendto_retval == recvfrom_retval)?
"FULL":
"PARTIAL", recvfrom_retval);
638 if (ufds[1].revents & POLLIN || ufds[1].revents & POLLPRI) {
639 endpoint_len =
sizeof(endpoint);
640 if ((recvfrom_retval = recvfrom(ssock, network_buffer,
sizeof(network_buffer), 0,
641 (
struct sockaddr *)&endpoint, &endpoint_len)) == -1) {
649 if (recvfrom_retval >= 0) {
666 if ((sendto_retval = sendto(lsock, network_buffer, recvfrom_retval, 0,
667 (
struct sockaddr *)&previous_endpoint,
addr_len(&previous_endpoint))) == -1) {
680 "SEND (%s, %d) -> (%s, %d) (LISTEN PORT): %zd bytes (%s WRITE %zd bytes)",
684 (sendto_retval == recvfrom_retval)?
"FULL":
"PARTIAL", recvfrom_retval);
708static int parse_addr(
const char *str,
int port,
struct sockaddr_storage *out) {
709 struct sockaddr_in *a4 = (
struct sockaddr_in *)out;
710 struct sockaddr_in6 *a6 = (
struct sockaddr_in6 *)out;
712 memset(out, 0,
sizeof(*out));
717 if (strchr(str,
'%') != NULL)
720 if (inet_pton(AF_INET, str, &a4->sin_addr) == 1) {
721 out->ss_family = AF_INET;
722 a4->sin_port = htons(port);
725 if (inet_pton(AF_INET6, str, &a6->sin6_addr) == 1) {
726 out->ss_family = AF_INET6;
727 a6->sin6_port = htons(port);
737static const char *
addr_tostring(
const struct sockaddr_storage *sa,
char *buf,
size_t len) {
739 if (sa->ss_family == AF_INET6)
740 ptr = &((
const struct sockaddr_in6 *)sa)->sin6_addr;
742 ptr = &((
const struct sockaddr_in *)sa)->sin_addr;
743 return inet_ntop((
int)sa->ss_family, ptr, buf, (socklen_t)len) ? buf :
"?";
749static int addr_port(
const struct sockaddr_storage *sa) {
750 if (sa->ss_family == AF_INET6)
751 return ntohs(((
const struct sockaddr_in6 *)sa)->sin6_port);
752 return ntohs(((
const struct sockaddr_in *)sa)->sin_port);
758static socklen_t
addr_len(
const struct sockaddr_storage *sa) {
759 return (sa->ss_family == AF_INET6)
760 ? (socklen_t)
sizeof(
struct sockaddr_in6)
761 : (socklen_t)
sizeof(
struct sockaddr_in);
768 return sa->ss_family == 0;
774static int addr_equal(
const struct sockaddr_storage *a,
const struct sockaddr_storage *b) {
775 if (a->ss_family != b->ss_family)
777 if (a->ss_family == AF_INET) {
778 const struct sockaddr_in *a4 = (
const struct sockaddr_in *)a;
779 const struct sockaddr_in *b4 = (
const struct sockaddr_in *)b;
780 return a4->sin_addr.s_addr == b4->sin_addr.s_addr &&
781 a4->sin_port == b4->sin_port;
783 if (a->ss_family == AF_INET6) {
784 const struct sockaddr_in6 *a6 = (
const struct sockaddr_in6 *)a;
785 const struct sockaddr_in6 *b6 = (
const struct sockaddr_in6 *)b;
786 return memcmp(&a6->sin6_addr, &b6->sin6_addr,
sizeof(
struct in6_addr)) == 0 &&
787 a6->sin6_port == b6->sin6_port;
806static int socket_setup(
const int debug_level,
const char *desc,
const char *xaddr,
const int xport,
const char *xif,
int xfamily,
struct sockaddr_storage *xsock_name) {
808 const int enable = 1;
810 struct sockaddr_storage addr;
812 memset(&addr, 0,
sizeof(addr));
821 family = (int)addr.ss_family;
825 addr.ss_family = (sa_family_t)family;
826 if (family == AF_INET6) {
827 ((
struct sockaddr_in6 *)&addr)->sin6_addr = in6addr_any;
828 ((
struct sockaddr_in6 *)&addr)->sin6_port = htons(xport);
830 ((
struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY;
831 ((
struct sockaddr_in *)&addr)->sin_port = htons(xport);
845 if ((xsock = socket(family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
854 unsigned int xif_idx;
858 if ((xif_idx = if_nametoindex(xif)) == 0) {
859 perror(
"if_nametoindex");
866 int if_level = (family == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
867 int if_optname = (family == AF_INET6) ? IPV6_BOUND_IF : IP_BOUND_IF;
868 if (setsockopt(xsock, if_level, if_optname, &xif_idx,
sizeof(xif_idx)) == -1) {
869 perror(
"setsockopt");
878 if (strlen(xif) >= IFNAMSIZ) {
883 if (setsockopt(xsock, SOL_SOCKET, SO_BINDTODEVICE, xif, strlen(xif) + 1) == -1) {
884 perror(
"setsockopt");
896 if (setsockopt(xsock, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int)) < 0) {
897 perror(
"setsockopt");
904 if (fcntl(xsock, F_SETFL, O_NONBLOCK) == -1) {
912 if (bind(xsock, (
struct sockaddr *)&addr,
addr_len(&addr)) == -1) {
919 socklen_t xsock_name_len =
sizeof(*xsock_name);
920 if (getsockname(xsock, (
struct sockaddr *)xsock_name, &xsock_name_len) == -1) {
921 perror(
"getsockname");
940 struct addrinfo hints;
941 struct addrinfo *res;
942 char buf[INET6_ADDRSTRLEN];
946 memset(&hints, 0,
sizeof(hints));
947 hints.ai_family = AF_UNSPEC;
948 hints.ai_socktype = SOCK_DGRAM;
950 if ((rc = getaddrinfo(host, NULL, &hints, &res)) != 0) {
958 const void *addr_ptr = (res->ai_family == AF_INET6)
959 ? (
const void *)&((
struct sockaddr_in6 *)res->ai_addr)->sin6_addr
960 : (
const void *)&((
struct sockaddr_in *)res->ai_addr)->sin_addr;
962 if (inet_ntop(res->ai_family, addr_ptr, buf,
sizeof(buf)) == NULL) {
973 if ((retval = strdup(buf)) == NULL) {
1021void usage(
const char *argv0,
const char *message) {
1022 if (message != NULL)
1023 fprintf(stderr,
"%s\n", message);
1025 fprintf(stderr,
"Usage: %s\n", argv0);
1026 fprintf(stderr,
" [--listen-address <address>] --listen-port <port> [--listen-interface <interface>]\n");
1027 fprintf(stderr,
" [--connect-address <address> | --connect-host <hostname> --connect-port <port>\n");
1028 fprintf(stderr,
" [--send-address <address>] [--send-port <port>] [--send-interface <interface>]\n");
1029 fprintf(stderr,
" [--listen-address-strict] [--connect-address-strict]\n");
1030 fprintf(stderr,
" [--listen-sender-address <address>] [--listen-sender-port <port>]\n");
1031 fprintf(stderr,
" [--ignore-errors] [--stop-errors]\n");
1032 fprintf(stderr,
" [--stats] [--verbose] [--debug] [--version]\n");
1033 fprintf(stderr,
"\n");
1034 fprintf(stderr,
"--stats Display sent/received bytes statistics every 60 seconds (optional)\n");
1035 fprintf(stderr,
"--verbose Verbose mode, can be specified multiple times (optional)\n");
1036 fprintf(stderr,
"--debug Debug mode (optional)\n");
1037 fprintf(stderr,
"--version Display the version and exit\n");
1038 fprintf(stderr,
"\n");
1039 fprintf(stderr,
"--listen-address <address> Listen address, IPv4 or IPv6 (optional)\n");
1040 fprintf(stderr,
"--listen-port <port> Listen port (required)\n");
1041 fprintf(stderr,
"--listen-interface <interface> Listen interface name (optional)\n");
1042 fprintf(stderr,
"--listen-address-strict Only receive packets from the same source as the first packet (optional)\n");
1043 fprintf(stderr,
"\n");
1044 fprintf(stderr,
"--connect-address <address> Connect address, IPv4 or IPv6 (optional if --connect-host is specified)\n");
1045 fprintf(stderr,
"--connect-host <hostname> Connect host, overwrites --connect-address if both are specified (optional if --connect-address is specified)\n");
1046 fprintf(stderr,
"--connect-port <port> Connect port (required)\n");
1047 fprintf(stderr,
"--connect-address-strict Only receive packets from --connect-address / --connect-port (optional)\n");
1048 fprintf(stderr,
"\n");
1049 fprintf(stderr,
"--send-address <address> Send packets from address, IPv4 or IPv6 (optional)\n");
1050 fprintf(stderr,
"--send-port <port> Send packets from port (optional)\n");
1051 fprintf(stderr,
"--send-interface <interface> Send packets from interface (optional)\n");
1052 fprintf(stderr,
"\n");
1053 fprintf(stderr,
"--listen-sender-address <address> Listen endpoint only accepts packets from this source address, IPv4 or IPv6 (optional)\n");
1054 fprintf(stderr,
"--listen-sender-port <port> Listen endpoint only accepts packets from this source port (optional)\n");
1055 fprintf(stderr,
" (must be set together, --listen-address-strict is implied)\n");
1056 fprintf(stderr,
"\n");
1057 fprintf(stderr,
"--ignore-errors Ignore most receive or send errors (unreachable, etc.) instead of exiting (optional) (default)\n");
1058 fprintf(stderr,
"--stop-errors Exit on most receive or send errors (unreachable, etc.) (optional)\n");
1059 fprintf(stderr,
"\n");
1069#define HUMAN_READABLE_FORMAT "%.1lf%c"
1074#define HRF HUMAN_READABLE_FORMAT
1079#define HUMAN_READABLE(X) int_to_human_value((X)), int_to_human_char((X))
1084#define HUMAN_READABLE_SIZES { ' ', 'K', 'M', 'G', 'T', 'P', 'E' }
1089#define HUMAN_READABLE_SIZES_COUNT 7
1136 value = value / 1000;
1163 return human_readable_sizes[count];
1183 if (time_delta_total < 1)
1184 time_delta_total = 1;
Definition udp-redirect.c:169
int lstrict
Strict mode for listener (set endpoint on first packet arrival).
Definition udp-redirect.c:182
char * lsaddr
Listen port expects packets from this address.
Definition udp-redirect.c:185
int cstrict
Strict mode for sender (only accept from caddr / cport).
Definition udp-redirect.c:183
char * laddr
Listen address.
Definition udp-redirect.c:170
char * lif
Listen interface.
Definition udp-redirect.c:172
int lport
Listen port.
Definition udp-redirect.c:171
int sport
Send packets from port.
Definition udp-redirect.c:179
int eignore
Ignore most recvfrom / sendto errors.
Definition udp-redirect.c:188
int stats
Display stats every 60 seconds.
Definition udp-redirect.c:190
char * caddr
Connect address.
Definition udp-redirect.c:174
int lsport
Listen port only expects packets from this port.
Definition udp-redirect.c:186
char * saddr
Send packets from address.
Definition udp-redirect.c:178
char * sif
Send packets from interface.
Definition udp-redirect.c:180
int cport
Connect port.
Definition udp-redirect.c:176
char * chost
Connect host.
Definition udp-redirect.c:175
Definition udp-redirect.c:200
unsigned long count_listen_byte_receive_total
Cumulative bytes received on the listen socket.
Definition udp-redirect.c:217
unsigned long count_connect_packet_send
Packets forwarded to the remote endpoint this window.
Definition udp-redirect.c:213
time_t time_display_last
Wall-clock time of the last statistics_display() call; used to compute the window duration.
Definition udp-redirect.c:201
unsigned long count_connect_byte_send_total
Cumulative bytes forwarded to the remote endpoint.
Definition udp-redirect.c:226
unsigned long count_connect_byte_receive
Bytes received on the send socket this window.
Definition udp-redirect.c:211
unsigned long count_connect_byte_receive_total
Cumulative bytes received on the send socket.
Definition udp-redirect.c:223
unsigned long count_connect_byte_send
Bytes forwarded to the remote endpoint this window.
Definition udp-redirect.c:214
unsigned long count_connect_packet_send_total
Cumulative packets forwarded to the remote endpoint.
Definition udp-redirect.c:225
unsigned long count_listen_byte_send
Bytes sent back on the listen socket this window.
Definition udp-redirect.c:208
unsigned long count_connect_packet_receive_total
Cumulative packets received on the send socket.
Definition udp-redirect.c:222
unsigned long count_listen_packet_send
Packets sent back on the listen socket (connect→client) this window.
Definition udp-redirect.c:207
unsigned long count_connect_packet_receive
Packets received on the send socket (from remote endpoint) this window.
Definition udp-redirect.c:210
unsigned long count_listen_byte_receive
Bytes received on the listen socket this window.
Definition udp-redirect.c:205
unsigned long count_listen_packet_receive
Packets received on the listen socket this window.
Definition udp-redirect.c:204
unsigned long count_listen_byte_send_total
Cumulative bytes sent back on the listen socket.
Definition udp-redirect.c:220
unsigned long count_listen_packet_receive_total
Cumulative packets received on the listen socket.
Definition udp-redirect.c:216
time_t time_display_first
Wall-clock time of the first statistics_display() call; used to compute the cumulative duration.
Definition udp-redirect.c:202
unsigned long count_listen_packet_send_total
Cumulative packets sent back on the listen socket.
Definition udp-redirect.c:219
#define main
Definition udp-redirect-test.c:627
#define PARSE_PORT(src, dest, label)
Definition udp-redirect.c:104
DEBUG_LEVEL
The available debug levels.
Definition udp-redirect.c:92
@ DEBUG_LEVEL_ERROR
Error messages.
Definition udp-redirect.c:93
@ DEBUG_LEVEL_DEBUG
Debug messages.
Definition udp-redirect.c:96
@ DEBUG_LEVEL_VERBOSE
Verbose messages.
Definition udp-redirect.c:95
@ DEBUG_LEVEL_INFO
Informational messages.
Definition udp-redirect.c:94
char int_to_human_char(double value)
Definition udp-redirect.c:1159
#define HUMAN_READABLE_SIZES_COUNT
Definition udp-redirect.c:1089
#define NETWORK_BUFFER_SIZE
Definition udp-redirect.c:62
double int_to_human_value(double value)
Definition udp-redirect.c:1149
#define DEBUG(debug_level_local, debug_level_message, fmt,...)
Definition udp-redirect.c:123
static int addr_is_unset(const struct sockaddr_storage *sa)
Definition udp-redirect.c:767
static socklen_t addr_len(const struct sockaddr_storage *sa)
Definition udp-redirect.c:758
static int addr_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
Definition udp-redirect.c:774
#define ERRNO_IGNORE_SET(X, Y)
Definition udp-redirect.c:82
void settings_initialize(struct settings *s)
Definition udp-redirect.c:991
#define ERRNO_IGNORE_INIT(X)
Definition udp-redirect.c:77
static int socket_setup(const int debug_level, const char *desc, const char *xaddr, const int xport, const char *xif, int xfamily, struct sockaddr_storage *xsock_name)
Definition udp-redirect.c:806
static char * resolve_host(int debug_level, const char *host)
Definition udp-redirect.c:939
static int addr_port(const struct sockaddr_storage *sa)
Definition udp-redirect.c:749
static struct option longopts[]
Definition udp-redirect.c:134
#define MAX_ERRNO
Definition udp-redirect.c:72
#define STATISTICS_DELAY_SECONDS
Definition udp-redirect.c:57
#define HUMAN_READABLE_SIZES
Definition udp-redirect.c:1084
static const char * addr_tostring(const struct sockaddr_storage *sa, char *buf, size_t len)
Definition udp-redirect.c:737
static double int_to_human_scale(double value, int *count_out)
Definition udp-redirect.c:1132
#define HUMAN_READABLE(X)
Definition udp-redirect.c:1079
#define ERRNO_IGNORE_CHECK(X, Y)
Definition udp-redirect.c:87
#define UDP_REDIRECT_VERSION
Definition udp-redirect.c:52
void usage(const char *argv0, const char *message)
Definition udp-redirect.c:1021
static int parse_addr(const char *str, int port, struct sockaddr_storage *out)
Definition udp-redirect.c:708
void statistics_display(int debug_level, struct statistics *st, time_t now)
Definition udp-redirect.c:1172
void statistics_initialize(struct statistics *st)
Definition udp-redirect.c:1095
#define HRF
Definition udp-redirect.c:1074