|
UDP Redirect
|
#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <getopt.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <net/if.h>#include <arpa/inet.h>#include <fcntl.h>#include <poll.h>#include <string.h>#include <math.h>#include <netdb.h>#include <time.h>

Go to the source code of this file.
Classes | |
| struct | settings |
| struct | statistics |
Macros | |
| #define | UDP_REDIRECT_VERSION "2.2.0" |
| #define | STATISTICS_DELAY_SECONDS 60 |
| #define | NETWORK_BUFFER_SIZE 65535 |
| #define | EOK 0 |
| Readability: errno value for OK. | |
| #define | MAX_ERRNO 256 |
| #define | ERRNO_IGNORE_INIT(X) |
| #define | ERRNO_IGNORE_SET(X, Y) |
| #define | ERRNO_IGNORE_CHECK(X, Y) |
| #define | PARSE_PORT(src, dest, label) |
| #define | DEBUG(debug_level_local, debug_level_message, fmt, ...) |
| #define | HUMAN_READABLE_FORMAT "%.1lf%c" |
| #define | HRF HUMAN_READABLE_FORMAT |
| #define | HUMAN_READABLE(X) |
| #define | HUMAN_READABLE_SIZES { ' ', 'K', 'M', 'G', 'T', 'P', 'E' } |
| #define | HUMAN_READABLE_SIZES_COUNT 7 |
Enumerations | |
| enum | DEBUG_LEVEL { DEBUG_LEVEL_ERROR = 0 , DEBUG_LEVEL_INFO = 1 , DEBUG_LEVEL_VERBOSE = 2 , DEBUG_LEVEL_DEBUG = 3 } |
| The available debug levels. More... | |
Functions | |
| 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) |
| static char * | resolve_host (int debug_level, const char *host) |
| static int | parse_addr (const char *str, int port, struct sockaddr_storage *out) |
| static const char * | addr_tostring (const struct sockaddr_storage *sa, char *buf, size_t len) |
| static int | addr_port (const struct sockaddr_storage *sa) |
| static socklen_t | addr_len (const struct sockaddr_storage *sa) |
| static int | addr_is_unset (const struct sockaddr_storage *sa) |
| static int | addr_equal (const struct sockaddr_storage *a, const struct sockaddr_storage *b) |
| void | settings_initialize (struct settings *s) |
| void | usage (const char *argv0, const char *message) |
| void | statistics_initialize (struct statistics *st) |
| double | int_to_human_value (double value) |
| char | int_to_human_char (double value) |
| void | statistics_display (int debug_level, struct statistics *st, time_t now) |
| int | main (int argc, char *argv[]) |
| static double | int_to_human_scale (double value, int *count_out) |
Variables | |
| static struct option | longopts [] |
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
A single-file, high-performance UDP packet redirector supporting IPv4 and IPv6. Useful when layer-level redirection (e.g., firewall rules) is impractical — common use cases include Wireguard VPN, DNS, and game servers.
Design overview:
Targets: Linux x86-64 and macOS/Darwin arm64.
| #define DEBUG | ( | debug_level_local, | |
| debug_level_message, | |||
| fmt, | |||
| ... ) |
Standard debug macro requiring a locally defined debug level. Adapted from the excellent https://github.com/jleffler/soq/blob/master/src/libsoq/debug.h
Note that VA_ARGS is a GCC extension; used for convenience to support DEBUG without format arguments.
| #define EOK 0 |
Readability: errno value for OK.
| #define ERRNO_IGNORE_CHECK | ( | X, | |
| Y ) |
| #define ERRNO_IGNORE_INIT | ( | X | ) |
Initialize errno ignore set
| #define ERRNO_IGNORE_SET | ( | X, | |
| Y ) |
Set errno ignore boolean for a specific errno
| #define HRF HUMAN_READABLE_FORMAT |
Shortcut for the above, for shorter printf
| #define HUMAN_READABLE | ( | X | ) |
Hardcoded invocations of int to human readable, compatible with HUMAN_READABLE_FORMAT in printf
| #define HUMAN_READABLE_FORMAT "%.1lf%c" |
Hardcoded printf format for a human readable value made of a float and a char
| #define HUMAN_READABLE_SIZES { ' ', 'K', 'M', 'G', 'T', 'P', 'E' } |
The human readable size suffixes
| #define HUMAN_READABLE_SIZES_COUNT 7 |
The number of human readable size suffixes
| #define MAX_ERRNO 256 |
Maximum known errno, used to ignore harmless sendto / recvfrom errors
| #define NETWORK_BUFFER_SIZE 65535 |
The size of the network buffer used for receiving / sending packets
| #define PARSE_PORT | ( | src, | |
| dest, | |||
| label ) |
Parse a port number from a string (optarg). Sets dest to the parsed value, or prints label and exits on error. Validates that the string is a pure decimal integer in [0, 65535].
| #define STATISTICS_DELAY_SECONDS 60 |
The delay in seconds between displaying statistics
| #define UDP_REDIRECT_VERSION "2.2.0" |
The udp-redirect version
| enum DEBUG_LEVEL |
|
static |
Return 1 if both sockaddr_storage values have the same family, address, and port.

