Browse Source

initial renaming

tags/0.2
Ralph Rönnquist 1 year ago
commit
c593cf1bd8
21 changed files with 2797 additions and 0 deletions
  1. +11
    -0
      .gitignore
  2. +59
    -0
      Makefile
  3. +5
    -0
      debian/changelog
  4. +1
    -0
      debian/compat
  5. +19
    -0
      debian/control
  6. +22
    -0
      debian/copyright
  7. +27
    -0
      debian/rules
  8. +1
    -0
      debian/source/format
  9. +22
    -0
      debian/udptap.debhelper.log
  10. +2
    -0
      debian/udptap.substvars
  11. +135
    -0
      htable.c
  12. +71
    -0
      htable.h
  13. +71
    -0
      queue.c
  14. +25
    -0
      queue.h
  15. +19
    -0
      readme.adoc
  16. +36
    -0
      rrqnet-cron.sh
  17. +125
    -0
      rrqnet-cron.sh.8.adoc
  18. +444
    -0
      rrqnet.8.adoc
  19. +1648
    -0
      rrqnet.c
  20. +38
    -0
      set-source-route.sh
  21. +16
    -0
      sockaddr.h

+ 11
- 0
.gitignore View File

@@ -0,0 +1,11 @@
debian/.debhelper
debian/debhelper-build-stamp
debian/files
debian/rrqnet.debhelper.log
debian/rrqnet.substvars
debian/rrqnet
rrqnet
rrqnet-cron.sh.8
rrqnet-cron.sh.8.html
rrqnet.8
rrqnet.8.html

+ 59
- 0
Makefile View File

@@ -0,0 +1,59 @@
SBINDIR = $(DESTDIR)/usr/local/sbin
ETCDIR = $(DESTDIR)/etc/rrqnet
MAN1DIR = $(DESTDIR)/usr/local/share/man/man1
MAN8DIR = $(DESTDIR)/usr/local/share/man/man8

SBINFILES = rrqnet rrqnet-cron.sh
ETCFILES = set-source-route.sh
MAN1FILES =
MAN8FILES = rrqnet.8 rrqnet-cron.sh.8
HTMLDOC = $(MAN8FILES:%=%.html)

all: $(SBINFILES) $(ETCFILES) $(MAN1FILES) $(MAN8FILES) $(HTMLDOC)

$(HTMLDOC): %.html: %.adoc
asciidoc -bhtml $^

$(MAN8FILES): %: %.adoc
a2x -d manpage -f manpage $^

rrqnet: LDFLAGS += -lpthread
rrqnet: rrqnet.c htable.h htable.c sockaddr.h queue.h queue.c

rrqnet.E: rrqnet.c htable.c
$(CC) -W -Wall $^ > $@

COMPILEOPTS = -g -W -Wall
#COMPILEOPTS = -pg -no-pie -g -DGPROF

$(filter-out %.sh,$(SBINFILES)): %: %.c
$(CC) $(COMPILEOPTS) -static -o $@ $^ $(LDFLAGS)

.PHONY: clean
clean:
rm -f $(filter-out %.sh,$(SBINFILES))

# Installation targets

INSTALLTARGETS = $(addprefix $(SBINDIR)/,$(SBINFILES))
INSTALLTARGETS += $(addprefix $(ETCDIR)/,$(ETCFILES))
INSTALLTARGETS += $(addprefix $(MAN1DIR)/,$(MAN1FILES))
INSTALLTARGETS += $(addprefix $(MAN8DIR)/,$(MAN8FILES))

#INSTALL = install -b -S orig
INSTALL = install

$(addprefix $(ETCDIR)/,conf.d keys):
mkdir -p $@

$(ETCCFG)/cron.sh: rrqnet-cron.sh
$(INSTALL) -D -T $< $@

$(SBINDIR)/% $(ETCDIR)/% $(MAN1DIR)/% $(MAN8DIR)/%: %
$(INSTALL) -D -T $< $@

install: $(INSTALLTARGETS)

BUILDPACKAGE = -us -uc --build=full
deb:
PREFIX= INCLUDE_PREFIX=/usr dpkg-buildpackage $(BUILDPACKAGE)

+ 5
- 0
debian/changelog View File

@@ -0,0 +1,5 @@
rrqnet (0.1) experimental; urgency=medium

* Initial release (restart from udptap=0.2.5)

-- Ralph Ronnquist <ralph.ronnquist@gmail.com> Wed, 17 Jun 2020 20:34:04 +1000

+ 1
- 0
debian/compat View File

@@ -0,0 +1 @@
9

+ 19
- 0
debian/control View File

@@ -0,0 +1,19 @@
Source: rrqnet
Section: admin
Priority: optional
Maintainer: Ralph Ronnquist <ralph.ronnquist@gmail.com>
Build-Depends: debhelper (>= 9), asciidoc, docbook-xml, libxslt1-dev, xsltproc,
docbook-xsl
Standards-Version: 3.9.8
Homepage: <insert the upstream URL, if relevant>
Vcs-Git: https://gitea.devuan.dev/devuan/rrqnet.git

