added ipv6 sending
[rrq/rrqnet.git] / rrqnet.c
index 739eff6181cded40da5b396b2e5b19c23b0574b2..7c702208d5b6e6bf290e4680dfd50384511f0a84 100644 (file)
--- a/rrqnet.c
+++ b/rrqnet.c
@@ -66,7 +66,8 @@ struct Allowed {
 // Details of actualized connections.
 struct Remote {
     struct SockAddr uaddr;     // The remote IP address
-    struct SockAddr laddr;     // The local IP address
+    struct SockAddr laddr;     // The local IP for this remote
+    int ifindex;               // The local interface index
     struct Allowed *spec;      // Rule being instantiated
     struct timeval rec_when;   // Last received packet time, in seconds
 };
@@ -85,7 +86,10 @@ typedef struct _PacketItem {
     QueueItem base;
     int fd;
     struct SockAddr src; // the remote IP for this packet
-    struct SockAddr dst; // the local IP for this packet
+    union {
+       struct in_pktinfo in4;
+       struct in6_pktinfo in6;
+    } dstinfo;         // The PKTINFO for this packet
     ssize_t len;
     unsigned char buffer[ BUFSIZE ];
 } PacketItem;
@@ -177,6 +181,7 @@ static int stdio = 0; // Default is neither stdio nor tap
 static char *tap = 0; // Name of tap, if any, or "-" for stdio
 static int tap_fd = 0; // Also used for stdin in stdio mode
 static int udp_fd;
+static int udp_port;
 static int threads_count = 0;
 static int buffers_count = 0;
 
@@ -916,6 +921,90 @@ static int write_tap(unsigned char *buf, int n) {
     return dowrite( tap_fd, buf, n );
 }
 
+
+// All sorts of fiddling is needed to set the source address for UDP
+// And 2 different ways for pure ipv4 versus ipv6 sockets
+static void sendpacket4(unsigned char *buf, int n,struct Remote *r) {
+    // The UDP socket is pure ipv4
+    struct iovec data[1] = {{ .iov_base = buf, .iov_len = n }};
+    struct {
+       struct cmsghdr hdr;
+       struct in_pktinfo data;
+    } control = {
+       .hdr.cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)),
+       .hdr.cmsg_level = IPPROTO_IP,
+       .hdr.cmsg_type = IP_PKTINFO,
+       .data.ipi_ifindex = r->ifindex,
+       .data.ipi_spec_dst = r->laddr.in4.sin_addr
+    };
+    struct msghdr msg = {
+       .msg_name = &r->uaddr.in4,
+       .msg_namelen = sizeof( struct sockaddr_in ),
+       .msg_iov = data,
+       .msg_iovlen = 1,
+       .msg_control =  &control,
+       .msg_controllen = CMSG_SPACE( sizeof( struct in_pktinfo ) ),
+       .msg_flags = 0 // unused
+    };
+    VERBOSE2OUT( "sendmsg ipv4 %lu from %s to %s\n",
+                msg.msg_controllen,
+                inet_stoa( &r->laddr ),
+                inet_stoa( &r->uaddr ) );
+    if ( sendmsg( udp_fd, &msg, 0 ) < n ) {
+       perror( "Writing socket" );
+    }
+}
+
+static void sendpacket6(unsigned char *buf, int n,struct Remote *r) {
+    // The UDP socket is ipv6, possibly with mapped ipv4 address(es)
+    struct iovec data[1] = {{ .iov_base = buf, .iov_len = n }};
+    struct {
+       struct cmsghdr hdr;
+       union {
+           struct in_pktinfo in4;
+           struct in6_pktinfo in6;
+       } data;
+    } control;
+    struct msghdr msg = {
+       .msg_name = &r->uaddr.in6,
+       .msg_namelen = sizeof( struct sockaddr_in6 ),
+       .msg_iov = data,
+       .msg_iovlen = 1,
+       .msg_control = 0,
+       .msg_controllen = 0,
+       .msg_flags = 0 // unused
+    };
+    if ( r->ifindex ) {
+       switch ( r->laddr.in.sa_family ) {
+       case AF_INET6:
+           control.hdr.cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+           control.hdr.cmsg_level = IPPROTO_IPV6;
+           control.hdr.cmsg_type = IPV6_PKTINFO;
+           control.data.in6.ipi6_ifindex = r->ifindex;
+           memcpy( &control.data.in6.ipi6_addr, &r->laddr.in6.sin6_addr, 16 );
+           msg.msg_control = &control;
+           msg.msg_controllen = CMSG_SPACE( sizeof( struct in6_pktinfo ) );
+           break;
+       case AF_INET:
+           control.hdr.cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+           control.hdr.cmsg_level = IPPROTO_IP;
+           control.hdr.cmsg_type = IP_PKTINFO;
+           control.data.in4.ipi_ifindex = r->ifindex;
+           control.data.in4.ipi_spec_dst = r->laddr.in4.sin_addr;
+           msg.msg_control = &control;
+           msg.msg_controllen = CMSG_SPACE( sizeof( struct in_pktinfo ) );
+           break;
+       }
+    }
+    VERBOSE2OUT( "sendmsg ipv6 %d from %s to %s\n",
+                r->ifindex,
+                inet_stoa( &r->laddr ),
+                inet_stoa( &r->uaddr ) );
+    if ( sendmsg( udp_fd, &msg, 0 ) < n ) {
+       perror( "Writing socket" );
+    }
+}
+
 // Write a packet via the given Interface with encryption as specified.
 static void write_remote(unsigned char *buf, int n,struct Remote *r) {
     // A packet buffer
@@ -923,9 +1012,8 @@ static void write_remote(unsigned char *buf, int n,struct Remote *r) {
     if ( n < 12 ) {
        VERBOSE2OUT( "SENDing %d bytes to %s\n", n, inet_stoa( &r->uaddr ) );
     } else {
-       VERBOSE2OUT( "SENDing %d bytes %s -> %s from %s to %s\n", n,
+       VERBOSE2OUT( "SENDing %d bytes %s -> %s to %s\n", n,
                     inet_mtoa( buf+6 ), inet_mtoa( buf ),
-                    inet_stoa( &r->laddr ),
                     inet_stoa( &r->uaddr ) );
     }
     memcpy( output, buf, n ); // Use the private buffer for delivery
@@ -951,82 +1039,10 @@ static void write_remote(unsigned char *buf, int n,struct Remote *r) {
     } else if ( r->spec->psk.keyfile ) {
        encrypt( output, n, &r->spec->psk );
     }
-    // Reserve for packet addressing
-    struct in_pktinfo pkt4info = {
-       .ipi_ifindex = 0,  /* Interface index */
-       .ipi_spec_dst.s_addr = 0, /* Local address */
-       .ipi_addr.s_addr = 0,     /* Header Destination address */
-    };
-    struct in6_pktinfo pkt6info = {
-       .ipi6_addr.s6_addr32 = { 0, 0, 0, 0 },
-       .ipi6_ifindex = 0,
-    };
-    void *pktinfo = 0;
-    int pktinfosize = 0;
-    struct sockaddr_in *sock4 = &r->uaddr.in4;
-    struct sockaddr_in6 *sock6 = &r->uaddr.in6;
-    void *sock;
-    size_t size;
-    int flags = 0;
     if ( udp6 ) {
-       // Note that the size of +struct sockaddr_in6+ is actually
-       // larger than the size of +struct sockaddr+ (due to the
-       // addition of the +sin6_flowinfo+ field). It results in the
-       // following cuteness for passing arguments to +sendto+.
-       sock = sock6;
-       size = sizeof( struct sockaddr_in6 );
-       VERBOSE2OUT( "IPv6 UDP %d %s %s\n", udp_fd,
-                    inet_stoa( &r->laddr ),
-                    inet_stoa( &r->uaddr ) );
-       switch ( r->laddr.in.sa_family ) {
-       case AF_INET6:
-           memcpy( &pkt6info.ipi6_addr, &sock6->sin6_addr, 16 );
-           pktinfo = &pkt6info;
-           pktinfosize = sizeof( pkt6info );
-           flags = IPV6_PKTINFO;
-           break;
-       case AF_INET:
-           memcpy( &pkt4info.ipi_spec_dst, &sock4->sin_addr, 4 );
-           pktinfo = &pkt4info;
-           pktinfosize = sizeof( pkt4info );
-           flags = IP_PKTINFO;
-           break;
-       }
+       sendpacket6( output, n, r );
     } else {
-       sock = sock4;
-       size = sizeof( struct sockaddr_in );
-       VERBOSE2OUT( "IPv4 UDP %d %s %s\n", udp_fd,
-                    inet_stoa( &r->laddr ),
-                    inet_stoa( &r->uaddr ) );
-       memcpy( &pkt4info.ipi_spec_dst, &sock4->sin_addr, 4 );
-       pktinfo = &pkt4info;
-       pktinfosize = sizeof( pkt4info );
-       flags = IP_PKTINFO;
-    }
-    VERBOSE2OUT( "SEND %d bytes from %s to %s [%s -> %s]\n",
-                n,
-                inet_stoa( &r->laddr ),
-                inet_stoa( &r->uaddr ),
-                ( n < 12 )? "" : inet_mtoa( buf+6 ),
-                ( n < 12 )? "" : inet_mtoa( buf )
-             );
-    // IS sendmsg thread safe??
-    struct iovec data[1] = {{ output, n }};
-    struct msghdr msg = {
-       .msg_name = sock,
-       .msg_namelen = size,
-       .msg_iov = data,
-       .msg_iovlen = 1,
-       .msg_control = pktinfo,
-       .msg_controllen = pktinfosize,
-       .msg_flags = 0 // unused
-    };
-    if ( sendmsg( udp_fd, &msg, flags ) < n ) {
-       perror( "Writing socket" );
-       // Invalidate remote temporarily instead? But if it's an
-       // "uplink" it should be retried eventually...
-       // For now: just ignore the error.
-       // exit( 1 );
+       sendpacket4( output, n, r );
     }
 }
 
@@ -1197,8 +1213,28 @@ static void route_packet(PacketItem *pi) {
     struct SockAddr *src = &pi->src;
     struct Interface *x = input_check( buf, len, src );
     if ( x == 0 ) {
+       VERBOSE2OUT( "not a nice packet\n" );
        return; // not a nice packet
     }
+    // Set the local addressing for the remote, unless set already
+    if ( x->remote->ifindex == 0 ) {
+       if ( udp6 ) {
+           x->remote->ifindex = pi->dstinfo.in6.ipi6_ifindex;
+           x->remote->laddr.in6.sin6_family = AF_INET6;
+           x->remote->laddr.in6.sin6_port = htons( udp_port );
+           memcpy( &x->remote->laddr.in6.sin6_addr,
+                   &pi->dstinfo.in6.ipi6_addr,
+                   16 );
+           unmap_if_mapped( &x->remote->laddr );
+       } else {
+           x->remote->ifindex = pi->dstinfo.in4.ipi_ifindex;
+           x->remote->laddr.in4.sin_family = AF_INET;
+           x->remote->laddr.in4.sin_port = htons( udp_port );
+           memcpy( &x->remote->laddr.in4.sin_addr,
+                   &pi->dstinfo.in4.ipi_spec_dst,
+                   4 );
+       }
+    }
     if ( ( *buf & 1 ) == 0 ) {
        // unicast
        struct Interface *y = 0; // reuse for destination interface
@@ -1217,11 +1253,9 @@ static void route_packet(PacketItem *pi) {
            Interface_DEL( y ); // Need to see this interface again
            return;
        }
-       // Set the local address for the remote
-       memcpy( &x->remote->laddr, &pi->dst, sizeof( pi->dst ) );
-       VERBOSE2OUT( "RECV route %s -> %s using %s\n",
-                    inet_mtoa( buf+6 ), inet_mtoa( buf ),
-                    inet_stoa( &x->remote->laddr ) );
+       VERBOSE2OUT( "RECV route %s -> %s\n",
+                    inet_mtoa( buf+6 ),
+                    inet_mtoa( buf ) );
        write_remote( buf, len, y->remote );
        return;
     }
@@ -1232,9 +1266,10 @@ static void route_packet(PacketItem *pi) {
        perror( "RECV time" );
        now.tv_sec = time( 0 );
     }
-    VERBOSE2OUT( "BC %s -> %s from %s\n",
+    VERBOSE2OUT( "BC %s -> %s from %s to %s\n",
                 inet_mtoa( buf+6 ), inet_mtoa( buf ),
-                inet_stoa( &x->remote->uaddr ) );
+                inet_stoa( &x->remote->uaddr ),
+                inet_stoa( &x->remote->laddr ) );
     struct Remote *r;
     unsigned int i = 0;
     Remote_LOCK;
@@ -1285,12 +1320,11 @@ static void *packet_handler(void *data) {
        } else {
            if ( udp6 ) {
                unmap_if_mapped( &todo->src );
-               unmap_if_mapped( &todo->dst );
            }
            route_packet( todo );
        }
        memset( &todo->src, 0, sizeof( struct SockAddr ) );
-       memset( &todo->dst, 0, sizeof( struct SockAddr ) );
+       memset( &todo->dstinfo, 0, sizeof( todo->dstinfo ) );
        Queue_addItem( &todolist.free, (QueueItem*) todo );
     }
     return 0;
@@ -1336,17 +1370,13 @@ inline static ssize_t recvpacket(int fd,PacketItem *p) {
     struct cmsghdr *cmsg = CMSG_FIRSTHDR( &msg );
     if ( cmsg ) {
        if ( udp6 ) {
-           struct in6_pktinfo *pinf = (struct in6_pktinfo*) CMSG_DATA( cmsg );
-           p->dst.in6.sin6_family = AF_INET6;
-           memcpy( &p->dst.in6.sin6_addr, &pinf->ipi6_addr, 16 );
-           VERBOSE2OUT( "DEST= udp6 %d %s\n",
-                        pinf->ipi6_ifindex, inet_stoa( &p->dst ) );
+           memcpy( &p->dstinfo.in6, CMSG_DATA( cmsg ),
+                   sizeof( struct in6_pktinfo ) );
+           VERBOSE2OUT( "DEST= udp6 %d\n", p->dstinfo.in6.ipi6_ifindex );
        } else {
-           struct in_pktinfo *pinf = (struct in_pktinfo*) CMSG_DATA( cmsg );
-           p->dst.in4.sin_family = AF_INET;
-           p->dst.in4.sin_addr = pinf->ipi_addr;
-           VERBOSE2OUT( "DEST= %d %s\n",
-                        pinf->ipi_ifindex, inet_stoa( &p->dst ) );
+           memcpy( &p->dstinfo.in4, CMSG_DATA( cmsg ),
+                   sizeof( struct in_pktinfo ) );
+           VERBOSE2OUT( "DEST= %d\n", p->dstinfo.in4.ipi_ifindex );
        }
     }
     return p->len;
@@ -1547,7 +1577,7 @@ static void *doreadTap(void *data) {
 // ip = ipv4 | [ipv6]
 int main(int argc, char *argv[]) {
     pthread_t thread; // Temporary thread id
-    int port, i;
+    int i;
     progname = (unsigned char *) argv[0];
     ///// Parse command line arguments
     i = 1;
@@ -1622,7 +1652,7 @@ int main(int argc, char *argv[]) {
        ENSUREARGS( 1 );
     }
     // then: required port
-    if ( sscanf( argv[i++], "%d", &port ) != 1 ) {
+    if ( sscanf( argv[i++], "%d", &udp_port ) != 1 ) {
        fprintf( stderr, "Bad local port: %s\n", argv[i-1] );
        usage();
     }
@@ -1695,7 +1725,7 @@ int main(int argc, char *argv[]) {
        }
        struct sockaddr_in udp_addr = {
            .sin_family = AF_INET,
-           .sin_port = htons( port ),
+           .sin_port = htons( udp_port ),
        };
        if ( udp_source.family == 0 ) {
            udp_addr.sin_addr.s_addr = htonl( INADDR_ANY );
@@ -1720,7 +1750,7 @@ int main(int argc, char *argv[]) {
        }
        struct sockaddr_in6 udp6_addr = {
            .sin6_family = AF_INET6,
-           .sin6_port = htons( port ),
+           .sin6_port = htons( udp_port ),
        };
        memcpy( udp6_addr.sin6_addr.s6_addr, udp_source.address, 16 );
        if ( bind( udp_fd, (struct sockaddr*) &udp6_addr, sizeof(udp6_addr))) {