|
static |
Return 1 if the address is the zero-initialised "unset" sentinel (ss_family == 0).

|
static |
Return the wire size of a sockaddr_storage appropriate for its address family.

|
static |
Return the port from a sockaddr_storage in host byte order.

|
static |
Format the address portion of a sockaddr_storage into buf using inet_ntop.

| char int_to_human_char | ( | double | value | ) |
Convert a value to human readable (i.e., 1500 = 1.5K). Divide by 1000, not 1024.
| [in] | value | The value to be converted |


|
static |
Shared scaling helper: divides value by 1000 repeatedly until it is <= 1000 or the maximum prefix is reached.
| [in] | value | The raw value to scale. |
| [out] | count_out | The number of divisions performed (index into the human-readable suffix array). |

| double int_to_human_value | ( | double | value | ) |
Convert a value to human readable (i.e., 1500 = 1.5K). Divide by 1000, not 1024.
| [in] | value | The value to be converted |


| int main | ( | int | argc, |
| char * | argv[] ) |
Main program function.
| [in] | argc | The count of arguments, including the program name. |
| [in] | argv | The program arguments |
Accept the packet IF:
Accept the packet IF:

|
static |
Parse an IPv4 or IPv6 address string into a sockaddr_storage. Sets ss_family, the address field, and the port.
| [in] | str | Dotted-quad IPv4 or colon-separated IPv6 address string. |
| [in] | port | Port number in host byte order. |
| [out] | out | Zeroed sockaddr_storage to fill. |

|
static |
Resolve a host to an IP address.
| [in] | debug_level | The debug level to be used for the DEBUG() macro |
| [in] | host | The host to resolve |

| void settings_initialize | ( | struct settings * | s | ) |
Initialize settings.
| [out] | s | The settings structure to initialize. |

|
static |
Creates a UDP socket on the specified address, port and interface, returning the socket and the socket name (if either arguments were NULL or 0).
| [in] | debug_level | The debug level to be used for the DEBUG() macro |
| [in] | desc | The caller description, added to debug messages |
| [in] | xaddr | The address for the socket to be created, or NULL for ANY |
| [in] | xport | The port for the socket to be created, or 0 for random (decided by bind()) |
| [in] | xif | The OS interface name to bind to, or NULL for all interfaces. |
| [in] | xfamily | AF_INET or AF_INET6; used only when xaddr is NULL. |
| [out] | xsock_name | The name of the socket created. |


| void statistics_display | ( | int | debug_level, |
| struct statistics * | st, | ||
| time_t | now ) |
Display the stored statistics
| [in] | debug_level | The debug level to be used for the DEBUG() macro |
| [in] | st | The statistics structure |
| [in] | now | The current time |

| void statistics_initialize | ( | struct statistics * | st | ) |
Initialize statistics.
| [out] | st | The statistics structure to initialize. |

| void usage | ( | const char * | argv0, |
| const char * | message ) |
Displays the program usage and exit with error.
| [in] | argv0 | The program name as started, e.g., /usr/local/bin/udp-redirect |
| [in] | message | The error message, or NULL |

|
static |
Command line options.