Add optional source address for UDP.
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Tue, 8 Nov 2022 05:49:49 +0000 (16:49 +1100)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Tue, 8 Nov 2022 05:49:49 +0000 (16:49 +1100)
rrqnet.c

index efd7066b457cb2438d96f1246cc553ad152cc3ed..ea0a3aff557dfdc0f6f1dbdb60e6b596f49d1598 100644 (file)
--- a/rrqnet.c
+++ b/rrqnet.c
@@ -188,6 +188,12 @@ static struct {
 // Flag to signal the UDP socket as being ipv6 or not (forced ipv4)
 static int udp6 = 1;
 
+// The given UDP source address, if any
+static struct {
+    int family;
+    unsigned char address[16];
+} udp_source;
+
 // Flag to indicate tpg transport patch = avoid UDP payload of 1470
 // bytes by adding 2 tag-along bytes
 static int tpg_quirk = 0;
@@ -728,6 +734,38 @@ static int parse_mcast(char *arg) {
     return 0;
 }
 
+//** IP address parsing utility for UDP source address
+// Return 0 if ok and 1 otherwise
+// Formats: <ipv4-address> or <ipv6-address>
+// The ipv4 address should be a multicast address in ranges
+// 224.0.0.0/22, 232.0.0.0/7, 234.0.0.0/8 or 239.0.0.0/8
+// though it's not checked here.
+static int parse_udp_source(char *arg) {
+    if ( inet_pton( AF_INET6, arg, udp_source.address ) ) {
+       // An ipv6 address is given.
+       if ( udp6 ) {
+           udp_source.family = AF_INET6;
+           return 0;
+       }
+       return 1;
+    }
+    if ( ! inet_pton( AF_INET, arg, udp_source.address ) ) {
+       return 1;
+    }
+
+    // An ipv4 address is given.
+    if ( udp6 ) {
+       // Translate into ipv6-encoded ipv4
+       memmove( udp_source.address + 12, udp_source.address, 4 );
+       memset( udp_source.address, 0, 10 );
+       memset( udp_source.address + 10, -1, 2 );
+       udp_source.family = AF_INET6;
+    } else {
+       udp_source.family = AF_INET;
+    }
+    return 0;
+}
+
 // Utility that sets upt the multicast socket, which is used for
 // receiving multicast packets.
 static void setup_mcast() {
@@ -1312,9 +1350,16 @@ static void usage(void) {
     fprintf( stderr, "Packet tunneling over UDP, multiple channels, " );
     fprintf( stderr, "version 1.5.3\n" );
     fprintf( stderr, "Usage: " );
-    fprintf( stderr,
- "%s [-v] [-tpg] [-4] [-B n] [-T n] [-m mcast] [-t tap] port [remote]+ \n",
-            progname );
+    fprintf( stderr, "%s [options] port [remote]+ \n", progname );
+    fprintf( stderr, "** options must be given or omitted in order!!\n" );
+    fprintf( stderr, " -v        = verbose log, -vv or -vvv for more logs\n" );
+    fprintf( stderr, " -tpg      = UDP transport quirk: avoid bad sizes\n" );
+    fprintf( stderr, " -4        = use an ipv4 UDP socket\n" );
+    fprintf( stderr, " -B n      = use n buffers (2*threads) by default\n");
+    fprintf( stderr, " -T n      = use n delivery threads (5 bu default)\n" );
+    fprintf( stderr, " -m mcast  = allow remotes on multicast address\n" );
+    fprintf( stderr, " -t tap    = use the nominated tap (or - for stdio)\n" );
+    fprintf( stderr, " -I source = use given source address for UDP\n" );
     exit( 1 );
 }
 
@@ -1472,6 +1517,15 @@ int main(int argc, char *argv[]) {
        i += 2;
        ENSUREARGS( 1 );
     }
+    // Then optional source address for UDP
+    if ( strncmp( "-I", argv[i], 2 ) == 0 ) {
+       ENSUREARGS( 2 );
+       if ( parse_udp_source( argv[i+1] ) ) {
+           usage();
+       }
+       i += 2;
+       ENSUREARGS( 1 );
+    }
     // then: required port
     if ( sscanf( argv[i++], "%d", &port ) != 1 ) {
        fprintf( stderr, "Bad local port: %s\n", argv[i-1] );
@@ -1547,8 +1601,12 @@ int main(int argc, char *argv[]) {
        struct sockaddr_in udp_addr = {
            .sin_family = AF_INET,
            .sin_port = htons( port ),
-           .sin_addr.s_addr = htonl(INADDR_ANY),
        };
+       if ( udp_source.family == 0 ) {
+           udp_addr.sin_addr.s_addr = htonl( INADDR_ANY );
+       } else {
+           udp_addr.sin_addr.s_addr = *((uint32_t*) udp_source.address); 
+       }
        if ( bind( udp_fd, (struct sockaddr*) &udp_addr, sizeof(udp_addr))) {
            fprintf( stderr, "Error binding socket!\n");
            exit(1);
@@ -1563,8 +1621,8 @@ int main(int argc, char *argv[]) {
        struct sockaddr_in6 udp6_addr = {
            .sin6_family = AF_INET6,
            .sin6_port = htons( port ),
-           .sin6_addr = IN6ADDR_ANY_INIT,
        };
+       memcpy( udp6_addr.sin6_addr.s6_addr, udp_source.address, 16 );
        if ( bind( udp_fd, (struct sockaddr*) &udp6_addr, sizeof(udp6_addr))) {
            fprintf( stderr, "Error binding socket!\n");
            exit(1);