Package: rrqnet
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, bash, bridge-utils, iproute2,
lsof, net-tools, util-linux
Description: Packet tunneling over UDP, multiple channels
rrqnet is a bi-directional networking plug that channels packets
between a UDP port and either or a tap interface or standard
input/output. It is configured on the command line by declarations of
the remotes it may communicate with.

+ 22
- 0
debian/copyright View File

@@ -0,0 +1,22 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: rrqnet
Source: <url://example.com>

Files: debian/*
Copyright: 2020 Ralph Ronnquist <ralph@ascii>
License: GPL-2+
This package 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.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

+ 27
- 0
debian/rules View File

@@ -0,0 +1,27 @@
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#export DH_VERBOSE = 1


# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all

# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed


%:
dh $@


# dh_make generated override targets
# This is example for Cmake (See https://bugs.debian.org/641051 )
#override_dh_auto_configure:
# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)

override_dh_usrlocal:
true

+ 1
- 0
debian/source/format View File

@@ -0,0 +1 @@
3.0 (native)

+ 22
- 0
debian/udptap.debhelper.log View File

@@ -0,0 +1,22 @@
dh_update_autotools_config
dh_auto_configure
dh_auto_build
dh_auto_test
dh_prep
dh_auto_install
dh_installdocs
dh_installchangelogs
dh_perl
dh_usrlocal
dh_link
dh_strip_nondeterminism
dh_compress
dh_fixperms
dh_missing
dh_strip
dh_makeshlibs
dh_shlibdeps
dh_installdeb
dh_gencontrol
dh_md5sums
dh_builddeb

+ 2
- 0
debian/udptap.substvars View File

@@ -0,0 +1,2 @@
misc:Depends=
misc:Pre-Depends=

+ 135
- 0
htable.c View File

@@ -0,0 +1,135 @@
#include <stdlib.h>
#include <string.h>
#include "htable.h"

//// Generic hash table implementation

// Determine the index for a key. On match, it returns the index into
// the table. On mismatch it returns -i-1 for the first free spot
// where the keyed element would fit.
static int htindex(htable *table,unsigned char *key) {
if ( table->data == 0 ) {
table->data = calloc( 256, sizeof( unsigned char* ) );
table->size = 256;
}
unsigned int hash = (*table->hashcode)( table, key ) % table->size;
unsigned int i = hash;
int hole = -1;
for ( ;; ) {
unsigned char *x = table->data[ i++ ];
if ( x == 0 ) {
return ( hole >= 0 )? -hole : - (int) i;
}
if ( x == (unsigned char *)1 ) {
if ( hole < 0 ) {
hole = i;
}
} else if ( memcmp( x + table->offset, key, table->esize ) == 0 ) {
return i-1;
}
if ( i >= table->size ) {
i = 0;
}
if ( i == hash ) {
break;
}
}
return -1;
}

// Find the keyed element, and assign the x pointer, or assign 0.
// Returns 1 if element is found and 0 otherwise.
int htfind(htable *table,void *key,unsigned char **x) {
pthread_mutex_lock( &table->lock );
int i = htindex( table, key );
if ( i < 0 ) {
*x = 0;
pthread_mutex_unlock( &table->lock );
return 0;
}
*x = table->data[ i ];
pthread_mutex_unlock( &table->lock );
return 1;
}

// Forward
static void htgrow(htable *table);

// Add the given element.
void htadd(htable *table,unsigned char *p) {
pthread_mutex_lock( &table->lock );
if ( table->fill >= table->size / 2 ) {
htgrow( table );
}
int i = htindex( table, p + table->offset );
if ( i < 0 ) {
i = -i - 1;
if ( table->data[ i ] != 0 ) {
table->holes--;
} else {
table->fill++;
}
} else {
// There is a match already what to do?
}
table->data[ i ] = p;
pthread_mutex_unlock( &table->lock );
}

// Return the next element starting at i, or 0 if there are no more.
// Also increment the index to be of the element + 1, or -1 if there
// are no more elements.
static unsigned char *htnext(htable *table,int *i) {
if ( *i < 0 ) {
return 0;
}
unsigned char **p = table->data + *i;
unsigned char **e = table->data + table->size;
for ( ; p < e; p++ ) {
(*i) += 1;
if ( *p != 0 || *p != (unsigned char*)1 ) {
return *p;
}
}
(*i) = -1;
return 0;
}

static void htrehash(htable *table,unsigned int size) {
htable old = *table;
table->data = calloc( size, sizeof( unsigned char * ) );
table->size = size;
table->fill = 0;
table->holes = 0;
int i = 0;
unsigned char *p;
while ( ( p = htnext( &old, &i ) ) != 0 ) {
htadd( table, p );
}
free( old.data );
}

static void htgrow(htable *table) {
if ( table->data == 0 ) {
table->data = calloc( 256, sizeof( unsigned char * ) );
table->size = 256;
table->fill = 0;
table->holes = 0;
return;
}
htrehash( table, table->size << 8 );
}

// Delete the given element.
void htdelete(htable *table,unsigned char *p) {
pthread_mutex_lock( &table->lock );
int i = htindex( table, p + table->offset );
if ( i >= 0 ) {
table->data[ i ] = (unsigned char *)1;
table->holes += 1;
if ( table->holes > table->fill / 2 ) {
htrehash( table, table->size );
}
}
pthread_mutex_unlock( &table->lock );
}

+ 71
- 0
htable.h View File

@@ -0,0 +1,71 @@
// A hashtable implementation with vectorized hashcode function.
//
// A hashtable is array of pointers, or the values 0 and 1, which mark
// unused and cleared slots respectively.
//
// Hash collision is handled by using the next usused or cleared slot
// by increasing, cycled indexing. An addition when the fill exceeds
// half the size causes a rehash into a new, double size table,
// without any cleared slots (i.e., the fill might be reduced after
// rehash, due to cleared slots).
//
// The fill count includes cleared entries, which counts deleted
// entries. A deletion that makes the number of cleared slots to
// exceed half the fill also causes a rehash into a new, same size
// table, without any cleared clots.
//
// The data elements have a key part, which is a contiguous portion of
// bytes at a given offset. The offset and size of the key are the
// same for all elements.
//
#ifndef HTABLE_H
#define HTABLE_H

#define __USE_GNU 1
#include <pthread.h>

// The hashtable head data structure: htable
typedef struct _htable {
unsigned char **data; // array of entries
unsigned int size; // total array size
unsigned int offset; // offset into elements for the key part
unsigned int esize; // byte size of the key part
unsigned int fill; // number of added elements
unsigned int holes; // number of deleted
int (*hashcode)(struct _htable *table,unsigned char *key);
pthread_mutex_t lock;
} htable;

// Determine the index for a key. On match, it returns the index into
// the table. On mismatch it returns -i-1 for the first free spot
// where the keyed element would fit.
//int htindex(htable *table,unsigned char *key);

// Find the keyed element, and assign the x pointer, or assign 0.
// Returns 1 if element is found and 0 otherwise.
int htfind(htable *table,void *key,unsigned char **x);

// Add the given element.
void htadd(htable *table,unsigned char *p);

// Return the next element starting at i, or 0 if there are no more.
// Also increment the index to be of the element + 1, or -1 if there
// are no more elements.
//unsigned char *htnext(htable *table,int *i);

// Delete the given element.
void htdelete(htable *table,unsigned char *p);

// Special macro for htable initialization giving the record type, the
// key field and hashcode funtion.
// type = (base) data type for the elements
// member = the key field
// hashcodefn = function computing hashcode for a key
// assigns .compare = memcmp
#define HTABLEINIT(type,member,hashcodefn) \
{ .data = 0, .size = 0, .fill = 0, \
.holes = 0, .offset = offsetof( type, member ), \
.esize = sizeof( ((type *)0)->member ), .hashcode = hashcodefn, \
.lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP }

#endif // HTABLE_H

+ 71
- 0
queue.c View File

@@ -0,0 +1,71 @@
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"

void Queue_addItem(Queue *list,QueueItem *item) {
if ( pthread_mutex_lock( &list->mutex ) ) {
perror( "FATAL" );
exit( 1 );
}
item->next = 0; // just in case
if ( list->last ) {
list->last->next = item;
} else {
list->head = item;
}
list->last = item;
if ( sem_post( &list->count ) ) {
perror( "FATAL" );
exit( 1 );
}
if ( pthread_mutex_unlock( &list->mutex ) ) {
perror( "FATAL" );
exit( 1 );
}
}

QueueItem *Queue_getItem(Queue *list) {
QueueItem *item;
if ( sem_wait( &list->count ) ) {
perror( "FATAL" );
exit( 1 );
}
if ( pthread_mutex_lock( &list->mutex ) ) {
perror( "FATAL" );
exit( 1 );
}
item = list->head;
list->head = item->next;
if ( list->head == 0 ) {
list->last = 0;
}
if ( pthread_mutex_unlock( &list->mutex ) ) {
perror( "FATAL" );
exit( 1 );
}
return item;
}

void Queue_initialize(Queue *list,int n,size_t size) {
if ( pthread_mutex_lock( &list->mutex ) ) {
perror( "FATAL" );
exit( 1 );
}
if ( list->head == 0 ) {
int i = 0;
for ( ; i < n; i++ ) {
QueueItem *x = (QueueItem *) calloc( 1, size );
if ( list->head ) {
list->last->next = x;
} else {
list->head = x;
}
list->last = x;
}
sem_init( &list->count, 0, n );
}
if ( pthread_mutex_unlock( &list->mutex ) ) {
perror( "FATAL" );
exit( 1 );
}
}

+ 25
- 0
queue.h View File

@@ -0,0 +1,25 @@
#ifndef queue_H
#define queue_H

#include "sockaddr.h"
#include <semaphore.h>
#include <pthread.h>

typedef struct _QueueItem {
struct _QueueItem *next;
char data[];
} QueueItem;

typedef struct _Queue {
QueueItem *head;
QueueItem *last;
sem_t count;
pthread_mutex_t mutex;
} Queue;

extern void Queue_addItem(Queue *list,QueueItem *item);
extern QueueItem *Queue_getItem(Queue *list);
extern void Queue_initialize(Queue *list,int n,size_t size);

#endif


+ 19
- 0
readme.adoc View File

@@ -0,0 +1,19 @@
About rrqnet
============

This repository holds the sources for *rrqnet*, which concern VPN
building over UDP transport. It operates at Ethernet level, which
means it transports both ipv4 and ipv6, and it can thus be used for
ipv6 over ipv4 tunneling, or ipv4 tunneling over ipv6, as well as ipv4
VPN or ipv6 VPN.

MAN PAGES
---------
* link:rrqnet.8.adoc[The rrqnet man page]
* link:rrqnet-cron.sh.8.adoc[The rrqnet-cron.sh man page]

Devuan Packages
---------------

* The *rrqnet* package contains the binaries and their man pages,
as well as the utility scripting.

+ 36
- 0
rrqnet-cron.sh View File

@@ -0,0 +1,36 @@
#!/bin/bash
#
# Cron-driven bot to start a rrqnet cables or switch unless already up

RRQNET=/usr/local/sbin/rrqnet

function start-switch() {
. $1
exec $RRQNET $VERBOSE -4 $PORT ${VPN[@]}
}

function start-cable() {
. $1
ip link show dev $TAP > /dev/null || \
{ ip tuntap add $TAP mode tap ; ip link set dev $TAP up ; }
[ -z "$MAC" ] || ifconfig $TAP | grep -q "ether $MAC" || \
ifconfig $TAP hw ether $MAC
[ -z "$IP" ] || ip addr show dev $TAP | grep -q $IP || \
ip addr add $IP dev $TAP
[ -z "$BR" ] || brctl show | grep -q $TAP || \
brctl addif $BR $TAP
exec $RRQNET $VERBOSE -4 ${OPTIONS[@]} -t $TAP $PORT ${VPN[@]}
}

for CABLE in $* ; do
CONF=/etc/rrqnet/conf.d/$CABLE.conf
eval $(grep ^PORT= $CONF)
lsof -i :$PORT > /dev/null && continue
eval $(grep ^TAP= $CONF)
LOG=/tmp/$CABLE.log
if [ -z "$TAP" ] ; then
( start-switch $CONF < /dev/null >> $LOG 2>&1 & )
else
( start-cable $CONF /dev/null >> $LOG 2>&1 & )
fi
done

+ 125
- 0
rrqnet-cron.sh.8.adoc View File

@@ -0,0 +1,125 @@
rrqnet-cron.sh(8)
=================
:doctype: manpage
:revdate: {sys:date "+%Y-%m-%d %H:%M:%S"}

NAME
----
rrqnet-cron.sh - Management script to uphold a *rrqnet* plug.

SYNOPSIS
--------
*rrqnet-cron.sh* _vpn_ ...

DESCRIPTION
-----------

*rrqnet-cron.sh* is a management script for upholding a *rrqnet* plug
for a nominated VPN confguration. The given _vpn_, or several, is the
pathname relative to the configuration root directory and with a
+.conf+ extension added, as in +/etc/rrqnet/conf.d/+*vpn*+.conf+.

The following is a configuration file example:

./etc/rrqnet/conf.d/tap0-client.conf
----
TAP=tap0
MAC=02:00:00:00:01:00
BR=
IP=192.168.10.2
PORT=1500
OPTIONS=( )
VPN=( 10.61.4.72:2020=/etc/rrqnet/keys/example.key )
VERBOSE=-v
----

* The `TAP` assignment names the tap interface to use.
* The optional MAC assignment, if provided, tells *rrqnet-cron.sh* to
set the Ethernet address of the tap interface as given.
* The optional `BR` assignment, if provided, tells *rrqnet-cron.sh*
to add the tap interface to the bridge upon start.
* The optional `IP` assignment, if provided, tells *rrqnet-cron.sh*
how to configure the tap interface when it is brought up. If empty,
the tap interface is brought up without confgiured IP address.
* The `PORT` assignment declares which port *rrqnet* should listen
on. It will listen on that port on all interfaces.
* The optional `OPTIONS` is intended for the -B and -T options to
*rrqnet*.
* The `VPN` assignment declares the remotes for *rrqnet*.
* The optional `VERBOSE` assignment, which must be `-v`, `-vv` or
`-vvv` unless empty, defines the verbosity level for *rrqnet*.

The above example declares an uplink remote at example ivp4 address
`10.61.4.72`, port 2020, and using a transport encryption key. The
remote host at that IP address should have a corresponding
declaration, perhaps as follows:

./etc/rrqnet/conf.d/tap0-server.conf
----
TAP=tap0
IP=192.168.10.1
PORT=2020
VPN=( 0.0.0.0/0=/etc/rrqnet/keys/example.key )
----

That "server" declaration is allows UDP packets from any host and
port, requiring the them to use the same transport encryption key. The
*rrqnet* "server" plug then works like a switch, which forwards
packets between connections as well as to and from the tap. Actual
connections are identified by the remote MAC addresses, and it's up to
the remote ends to resolve IP addresses to the MAC addresses on the
virtual net, which in the example would be +192.168.10.0/24+.

The +VPN+ variable may have multiple remote declarations, and include
both up-links and down-links, with or without thransport encryption
keys. E.g.,
----
VPN=( 192.168.0.0/16:1400 10.61.4.72:2020=/sec/example.key )
----

A VPN assignment like that would both allow remotes in IP range
+192.168.0.0/16+, port 1400, without transport key, and have an
up-link to that example "server" remote above (though, for the sake of
example, having its key residing at a different pathname).

crontab set up
~~~~~~~~~~~~~~

The script *rrqnet-cron.sh* is intended to be set up in *crontab*, by
a line as follows:
----
* * * * * /usr/local/sbin/rrqnet-cron.sh tap0-client
----

By that *crontab* line, the script will be invoked every minute for
unsuting that the *rrqnet* plug declared by
+/etc/rrqnet/conf.d/tap0-client.conf+, is still running, and otherwise
start or restart it.

The script uses a lock file that gets named by the `TAP` assignment,
as in +/var/lock/rrqnet-tap0+, for the example. This allows an
alternative management set up, for a *rrqnet* cable to be maintained
with an almost immediate restart when it goes down, through a simple
loop like the following:
----
# while flock /var/lock/rrqnet-tap0 ; do rrqnet-cron.sh tap0 done
----
That control loop would be waiting for the running *rrqnet* to release
the file lock on +/var/lock/rrqnet-tap0+, and then, almost immediately
restart the virtual cable.

NOTES
-----

Note that *rrqnet-cron.sh* sources the configuration file and exits
after optionally spawning a *rrqnet* daemon. On may therefore safely
just change the cable set up, and kill *rrqnet* in order apply that
changed set up.

SEE ALSO
--------
*rrqnet(8)* - Packet tunneling over UDP, multiple channels

AUTHOR
------
Ralph Rönnquist <ralph.ronnquist@gmail.com>

+ 444
- 0
rrqnet.8.adoc View File

@@ -0,0 +1,444 @@
rrqnet(8)
=========
:doctype: manpage
:revdate: {sys:date "+%Y-%m-%d %H:%M:%S"}
:COLON: :
:EQUALS: =

NAME
----
rrqnet - Packet tunneling over UDP, multiple channels

SYNOPSIS
--------
*rrqnet* [ OPTIONS ] _port_ ( _remote_ [*-i* _mac_]* )*

DESCRIPTION
-----------
*rrqnet* is a bi-directional networking plug that channels Ethernet
packets between a UDP port and either a tap interface or standard
input/output. It is configured on the command line by declarations of
the remotes it may communicate with.

OPTIONS
-------

Note that any options must be given in the fixed order:

[-v] [-4] [-B n] [-T n] [-m mcast] [-t tap]

*-v*::

This tells *rrqnet* to log its operation on +stderr+. Use *-vv* to
also see logs about connections and messaging, or *-vvv* for *rrqnet*
to be insanely verbose on +stderr+ about virtually everything.

*-4*::

This directs *rrqnet* to use an ipv4-only socket for its UDP. By
default it opens a dual ipv4/ipv6 socket and internally it then uses
address mapping for ipv4 (i.e. the ::ffff/96 prefix).

*-B* _n_::

This sets the number of receive buffers *rrqnet* should use. The
default is computed to be twice the number of dispatch threads (see
*-T* below). Receive buffers are pre-allocated and recycled.

*-T* _n_::

This sets the number of dispatch threads *rrqnet* should use. The
default is 5. The (additional) main thread handles packet reception,
where it immediately puts received packets into the buffer queue which
is serviced by the dispatch threads for optional decryption, dispatch
decision, optional encryption and delivery.

*-m* _mcast_::

This tells *rrqnet* to open an ipv4 UDP multicast channel as an
additional remote channel.

*-t* _tap_::

This tells *rrqnet* to open the nominated tap (the _tap_ interface
name) as local channel.

* When a tap is used, stdin and stdout are closed, but stderr remains
open.

* Without a *-t* argument, *rrqnet* will merely operate as a virtual
switch among its channels.

* With "*-*" as tap name, *rrqnet* will use stdin/stdout as local
networking channel in a format compatible with VDE plugs.

_address-block[:port][=cryptfile]_ [ *-i* _mac_[,_mac_]* ]::

Remotes are declared as +ipv4+ or +ipv6+ network address blocks
optionally with port and transport encryption key file pathname, and
optionally with specific MAC addresses (in a comma separated list) to
ignore. Note that an ipv6 address block might need surrounding square
brackets, to avoid confusion with the port number.

DETAILED DESCRIPTION
--------------------

The intended use of *rrqnet* is to provide VPN (virtual private
network) connectivity between hosts. Each VPN host runs its own
*rrqnet* daemon to channel the traffic to/from tap interfaces on the
hosts via UDP messaging between the hosts.

*rrqnet* is prepared for almost any network layout, even including a
collection of fully connected hosts, although the more common is a
"star' formation. See the EXAMPLES section for inspiration.

*rrqnet* includes logic aiming to protect against broadcast cycles.
Howewer it does not have the more advanced spanning tree logic that is
offered by bridge interfaces. In general it's best to avoid cycles and
rather run several *rrqnet* on a host with their local taps connected
in a bridge interface.

By default *rrqnet* opens an +ipv6+ socket on the given port. This
mode handles both +ipv6+ and +ipv4+ remotes with +ipv4+ remotes
handled by means of ipv6-mapped address translations. If *-4* is
given, *rrqnet* opens an +ipv4+ socket instead, and it cannot then
have +ipv6+ remotes.

A *rrqnet* daemon delivers the packets received from the local end,
i.e., the _tap_ or _stdio_, to known remote ends according the
targeted MAC addresses and established remote channels. Likewise,
packets from remotes are delivered to the local end or to other
remotes according to the targeted MAC addresses. If a packet is an
Ethernet broadcast, it is delivered to all (known) channels except the
one it came from.

If *rrqnet* is started without *-t* option it will operate like an
Ethernet switch that provides connectivity among its +UDP+ channels
without involving the host network other than for the tunneling.


REMOTE DECLARTIONS
~~~~~~~~~~~~~~~~~~

.ipv4 address block

This format declares remotes by +ipv4+ address, with optional network
prefix length (0-32), optional port (1-65535) and/or optional key file
pathname. *updtap* will accept packets from sources that match. If the
network prefix length, +n+, is omitted or given as 32, and a port is
given, then the remote is taken as an _uplink_.

.matching ipv4 uplink and downlink
====
----
[1.2.3.4]# rrqnet -t vpn0 2300 5.6.7.1:2300=/sec/vpn0.key
[5.6.7.1]# rrqnet -t vpn0 2300 1.2.3.0/24:2300=/sec/vpn0.key
----
====

.plain ipv6 address block

This format declares remotes by ipv6 address, with optional network
prefix length (0-128) and/or optional key file pathname. *updtap* will
accept packets from sources that match. This format (without square
brackets) is without port number part, and it thus only declares a
prefix mask for allowed sender hosts.

.ipv6 address block within square brackets

This format declares remotes by ipv6 address, with optional network
prefix length (0-128) within square brackets, then optionally a port
number and/or an optional key file pathname. *updtap* will accept
packets from sources that match. If the network prefix length, +n+, is
omitted, or given as 128, and a port number is given, then it declares
an _uplink_.

.matching ipv6 uplink and downlink
====
----
[fd::4]# rrqnet -t vpn0 2300 '[fe::1:4]:2300=/sec/vpn0.key'
[fe::1:4]# rrqnet -t vpn0 2300 '[fd::/120]:2300=/sec/vpn0.key'
----
====

Remotes are declarations to match the source IP addresses for UDP
traffic. It is either a full host and port address, or an address
block with or without port number. A full network address and port
(e.g +[fe::1:4]:2300+ of example 2) declares an _uplink_ that the
declaring *rrqnet* daemon will establish and maintain by means of
regular heartbeat messaging. An address block declaration defines the
mask for allowed incoming connections, aka _downlinks_, that teh
declaring dameon expects are maintained as uplinks by the remote
*rrqnet* daemons.

The *-i* option, if used for a remote declaration, is followed by a
comma separated list of the MAC addresses to ignore on the associated
channel. The *rrqnet* daemon will then just drop any packet with those
MAC addresses (whether source or destination) on the remotes of the
channel.

MULTICAST CHANNEL
-----------------

With the *-m* option, the *rrqnet* daemon also listens for packets on
the declared ipv4 multicast address. It is then treated as a separate,
persistent remote channel.

A multicast channel is declared using the format:
*ipv4:port{=keyfile}*. I.e. it includes the multicast ipv4 address,
the port number (1-65535), and optionally a key file pathname. The
multicast channel is an additional communication channel, but anything
received on it will be treated as being from the multicast IP rather
than the actual source IP.

.multicast example without other remotes
====
----
# rrqnet -m 244.0.2.1:2000 -t vpn0
----
====

The multicast channel is compatible with QEMU multicast socket.

TRANSPORT ENCRYPTION
--------------------

Transport encryption is added to a channel by menas of using a shared
key. This is a sizable file, say 1 Mb, of binary data that is used for
scrambling the network packets. For example, 1 Mb random data is fine.

.preparing 1 Mb key file with random content
====
----
dd if=/dev/random of=/sec/keyfile bs=1M count=1
----
====

The key file needs to be copied to all hosts.

A channel that has a key file is declared by appending it to the
channel declaration, as in the following example.

.another example of a downlink with encryption
====
----
[10.0.0.1]# rrqnet -v -t tap0 1400 10.2.0.0/16:1400=/sec/keyfile
----
====

That declaration says that all channels with hosts of ipv4 address
+10.2.0.0/16+, port 1400, use the file +/sec/keyfile+ for transport
encryption.


FURTHER EXAMPLES
----------------

Simple rrqnet set up (ipv6)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is an example set up for connecting two hosts with *rrqnet*,
without transport encryption. We pretend these hosts are mutually
reachable with ipv6 addresses +fe::2+ and +fe::1:3+ respectively, and
we want to use the ipv6 network +fd::1000:0/120+ over *rrqnet*. A
nominal set up might then be as follows:

.simple rrqnet set up (ipv6)
====
----
[fe::2]# ip tuntap add tap0 mode tap
[fe::2]# ifconfig tap0 fd::1000:10/120 up
[fe::2]# rrqnet -v -t tap0 1400 '[fe::1:3]:1400' &
--
[fe::1:3]# ip tuntap add tap0 mode tap
[fe::1:3]# ifconfig tap0 fd::1000:20/120 up
[fe::1:3]# rrqnet -v -t tap0 1400 '[fe::2]:1400' &
----
====

Thus, the host +fe::2+ is set up with a tap, +tap0+, having +ipv6+
address and net +fd::1000:10/120+, and a *rrqnet* daemon for the tap
and UDP port +1400+ that uplinks to a remote *rrqnet* at +fe::1:3+
port +1400+. Similarly, the host +fe::1:3+ is set up with a tap
+tap0+, having +ipv6+ address and net +fd::1000:20/120+, and a *rrqnet*
daemon for the tap and UDP port +1400+ that uplinks to a remote
*rrqnet* at +fe::2+ port +1400+.

This example also needs ipv6 address resolution set up, which uses the
MAC addresses of the two taps. Let's say it's +02:00:00:00:00:02+ for
+tap0+ on +fe::2+ and +04:00:00:00:00:04+ for +tap0+ on +fe::1:3+.
Then address resolution is established with the following:

.example of ipv6 address resolution set up
====
----
[fe::2]# ip neigh add fd::1000:20 dev tap0 lladdr 04:00:00:00:00:04
--
[fe::1:3]# ip neigh add fd::1000:10 dev tap0 lladdr 02:00:00:00:00:02
----
====

Simple rrqnet set up (ipv4)
~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is an example set up for connecting two hosts with *rrqnet*,
without transport encryption. We pretend these hosts are mutually
reachable with ipv4 addresses +10.0.0.1+ and +192.168.0.1+
respectively, and we want to use the ipv4 network +10.100.100.0/24+
over *rrqnet*. A nominal set up might be as follows:

.Simple rrqnet set up (ipv4)
====
----
[10.0.0.1]# ip tuntap add tap0 mode tap
[10.0.0.1]# ifconfig tap0 10.100.100.1/24 up
[10.0.0.1]# rrqnet -v -t tap0 1400 192.168.0.1:1400 &
--
[192.168.0.1]# ip tuntap add tap0 mode tap
[192.168.0.1]# ifconfig tap0 10.100.100.2/24 up
[192.168.0.1]# rrqnet -v -t tap0 1400 10.0.0.1:1400 &
----
====

Thus, the host +10.0.0.1+ is set up with a tap, +tap0+, having ipv4
address and net +10.100.100.1/24+, and a *rrqnet* daemon for the tap
and UDP port +1400+ that uplinks to a remote *rrqnet* at +192.168.0.1+
port +1400+. Similarly, the host +192.168.0.1+ is set up with a tap
+tap0+, having +ipv4+ address and net +10.100.100.2/24+, and a
*rrqnet* daemon for the tap and UDP port +1400+ that uplinks to a
remote *rrqnet* at +10.0.0.1+ port 1400.

The kernel automagically performs ipv4 address resolution to learn the
MAC addresses associated with the remote ipv4 addresses through the
taps.

rrqnet set up through NAT
~~~~~~~~~~~~~~~~~~~~~~~~~

If one of the hosts, say +192.168.0.1+ is behind a NAT router with
different IP, say, a dynamic IP on the net +10.2.0.0/16+, we need a
different set up. In this scenario, the first host would be set up as
a "server" and the second a client that would utilize the router's NAT
function for return traffic. The set up would be as follows:

.rrqnet set up through NAT
====
----
[10.0.0.1]# ip tuntap add tap0 mode tap
[10.0.0.1]# ifconfig tap0 10.100.100.1 up
[10.0.0.1]# rrqnet -v -t tap0 1400 10.2.0.0/16:1400 &
--
[192.168.0.1]# ip tuntap add tap0 mode tap
[192.168.0.1]# ifconfig tap0 10.100.100.2 up
[192.168.0.1]# rrqnet -v -t tap0 1400 10.0.0.1:1400 &
----
====

Thus, the "server" is set up to allow connections from any host on the
network +10.2.0.0/16+, port 1400, while the "client" is set up the
same way as in the simple example above. The client will establish and
uphold the connection by virtue of its 30 second "heart beat", and
return traffic will be channeled via the router's NAT function.

Note that the server sees the _external_ IP of the client and not its
_internal_ IP. The server's *rrqnet* therefor has a remote declaration
to allow messages from that external IP, and in the example case, even
an address block of a 16 bit common prefix (the choice of a 16 bit
prefix is merely for the sake of this example).

Multiple client hosts
~~~~~~~~~~~~~~~~~~~~~

In a "client-server" set up, there can be any number of "client"
hosts. However, the "clients" behind a common NAT router must then use
distinct ports as otherwise the router will be confused about the
return traffic.

With multiple remote channels, a *rrqnet* daemon serves as a network
switch that forwards traffic in between the channels as well as to and
from the "server" tap. The daemon also forwards Ethernet broadcasts
out on all established channels in support of ARP messaging.

Further, a *rrqnet* daemon may be both a "server" with down-link
channels, and a "client" with one or more up-link channels, all at the
same time. Such a daemon forwards traffic between all established
channels by means of the Ethernet addresses, as well as broadcasts
onto all channels

Stdio network
~~~~~~~~~~~~~
The *rrqnet* daemon may be set up to use standard input/output rather
than a tap for local network traffic. This operation mode has some
rare use cases, such as linking two *rrqnet* daemons, or connecting to
a VDE network. For example:

.stdio network between two updtap plugs
====
----
# dpipe rrqnet 1400 0.0.0.0/0=keyfile0 = rrqnet 1401 0.0.0.0/0=keyfile1 &
----
====

That example set up would make a connection between the two "server"
daemons operating at different UDP ports, accepting messages from any
ipv4 host, where port +1400+ has +keyfile0+ for transport encryption,
and +1401+ has +keyfile1+ for transport encryption.

Another example would be for connecting the *rrqnet* traffic to a VDE
network via a +vde_plug+ as in the following example:

.stdio network to a vde_plug
====
----
# dpipe rrqnet 1400 0.0.0.0/0 = vde_plug /tmp/vde1.ctl &
----
====

Note that *rrqnet* and +vde_plug+ use compatible stdio packet
representation.

NOTES
-----

The UDP receiver in *rrqnet* accepts packets from the specified remote
ends only, but it doesn't perform any payload verification. Messages
smaller than 12 bytes are taken as "heartbeats", and larger messages
are first decrypted as applicable, then treated as Ethernet messages
and delivered according to their destination MAC addresses. *rrqnet*
versions after 0.2.3 adds some data to the Ethernet packet in the UDP
communication.

*rrqnet* bridges its connections, and forwards Ethernet broadcasts to
all known end-points except the incoming one. The input logic takes
care of avoiding broadcast cycles. Still, the safe advice is that one
should avoid a cyclic *rrqnet* set up except for single-hop cliques of
host groups.

*rrqnet* does not have any Spanning Tree Logic (STL), but only some
simple timing logic based on binding MAC addresses to remotes. That
binding is sticky for a short time: 6s for broadcast and 20s for
unicast. Any packet received during that time from the same MAC
address via another remote is dropped. Also, a downlink without
incoming traffic for 3 minutes is considered stale.

*rrqnet* sends a "heartbeat" of an empty UDP message on its uplinks
every 30 seconds. This is done in order to maintain the channel with
the remote end without actually binding any MAC address.

When the local input is a tap, *rrqnet* closes standard input and
standard output, but not standard error, before entering the packet
handling loop.

Using +-t -+ for stdin/stdout packet traffic is compatible with
+vde_plug+.

SEE ALSO
--------
*rrqnet-cron.sh(8)* - Management script to uphold a *rrqnet* plug.

*udptun(8)* - Packet tunneling over UDP.

*vde_plug(1)* - Virtual Distributed Ethernet plug.

AUTHOR
------
Ralph Rönnquist <ralph.ronnquist@gmail.com>

+ 1648
- 0
rrqnet.c
File diff suppressed because it is too large
View File


+ 38
- 0
set-source-route.sh View File

@@ -0,0 +1,38 @@
#!/bin/bash
#
# rrqnet helper script for establishing a rule based route table for a
# given interface with given IP with default route to given gateway IP
# on the interface link.
# $1 = interface
# $2 = interface IP
# $3 = gateway IP
#
# This will retry every second until the setup is successful. Should
# be spawned in a conf file, eg:
# /etc/rrqnet/set-source-route.sh $TAP $IP $GW > /dev/null 2>&1 &
#

TAP=$1
IP=$2
GW=$3
: ${TIX:=200}

function set-source-route() {
grep -q "$TIX $TAP" /etc/iproute2/rt_tables || \
echo "$TIX $TAP" >> /etc/iproute2/rt_tables
if [ -z "$(ip rule list from ${IP%/*})" ] ; then
ip rule add from ${IP%/*} lookup $TAP || return 1
fi
if [ -z "$(ip route show table $TAP | grep ^$GW)" ] ; then
ip route add $GW dev $TAP scope link src ${IP%/*} table $TAP || \
return 1
fi
if [ -z "$(ip route show table $TAP | grep default)" ] ; then
ip route add default via $GW dev $TAP table $TAP || return 1
fi
ip route show table $TAP
}

set-source-route && exit 0
sleep 1
exec $0 $*

+ 16
- 0
sockaddr.h View File

@@ -0,0 +1,16 @@
#ifndef sockaddr_H
#define sockaddr_H

#include <arpa/inet.h>
#include <sys/socket.h>

// IP Address union, with flag field
struct SockAddr {
union {
struct sockaddr in;
struct sockaddr_in in4;
struct sockaddr_in6 in6;
} ;
};

#endif

Loading…
Cancel
Save