#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
// Details of actualized connections.
struct Remote {
- struct SockAddr uaddr;
+ struct SockAddr uaddr; // The remote 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
};
typedef struct _PacketItem {
QueueItem base;
int fd;
- struct SockAddr src;
+ 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;
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;
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
+ };
+ if ( r->laddr.in.sa_family && 0 ) {
+ msg.msg_control = &control;
+ msg.msg_controllen = CMSG_SPACE( sizeof( struct in_pktinfo ) );
+ }
+ VERBOSE2OUT( "sendmsg %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" );
+ }
+#if 0 // ILLUSTRATION
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ struct in_pktinfo *pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
+ pktinfo->ipi_ifindex = src_interface_index;
+ pktinfo->ipi_spec_dst = src_addr;
+ msg.msg_controllen =
+ return sendmsg( fd, &msg, IP_PKTINFO );
+#endif
+}
+
+static void sendpacket6(unsigned char *buf, int n,struct Remote *r) {
+ //NYI
+ (void)buf;
+ (void)n;
+ (void)r;
+}
+
// 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 ) );
}
} 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 );
}
}
}
// 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
+ 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 );
+ } 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
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;
}
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;
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;
}
}
+// 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 = udp6? sizeof( p->src.in6 ) : sizeof( p->src.in4 ),
+ .msg_iov = buffer,
+ .msg_iovlen = 1,
+ .msg_control = data,
+ .msg_controllen = sizeof( data ),
+ .msg_flags = 0 // Return value
+ };
+ p->len = recvmsg( fd, &msg, udp6? 0 : IP_PKTINFO );
+ struct cmsghdr *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 {
+ memcpy( &p->dstinfo.in4, CMSG_DATA( cmsg ),
+ sizeof( struct in_pktinfo ) );
+ VERBOSE2OUT( "DEST= %d\n", p->dstinfo.in4.ipi_ifindex );
+ }
+ }
+ return p->len;
+}
+
+
+
// Read a full UDP packet into the given buffer, associate with a
// connection, or create a new connection, the decrypt the as
// specified, and capture the sender MAC address. The connection table
int fd = ((ReaderData *) data)->fd;
while ( 1 ) {
PacketItem *todo = (PacketItem *) Queue_getItem( &todolist.free );
- socklen_t addrlen =
- udp6? sizeof( todo->src.in6 ) : sizeof( todo->src.in4 );
- memset( &todo->src, 0, sizeof( todo->src ) );
todo->fd = fd;
- todo->len = recvfrom(
- fd, todo->buffer, BUFSIZE, 0, &todo->src.in, &addrlen );
- if ( todo->len == -1) {
+ VERBOSE3OUT( "Reading packet on %d\n", fd );
+ ssize_t len = recvpacket( fd, todo );
+ if ( len == -1) {
perror( "Receiving UDP" );
exit( 1 );
}
#ifdef GPROF
- if ( todo->len == 17 &&
+ if ( len == 17 &&
memcmp( todo->buffer, "STOPSTOPSTOPSTOP", 16 ) == 0 ) {
exit( 0 );
}
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" );
+ fprintf( stderr, " -S source = use given source address for UDP\n" );
exit( 1 );
}
// 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;
ENSUREARGS( 1 );
}
// Then optional source address for UDP
- if ( strncmp( "-I", argv[i], 2 ) == 0 ) {
+ if ( strncmp( "-S", argv[i], 2 ) == 0 ) {
ENSUREARGS( 2 );
if ( parse_udp_source( argv[i+1] ) ) {
usage();
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();
}
}
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 );
exit(1);
}
VERBOSEOUT( "Using ipv4 UDP at %d\n", udp_fd );
+ int opt = 1;
+ if ( setsockopt( udp_fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) ) {
+ fprintf( stderr, "Error configuring socket!\n");
+ exit(1);
+ }
} else {
// set up ipv6 socket
if ( ( udp_fd = socket( AF_INET6, SOCK_DGRAM, 0 ) ) == 0 ) {
}
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))) {
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