X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=rrqnet.c;h=7c702208d5b6e6bf290e4680dfd50384511f0a84;hb=refs%2Fheads%2Fwip-capture-dest-ip;hp=cf0617ed2b198d2d6124c12ddb10bf1a979ec245;hpb=6cbe512525bf95cd04e7416f865c4a225d092490;p=rrq%2Frrqnet.git diff --git a/rrqnet.c b/rrqnet.c index cf0617e..7c70220 100644 --- 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 iaddr; // The receiving 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 }; @@ -84,8 +85,11 @@ struct Interface { typedef struct _PacketItem { QueueItem base; int fd; - struct SockAddr src; - struct SockAddr dst; + struct SockAddr src; // the remote 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,14 +921,98 @@ 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 unsigned char output[ BUFSIZE ]; if ( n < 12 ) { - VERBOSE2OUT( "SEND %d bytes to %s\n", n, inet_stoa( &r->uaddr ) ); + VERBOSE2OUT( "SENDing %d bytes to %s\n", n, inet_stoa( &r->uaddr ) ); } else { - VERBOSE2OUT( "SEND %d bytes %s -> %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->uaddr ) ); } @@ -950,33 +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 ); } - struct sockaddr *sock = &r->uaddr.in; - size_t size; - if ( sock->sa_family == AF_INET6 ) { - // 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+. - size = sizeof( struct sockaddr_in6 ); - VERBOSE2OUT( "IPv6 UDP %d %s\n", - udp_fd, inet_stoa( (struct SockAddr*) sock ) ); + if ( udp6 ) { + sendpacket6( output, n, r ); } else { - size = sizeof( struct sockaddr_in ); - VERBOSE2OUT( "IPv4 UDP %d %s\n", - udp_fd, inet_stoa( (struct SockAddr*) sock ) ); - } - VERBOSE2OUT( "SEND %d bytes to %s [%s -> %s]\n", - n, inet_stoa( (struct SockAddr*) sock ), - ( n < 12 )? "" : inet_mtoa( buf+6 ), - ( n < 12 )? "" : inet_mtoa( buf ) - ); - // IS sendto thread safe?? - if ( sendto( udp_fd, output, n, 0, sock, size ) < 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 ); } } @@ -1141,11 +1207,34 @@ static struct Interface *input_check( } // Check packet and deliver out -static void route_packet(unsigned char *buf,int len,struct SockAddr *src) { +static void route_packet(PacketItem *pi) { + unsigned char *buf = pi->buffer; + int len = pi->len; + 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 @@ -1164,9 +1253,9 @@ static void route_packet(unsigned char *buf,int len,struct SockAddr *src) { Interface_DEL( y ); // Need to see this interface again return; } - VERBOSE2OUT( "RECV route %s -> %s to %s\n", - inet_mtoa( buf+6 ), inet_mtoa( buf ), - inet_stoa( &y->remote->uaddr ) ); + VERBOSE2OUT( "RECV route %s -> %s\n", + inet_mtoa( buf+6 ), + inet_mtoa( buf ) ); write_remote( buf, len, y->remote ); return; } @@ -1177,9 +1266,10 @@ static void route_packet(unsigned char *buf,int len,struct SockAddr *src) { 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; @@ -1224,14 +1314,17 @@ static void *packet_handler(void *data) { for ( ;; ) { PacketItem *todo = (PacketItem *) Queue_getItem( &todolist.full ); if ( todo->fd == mcast.fd ) { - // Patch multicast address as source for multicast packet - route_packet( todo->buffer, todo->len, &mcast.sock ); + // Patch in the multicast address as source for multicast packet + memcpy( &todo->src, &mcast.sock, sizeof( todo->src ) ); + route_packet( todo ); } else { if ( udp6 ) { unmap_if_mapped( &todo->src ); } - route_packet( todo->buffer, todo->len, &todo->src ); + route_packet( todo ); } + memset( &todo->src, 0, sizeof( struct SockAddr ) ); + memset( &todo->dstinfo, 0, sizeof( todo->dstinfo ) ); Queue_addItem( &todolist.free, (QueueItem*) todo ); } return 0; @@ -1259,39 +1352,31 @@ void todolist_initialize(int nbuf,int nthr) { } } -static ssize_t recvpacket(int fd,PacketItem *p) { - int flags = udp6? IPV6_PKTINFO : IP_PKTINFO; - socklen_t addrlen = - udp6? sizeof( p->src.in6 ) : sizeof( p->src.in4 ); - struct iovec buffer = { - .iov_base = p->buffer, - .iov_len = BUFSIZE - }; - char data[1000]; +// Reads a UDP packet on the given file descriptor and captures the +// source and destination addresses of the UDP message. +inline static ssize_t recvpacket(int fd,PacketItem *p) { + char data[100]; // Data area for "pktinfo" + struct iovec buffer[1] = {{ p->buffer, BUFSIZE }}; struct msghdr msg = { .msg_name = &p->src.in, - .msg_namelen = addrlen, - .msg_iov = &buffer, + .msg_namelen = udp6? sizeof( p->src.in6 ) : sizeof( p->src.in4 ), + .msg_iov = buffer, .msg_iovlen = 1, .msg_control = data, .msg_controllen = sizeof( data ), - .msg_flags = flags // Return value + .msg_flags = 0 // Return value }; - p->len = recvmsg( fd, &msg, flags ); + p->len = recvmsg( fd, &msg, udp6? 0 : IP_PKTINFO ); struct cmsghdr *cmsg = CMSG_FIRSTHDR( &msg ); - VERBOSE3OUT( "anc %p %ld\n", cmsg, p->len ); - for ( ; cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) ) { - VERBOSE3OUT( "anc type = %d\n", cmsg->cmsg_type ); - } - cmsg = CMSG_FIRSTHDR( &msg ); if ( cmsg ) { if ( udp6 ) { + 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; - VERBOSE3OUT( "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; @@ -1309,17 +1394,8 @@ static void *doreadUDP(void *data) { int fd = ((ReaderData *) data)->fd; while ( 1 ) { PacketItem *todo = (PacketItem *) Queue_getItem( &todolist.free ); -#if 0 - socklen_t addrlen = - udp6? sizeof( todo->src.in6 ) : sizeof( todo->src.in4 ); -#endif - memset( &todo->src, 0, sizeof( todo->src ) ); todo->fd = fd; -#if 0 - todo->len = recvfrom( - fd, todo->buffer, BUFSIZE, 0, &todo->src.in, &addrlen ); -#endif - VERBOSE3OUT( "Reading packet\n" ); + VERBOSE3OUT( "Reading packet on %d\n", fd ); ssize_t len = recvpacket( fd, todo ); if ( len == -1) { perror( "Receiving UDP" ); @@ -1501,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; @@ -1576,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(); } @@ -1649,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 ); @@ -1674,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))) { @@ -1682,6 +1758,12 @@ int main(int argc, char *argv[]) { exit(1); } VERBOSEOUT( "Using ipv6 UDP at %d\n", udp_fd ); + int opt = 1; + if ( setsockopt( + udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) ) { + fprintf( stderr, "Error configuring socket!\n"); + exit(1); + } } // If not using stdio for local traffic, then stdin and stdout are // closed here, so as to avoid that any other traffic channel gets