Added CMake support with managed protocol settings
authorRick van Rein <rick@openfortress.nl>
Fri, 26 Jul 2019 13:11:47 +0000 (13:11 +0000)
committerRick van Rein <rick@openfortress.nl>
Fri, 26 Jul 2019 13:11:47 +0000 (13:11 +0000)
The protocol settings end up in CMakeCache.txt and can be edited
with the customary CMake utilities.  If not, they are kept at the
built-in, UNSTANDARDISED default values.  Expect these parts to
change in an incompatible manner without notification as a result
of future acceptance of this specification in RFC form.

12 files changed:
6bed4peer.c [deleted file]
6bed4peer.man [deleted file]
6bed4router.c [deleted file]
6bed4router.man [deleted file]
CMakeLists.txt
configure [deleted file]
doc/man/6bed4peer.man [new file with mode: 0644]
doc/man/6bed4router.man [new file with mode: 0644]
nonstd.h.in [deleted file]
src/6bed4peer.c [new file with mode: 0644]
src/6bed4router.c [new file with mode: 0644]
src/nonstd.h.in [new file with mode: 0644]

diff --git a/6bed4peer.c b/6bed4peer.c
deleted file mode 100644 (file)
index b4670f9..0000000
+++ /dev/null
@@ -1,2007 +0,0 @@
-/* 6bed4/peer.c -- Peer-to-Peer IPv6-anywhere with 6bed4 -- peer.c
- *
- * This is an implementation of neighbour and router discovery over a
- * tunnel that packs IPv6 inside UDP/IPv4.  This tunnel mechanism is
- * so efficient that the server administrators need not mind if it is
- * distributed widely.
- *
- * From: Rick van Rein <rick@openfortress.nl>
- */
-
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <getopt.h>
-#include <fcntl.h>
-#include <time.h>
-
-#include <syslog.h>
-#ifndef LOG_PERROR
-#define LOG_PERROR LOG_CONS            /* LOG_PERROR is non-POSIX, LOG_CONS is */
-#endif
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/select.h>
-#include <sys/ioctl.h>
-
-#include <net/if.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/udp.h>
-#include <netinet/icmp6.h>
-#include <arpa/inet.h>
-
-#include <asm/types.h>
-//#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_ether.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-/* The following will initially fail, due to an IANA obligation to avoid
- * default builds with non-standard options.
- */
-#include "nonstd.h"
-
-
-#define MTU 1280
-#define PREFIX_SIZE 114
-
-typedef enum {
-       METRIC_LOW,
-       METRIC_MEDIUM,
-       METRIC_HIGH
-} metric_t;
-
-/*
- * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
- * the -d option leads to an error, or to an attempt to setup the tunnel.
- * The setup_tunnel() function used for that is defined per platform, such
- * as for LINUX.  Remember to maintain the manpage's optionality for -d.
- */
-#undef HAVE_SETUP_TUNNEL
-
-
-/* Global variables */
-
-char *program;
-
-int rtsox = -1;
-int v4sox = -1;
-int v6sox = -1;
-int v4mcast = -1;
-
-uint8_t v4qos = 0;             /* Current QoS setting on UDP/IPv4 socket */
-uint32_t v6tc = 0;             /* Current QoS used by the IPv6 socket */
-uint8_t v4ttl = 64;            /* Default TTL setting on UDP/IPv4 socket */
-int v4ttl_mcast = -1;          /* Multicast TTL for LAN explorations */
-
-char *v4server = NULL;
-char *v6server = NULL;
-char v6prefix [INET6_ADDRSTRLEN];
-uint8_t v6lladdr [6];
-
-const uint8_t v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-uint8_t v6listen_linklocal_complete [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-struct sockaddr_nl rtname;
-struct sockaddr_nl rtkernel;
-
-struct sockaddr_in  v4name;
-struct sockaddr_in  v4peer;
-struct sockaddr_in6 v6name;
-
-struct sockaddr_in v4bind;
-struct sockaddr_in v4allnodes;
-
-struct in6_addr v6listen;
-//TODO:NEEDNOT// struct in6_addr v6listen_complete;
-struct in_addr  v4listen;
-
-
-struct {
-#ifdef PEER_USE_TAP
-       struct ethhdr eth;
-#else
-       struct tun_pi tun;
-#endif
-       union {
-               struct {
-                       struct ip6_hdr v6hdr;
-                       uint8_t data [MTU - sizeof (struct ip6_hdr)];
-               } idata;
-               struct {
-                       struct ip6_hdr v6hdr;
-                       struct icmp6_hdr v6icmphdr;
-               } ndata;
-       } udata;
-} __attribute__((packed)) v4data6;
-
-#ifdef PEER_USE_TAP
-#define v4ether        (v4data6.eth)
-#else
-#define v4tunpi6       (v4data6.tun)
-#endif
-#define v4data         ((uint8_t *) &v4data6.udata)
-#define v4hdr6         (&v4data6.udata.idata.v6hdr)
-#define v4src6         (&v4data6.udata.idata.v6hdr.ip6_src)
-#define v4dst6         (&v4data6.udata.idata.v6hdr.ip6_dst)
-
-#define v4v6plen       (v4data6.udata.ndata.v6hdr.ip6_plen)
-#define v4v6nexthdr    (v4data6.udata.ndata.v6hdr.ip6_nxt)
-#define v4v6hoplimit   (v4data6.udata.ndata.v6hdr.ip6_hops)
-
-#define v4icmp6                (&v4data6.udata.ndata.v6icmphdr)
-#define v4v6icmpdata   (v4data6.udata.ndata.v6icmphdr.icmp6_data8)
-#define v4v6icmptype   (v4data6.udata.ndata.v6icmphdr.icmp6_type)
-#define v4v6icmpcode   (v4data6.udata.ndata.v6icmphdr.icmp6_code)
-#define v4v6icmpcksum  (v4data6.udata.ndata.v6icmphdr.icmp6_cksum)
-#define v4v6ndtarget   (&v4data6.udata.ndata.v6icmphdr.icmp6_data8 [4])
-
-
-struct {
-#ifdef PEER_USE_TAP
-       struct ethhdr eth;
-#else
-       struct tun_pi tun;
-#endif
-       union {
-               uint8_t data [MTU];
-               struct {
-                       struct ip6_hdr v6hdr;
-                       struct icmp6_hdr v6icmp;
-               } __attribute__((packed)) ndata;
-       } udata;
-}  __attribute__((packed)) v6data6;
-
-#ifdef PEER_USE_TAP
-#define v6ether                (v6data6.eth)
-#else
-#define v6tuncmd       (v6data6.tun)
-#endif
-#define v6data         (v6data6.udata.data)
-#define v6hdr6         (&v6data6.udata.ndata.v6hdr)
-#define v6hops         (v6data6.udata.ndata.v6hdr.ip6_hops)
-#define v6type         (v6data6.udata.ndata.v6hdr.ip6_nxt)
-#define v6plen         (v6data6.udata.ndata.v6hdr.ip6_plen)
-#define v6src6         (&v6data6.udata.ndata.v6hdr.ip6_src)
-#define v6dst6         (&v6data6.udata.ndata.v6hdr.ip6_dst)
-#define v6icmp6type    (v6data6.udata.ndata.v6icmp.icmp6_type)
-#define v6icmp6code    (v6data6.udata.ndata.v6icmp.icmp6_code)
-#define v6icmp6data    (v6data6.udata.ndata.v6icmp.icmp6_data8)
-#define v6icmp6csum    (v6data6.udata.ndata.v6icmp.icmp6_cksum)
-#define v6ndtarget     (&v6data6.udata.ndata.v6icmp.icmp6_data16[2])
-
-#ifdef PEER_USE_TAP
-#define HDR_SIZE        (sizeof(struct ethhdr))
-#else
-#define HDR_SIZE        (sizeof(struct tun_pi))
-#endif
-
-/* Structure for tasks in neighbor discovery queues
- */
-struct ndqueue {
-       struct ndqueue *next;
-       struct timeval tv;
-       struct in6_addr source;
-       struct in6_addr target;
-       uint8_t source_lladdr [6];
-       uint8_t todo_lancast, todo_direct;
-};
-
-/* Round-robin queue for regular tasks, starting at previous value */
-struct ndqueue *ndqueue = NULL;
-struct ndqueue *freequeue = NULL;
-uint32_t freequeue_items = 100;
-
-/* The time for the next scheduled maintenance: routersol or keepalive.
- * The milliseconds are always 0 for maintenance tasks.
- */
-time_t maintenance_time_sec;
-time_t maintenance_time_cycle = 0;
-time_t maintenance_time_cycle_max = 30;
-bool got_lladdr = false;
-time_t keepalive_period = 30;
-time_t keepalive_ttl = -1;
-
-/* The network packet structure of a 6bed4 Router Solicitation */
-
-uint8_t ipv6_router_solicitation [] = {
-       // IPv6 header
-       0x60, 0x00, 0x00, 0x00,
-       16 >> 8, 16 & 0xff, IPPROTO_ICMPV6, 255,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,          // unspecd src
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
-       // ICMPv6 header: router solicitation
-       ND_ROUTER_SOLICIT, 0, 0x7a, 0xae,       // Checksum courtesy of WireShark :)
-       // ICMPv6 body: reserved
-       0, 0, 0, 0,
-       // ICMPv6 option: source link layer address 0x0001 (end-aligned)
-       0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
-};
-
-uint8_t ipv6_defaultrouter_neighbor_advertisement [] = {
-       // IPv6 header
-       0x60, 0x00, 0x00, 0x00,
-       32 >> 8, 32 & 0xff, IPPROTO_ICMPV6, 255,
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // src is default router
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,// dst is all-nodes multicast, portable?
-       // ICMPv6 header: neighbor solicitation
-       ND_NEIGHBOR_ADVERT, 0, 0x36, 0xf2,              // Checksum courtesy of WireShark :)
-       // ICMPv6 Neighbor Advertisement: flags
-       0x40, 0, 0, 0,
-       // Target: fe80::
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // the targeted neighbor
-       // ICMPv6 option: target link layer address
-       2, 1,
-       UDP_PORT_6BED4 & 0xff, UDP_PORT_6BED4 >> 8,
-       SERVER_6BED4_IPV4_INT0, SERVER_6BED4_IPV4_INT1, SERVER_6BED4_IPV4_INT2, SERVER_6BED4_IPV4_INT3
-};
-
-uint8_t router_linklocal_address [] = {
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
-};
-
-//TODO// Complete with the if-id of the 6bed4 Router:
-uint8_t router_linklocal_address_complete [] = {
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
-};
-
-uint8_t client1_linklocal_address [] = {
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
-};
-
-uint8_t allnodes_linklocal_address [] = {
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
-};
-
-uint8_t allrouters_linklocal_address [] = {
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x02,
-};
-
-uint8_t solicitednodes_linklocal_prefix [13] = {
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff
-};
-
-bool default_route = false;
-
-bool foreground = false;
-
-bool log_to_stderr = false;
-
-bool multicast = true;
-
-
-/*
- *
- * Driver routines
- *
- */
-
-#ifndef INTERFACE_NAME_6BED4
-#define INTERFACE_NAME_6BED4 "6bed4"
-#endif
-
-#ifdef LINUX
-#define HAVE_SETUP_TUNNEL
-static struct ifreq ifreq;
-static bool have_tunnel = false;
-/* Implement the setup_tunnel() command for Linux.
- * Return true on success, false on failure.
- */
-bool setup_tunnel (void) {
-       if (v6sox == -1) {
-               v6sox = open ("/dev/net/tun", O_RDWR);
-       }
-       if (v6sox == -1) {
-               syslog (LOG_ERR, "%s: Failed to access tunnel driver on /dev/net/tun: %s\n", program, strerror (errno));
-               return 0;
-       }
-       bool ok = true;
-       int flags = fcntl (v6sox, F_GETFL, 0);
-       if (flags == -1) {
-               syslog (LOG_CRIT, "Failed to retrieve flags for the tunnel file descriptor: %s\n", strerror (errno));
-               ok = false;
-       }
-       if (!have_tunnel) {
-               memset (&ifreq, 0, sizeof (ifreq));
-               strncpy (ifreq.ifr_name, INTERFACE_NAME_6BED4, IFNAMSIZ);
-#ifdef PEER_USE_TAP
-               ifreq.ifr_flags = IFF_TAP | IFF_NO_PI;
-#else
-               ifreq.ifr_flags = IFF_TUN;
-#endif
-               if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
-                       syslog (LOG_CRIT, "Failed to set interface name: %s\n", strerror (errno));
-                       ok = false;
-               } else {
-                       have_tunnel = ok;
-               }
-               ifreq.ifr_name [IFNAMSIZ] = 0;
-               ifreq.ifr_ifindex = if_nametoindex (ifreq.ifr_name);
-syslog (LOG_DEBUG, "Found Interface Index %d for name %s\n", ifreq.ifr_ifindex, ifreq.ifr_name);
-               ok = ok & (ifreq.ifr_ifindex != 0);
-       }
-       char cmd [512+1];
-       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.forwarding=0", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               fprintf (stderr, "Failed command: %s\n", cmd);
-               ok = false;
-       }
-       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.accept_dad=0", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               fprintf (stderr, "Failed command: %s\n", cmd);
-               ok = false;
-       }
-       if (!ok) {
-               close (v6sox);  /* This removes the tunnel interface */
-               v6sox = -1;
-       }
-       return ok;
-}
-
-bool setup_tunnel_address (void) {
-       bool ok = have_tunnel;
-       char cmd [512+1];
-
-       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.autoconf=0", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       snprintf (cmd, 512, "/sbin/ip link set %s up mtu %d", ifreq.ifr_name, MTU);
-       if (ok && system (cmd) != 0) {
-               fprintf (stderr, "Failed command: %s\n", cmd);
-               ok = false;
-       }
-       snprintf (cmd, 512, "/sbin/ip -6 addr add %s/114 dev %s", v6prefix, ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               fprintf (stderr, "Failed command: %s\n", cmd);
-               ok = false;
-       }
-       if (default_route) {
-               snprintf (cmd, 512, "/sbin/ip -6 route add default via fe80:: dev %s metric 1042", ifreq.ifr_name);
-               if (ok && system (cmd) != 0) {
-                       fprintf (stderr, "Failed command: %s\n", cmd);
-                       ok = false;
-               }
-       }
-       return ok;
-}
-#endif /* LINUX */
-
-
-/*
- *
- * Utility functions
- *
- */
-
-
-
-/* Produce an IPv6 address following the 6bed4 structures.
- *  - The top half is taken from v6listen
- *  - The bottom contains IPv4 address and port from v4name
- *  - The last 14 bits are filled with the lanip parameter
- */
-void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
-       memcpy (&dst_ip6->s6_addr [0], &v6listen, 8);
-       dst_ip6->s6_addr32 [2] = v4name.sin_addr.s_addr;
-       dst_ip6->s6_addr16 [6] = v4name.sin_port;
-       dst_ip6->s6_addr  [14] = ((dst_ip6->s6_addr [8] & 0x03) << 6)
-                              | ((lanip >> 8) & 0x3f);
-       dst_ip6->s6_addr  [15] = (lanip & 0xff);
-       dst_ip6->s6_addr  [8] &= 0xfc;
-}
-
-/* Look for an entry in the 50ms-cycled Neighbor Discovery queue.
- * Match the target address.  Return the entry found, or NULL.
- */
-struct ndqueue *findqueue (struct in6_addr *target) {
-       struct ndqueue *ndq = ndqueue;
-       if (ndq) {
-               do {
-                       if (memcmp (target, &ndq->target, 16) == 0) {
-                               return ndq;
-                       }
-                       ndq = ndq->next;
-               } while (ndq != ndqueue);
-       }
-       return NULL;
-}
-
-/* Enter an item in the 50ms-cycled Neighbor Discovery queue.
- * Retrieve its storage space from the free queue.
- * TODO: Avoid double entries by looking up entries first -> "requeue?"
- */
-static int TODO_qlen;
-void enqueue (struct in6_addr *target, struct in6_addr *v6src, uint8_t *source_lladdr) {
-       //
-       // Refuse to create double entries
-       if (findqueue (target)) {
-               return;
-       }
-       //
-       // Allocate a free item to enqueue
-       struct ndqueue *new = freequeue;
-       if (!new) {
-               // Temporarily overflown with ND -> drop the request
-               return;
-       }
-char tgt [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, target, tgt, sizeof (tgt));
-syslog (LOG_DEBUG, "Queue++ => %d, looking for %s\n", ++TODO_qlen, tgt);
-       freequeue = freequeue->next;
-       //
-       // Setup the new entry with target details
-       memcpy (&new->target, target, sizeof (new->target));
-       memcpy (&new->source, v6src, sizeof (new->source));
-       memcpy (&new->source_lladdr, source_lladdr, sizeof (new->source_lladdr));
-       new->todo_lancast = (v4mcast == -1)? 0: 2;
-       new->todo_direct = 3;
-       //
-       // Time the new item to run instantly
-       new->tv.tv_sec = 0;
-       //
-       // Enqueue the new item in front of the queue
-       if (ndqueue) {
-               new->next = ndqueue->next;
-               ndqueue->next = new;
-       } else {
-               new->next = new;
-               ndqueue = new;
-       }
-}
-
-/* Remove an item from the 50ms-cycled Neighbor Discovery queue.
- * Enter its storage space in the free queue.
- */
-void dequeue (struct ndqueue *togo) {
-       struct ndqueue *prev = ndqueue;
-       do {
-               if (prev->next == togo) {
-                       if (togo->next != togo) {
-                               prev->next = togo->next;
-                               if (ndqueue == togo) {
-                                       ndqueue = togo->next;
-                               }
-                       } else {
-                               // Must be the only queued item
-                               ndqueue = NULL;
-                       }
-                       togo->next = freequeue;
-                       freequeue = togo;
-syslog (LOG_DEBUG, "Queue-- => %d\n", --TODO_qlen);
-                       return;
-               }
-               prev = prev->next;
-       } while (prev != ndqueue);
-}
-
-
-/*
- * Calculate the ICMPv6 checksum field
- */
-uint16_t icmp6_checksum (uint8_t *ipv6hdr, size_t payloadlen) {
-       uint16_t plenword = htons (payloadlen);
-       uint16_t nxthword = htons (IPPROTO_ICMPV6);
-       uint16_t *areaptr [] = { (uint16_t *) &ipv6hdr [8], (uint16_t *) &ipv6hdr [24], &plenword, &nxthword, (uint16_t *) &ipv6hdr [40], (uint16_t *) &ipv6hdr [40 + 4] };
-       uint8_t areawords [] = { 8, 8, 1, 1, 1, payloadlen/2 - 2 };
-       uint32_t csum = 0;
-       u_int8_t i, j;
-       for (i=0; i < 6; i++) {
-               uint16_t *area = areaptr [i];
-               for (j=0; j<areawords [i]; j++) {
-                       csum += ntohs (area [j]);
-               }
-       }
-       csum = (csum & 0xffff) + (csum >> 16);
-       csum = (csum & 0xffff) + (csum >> 16);
-       csum = htons (~csum);
-       return csum;
-}
-
-
-/*
- * Send a Redirect reply.  This is in response to a v4v6data message,
- * and is directed straight at the origin's address but sent with a
- * lower metric.
- *
- * Note: Although the packet arrived in v4data6, the reply is built
- *       in v6data6 and sent from there as though it had come from
- *       the IPv6 stack.
- */
-void redirect_reply (uint8_t *ngbc_llremote, metric_t ngbc_metric) {
-       void handle_6to4_plain_unicast (const ssize_t pktlen, const uint8_t *lladdr);
-       v6icmp6type = ND_REDIRECT;
-       v6icmp6code = 0;
-       v6icmp6data [0] =
-       v6icmp6data [1] =
-       v6icmp6data [2] =
-       v6icmp6data [3] = 0;            // Reserved
-       memcpy (v6icmp6data + 4, &v6listen, 16);
-                                       // Target IPv6 address
-       switch (ngbc_metric) {
-                                       // Destination Address suggestion
-       case METRIC_LOW:
-               //
-               // Redirect to the local-subnet IPv4 address
-               memcpy (v6icmp6data + 4 + 16, v6listen_linklocal, 8);
-               v6icmp6data [4 + 16 + 8 ] = v4peer.sin_port & 0x00ff;
-               v6icmp6data [4 + 16 + 9 ] = v4peer.sin_port >> 8;
-               memcpy (v6icmp6data + 4 + 16 + 12, &v4peer.sin_addr, 4);
-               v6icmp6data [4 + 16 + 10] = v4v6icmpdata [4 + 16 + 12];
-               v6icmp6data [4 + 16 + 11] = 0xff;
-               v6icmp6data [4 + 16 + 12] = 0xfe;
-               break;
-       case METRIC_MEDIUM:
-               memcpy (v6icmp6data + 4 + 16, v6listen_linklocal_complete, 16);
-               break;
-       case METRIC_HIGH:
-       default:
-               return;         /* no cause for Redirect, drop */
-       }
-       v6type = IPPROTO_ICMPV6;
-       v6plen = htons (8 + 16 + 16);
-       memcpy (v6src6, &v6listen, 16);
-       memcpy (v6dst6, v4src6, 16);
-       v6icmp6csum = icmp6_checksum ((uint8_t *) v4hdr6, 8 + 16 + 16);
-       handle_6to4_plain_unicast (HDR_SIZE + 40 + 8 + 16 + 16, ngbc_llremote);
-} 
-
-
-/* Append the current prefix to an ICMPv6 message.  Incoming optidx
- * and return values signify original and new offset for ICMPv6 options.
- * The endlife parameter must be set to obtain zero lifetimes, thus
- * instructing the tunnel client to stop using an invalid prefix.
- */
-size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
-       v6icmp6data [optidx++] = 3;     // Type
-       v6icmp6data [optidx++] = 4;     // Length
-       v6icmp6data [optidx++] = 114;   // This is a /114 prefix
-#ifndef COMPENSATE_FOR_AUTOCONF
-       v6icmp6data [optidx++] = 0xc0;  // L=1, A=1, Reserved1=0
-#else
-       //TODO// Temporary fix: "ip -6 addr add .../64 dev 6bed4"
-       v6icmp6data [optidx++] = 0x80;  // L=1, A=0, Reserved1=0
-#endif
-       memset (v6icmp6data + optidx, endlife? 0x00: 0xff, 8);
-       optidx += 8;
-                                       // Valid Lifetime: Zero / Infinite
-                                       // Preferred Lifetime: Zero / Infinite
-       memset (v6icmp6data + optidx, 0, 4);
-       optidx += 4;
-                                       // Reserved2=0
-       addr_6bed4 ((struct in6_addr *) (v6icmp6data + optidx), 0);
-                                       // Set IPv6 prefix
-       optidx += 16;
-       return optidx;
-}
-
-
-/*
- * Construct a Neighbor Advertisement message, providing the
- * Public 6bed4 Service as the link-local address.
- *
- * This is done immediately when the IPv6 stack requests the link-local
- * address for fe80:: through Router Solicition.  In addition, it is the
- * fallback response used when attempts to contact the remote peer at its
- * direct IPv4 address and UDP port (its 6bed4 address) fails repeatedly.
- *
- * This routine is called with info==NULL to respond to an fe80::
- * Neighbor Solicitation, otherwise with an info pointer containing
- * a target IPv6 address to service.
- */
-void advertise_6bed4_public_service (struct ndqueue *info) {
-#ifdef PEER_USE_TAP
-
-       if (info) {
-               memcpy (v6ether.h_dest, info->source_lladdr, 6);
-       } else {
-               memcpy (v6ether.h_dest, v6ether.h_source, 6);
-       }
-       memcpy (v6ether.h_source, SERVER_6BED4_PORT_IPV4_MACSTR, 6);
-#endif
-       memcpy (v6data, ipv6_defaultrouter_neighbor_advertisement, 8);
-       if (info) {
-               memcpy (v6dst6, &info->source, 16);
-       } else {
-               memcpy (v6dst6, v6src6, 16);
-       }
-       if (info) {
-               memcpy (v6src6, &info->target, 16);
-       } else {
-               memcpy (v6src6, router_linklocal_address_complete, 16);
-       }
-       //TODO:OVERWROTE// memcpy (v6data + 8, ipv6_defaultrouter_neighbor_advertisement + 8, 16);
-       memcpy (v6data + 8 + 16 + 16, ipv6_defaultrouter_neighbor_advertisement + 8 + 16 + 16, sizeof (ipv6_defaultrouter_neighbor_advertisement) - 8 - 16 - 16);
-       if (info) {
-               // Overwrite target only for queued requests
-               memcpy (&v6icmp6data [4], &info->target, 16);
-       }
-       v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 32);
-       int sent = write (v6sox, &v6data6, HDR_SIZE + sizeof (ipv6_defaultrouter_neighbor_advertisement));
-       if (info) {
-               syslog (LOG_DEBUG, "TODO: Neighbor Discovery failed to contact directly -- standard response provided\n");
-       } else {
-               syslog (LOG_DEBUG, "TODO: Neighbor Discovery for Public 6bed4 Service -- standard response provided\n");
-       }
-}
-
-
-/*
- * Validate the originator's IPv6 address.  It should match the
- * UDP/IPv4 coordinates of the receiving 6bed4 socket.  Also,
- * the /64 prefix (but not the /114 prefix!) must match v6listen.
- */
-bool validate_originator (struct in6_addr *ip6) {
-       uint32_t addr;
-       //
-       // Communication from the configured router is welcomed
-       //TODO// Why should we trust the ip6 address at face value?
-       if ((v4name.sin_addr.s_addr == v4peer.sin_addr.s_addr)
-                       && (v4name.sin_port == v4peer.sin_port)) {
-               return true;
-       }
-       //
-       // Require non-local top halves to match our v6listen_linklocal address
-       //TODO// When do we receive local top halves?
-       //TODO// We should really be more flexible and allow fallback addrs
-       if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
-               if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
-                       return false;
-               }
-       }
-       //
-       // Require the sender port to appear in its IPv6 address
-       if (v4name.sin_port != ip6->s6_addr16 [6]) {
-               return false;
-       }
-       //
-       // Require the sender address to appear in its IPv6 address
-       addr = ntohl (ip6->s6_addr32 [2]) & 0xfcffffff;
-       addr |= ((uint32_t) (ip6->s6_addr [14] & 0xc0)) << (24-6);
-       if (addr != ntohl (v4name.sin_addr.s_addr)) {
-               return false;
-       }
-       //
-       // We passed with flying colours
-       return true;
-}
-
-
-/*
- * Translate a Link-Local Address to its metric.  The metrics are
- * numbered so that a higher number indicates a more costly path
- * over which to connect.  The values of the metric should not be
- * published, but be treated as an opaque value with a complete
- * ordering (that is: <, <=, >=, > relations) defined on it.
- */
-metric_t lladdr_metric (uint8_t *lladdr) {
-       uint32_t ipv4 = * (uint32_t *) (lladdr + 2);
-       //
-       // Metric 2: The 6bed4 Router address
-       if (ipv4 == v4peer.sin_addr.s_addr) {
-               return METRIC_HIGH;
-       }
-       //
-       // Metric 0: Private Addresses, as per RFC 1918
-       if ((ipv4 & 0xff000000) == 0x0a000000) {
-               return METRIC_LOW;      /* 10.0.0./8 */
-       }
-       if ((ipv4 & 0xffff0000) == 0xc0a80000) {
-               return METRIC_LOW;      /* 192.168.0.0/16 */
-       }
-       if ((ipv4 & 0xfff00000) == 0xac100000) {
-               return METRIC_LOW;      /* 172.16.0.0/12 */
-       }
-       //
-       // Metric 1: Direct IPv4 contact is any other address
-       //           Correctness should be checked elsewhere
-       return METRIC_MEDIUM;
-}
-
-
-/*
- * Retrieve the Link-Local Address, if any, for a given 6bed4 Peer.
- * Return true on success, false on failure to find it.  The lladdr
- * parameter is only overwritten in case of success.
- *
- * Obviously, there is a point where it is more efficient to not
- * lookup the cache for every request, but to cache it locally
- * and limit the lookup frequency.  This low-traffic optimal version
- * is used here for initial simplicity, and because this is a peer
- * daemon and a reference implementation.  But who knows what people
- * will submit as patches...
- *
- * Note: This code is specific to Linux, but note that BSD also has a
- *       NetLink concept, so it may port without needing to resort to
- *       shell commands running slowly in separate processes.
- * Note: The interface for Linux is under-documented.  Work may be
- *       needed to handle exception situations, such as going over
- *       invisible boundaries on the number of neighbours.  Similarly,
- *       the use of alignment macros is rather unclear.  This is not
- *       how I prefer to write code, but it's the best I can do now.
- */
-bool lookup_neighbor (uint8_t *ipv6, uint8_t *lladdr) {
-       struct mymsg {
-               struct nlmsghdr hd;
-               struct ndmsg nd;
-               uint8_t arg [16384];
-       } msg;
-       memset (&msg, 0, sizeof (struct nlmsghdr) + sizeof (struct ndmsg));
-       msg.hd.nlmsg_len = NLMSG_LENGTH (sizeof (msg.nd));
-       msg.hd.nlmsg_type = RTM_GETNEIGH;
-       msg.hd.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT /* | NLM_F_MATCH */;
-       msg.hd.nlmsg_pid = rtname.nl_pid;
-       msg.nd.ndm_family = AF_INET6;
-       msg.nd.ndm_state = NUD_REACHABLE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT | NUD_STALE;   // Ignored by the kernel?
-       msg.nd.ndm_ifindex = ifreq.ifr_ifindex; // Ignored by the kernel?
-       // How to select an IPv6 address?  Ignored by the kernel?
-#if 0
-       struct rtattr *ra1 = (struct rtattr *) (((char *) &msg) + sizeof (struct nlmsghdr) + sizeof (struct ndmsg));
-       ra1->rta_type = NDA_DST;        // lookup IPv6 address
-       ra1->rta_len = RTA_LENGTH(16);
-       msg.hd.nlmsg_len = NLMSG_ALIGN (msg.hd.nlmsg_len) + RTA_LENGTH (16);
-       memcpy (RTA_DATA (ra1), ipv6, 16);
-#endif
-       if (send (rtsox, &msg, msg.hd.nlmsg_len, MSG_DONTWAIT) == -1) {
-               return false;
-       }
-       ssize_t recvlen;
-       uint16_t pos = 0;
-{ char buf [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, ipv6, buf, sizeof (buf)); syslog (LOG_DEBUG, "Looking up v6addr %s\n", buf); }
-       while (recvlen = recv (rtsox, ((char *) &msg) + pos, sizeof (msg) - pos, MSG_DONTWAIT), recvlen > 0) {
-syslog (LOG_DEBUG, "Message of %zd bytes from neighbor cache, total is now %zd\n", recvlen, pos + recvlen);
-               recvlen += pos;
-               pos = 0;
-               struct mymsg *resp;
-               while (resp = (struct mymsg *) (((char *) &msg) + pos),
-                               (pos + sizeof (struct nlmsghdr) <= recvlen) &&
-                               (pos + resp->hd.nlmsg_len <= recvlen)) {
-                       bool ok = true, match = false;
-                       uint8_t *result = NULL;
-                       if (resp->hd.nlmsg_type == NLMSG_DONE) {
-                               return false;
-                       } else if (resp->hd.nlmsg_type != RTM_NEWNEIGH) {
-                               syslog (LOG_ERR, "Kernel sent an unexpected nlmsg_type 0x%02x, ending neighbor interpretation", resp->hd.nlmsg_type);
-                               ok = false;
-                       } else if (resp->nd.ndm_ifindex != ifreq.ifr_ifindex) {
-                               syslog (LOG_ERR, "Kernel sent an unexpected interface index");
-                               ok = false;
-                       } else if (resp->nd.ndm_family != AF_INET6) {
-                               syslog (LOG_ERR, "Kernel reported unknown neighbor family %d\n", resp->nd.ndm_family);
-                               ok = false;
-                       } else
-                       if (!(resp->nd.ndm_state & (NUD_REACHABLE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT | NUD_STALE))) {
-                               syslog (LOG_ERR, "Kernel sent a funnily flagged interface state");
-                               ok = false;
-                       }
-                       struct rtattr *ra = (struct rtattr *) ((char *) &resp + pos + sizeof (struct nlmsghdr) + sizeof (struct ndmsg) + 8);
-                       ssize_t rapos = 0;
-                       while (ok && (rapos + ra->rta_len <= resp->hd.nlmsg_len)) {
-                               switch (ra->rta_type) {
-                               case NDA_DST:
-{ char buf [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, RTA_DATA (ra), buf, sizeof (buf)); syslog (LOG_DEBUG, "Comparing against %s\n", buf); }
-                                       if (memcmp (ipv6, RTA_DATA (ra), 16) == 0) {
-                                               match = true;
-                                       }
-                                       break;
-                               case NDA_LLADDR:
-                                       result = RTA_DATA (ra);
-                                       break;
-                               case NDA_PROBES:
-                               case NDA_CACHEINFO:
-                               default:
-                                       break;  /* not of interest, skip */
-                               }
-                               rapos += ((ra->rta_len - 1) | 0x00000003) + 1;
-                               ra = (struct rtattr *) (((char *) ra) + (((ra->rta_len - 1) | 0x0000003) + 1));
-                       }
-                       if (ok && match && result) {
-                               memcpy (lladdr, result, 6);
-                               return true;    /* Yippy! Erfolg! */
-                       }
-                       pos += resp->hd.nlmsg_len;
-               }
-               // Copy remaining partial message to the beginning, continue from there
-               memcpy (&msg, ((char *) &msg) + pos, recvlen - pos);
-               pos = recvlen - pos;
-       }
-       return false;
-}
-
-
-/*
- * Major packet processing functions
- */
-
-
-/* Handle the IPv4 message pointed at by msg, checking if (TODO:HUH?) the IPv4:port
- * data matches the lower half of the IPv6 sender address.  Drop silently
- * if this is not the case.  TODO: or send ICMP?
- */
-void handle_4to6_plain (ssize_t v4datalen, struct sockaddr_in *sin) {
-       //
-       // Send the unwrapped IPv6 message out over v6sox
-#ifdef PEER_USE_TAP
-       v4ether.h_proto = htons (ETH_P_IPV6);
-       memcpy (v4ether.h_dest,   v6lladdr, 6);
-       v4ether.h_source [0] = ntohs (sin->sin_port) & 0xff;
-       v4ether.h_source [1] = ntohs (sin->sin_port) >> 8;
-       memcpy (v4ether.h_source + 2, &sin->sin_addr, 4);
-#endif
-syslog (LOG_INFO, "Writing IPv6, result = %zd\n",
-       write (v6sox, &v4data6, HDR_SIZE + v4datalen)
-)
-       ;
-}
-
-
-/* Handle the IPv4 message pointed at by msg as a neighbouring command.
- *
- * Type        Code    ICMPv6 meaning                  Handling
- * ----        ----    -----------------------------   ----------------------------
- * 133 0       Router Solicitation             Ignore
- * 134 0       Router Advertisement            Setup Tunnel with Prefix
- * 135 0       Neighbour Solicitation          Send Neighbour Advertisement
- * 136 0       Neighbour Advertisement         Ignore
- * 137 0       Redirect                        Ignore
- */
-void handle_4to6_nd (struct sockaddr_in *sin, ssize_t v4ngbcmdlen) {
-       uint16_t srclinklayer;
-       uint8_t *destprefix = NULL;
-#ifdef TODO_DEPRECATED
-       uint8_t *destlladdr = NULL;
-#endif
-       struct ndqueue *ndq;
-       if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
-               return;
-       }
-       //
-       if (v4v6icmpcode != 0) {
-               return;
-       }
-       if (icmp6_checksum (v4data, v4ngbcmdlen - sizeof (struct ip6_hdr)) != v4v6icmpcksum) {
-               return;
-       }
-       //
-       // Approved.  Perform neighbourly courtesy.
-       switch (v4v6icmptype) {
-       case ND_ROUTER_SOLICIT:
-               return;         /* this is not a router, drop */
-       case ND_ROUTER_ADVERT:
-               //
-               // Validate Router Advertisement
-               if (ntohs (v4v6plen) < sizeof (struct icmp6_hdr) + 16) {
-                       return;   /* strange length, drop */
-               }
-               if (v4v6icmpdata [1] & 0x80 != 0x00) {
-                       return;   /* indecent proposal to use DHCPv6, drop */
-               }
-               if (memcmp (&v4src6->s6_addr, router_linklocal_address, 16) != 0) {
-                       return;   /* not from router, drop */
-               }
-               if (memcmp (&v4dst6->s6_addr, client1_linklocal_address, 8) != 0) {
-                       if (memcmp (&v4dst6->s6_addr, allnodes_linklocal_address, 16) != 0) {
-                               return;   /* no address setup for me, drop */
-                       }
-               }
-               if (v4dst6->s6_addr [8] & 0x01) {
-                       syslog (LOG_WARNING, "TODO: Ignoring (by accepting) an odd public UDP port revealed in a Router Advertisement -- this could cause confusion with multicast traffic\n");
-               }
-               size_t rdofs = 12;
-               //TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
-               while (rdofs + 4 < ntohs (v4v6plen)) {
-                       if (v4v6icmpdata [rdofs + 1] == 0) {
-                               return;   /* zero length option */
-                       }
-#ifdef TODO_DEPRACATED
-                       if ((v4v6icmpdata [rdofs + 0] == ND_OPT_DESTINATION_LINKADDR) && (v4v6icmpdata [rdofs + 1] == 1)) {
-                               if (v4v6icmpdata [rdofs + 2] & 0x01) {
-                                       syslog (LOG_WARNING, "TODO: Ignoring an odd UDP port offered in a Router Advertisement over 6bed4\n");
-                               }
-                               syslog (LOG_INFO, "TODO: Set tunnel link-local address to %02x:%02x:%02x:%02x:%02x:%02x\n", v4v6icmpdata [rdofs + 2], v4v6icmpdata [rdofs + 3], v4v6icmpdata [rdofs + 4], v4v6icmpdata [rdofs + 5], v4v6icmpdata [rdofs + 6], v4v6icmpdata [rdofs + 7]);
-                               destlladdr = &v4v6icmpdata [rdofs + 2];
-                               /* continue with next option */
-                       } else
-#endif
-                       if (v4v6icmpdata [rdofs + 0] != ND_OPT_PREFIX_INFORMATION) {
-                               /* skip to next option */
-                       } else if (v4v6icmpdata [rdofs + 1] != 4) {
-                               return;   /* bad length field */
-                       } else if (rdofs + (v4v6icmpdata [rdofs + 1] << 3) > ntohs (v4v6plen) + 4) {
-                               return;   /* out of packet length */
-                       } else if (v4v6icmpdata [rdofs + 3] & 0xc0 != 0xc0) {
-                               /* no on-link autoconfig prefix */
-                       } else if (v4v6icmpdata [rdofs + 2] != PREFIX_SIZE) {
-                               /* not a /114 prefix, so no 6bed4 offer */
-                               return;
-                       } else {
-                               destprefix = &v4v6icmpdata [rdofs + 16];
-                       }
-                       rdofs += (v4v6icmpdata [rdofs + 1] << 3);
-               }
-               if (destprefix) {
-                       memcpy (v6listen.s6_addr + 0, destprefix, 16);
-                       v6listen.s6_addr [14] &= 0xc0;
-                       v6listen.s6_addr [15]  = 0x01;  // choose client 1
-                       memcpy (v6listen_linklocal_complete + 0, v6listen_linklocal, 8);
-                       memcpy (v6listen_linklocal_complete + 8, v6listen.s6_addr + 8, 8);
-                       memcpy (v6lladdr, destprefix + 8, 6);
-                       //TODO// Is v6lladdr useful?  Should it include lanip?
-                       v6lladdr [0] &= 0xfc;
-                       v6lladdr [0] |= (destprefix [14] >> 6);
-                       inet_ntop (AF_INET6,
-                               &v6listen,
-                               v6prefix,
-                               sizeof (v6prefix));
-                       syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
-                       setup_tunnel_address ();  //TODO// parameters?
-                       got_lladdr = true;
-                       maintenance_time_cycle = maintenance_time_cycle_max;
-                       maintenance_time_sec = time (NULL) + maintenance_time_cycle;
-               }
-               return;
-       case ND_NEIGHBOR_SOLICIT:
-               //
-               // Validate Neigbour Solicitation (trivial)
-               //
-               // Replicate the message over the IPv6 Link (like plain IPv6)
-               if (v4ngbcmdlen < 24) {
-                       return;         /* too short, drop */
-               }
-               syslog (LOG_DEBUG, "%s: Replicating Neighbor Solicatation from 6bed4 to the IPv6 Link\n", program);
-char buf [INET6_ADDRSTRLEN]; uint8_t ll [6]; if ((memcmp (v4src6, v6listen_linklocal, 8) != 0) && (memcmp (v4src6, &v6listen, 8) != 0)) { inet_ntop (AF_INET6, v4src6, buf, sizeof (buf)); syslog (LOG_DEBUG, "Source IPv6 address %s from wrong origin\n", buf); } else { uint8_t pfaddr [16]; memcpy (pfaddr, v6listen.s6_addr, 8); memcpy (pfaddr + 8, v4src6->s6_addr + 8, 8); inet_ntop (AF_INET6, pfaddr, buf, sizeof (buf)); if (lookup_neighbor (pfaddr, ll)) { syslog (LOG_DEBUG, "Source IPv6 %s has Link-Local Address %02x:%02x:%02x:%02x:%02x:%02x with metric %d\n", buf, ll [0], ll [1], ll [2], ll [3], ll [4], ll [5], lladdr_metric (ll)); } else { syslog (LOG_DEBUG, "Source IPv6 %s is unknown to me\n", buf); } }
-uint8_t optofs = 4 + 16;
-#if 0
-uint8_t *srcll = NULL; /* TODO -- use 6bed4 Network sender instead! */
-while ((40 + 4 + optofs + 2 < v4ngbcmdlen) && (40 + 4 + optofs + 8 * v4v6icmpdata [optofs + 1] <= v4ngbcmdlen)) {
-if (v4v6icmpdata [optofs] == 1) {
-srcll = v4v6icmpdata + optofs + 2;
-}
-optofs += 8 * v4v6icmpdata [optofs + 1];
-}
-if (srcll) { syslog (LOG_DEBUG, "ND-contained Source Link-Layer Address %02x:%02x:%02x:%02x:%02x:%02x has metric %d\n", srcll [0], srcll [1], srcll [2], srcll [3], srcll [4], srcll [5], lladdr_metric (srcll)); }
-#endif
-               //
-               // We should attach a Source Link-Layer Address, but
-               // we cannot automatically trust the one provided remotely.
-               // Also, we want to detect if routes differ, and handle it.
-               //
-               // 0. if no entry in the ngb.cache
-               //    then use 6bed4 server in ND, initiate ngb.sol to src.ll
-               //         impl: use 6bed4-server lladdr, set highest metric
-               // 1. if metric (ngb.cache) < metric (src.ll)
-               //    then retain ngb.cache, send Redirect to source
-               // 2. if metric (ngb.cache) > metric (src.ll)
-               //    then retain ngb.cache, initiate ngb.sol to src.ll
-               // 3. if metric (ngb.cache) == metric (src.ll)
-               //    then retain ngb.cache
-               //
-               uint8_t src_lladdr [6];
-               src_lladdr [0] = ntohs (v4name.sin_port) & 0x00ff;
-               src_lladdr [1] = ntohs (v4name.sin_port) >> 8;
-               memcpy (src_lladdr + 2, &v4name.sin_addr, 4);
-               metric_t src_metric = lladdr_metric (src_lladdr);
-               v4v6icmpdata [4+16+0] = 1;      /* Option: Source LLaddr */
-               v4v6icmpdata [4+16+1] = 1;      /* Length: 1x 8 bytes */
-               uint8_t *ngbc_lladdr = v4v6icmpdata + 4+16+2;
-               uint8_t ngbc_ipv6 [16];
-               if (memcmp (v4src6, v6listen_linklocal, 8)) {
-                       memcpy (ngbc_ipv6 + 0, &v6listen, 8);
-                       memcpy (ngbc_ipv6 + 8, v4src6 + 8, 8);
-               } else {
-                       memcpy (ngbc_ipv6, v4src6, 16);
-               }
-               bool ngbc_cached = lookup_neighbor (ngbc_ipv6, ngbc_lladdr);
-               metric_t ngbc_metric;
-               if (ngbc_cached) {
-                       ngbc_metric = lladdr_metric (ngbc_lladdr);
-               } else {
-                       ngbc_metric = METRIC_HIGH; /* trigger local ngbsol */
-                       memcpy (ngbc_lladdr, SERVER_6BED4_PORT_IPV4_MACSTR, 6);
-syslog (LOG_DEBUG, "Failed to find neighbor in cache, initialising it with the high metric\n");
-               }
-               syslog (LOG_DEBUG, "Metric analysis: source lladdr %02x:%02x:%02x:%02x:%02x:%02x metric %d, neighbor cache lladdr %02x:%02x:%02x:%02x:%02x:%02x metric %d\n", src_lladdr [0], src_lladdr [1], src_lladdr [2], src_lladdr [3], src_lladdr [4], src_lladdr [5], src_metric, ngbc_lladdr [0], ngbc_lladdr [1], ngbc_lladdr [2], ngbc_lladdr [3], ngbc_lladdr [4], ngbc_lladdr [5], ngbc_metric);
-               //
-               // Replicate the ngb.sol with the selected ngbc-lladdr
-               v4v6icmpcksum = icmp6_checksum ((uint8_t *) v4hdr6, 8 + 16 + 8);
-               handle_4to6_plain (40 + 24 + 8, &v4name);
-               //
-               // If needed, initiate Neigbor Solicitation to the source
-               // Note: Also when !ngbc_cached as the router is then cached
-               if (ngbc_metric > src_metric) {
-syslog (LOG_DEBUG, "Trying to find the more direct route that the remote peer seems to be using\n");
-                       enqueue ((struct in6_addr *) v4src6, &v6listen, v6lladdr);
-               }
-               //
-               // If needed, ask the source to redo Neighbor Solicitation
-               if (ngbc_metric < src_metric) {
-syslog (LOG_DEBUG, "Redirecting the remote peer to the more efficient route that I am using\n");
-                       redirect_reply (ngbc_lladdr, ngbc_metric);
-               }
-               return;
-       case ND_NEIGHBOR_ADVERT:
-               //
-               // Process Neighbor Advertisement coming in over 6bed4
-               // First, make sure it is against an item in the ndqueue
-               ndq = findqueue ((struct in6_addr *) v4v6ndtarget);
-               if (!ndq) {
-                       // Ignore advertisement -- it may be an attack
-                       return;
-               }
-               // Remove the matching item from the ndqueue
-               dequeue (ndq);
-               // Replicate the Neigbor Advertisement over the IPv6 Link (like plain IPv6)
-               v4v6icmpdata [0] |= 0xe0;       /* Router, Solicited, Override */
-               v4v6icmpdata [20] = 2;          /* Target Link-Layer Address */
-               v4v6icmpdata [21] = 1;          /* Length: 1x 8 bytes */
-               v4v6icmpdata [22] = ntohs (v4name.sin_port) & 0xff;
-               v4v6icmpdata [23] = ntohs (v4name.sin_port) >> 8;
-               memcpy (v4v6icmpdata + 24, &v4name.sin_addr, 4);
-               v4v6plen = htons (24 + 8);
-               v4v6icmpcksum = icmp6_checksum ((uint8_t *) v4hdr6, 24 + 8);
-               handle_4to6_plain (sizeof (struct ip6_hdr) + 24 + 8, &v4name);
-               return;
-       case ND_REDIRECT:
-               //
-               // Redirect indicates that a more efficient bypass exists than
-               // the currently used route.  The remote peer has established
-               // this and wants to share that information to retain a
-               // symmetric communication, which is helpful in keeping holes
-               // in NAT and firewalls open.
-               //
-               //TODO// BE EXTREMELY SELECTIVE BEFORE ACCEPTING REDIRECT!!!
-               //TODO:NOTYET// enqueue ((struct in6_addr *) v4v6ndtarget, &v6listen, v6lladdr);
-               return;
-       }
-}
-
-
-/* Receive a tunnel package, and route it to either the handler for the
- * tunnel protocol, or to the handler that checks and then unpacks the
- * contained IPv6.
- */
-void handle_4to6 (int v4in) {
-       uint8_t buf [1501];
-       ssize_t buflen;
-       socklen_t adrlen = sizeof (v4name);
-       //
-       // Receive IPv4 package, which may be tunneled or a tunnel request
-       buflen = recvfrom (v4in,
-                       v4data, MTU,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, &adrlen
-               );
-       if (buflen == -1) {
-               syslog (LOG_INFO, "%s: WARNING: Error receiving IPv4-side package: %s\n",
-                               program, strerror (errno));
-               return;         /* receiving error, drop */
-       }
-       if (buflen <= sizeof (struct ip6_hdr)) {
-               return;         /* received too little data, drop */
-       }
-       if ((v4data [0] & 0xf0) != 0x60) {
-               return;         /* not an IPv6 packet, drop */
-       }
-       if (!validate_originator (v4src6)) {
-               return;         /* source appears fake, drop */
-       }
-       /*
-        * Distinguish types of traffic:
-        * Non-plain, Plain Unicast, Plain Multicast
-        */
-       if ((v4v6nexthdr == IPPROTO_ICMPV6) &&
-                       (v4v6icmptype >= 133) && (v4v6icmptype <= 137)) {
-               //
-               // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
-               if (v4v6hoplimit != 255) {
-                       return;
-               }
-               handle_4to6_nd (&v4name, buflen);
-       } else {
-               //
-               // Plain Unicast or Plain Multicast (both may enter)
-               if (v4v6hoplimit-- <= 1) {
-                       return;
-               }
-               handle_4to6_plain (buflen, &v4name);
-       }
-}
-
-
-/*
- * Relay an IPv6 package to 6bed4, using the link-local address as it
- * is found in the Ethernet header.  Trust the local IPv6 stack to have
- * properly obtained this destination address through Neighbor Discovery
- * over 6bed4.
- */
-void handle_6to4_plain_unicast (const ssize_t pktlen, const uint8_t *lladdr) {
-       struct sockaddr_in v4dest;
-       memset (&v4dest, 0, sizeof (v4dest));
-       v4dest.sin_family = AF_INET;
-       v4dest.sin_port = htons (lladdr [0] | (lladdr [1] << 8));
-       memcpy (&v4dest.sin_addr, lladdr + 2, 4);
-       if (v6tc != (v6hdr6->ip6_vfc & htonl (0x0ff00000))) {
-               v6tc = v6hdr6->ip6_vfc & htonl (0x0ff00000);
-               v4qos = (ntohl (v6hdr6->ip6_vfc) & 0x0ff00000) >> 24;
-               if (setsockopt (v4sox, IPPROTO_IP, IP_TOS, &v4qos, sizeof (v4qos)) == -1) {
-                       syslog (LOG_ERR, "Failed to switch IPv4 socket to QoS setting 0x%02x\n", v4qos);
-               }
-       }
-       syslog (LOG_DEBUG, "%s: Sending IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %zd\n", program,
-       ((uint8_t *) &v4dest.sin_addr.s_addr) [0],
-       ((uint8_t *) &v4dest.sin_addr.s_addr) [1],
-       ((uint8_t *) &v4dest.sin_addr.s_addr) [2],
-       ((uint8_t *) &v4dest.sin_addr.s_addr) [3],
-       ntohs (v4dest.sin_port),
-               sendto (v4sox,
-                               v6data,
-                               pktlen - HDR_SIZE,
-                               MSG_DONTWAIT,
-                               (struct sockaddr *) &v4dest,
-                               sizeof (struct sockaddr_in))
-       )
-                               ;
-}
-
-
-/*
- * Handle a request for Neighbor Discovery over the 6bed4 Link.
- */
-void handle_6to4_nd (ssize_t pktlen) {
-       uint8_t lldest [6];
-       //
-       // Validate ICMPv6 message -- trivial, trust local generation
-       //
-       // Handle the message dependent on its type
-       switch (v6icmp6type) {
-       case ND_ROUTER_SOLICIT:
-               v6icmp6type = ND_ROUTER_ADVERT;
-               v6icmp6code = 0;
-               v6icmp6data [0] = 0;            // Cur Hop Limit: unspec
-               v6icmp6data [1] = 0x18;         // M=0, O=0,
-                                               // H=0, Prf=11=Low
-                                               // Reserved=0
-               //TODO: wire says 0x44 for router_adv.flags
-               size_t writepos = 2;
-               memset (v6icmp6data + writepos,
-                               default_route? 0xff: 0x00,
-                               2);             // Router Lifetime
-               writepos += 2;
-               memcpy (v6icmp6data + writepos,
-                               "\x00\x00\x80\x00",
-                               4);             // Reachable Time: 32s
-               writepos += 4;
-               memcpy (v6icmp6data + writepos,
-                               "\x00\x00\x01\x00",
-                               4);             // Retrans Timer: .25s
-               writepos += 4;
-               writepos = icmp6_prefix (writepos, 0);
-               v6plen = htons (4 + writepos);
-               memcpy (v6dst6, v6src6, 16);
-               memcpy (v6src6, v6listen_linklocal_complete, 16);
-               v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 4 + writepos);
-#ifdef PEER_USE_TAP
-               v6ether.h_proto = htons (ETH_P_IPV6);
-               memcpy (v6ether.h_dest, v6ether.h_source, 6);
-               memcpy (v6ether.h_source, v6lladdr, 6);
-#endif
-               syslog (LOG_INFO, "Replying Router Advertisement to the IPv6 Link, result = %zd\n",
-                       write (v6sox, &v6data6, HDR_SIZE + sizeof (struct ip6_hdr) + 4 + writepos)
-               )
-                       ;
-               break;
-       case ND_ROUTER_ADVERT:
-               return;         /* the IPv6 Link is no router, drop */
-       case ND_NEIGHBOR_SOLICIT:
-               //
-               // Neighbor Solicitation is treated depending on its kind:
-               //  - the 6bed4 Router address is answered immediately
-               //  - discovery for the local IPv6 address is dropped
-               //  - discovery for fe80::/64 addresses is answered
-               //  - other peers start a process in the ndqueue
-               if ((memcmp (v6ndtarget, router_linklocal_address, 16) == 0) ||
-                   (memcmp (v6ndtarget, router_linklocal_address_complete, 16) == 0)) {
-                       advertise_6bed4_public_service (NULL);
-               } else if (memcmp (v6ndtarget, &v6listen, 16) == 0) {
-                       return;         /* yes you are unique, drop */
-               } else if (memcmp (v6ndtarget, v6listen_linklocal, 8) == 0) {
-                       //
-                       // Construct response for fe80::/64
-                       v6icmp6type = ND_NEIGHBOR_ADVERT;
-                       v6icmp6data [0] = 0x60; /* Solicited, Override */
-                       v6icmp6data [20] = 2;   /* Target Link-Layer Address */
-                       v6icmp6data [21] = 1;   /* Length is 1x 8 bytes */
-                       v6icmp6data [22] = v6icmp6data [12] ^ 0x02;
-                       v6icmp6data [23] = v6icmp6data [13];
-                       v6icmp6data [24] = v6icmp6data [14];
-                       v6icmp6data [25] = v6icmp6data [17];
-                       v6icmp6data [26] = v6icmp6data [18];
-                       v6icmp6data [27] = v6icmp6data [19];
-                       v6plen = htons (4 + 28);
-                       memcpy (v6dst6, v6src6, 16);
-                       memcpy (v6src6, &v6listen, 16);
-#ifdef PEER_USE_TAP
-                       memcpy (v6ether.h_dest, v6ether.h_source, 6);
-                       memcpy (v6ether.h_source, v6lladdr, 6);
-#endif
-                       v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 4 + 28);
-syslog (LOG_DEBUG, "Sending trivial reply to fe80::/64 type query\n");
-                       write (v6sox, &v6data6, HDR_SIZE + sizeof (struct ip6_hdr) + 4 + 28);
-                       return;
-               } else {
-// TODO
-#ifdef PEER_USE_TAP
-                       enqueue ((struct in6_addr *) v6ndtarget, (struct in6_addr *) v6src6, v6ether.h_source);
-#endif
-               }
-               break;
-       case ND_NEIGHBOR_ADVERT:
-               lldest [0] = v6dst6->s6_addr [8] ^ 0x02;
-               lldest [1] = v6dst6->s6_addr [9];
-               lldest [2] = v6dst6->s6_addr [10];
-               lldest [3] = v6dst6->s6_addr [13];
-               lldest [4] = v6dst6->s6_addr [14];
-               lldest [5] = v6dst6->s6_addr [15];
-               handle_6to4_plain_unicast (pktlen, lldest);
-               break;
-       case ND_REDIRECT:
-               //TODO:NOT_IMPLEMENTED_YET:ND_REDIRECT_FROM_6BED4//
-               //
-               // Redirect indicates that a more efficient bypass exists than
-               // the currently used route.  The remote peer has established
-               // this and wants to share that information to retain a
-               // symmetric communication, which is helpful in keeping holes
-               // in NAT and firewalls open.
-               //
-               return;
-       }
-}
-
-/*
- * Receive an IPv6 package, check its address and pickup IPv4 address and
- * port, then package it as a tunnel message and forward it to IPv4:port.
- * Rely on the proper formatting of the incoming IPv6 packet, as it is
- * locally generated.
- */
-void handle_6to4 (void) {
-#ifndef PEER_USE_TAP
-       static uint8_t lladdr [] =
-       {
-               // UDP port
-               UDP_PORT_6BED4 & 0xff,
-               UDP_PORT_6BED4 >> 8,
-               // IPv4 address
-               SERVER_6BED4_IPV4_INT0,
-               SERVER_6BED4_IPV4_INT1,
-               SERVER_6BED4_IPV4_INT2,
-               SERVER_6BED4_IPV4_INT3
-       };
-#endif
-       //
-       // Receive the IPv6 package and ensure a consistent size
-       size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
-       if (rawlen == -1) {
-               return;         /* failure to read, drop */
-       }
-       if (rawlen < HDR_SIZE + sizeof (struct ip6_hdr) + 1) {
-               return;         /* packet too small, drop */
-       }
-#ifdef PEER_USE_TAP
-       if (v6ether.h_proto != htons (ETH_P_IPV6)) {
-               return;         /* not IPv6, drop */
-       }
-//TODO// syslog (LOG_DEBUG, "Packet from IPv6 stack, target %02x:%02x:%02x:%02x:%02x:%02x\n", v6ether.h_dest [0], v6ether.h_dest [1], v6ether.h_dest [2], v6ether.h_dest [3], v6ether.h_dest [4], v6ether.h_dest [5]);
-       //
-       // Ignore messages from the IPv6 stack to itself
-       if (memcmp (v6ether.h_dest, v6ether.h_source, 6) == 0) {
-               syslog (LOG_DEBUG, "TODO: Self-to-self messaging in IPv6 stack ignored\n");
-               return;
-       }
-#endif
-       /*
-        * Distinguish types of traffic:
-        * Non-plain, Plain Unicast, Plain Multicast
-        */
-       if ((v6type == IPPROTO_ICMPV6) &&
-                       (v6icmp6type >= 133) && (v6icmp6type <= 137)) {
-               //
-               // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
-syslog (LOG_DEBUG, "Forwarding non-plain unicast from IPv6 to 6bed4\n");
-               handle_6to4_nd (rawlen);
-       } else if ((v6dst6->s6_addr [0] != 0xff) && !(v6dst6->s6_addr [8] & 0x01)) {
-               //
-               // Plain Unicast
-               if (v6hops-- <= 1) {
-                       return;
-               }
-syslog (LOG_DEBUG, "Forwarding plain unicast from IPv6 to 6bed4\n");
-#ifdef PEER_USE_TAP
-               handle_6to4_plain_unicast (rawlen, v6ether.h_dest);
-#else
-               handle_6to4_plain_unicast (rawlen, lladdr);
-#endif
-
-       } else {
-               //
-               // Plain Multicast
-               //TODO:IGNORE_MULTICAST//
-               //TODO// syslog (LOG_DEBUG, "%s: Plain multicast from 6bed4 Link to 6bed4 Network is not supported\n", program);
-       }
-}
-
-
-/*
- * Send a single Neighbor Solicitation message over 6bed4.  This will
- * be sent to the given 6bed4 address, and is usually part of a series
- * of attempts to find a short-cut route to the 6bed4 peer.
- */
-void solicit_6bed4_neighbor (const struct ndqueue *info, const uint8_t *addr6bed4) {
-       memcpy (v6src6, &info->source, 16);
-       memcpy (v6dst6, &info->target, 16);
-       v6type = IPPROTO_ICMPV6;
-       v6hops = 255;
-       v6icmp6type = ND_NEIGHBOR_SOLICIT;
-       v6icmp6code = 0;
-       v6icmp6data [0] =
-       v6icmp6data [1] =
-       v6icmp6data [2] =
-       v6icmp6data [3] = 0x00;
-       memcpy (v6icmp6data + 4, &info->target, 16);
-       v6icmp6data [20] = 1;   // option type is Source Link-Layer Address
-       v6icmp6data [21] = 1;   // option length is 1x 8 bytes
-       memcpy (v6icmp6data + 22, v6lladdr, 6);
-       uint16_t pktlen = sizeof (struct ip6_hdr) + 4 + 28;
-       //OLD// v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 28 + 8);
-       v6plen = htons (4 + 28);
-       v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 4 + 28);
-       handle_6to4_plain_unicast (sizeof (struct ip6_hdr) + 8 + 28 + 10, addr6bed4);
-       //TODO// Why these +8 and +10 are needed, I don't know yet!
-}
-
-
-/*
- * Find a neighbor's 6bed4 address.  This is coordinated by the ndqueue,
- * which schedules such tasks and makes them repeat.  Furthermore, a few
- * attempts may be scheduled on the local network before attempts
- * shift to the direct target IPv4/UDP addresses.  Of course the local
- * network will only be scheduled if the public IPv4 address matches
- * the one for the local node.
- *
- * This process is dequeued by reverse Neighbor Advertisements.  If none
- * comes back in spite of the various Neighbor Solicitations sent, then
- * the final action is to send a Neighbor Advertisement to the host with
- * the Public 6bed4 Service as its target of last resort.  In case of
- * this last resort, the process is not continued any further; the
- * return value indicates whether the queue entry should be kept for
- * another round.
- */
-bool chase_neighbor_6bed4_address (struct ndqueue *info) {
-       uint8_t addr6bed4 [6];
-       static const uint8_t addr6bed4_lancast [8] = {
-               UDP_PORT_6BED4 & 0xff, UDP_PORT_6BED4 >> 8,
-               224, 0, 0, 1
-       };
-       if (info->todo_lancast > 0) {
-               // Attempt 1. Send to LAN multicast address (same public IP)
-               info->todo_lancast--;
-               solicit_6bed4_neighbor (info, addr6bed4_lancast);
-               return true;
-       } else if (info->todo_direct > 0) {
-               // Attempt 2. Send to target's direct IP address / UDP port
-               info->todo_direct--;
-               addr6bed4 [0] = info->target.s6_addr [8] ^ 0x02;
-               addr6bed4 [1] = info->target.s6_addr [9];
-               addr6bed4 [2] = info->target.s6_addr [10];
-               addr6bed4 [3] = info->target.s6_addr [13];
-               addr6bed4 [4] = info->target.s6_addr [14];
-               addr6bed4 [5] = info->target.s6_addr [15];
-               solicit_6bed4_neighbor (info, addr6bed4);
-               return true;
-       } else {
-               // Attempt 3. Respond with Public 6bed4 Service
-               syslog (LOG_INFO, "%s: Failed to find a bypass, passing back the 6bed4 Router\n", program);
-               advertise_6bed4_public_service (info);
-               return false;
-       }
-}
-
-
-/*
- * Perform Router Solicitation.  This is the usual mechanism that is used
- * on ethernet links as well, except that the 6bed4 permits fixed interface
- * identifiers; for this client, the interface identifier will be 0x0001.
- * The router always has interface identifier 0x0000 but it will now be
- * addressed at the all-routers IPv6 address 0xff02::2 with the general
- * source IPv6 address ::
- */
-void solicit_router (void) {
-       v4name.sin_family = AF_INET;
-       memcpy (&v4name.sin_addr.s_addr, &v4listen, 4);
-       v4name.sin_port = htons (UDP_PORT_6BED4);
-       int done = 0;
-       int secs = 1;
-// syslog (LOG_DEBUG, "%s: Sending RouterSolicitation-IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %d\n", program,
-// ((uint8_t *) &v4name.sin_addr.s_addr) [0],
-// ((uint8_t *) &v4name.sin_addr.s_addr) [1],
-// ((uint8_t *) &v4name.sin_addr.s_addr) [2],
-// ((uint8_t *) &v4name.sin_addr.s_addr) [3],
-// ntohs (v4name.sin_port),
-(
-       sendto (v4sox,
-                       ipv6_router_solicitation,
-                       sizeof (ipv6_router_solicitation),
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, sizeof (v4name)));
-}
-
-
-/*
- * Send a KeepAlive message.  This is an UDP/IPv4 message with no contents.
- * The router will not respond, but that is okay; outgoing traffic is the
- * way to keep holes in NAT and firewalls open.
- */
-void keepalive (void) {
-       v4name.sin_family = AF_INET;
-       memcpy (&v4name.sin_addr.s_addr, &v4listen, 4);
-       v4name.sin_port = htons (UDP_PORT_6BED4);
-       int done = 0;
-       int secs = 1;
-       setsockopt (v4sox, IPPROTO_IP, IP_TTL, &keepalive_ttl, sizeof (keepalive_ttl));
-       sendto (v4sox,
-                       "",
-                       0,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, sizeof (v4name));
-       setsockopt (v4sox, IPPROTO_IP, IP_TTL, &v4ttl, sizeof (v4ttl));
-}
-
-
-/* Regular maintenance is a routine that runs regularly to do one of two
- * generic tasks: either it sends Router Solicitation messages to the
- * Public 6bed4 Service, or it sends an empty UDP message somewhat in its
- * direction to keep NAT/firewall holes open.
- */
-void regular_maintenance (void) {
-       if (!got_lladdr) {
-               solicit_router ();
-               maintenance_time_cycle <<= 1;
-               maintenance_time_cycle += 1;
-               if (maintenance_time_cycle > maintenance_time_cycle_max) {
-                       maintenance_time_cycle = maintenance_time_cycle_max;
-               }
-               syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %ld seconds\n", maintenance_time_cycle);
-       } else {
-               syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
-               keepalive ();
-               maintenance_time_cycle = maintenance_time_cycle_max;
-       }
-       maintenance_time_sec = time (NULL) + maintenance_time_cycle;
-}
-
-
-/* Run the daemon core code, passing information between IPv4 and IPv6 and
- * responding to tunnel requests if so requested.
- */
-void run_daemon (void) {
-       fd_set io;
-       bool keep;
-       maintenance_time_sec = 0;       // trigger Router Solicitation
-       FD_ZERO (&io);
-       FD_SET (v4sox, &io);
-       FD_SET (v6sox, &io);
-       int nfds = (v4sox < v6sox)? (v6sox + 1): (v4sox + 1);
-       if (v4mcast != -1) {
-               FD_SET (v4mcast, &io);
-               if (v4mcast >= nfds) {
-                       nfds = v4mcast + 1;
-               }
-       }
-       while (1) {
-               struct timeval tout;
-               struct timeval now;
-               gettimeofday (&now, NULL);
-               if (maintenance_time_sec <= now.tv_sec) {
-                       regular_maintenance ();
-               }
-               tout.tv_sec = maintenance_time_sec - now.tv_sec;
-               tout.tv_usec = 0;
-               while (ndqueue && (
-                               ((ndqueue->next->tv.tv_sec == now.tv_sec)
-                                 && (ndqueue->next->tv.tv_usec <= now.tv_usec))
-                               || (ndqueue->next->tv.tv_sec < now.tv_sec))) {
-                       //
-                       // Run the entry's handler code
-                       syslog (LOG_DEBUG, "Queue at %ld.%03ld: Timed for %ld.%03ld", now.tv_sec, now.tv_usec / 1000, ndqueue->next->tv.tv_sec, ndqueue->next->tv.tv_usec / 1000);
-                       keep = chase_neighbor_6bed4_address (ndqueue->next);
-                       if (!keep) {
-                               dequeue (ndqueue->next);
-                               continue;
-                       }
-                       //
-                       // Make ndqueue point to the entry to run
-                       ndqueue = ndqueue->next;
-                       //
-                       // Add 50ms to the running time
-                       if (now.tv_usec < 950000) {
-                               ndqueue->tv.tv_usec = now.tv_usec +   50000;
-                               ndqueue->tv.tv_sec  = now.tv_sec  + 0;
-                       } else {
-                               ndqueue->tv.tv_usec = now.tv_usec -  950000;
-                               ndqueue->tv.tv_sec  = now.tv_sec  + 1;
-                       }
-               }
-               if (ndqueue && ((ndqueue->next->tv.tv_sec - now.tv_sec) < tout.tv_sec)) {
-                       tout.tv_sec  = ndqueue->next->tv.tv_sec  - now.tv_sec ;
-                       tout.tv_usec = ndqueue->next->tv.tv_usec - now.tv_usec;
-                       if (tout.tv_usec < 0) {
-                               tout.tv_usec += 1000000;
-                               tout.tv_sec  -= 1;
-                       }
-               }
-               if (select (nfds, &io, NULL, NULL, &tout) == -1) {
-                       syslog (LOG_ERR, "Select failed: %s\n", strerror (errno));
-               }
-               if (FD_ISSET (v4sox, &io)) {
-syslog (LOG_DEBUG, "Got unicast input\n");
-                       handle_4to6 (v4sox);
-               } else {
-                       FD_SET (v4sox, &io);
-               }
-               if (FD_ISSET (v6sox, &io)) {
-                       handle_6to4 ();
-               } else {
-                       FD_SET (v6sox, &io);
-               }
-               if (v4mcast != -1) {
-                       if (FD_ISSET (v4mcast, &io)) {
-syslog (LOG_DEBUG, "WOW: Got multicast input\n");
-                               handle_4to6 (v4mcast);
-                       } else {
-                               FD_SET (v4mcast, &io);
-                       }
-               }
-//fflush (stdout);
-       }
-}
-
-
-/* Option descriptive data structures */
-
-char *short_opt = "s:t:d:l:p:rk:feh";
-
-struct option long_opt [] = {
-       { "v4server", 1, NULL, 's' },
-       { "tundev", 1, NULL, 'd' },
-       { "default-route", 0, NULL, 'r' },
-       { "listen", 1, NULL, 'l' },
-       { "port", 1, NULL, 'p' },
-       { "ttl", 1, NULL, 't' },
-       { "foreground", 0, NULL, 'f' },
-       { "fork-not", 0, NULL, 'f' },
-       { "keepalive", 1, NULL, 'k' },
-       { "keepalive-period-ttl", 1, NULL, 'k' },
-       { "error-console", 0, NULL, 'e' },
-       { "help", 0, NULL, 'h' },
-       { NULL, 0, NULL, 0 }    /* Array termination */
-};
-
-
-/* Parse commandline arguments (and start to process them).
- * Return 1 on success, 0 on failure.
- */
-int process_args (int argc, char *argv []) {
-       int ok = 1;
-       int help = 0;
-       int done = 0;
-       unsigned long tmpport;
-       char *endarg;
-       default_route = false;
-       while (!done) {
-               switch (getopt_long (argc, argv, short_opt, long_opt, NULL)) {
-               case -1:
-                       done = 1;
-                       if (optind != argc) {
-                               fprintf (stderr, "%s: Extra arguments not permitted: %s...\n", program, argv [optind]);
-                               ok = 0;
-                       }
-                       break;
-               case 's':
-                       if (v4sox != -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: You can only specify a single server address\n", program);
-                               continue;
-                       }
-                       v4server = optarg;
-                       if (inet_pton (AF_INET, optarg, &v4peer.sin_addr) <= 0) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to parse IPv4 address %s\n", program, optarg);
-                               break;
-                       }
-                       memcpy (&v4listen, &v4peer.sin_addr, 4);
-                       v4sox = socket (AF_INET, SOCK_DGRAM, 0);
-                       if (v4sox == -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to allocate UDPv4 socket: %s\n", program, strerror (errno));
-                               break;
-                       }
-                       break;
-               case 'd':
-                       if (v6sox != -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Multiple -d arguments are not permitted\n", program);
-                               break;
-                       }
-                       v6sox = open (optarg, O_RDWR);
-                       if (v6sox == -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to open tunnel device %s: %s\n", program, optarg, strerror (errno));
-                               break;
-                       }
-                       break;
-               case 'r':
-                       if (default_route) {
-                               fprintf (stderr, "%s: You can only request default route setup once\n", program);
-                               ok = 0;
-                               break;
-                       }
-                       default_route = true;
-                       break;
-               case 'l':
-                       if (inet_pton (AF_INET, optarg, &v4bind.sin_addr.s_addr) != 1) {
-                               fprintf (stderr, "%s: IPv4 address %s is not valid\n", program, optarg);
-                               ok = 0;
-                               break;
-                       }
-                       break;
-               case 'p':
-                       tmpport = strtoul (optarg, &endarg, 10);
-                       if ((*endarg) || (tmpport > 65535)) {
-                               fprintf (stderr, "%s: UDP port number %s is not valid\n", program, optarg);
-                               ok = 0;
-                               break;
-                       }
-                       if (tmpport & 0x0001) {
-                               fprintf (stderr, "%s: UDP port number %ld is odd, which is not permitted\n", program, tmpport);
-                               ok = 0;
-                               break;
-                       }
-                       v4bind.sin_port = htons (tmpport);
-                       break;
-               case 'f':
-                       if (foreground) {
-                               fprintf (stderr, "%s: You can only request foreground operation once\n", program);
-                               ok = 0;
-                               break;
-                       }
-                       foreground = true;
-                       break;
-               case 'e':
-                       if (log_to_stderr) {
-                               fprintf (stderr, "%s: You can only specify logging to stderr once\n", program);
-                               ok = 0;
-                               break;
-                       }
-                       log_to_stderr = true;
-                       break;
-               case 't':
-                       if (v4ttl_mcast != -1) {
-                               fprintf (stderr, "%s: You can set the ttl for multicast once\n", program);
-                               ok = 0;
-                               break;
-                       }
-                       char *zero;
-                       long setting = strtol(optarg, &zero, 10);
-                       if (*zero || (setting < 0) || (setting > 255)) {
-                               fprintf (stderr, "%s: Multicast radius must be a number in the range 0 to 255, inclusive, not %s\n", program, optarg);
-                               ok = 0;
-                               break;
-                       }
-                       v4ttl_mcast = setting;
-                       break;
-               case 'k':
-                       if (keepalive_ttl != -1) {
-                               fprintf (stderr, "%s: You can only set the keepalive period and TTL once\n", program);
-                               ok = 0;
-                               break;
-                       }
-                       char *rest;
-                       keepalive_period = strtol (optarg, &rest, 10);
-                       if (*rest == ',') {
-                               rest++;
-                               keepalive_ttl = strtol (rest, &rest, 10);
-                               if ((keepalive_ttl < 0) || (keepalive_ttl > 255)) {
-                                       fprintf (stderr, "%s: The keepalive TTL must be in the range 0 to 255, inclusive\n", program);
-                                       ok = 0;
-                                       break;
-                               }
-                       } else {
-                               keepalive_ttl = 3;
-                       }
-                       if (*rest) {
-                               fprintf (stderr, "%s: The format for keepalive configuration is 'period,ttl' or just 'period', but not %s\n", program, optarg);
-                               exit (1);
-                       }
-                       break;
-               default:
-                       ok = 0;
-                       help = 1;
-                       /* continue into 'h' to produce usage information */
-               case 'h':
-                       help = 1;
-                       break;
-               }
-               if (help || !ok) {
-                       done = 1;
-               }
-       }
-       if (help) {
-#ifdef HAVE_SETUP_TUNNEL
-               fprintf (stderr, "Usage: %s [-r] [-d /dev/tunX]\n       %s -h\n", program, program);
-#else
-               fprintf (stderr, "Usage: %s [-r] -d /dev/tunX\n       %s -h\n", program, program);
-#endif
-               return 0;
-       }
-       if (!ok) {
-               return 0;
-       }
-       if (keepalive_ttl != -1) {
-               maintenance_time_cycle_max = keepalive_period;
-       } else {
-               keepalive_ttl = 3;
-       }
-#ifdef HAVE_SETUP_TUNNEL
-       if (v6sox == -1) {
-               if (geteuid () != 0) {
-                       fprintf (stderr, "%s: You should be root, or use -d to specify an accessible tunnel device\n", program);
-                       return 0;
-               } else {
-                       ok = setup_tunnel ();
-                       if (!ok) {
-                               fprintf (stderr, "Failed to setup tunnel\n");
-                       }
-               }
-       }
-#else /* ! HAVE_SETUP_TUNNEL */
-       if (v6sox == -1) {
-               fprintf (stderr, "%s: You must specify a tunnel device with -d that is accessible to you\n", program);
-               return 0;
-       }
-#endif /* HAVE_SETUP_TUNNEL */
-       return ok;
-}
-
-
-/* The main program parses commandline arguments and forks off the daemon
- */
-int main (int argc, char *argv []) {
-       //
-       // Initialise
-       program = strrchr (argv [0], '/');
-       if (program) {
-               program++;
-       } else {
-               program = argv [0];
-       }
-       memset (&v4name, 0, sizeof (v4name));
-       memset (&v4peer, 0, sizeof (v4peer));
-       memset (&v6name, 0, sizeof (v6name));
-       v4name.sin_family  = AF_INET ;
-       v4peer.sin_family  = AF_INET ;
-       v6name.sin6_family = AF_INET6;
-       // Fixed public server data, IPv4 and UDP:
-       v4server = SERVER_6BED4_IPV4_TXT;
-       v4peer.sin_addr.s_addr = htonl (SERVER_6BED4_IPV4_INT32);
-       v4name.sin_port = htons (UDP_PORT_6BED4);
-       v4peer.sin_port = htons (UDP_PORT_6BED4);
-       memcpy (&v4listen, &v4peer.sin_addr, 4);
-       memset (&v4bind, 0, sizeof (v4bind));
-       v4bind.sin_family = AF_INET;
-#ifndef PEER_USE_TAP
-       v4tunpi6.flags = 0;
-       v4tunpi6.proto = htons (ETH_P_IPV6);
-#endif
-       //
-       // Parse commandline arguments
-       openlog (program, LOG_NDELAY | LOG_PID | LOG_PERROR, LOG_DAEMON);
-       if (!process_args (argc, argv)) {
-               fprintf (stderr, "Argument processing failed\n");
-               exit (1);
-       }
-       if (!log_to_stderr) {
-               closelog ();
-               openlog (program, LOG_NDELAY | LOG_PID, LOG_DAEMON);
-       }
-       //
-       // Construct the 6bed4 Router's complete link-layer address
-       router_linklocal_address_complete [8] = (ntohs (v4peer.sin_port) & 0xff) ^ 0x02;
-       router_linklocal_address_complete [9] = ntohs (v4peer.sin_port) >> 8;
-       router_linklocal_address_complete [10] = ntohl (v4peer.sin_addr.s_addr) >> 24;
-       router_linklocal_address_complete [11] = 0xff;
-       router_linklocal_address_complete [12] = 0xfe;
-       memcpy (router_linklocal_address_complete + 13, &((uint8_t *) &v4peer.sin_addr) [1], 3);
-       //
-       // Create memory for the freequeue buffer
-       freequeue = calloc (freequeue_items, sizeof (struct ndqueue));
-       if (!freequeue) {
-               syslog (LOG_CRIT, "%s: Failed to allocate %d queue items\n", program, freequeue_items);
-               exit (1);
-       }
-       int i;
-       for (i = 1; i < freequeue_items; i++) {
-               freequeue [i].next = &freequeue [i-1];
-       }
-       freequeue = &freequeue [freequeue_items - 1];
-       //
-       // Create socket for normal outgoing (and return) 6bed4 traffic
-       if (v4sox == -1) {
-               v4sox = socket (AF_INET, SOCK_DGRAM, 0);
-               if (v4sox == -1) {
-                       syslog (LOG_CRIT, "%s: Failed to open a local IPv4 socket -- does your system still support IPv4?\n", program);
-                       exit (1);
-               }
-       }
-       struct sockaddr_in tmpaddr;
-       memset (&tmpaddr, 0, sizeof (tmpaddr));
-       tmpaddr.sin_family = AF_INET;
-       srand (getpid ());
-       uint16_t portn = rand () & 0x3ffe;
-       uint16_t port0 = portn + 16384;
-       //TODO// Move port iteration + allocation to separate function
-       while (portn < port0) {
-               tmpaddr.sin_port = htons ((portn & 0x3ffe) + 49152);
-               if (bind (v4sox, (struct sockaddr *) &tmpaddr, sizeof (tmpaddr)) == 0) {
-                       break;
-               }
-               portn += 2;
-       }
-       if (portn < port0) {
-               syslog (LOG_DEBUG, "Bound to UDP port %d\n", ntohs (tmpaddr.sin_port));
-       } else {
-               fprintf (stderr, "%s: All even dynamic ports rejected binding: %s\n", program, strerror (errno));
-               exit (1);
-       }
-       //
-       // Setup fragmentation, QoS and TTL options
-       u_int yes = 1;
-       u_int no = 0;
-#if defined(IP_DONTFRAG)
-       if (setsockopt (v4sox, IPPROTO_IP, IP_DONTFRAG, no, sizeof (no)) == -1) {
-               syslog (LOG_ERR, "Failed to permit fragmentation -- not all peers may be accessible with MTU 1280");
-       }
-#elif defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
-       int pmtuflag = IP_PMTUDISC_DONT;
-       if (setsockopt (v4sox, IP_MTU_DISCOVER, IP_MTU_DISCOVER, &pmtuflag, sizeof (pmtuflag)) == -1) {
-               syslog (LOG_ERR, "Failed to permit fragmentation -- not all peers may be accessible with MTU 1280");
-       }
-#else
-#warning "Target system lacks support for controlling packet fragmentation"
-#endif
-       socklen_t soxlen = sizeof (v4qos);
-       if (getsockopt (v4sox, IPPROTO_IP, IP_TOS, &v4qos, &soxlen) == -1) {
-               syslog (LOG_ERR, "Quality of Service is not supported by the IPv4-side socket");
-               v4qos = 0;
-       }
-       v6tc = htonl (v4qos << 20);
-       soxlen = sizeof (v4ttl);
-       if (getsockopt (v4sox, IPPROTO_IP, IP_TTL, &v4ttl, &soxlen) == -1) {
-               syslog (LOG_ERR, "Time To Live cannot be varied on the IPv4 socket");
-               v4ttl = 64;
-       }
-       //
-       // Bind to the IPv4 all-nodes local multicast address
-       memset (&v4allnodes, 0, sizeof (v4allnodes));
-       v4allnodes.sin_family = AF_INET;
-       v4allnodes.sin_port = htons (UDP_PORT_6BED4);
-       v4allnodes.sin_addr.s_addr = htonl ( INADDR_ANY );
-       if (multicast) {
-               v4mcast = socket (AF_INET, SOCK_DGRAM, 0);
-               if (v4mcast != -1) {
-                       struct ip_mreq mreq;
-                       mreq.imr_multiaddr.s_addr = htonl ( (224L << 24) | 1L);
-                       mreq.imr_multiaddr.s_addr = htonl ( INADDR_ANY );
-                       if (bind (v4mcast, (struct sockaddr *) &v4allnodes, sizeof (v4allnodes)) != 0) {
-                               close (v4mcast);
-                               v4mcast = -1;
-                               syslog (LOG_ERR, "No LAN bypass: Failed to bind to IPv4 all-nodes: %s\n", strerror (errno));
-                       } else if (setsockopt (v4mcast, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (yes)) == -1) {
-                               close (v4mcast);
-                               v4mcast = -1;
-                               syslog (LOG_ERR, "No LAN bypass: Failed to share multicast port: %s\n", strerror (errno));
-                       } else if (setsockopt (v4mcast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) {
-                               close (v4mcast);
-                               v4mcast = -1;
-                               syslog (LOG_ERR, "No LAN bypass: Failed to setup multicast: %s\n", strerror (errno));
-                       } else if ((v4ttl_mcast != -1) && (setsockopt (v4mcast, IPPROTO_IP, IP_MULTICAST_TTL, &v4ttl_mcast, sizeof (v4ttl_mcast)) == -1)) {
-                               close (v4mcast);
-                               v4mcast = -1;
-                               syslog (LOG_ERR, "No LAN bypass: Failed to configure the multicast radius: %s\n", strerror (errno));
-                       }
-#if 0
-                       if (bind (v4mcast, (struct sockaddr *) &v4allnodes, sizeof (v4allnodes)) != 0) {
-                               close (v4mcast);
-                               v4mcast = -1;
-                               syslog (LOG_ERR, "%s: No LAN bypass: Failed to bind to IPv4 all-nodes\n", program);
-                       } else if (listen (v4mcast, 10) != 0) {
-                               close (v4mcast);
-                               v4mcast = -1;
-                               syslog (LOG_ERR, "%s: No LAN bypass: Failed to listen to IPv4 all-nodes\n", program);
-                       }
-#endif
-               }
-       } else {
-               syslog (LOG_INFO, "%s: No LAN bypass: Not desired\n", program);
-       }
-       //
-       // Construct an rtnetlink socket for neighbor cache interaction
-       rtsox = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-       if (rtsox == -1) {
-               syslog (LOG_CRIT, "Failed to gain access to the neighbor cache: %s\n", strerror (errno));
-               exit (1);
-       }
-       memset (&rtname,   0, sizeof (rtname  ));
-       memset (&rtkernel, 0, sizeof (rtkernel));
-       rtname.nl_family = rtkernel.nl_family = AF_NETLINK;
-       rtname.nl_pid = getpid ();
-       if (bind (rtsox, (struct sockaddr *) &rtname, sizeof (rtname)) == -1) {
-               syslog (LOG_CRIT, "Failed to bind to the neighbor cache socket: %s\n", strerror (errno));
-               exit (1);
-       }
-       if (connect (rtsox, (struct sockaddr *) &rtkernel, sizeof (rtkernel)) == -1) {
-               syslog (LOG_CRIT, "Failed to connect to the neighbor cachr in the kernel; %s\n", strerror (errno));
-               exit (1);
-       }
-{ uint8_t testll [6];
-uint8_t test_address [] = { 0xfe, 0x80, 0,0,0,0,0,0, 0xc2, 0x25, 0x06, 0xff, 0xfe, 0xb0, 0x7e, 0xa6 };
-if (lookup_neighbor (test_address, testll)) {
-syslog (LOG_INFO, "Successfully retrieved LL: %02x:%02x:%02x:%02x:%02x:%02x\n", testll [0], testll [1], testll [2], testll [3], testll [4], testll [5]);
-} else { syslog (LOG_INFO, "Failed to find LL\n"); } }
-       //
-       // If port and/or listen arguments were provided, bind to them
-       if ((v4bind.sin_addr.s_addr != INADDR_ANY) || (v4bind.sin_port != 0)) {
-               if (bind (v4sox, (struct sockaddr *) &v4bind, sizeof (v4bind)) != 0) {
-                       syslog (LOG_CRIT, "%s: Failed to bind to local socket -- did you specify both address and port?\n", program);
-                       exit (1);
-               }
-       }
-       //
-       // Run the daemon
-       if (foreground) {
-               run_daemon ();
-       } else {
-               if (setsid () != -1) {
-                       syslog (LOG_CRIT, "%s: Failure to detach from parent session: %s\n", program, strerror (errno));
-                       exit (1);
-               }
-               switch (fork ()) {
-               case -1:
-                       syslog (LOG_CRIT, "%s: Failure to fork daemon process: %s\n", program, strerror (errno));
-                       exit (1);
-               case 0:
-                       close (0);
-                       if (! log_to_stderr) {
-                               close (1);
-                               close (2);
-                       }
-                       run_daemon ();
-                       break;
-               default:
-                       break;
-               }
-       }
-       //
-       // Report successful creation of the daemon
-       closelog ();
-       exit (0);
-}
-
diff --git a/6bed4peer.man b/6bed4peer.man
deleted file mode 100644 (file)
index d58807a..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-.TH 6BED4CLIENT 8 "Februari 1, 2011"
-.\" Please adjust this date whenever revising the manpage.
-.\"
-.\" Some roff macros, for reference:
-.\" .nh        disable hyphenation
-.\" .hy        enable hyphenation
-.\" .ad l      left justify
-.\" .ad b      justify to both left and right margins
-.\" .nf        disable filling
-.\" .fi        enable filling
-.\" .br        insert line break
-.\" .sp <n>    insert n+1 empty lines
-.\" for manpage-specific macros, see man(7)
-.SH NAME
-6bed4client \- client-side daemon for instant-on IPv6 service
-.SH SYNOPSYS
-.B 6bed4client
-[\fB\-d\fR \fI/dev/tunX\fR] [\fB\-r\fR] [\fB\-f\fR] [\fB\-l\fR \fIv4addr\fR] [\fB\-p\fR \fIport\fR] [\fB\-t\fR \fIhops\fR] [\fB-s \fIv4addr\fR]
-.PP
-.B 6bed4client
-[\fB\-h\fR]
-.SH DESCRIPTION
-.PP
-The \fB6bed4client\fR creates an instant-on, zero-config IPv6
-communication facility.  It is designed to work behind NAT and
-firewalls, and to find the shortest possible route to a communications
-peer.
-.PP
-The command usually works through a 6bed4 interface, often a tunnel,
-through which commands are passed to this daemon, which encapsulates
-the traffic into UDP and IPv4 before sending it.  Return UDP/IPv4
-traffic is decapsulated and offered through the 6bed4 interface.
-.SH OPTIMAL ROUTING
-The \fB6bed4client\fR goes through lengths to achieve optimal routing
-for all packets.  The existence of a public server ensures that
-IPv6 connections are always possible, but anything more direct is
-of course better.
-.PP
-Note that the structure of a 6bed4 IPv6 address is such that it
-reveals a host's public IPv4 address and an external UDP port used
-for the 6bed4 tunneling protocol.  This information can be used to
-derive both local addressing information, as well as remote.  This
-will only work for addresses that start with the standard prefix
-under which 6bed4 addresses are created.
-.PP
-If traffic is intended for the same public IPv4 address as the local
-node, then it is likely to be a host on the same local network.  In
-such cases, a Neighbor Solicitation is sent to the IPv4 all-hosts multicast
-address in an attempt to find a direct route on the LAN.  This may not
-always work, for instance in the presence of subnets without multicast
-forwarding between their segments.
-.PP
-More generally though, a remote peer has an IPv4 address and a UDP
-port over which it once commenced 6bed4 towards the public server,
-to obtain its IPv6 address.  In an attempt to find a direct route,
-the \fB6bed4client\fR will try to find a direct route to that
-endpoint.  If it succeeds to send a Neighbor Solicitation and
-receives back a Neighbor Advertisement, it has established a direct
-channel for IPv6 communications, and it can continue to use that
-instead of going through the public server.
-.PP
-Direct connections to an IPv4/UDP address will only fail if the
-remote system is behind symmetric NAT or a similar firewall.  In
-this case, an initiative from that remote system to contact the
-local system may still succeed, and give rise to a seconde attempt
-towards the remote peer, which should then succeed.  Only if both
-local and remote peers use symmetric NAT, will it be necessary
-to continue to communicate through the public 6bed4 server.
-.PP
-In general, local network traffic is preferred over anything
-else.  Second-best is direct traffic to a public IPv4/UDP address,
-and the public 6bed4 server would be the last resort.
-.SH SPECIAL CASES
-A system with access to native IPv6 can still use 6bed4, although
-it would not want to setup a default route over it.  The use of
-doing this is twofold: At first it unloads the public server from
-having to make the connection, and secondly it makes the connection
-between the local and remote host as direct as is possible over
-IPv4.  The mixed setup of native IPv6 and 6bed4 will not lead to
-any trouble, as 6bed4 traffic is easily recognised by the target
-address prefix, and the interface is setup to handle this.
-.PP
-It is possible to allocate a fixed 6bed4 address for a server, and
-publish it in DNS.  This would be as constant as the IPv4 address
-and UDP port assigned to the \fB6bed4client\fR, but most NAT and
-firewalls support port forwarding; the \fB\-p\fR option on the client
-can be used to support reception of incoming 6bed4 traffic on the
-forwarded port.
-.SH OPTIONS
-.TP
-\fB\-d\fR \fI/dev/tunX\fR
-.TP
-\fB\-\-tundev\fR \fI/dev/tunX\fR
-Instead of creating a tunnel for the duration that \fB6bed4server\fR runs,
-use one that already exists and that has already been setup with
-the proper IPv6 prefix.  This option makes it possible for
-non-root users to run \fB6bed4server\fR.  All that is required is acccess to
-the tunnel device by the user that runs \fB6bed4server\fR.  Optional on Linux.
-.TP
-\fB\-r\fR
-.TP
-\fB\-\-default\-route\fR
-Create a default route through the 6bed4 interface.  This means that the
-entire IPv6 Internet can be accessed through the 6bed4 interface.  This is
-not setup by default, as 6bed4 might also be used as an add-on interface
-that connects more directly to other 6bed4 hosts.
-.TP
-\fB\-l\fR \fIv4addr\fR
-.TP
-\fB\-\-listen\fR \fIv4addr\fR
-Listen for 6bed4 messages on the specified IPv4 address.  This will also
-be the address from which the traffic is sent.  This setting may be
-used together with \fB\-p\fR to control the daemon's behaviour such that
-it can be the target of a port forwarding setup in NAT or firewall.
-.TP
-\fB\-p\fR \fIport\fR
-.TP
-\fB\-\-port\fR \fIport\fR
-Let the 6bed4 daemon listen to the given UDP port.  This will also be
-the port from which the traffic is sent.  This setting may be used
-together with \fB\-l\fR to control the daemon's behaviour such that it
-can be the target of a port forwarding setup in NAT or firewall.
-.TP
-\fB\-s\fR
-.TP
-\fB\-\-v4server\fR \fIv4addr\fR
-Use the given IPv4 address as the fallback 6bed4 server instead of the
-default public server.  This is an experimental facility; it may lead to
-network inefficiencies or instabilities if the public server address cannot
-be found by comparison.  Use with caution, and please report any problems
-that you run into when using this option.  Do not assume this feature will
-always be present.
-.TP
-\fB\-f\fR
-.TP
-\fB\-\-foreground\fR
-.TP
-\fB\-\-fork\-not\fR
-Do not fork to the background.  Instead, stay on the foreground and listen
-to break signals.  This is primarily useful for testing, including while
-rolling out 6bed4 on a site.
-.TP
-\fB\-e\fR
-.TP
-\fB\-\-error\-console\fR
-Normally, debugging messages are sent tot syslog.  With this setting, the
-messages are instead printed to the console.
-On systems supporting the non-POSIX syslog extension LOG_PERROR, the output will be sent to stderr.
-On systems without that extension, the system console is used.
-.TP
-\fB\-k\fR \fItime\fR[,\fIreach\fR]
-.TP
-\fB\-\-keepalive \fItime\fR[,\fIreach\fR]
-This setting defines the keepalives sent to the Public 6bed4 Service.
-The \fItime\fR indicates the number of seconds between keepalives, the
-\fIreach\fR indicates the TTL to use on the traffic where supported;
-it is only needed to get outside NAT and firewalls, but not to reach
-the central infrastructure.  The default is \fB\-\-keepalive 30,3\fR
-and may be automatically determined in future versions.
-.TP
-\fB\-t\fR \fIhops\fR
-.TP
-\fB\-\-ttl\fR \fIhops\fR
-Set the multicast radius to the given number of hops, the default being 1.
-This number is used as the TTL on multicast messages, thereby determining
-whether routers are permitted to forward these packets.  The value 0
-indicates that no multicast should be used.  Values above 1 run the risk
-of deregulating the performance of 6bed4 on an unsuitable network, please
-read \fBLOCAL NETWORKING\fR below for details.
-.TP
-\fB\-u\fI
-.TP
-\fB\-\-udp-variability\fR
-TODO - argue, and maybe implement.
-Accept variations in remote UDP ports when comparing 6bed4 address with
-each other, or with an IPv6 address.  This reduces the security of the
-tunnel mechanism by permitting different processes, and possibly different
-users, on the remote end to take over from each other.  It also means that
-remote symmetric routers stand a chance of contacting this node over direct
-peering traffic.  This option is not helpful if the local node is a
-symmetric router; and if both peers run a symmetric router then there is
-never going to be direct traffic between the peers.
-.PP
-This option sets up support for remote peers that run a NAT router that is
-inherently unsuitable for peer-to-peer traffic.  The default setup is not
-kind to those routers, but it sends a much clearer signal about the origin
-of the problems, namely at the symmetric NAT router.  Being able to
-pinpoint the cause of a problem is probably more helpful than trying to
-deal with a few situations but fail on certain connections, where each
-end concludes that the other end must be at fault because direct connections
-only fail with that other party.
-.SH LOCAL NETWORKING
-Whenever possible, 6bed4 connections are connected directly over the locally
-attached network.  This optimises the traffic by not passing it through an
-external router.  But it also implies trust in the peers on a local network;
-for this reason, it is possible to set \fB\-\-ttl 0\fR and thereby
-disable the attempts to find peers locally.
-.PP
-The mechanism used to find peers locally is through multicast.  It is
-assumed that all hosts that can be reached over multicast can also be
-reached over unicast, given that their direct address were known.  The
-response to a multicast query through Neighbor Solicitation is a unicast
-response through Neighbor Advertisement, in both cases encapsulated in
-UDP and IPv4.
-.PP
-The default setting \fB\-\-ttl 1\fR works only on locally attached
-subnets.  This is generally safe, as this network is normally unfiltered.
-In places where filtering is applied within a subnet, the administrative
-staff should be prepared to stop confusion of network nodes; in case of
-6bed4, this means setting \fB\-\-ttl 0\fR to avoid relying on an open
-locally attached subnet.  This setting implies that the daemon does not
-listen for incoming queries over multicast.  The standards specify that
-multicast support is optional, so this does not break any standards.
-.PP
-Settings of \fB\-\-ttl 2\fR and beyond are more dangerous; it could
-lead to asymmetric routes if not properly configured on a network.  The
-problem of asymmetric routes being that one half might go through a
-hole in NAT, which closes when traffic does not flow through bidirectionally.
-The daemon goes through lengths to avoid this situation, and to that end it
-may generate Neighbor Solicitations and Redirects in response to every
-packet exchanged.  If you see this pattern, you almost certainly have an
-asymmetric routing situation.
-.PP
-To avoid asymmetric routes, all nodes should be able to find each other
-through multicast in both directions; if A can find B, then B should be
-able to find A.  Plain 6bed4 traffic should be able to pass in both
-directions as well as multicast traffic.  Note that multicast traffic is
-always sent to default UDP port 25788, but unicast traffic may be sent
-to any UDP port.  These additional requirements are the reason why the
-default settings are limited to the locally attached subnets.
-.SH BUGS
-This daemon does not pass on QoS headers as it should according to the
-specification.
-.PP
-The daemon needs to access the neighbor cache to be able to compare routes
-in both directions and ensure their symmetry.  It does this by accessing
-the AF_NETLINK(7) interface, more specifically NETLINK_ROUTE(7).  This
-introduces a number of potential problems.
-.PP
-First, the AF_NETLINK/NETLINK_ROUTE facility may limit portability to non-Linux platforms.
-The AF_NETLINK is the closest bet to a standard approach, and similar
-constructions exist on other platforms, so there may be no problem in
-reality.
-.PP
-Second, the AF_NETLINK/NETLINK_ROUTE documentation is incomplete, and unclear at some
-points.  This means that the current code may not work on all platforms;
-notably, the proper use of macros is insufficiently documented to support
-reliable porting to other platforms and newer kernel versions.  Another
-point of concern is whether message breakdown into partial messages has
-been covered accurately, as that process also has not been specified fully.
-.PP
-Thirdly, AF_NETLINK/NETLINK_ROUTE queries are not cached.  Every Neighbor Discovery
-that is accepted from a remote origin will trigger the process of
-comparing routes.  This may lead to scaling problems on very active
-nodes with lots of peers to communicate with simultaneously.
-.SH AUTHOR
-\fB6bed4client\fR was written by Rick van Rein from OpenFortress.
-It was created to support the 0cpm project.
diff --git a/6bed4router.c b/6bed4router.c
deleted file mode 100644 (file)
index 63b099d..0000000
+++ /dev/null
@@ -1,1261 +0,0 @@
-/* 6bed4/router.c -- traffic forwarding daemon for public TSP service
- *
- * This is an implementation of the profile that makes TSP service publicly
- * usable, that is without authentication.  However to avoid abuse of such
- * a service, it is not anonymous -- IPv6 addresses contain the IPv4 address
- * and port.
- *
- * This is an implementation of neighbour and router discovery over a
- * tunnel that packs IPv6 inside UDP/IPv4.  This tunnel mechanism is
- * targeted specifically at embedded devices that are to function on
- * any network, including IPv4-only, while being designed as IPv6-only
- * devices with a fallback to this tunnel.
- *
- * Interestingly, as a side-effect of this design the router daemon can be
- * stateless.  Any further requirements that are stateful are most likely
- * filtering, and that can be solved in stateful firewall configuration.
- *
- * The intention of TSP is to enable IPv4-only hosts to connecto to
- * IPv6 services; the public TSP profile adds to that the ability to
- * do it in a temporary manner.
- *
- * TODO: Should we translate ICMPv4 --> ICMPv6?
- *
- * From: Rick van Rein <rick@openfortress.nl>
- */
-
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdbool.h>
-
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-#include <getopt.h>
-#include <fcntl.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/select.h>
-#include <sys/ioctl.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-#include <netinet/icmp6.h>
-#include <arpa/inet.h>
-
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_ether.h>
-
-
-/* The following will initially fail, due to an IANA obligation to avoid
- * default builds with non-standard options.
- */
-#include "nonstd.h"
-
-
-#define MTU 1280
-
-/*
- * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
- * the -d option leads to an error, or to an attempt to setup the tunnel.
- * The setup_tunnel() function used for that is defined per platform, such
- * as for LINUX.  Remember to maintain the manpage's optionality for -d.
- */
-#undef HAVE_SETUP_TUNNEL
-
-
-/* Global variables */
-
-/* SCTP structures are far from standardised, so we define our own header */
-struct my_sctphdr {
-       uint16_t source, dest;
-       uint32_t vtag;
-       uint32_t cksum;
-};
-
-char *program;
-
-int v4sox = -1;
-int v6sox = -1;
-
-char *v4server = NULL;
-char *v6server = NULL;
-char *v6prefix = NULL;
-
-const uint8_t v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-uint8_t v6listen_linklocal_complete [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-uint8_t lladdr_6bed4 [6];
-
-struct sockaddr_in  v4name;
-struct sockaddr_in6 v6name;
-
-struct in6_addr v6listen;
-struct in6_addr v6listen_complete;
-struct in_addr  v4listen;
-
-
-struct {
-       struct tun_pi tun;
-       union {
-               struct {
-                       struct ip6_hdr v6hdr;
-                       uint8_t data [MTU - sizeof (struct ip6_hdr)];
-               } idata;
-               struct v4frame {
-                       struct ip6_hdr v6hdr;
-                       union {
-                               struct icmp6_hdr  v6icmphdr;
-                               struct my_sctphdr v6sctphdr;
-                               struct tcphdr     v6tcphdr ;
-                               struct udphdr     v6udphdr ;
-                               struct {
-                                       struct udphdr     v6udphdr;
-                                       struct my_sctphdr v6sctphdr;
-                               } tdata;
-                       } adata;
-               } ndata;
-       } udata;
-} v4data6;
-
-#define v4tunpi6       ( v4data6.tun)
-#define v4data         ((uint8_t *) &v4data6.udata)
-#define v4hdr6         (&v4data6.udata.idata.v6hdr)
-#define v4src6         (&v4data6.udata.idata.v6hdr.ip6_src)
-#define v4dst6         (&v4data6.udata.idata.v6hdr.ip6_dst)
-
-#define v4v6payload    ((uint16_t *) &v4data6.udata.ndata.adata)
-#define v4v6plen       ( v4data6.udata.ndata.v6hdr.ip6_plen)
-#define v4v6nexthdr    ( v4data6.udata.ndata.v6hdr.ip6_nxt)
-#define v4v6hoplimit   ( v4data6.udata.ndata.v6hdr.ip6_hops)
-
-#define v4v6icmpdata   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_data8)
-#define v4v6icmptype   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_type)
-#define v4v6icmpcode   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_code)
-#define v4v6icmpcksum  ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_cksum)
-
-#define v4v6sctpofs    (offsetof (struct v4frame,adata.v6sctphdr))
-#define v4v6sctpsrcport        ( v4data6.udata.ndata.adata.v6sctphdr.source)
-#define v4v6sctpdstport        ( v4data6.udata.ndata.adata.v6sctphdr.dest)
-#define v4v6sctpcksum  ( v4data6.udata.ndata.adata.v6sctphdr.cksum)
-
-#define v4v6tunsctpofs (offsetof (struct v4frame,adata.tdata.v6sctphdr))
-#define v4v6tunsctpsrcport ( v4data6.udata.ndata.adata.tdata.v6sctphdr.source)
-#define v4v6tunsctpdstport ( v4data6.udata.ndata.adata.tdata.v6sctphdr.dest)
-#define v4v6tunsctpcksum   ( v4data6.udata.ndata.adata.tdata.v6sctphdr.cksum)
-
-#define v4v6tcpsrcport ( v4data6.udata.ndata.adata.v6tcphdr.source)
-#define v4v6tcpdstport ( v4data6.udata.ndata.adata.v6tcphdr.dest)
-#define v4v6tcpcksum   ( v4data6.udata.ndata.adata.v6tcphdr.check)
-
-#define v4v6udpsrcport ( v4data6.udata.ndata.adata.v6udphdr.source)
-#define v4v6udpdstport ( v4data6.udata.ndata.adata.v6udphdr.dest)
-#define v4v6udpcksum   ( v4data6.udata.ndata.adata.v6udphdr.check)
-
-#define v4ngbsoltarget (&v4data6.udata.ndata.adata.v6icmphdr.icmp6_data8 [4])
-
-
-struct {
-       struct tun_pi tun;
-       union {
-               uint8_t data [MTU];
-               struct v6frame {
-                       struct ip6_hdr v6hdr;
-                       union {
-                               struct icmp6_hdr  v6icmphdr;
-                               struct my_sctphdr v6sctphdr;
-                               struct tcphdr     v6tcphdr ;
-                               struct udphdr     v6udphdr ;
-                               struct {
-                                       struct udphdr     v6udphdr;
-                                       struct my_sctphdr v6sctphdr;
-                               } tdata;
-                       } adata;
-               } ndata;
-       } udata;
-       uint8_t zero;
-} v6data6;
-
-#define v6tuncmd       ( v6data6.tun)
-#define v6data         ( v6data6.udata.data)
-#define v6hdr6         (&v6data6.udata.ndata.v6hdr)
-#define v6src6         (&v6data6.udata.ndata.v6hdr.ip6_src)
-#define v6dst6         (&v6data6.udata.ndata.v6hdr.ip6_dst)
-#define v6hoplimit     ( v6data6.udata.ndata.v6hdr.ip6_hops)
-#define v6nexthdr      ( v6data6.udata.ndata.v6hdr.ip6_nxt)
-
-#define v6icmptype     ( v6data6.udata.ndata.adata.v6icmphdr.icmp6_type)
-#define v6icmpcksum    ( v6data6.udata.ndata.adata.v6icmphdr.icmp6_cksum)
-
-#define v6sctpcksum    ( v6data6.udata.ndata.adata.v6sctphdr.cksum)
-#define v6tunsctpcksum ( v6data6.udata.ndata.adata.tdata.v6sctphdr.cksum)
-
-#define v6sctpofs      (offsetof(struct v6frame,adata.v6sctphdr))
-#define v6tunsctpofs   (offsetof(struct v6frame,adata.tdata.v6sctphdr))
-
-#define v6tcpcksum     ( v6data6.udata.ndata.adata.v6tcphdr.check)
-
-#define v6udpcksum     ( v6data6.udata.ndata.adata.v6udphdr.check)
-#define v6udpdstport   ( v6data6.udata.ndata.adata.v6udphdr.dest)
-#define v6udpsrcport   ( v6data6.udata.ndata.adata.v6udphdr.source)
-
-
-uint8_t router_linklocal_address [] = {
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
-};
-
-uint8_t democlient_linklocal_address [] = {
-       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
-};
-
-uint8_t allnodes_linklocal_address [] = {
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
-};
-
-uint8_t allrouters_linklocal_address [] = {
-       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x02,
-};
-
-
-
-#ifndef MAXNUM_MASQHOST
-#define MAXNUM_MASQHOST 4
-#endif
-
-uint16_t num_masqhost = 0;
-uint8_t masqhost [MAXNUM_MASQHOST][16];
-
-// ports for 's', 't', 'u' -- SCTP, TCP, UDP have increasing numbers
-
-#ifndef MAXNUM_PORTPAIRS
-#define MAXNUM_PORTPAIRS 10
-#endif
-
-// masqportpairs holds triples <lowport,highport,masqhostnr>
-uint16_t num_masqportpairs [3] = { 0, 0, 0 };
-uint16_t masqportpairs [3][3*MAXNUM_PORTPAIRS];
-
-// same for ICMPv6
-uint16_t icmp_num_portpairs = 0;
-uint16_t icmp_portpairs [3] = { 1, 1, 0 };
-
-
-
-/*
- *
- * Driver routines
- *
- */
-
-#ifndef INTERFACE_NAME_6BED4
-#define INTERFACE_NAME_6BED4 "6bed4"
-#endif
-
-#ifdef LINUX
-#define HAVE_SETUP_TUNNEL
-/* Implement the setup_tunnel() command for Linux.
- * Return 1 on success, 0 on failure.
- */
-int setup_tunnel (void) {
-       v6sox = open ("/dev/net/tun", O_RDWR);
-       if (v6sox == -1) {
-               fprintf (stderr, "%s: Failed to access tunnel driver on /dev/net/tun: %s\n", program, strerror (errno));
-               return 0;
-       }
-       int ok = 1;
-       struct ifreq ifreq;
-       memset (&ifreq, 0, sizeof (ifreq));
-       strncpy (ifreq.ifr_name, INTERFACE_NAME_6BED4, IFNAMSIZ);
-       ifreq.ifr_flags = IFF_TUN;
-       if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
-               ok = 0;
-       }
-       ifreq.ifr_name [IFNAMSIZ] = 0;
-       char cmd [512+1];
-       snprintf (cmd, 512, "/sbin/ip -6 addr flush dev %s", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       snprintf (cmd, 512, "/sbin/ip addr add fe80::0 dev %s scope link", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       snprintf (cmd, 512, "/sbin/ip -6 addr add %s dev %s", v6prefix, ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       snprintf (cmd, 512, "/sbin/ip link set %s up mtu 1280", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       if (!ok) {
-               close (v6sox);  /* This removes the tunnel interface */
-               fprintf (stderr, "Failed to setup tunnel \"%s\"\n", INTERFACE_NAME_6BED4);
-       }
-       return ok;
-}
-#endif /* LINUX */
-
-
-/*
- *
- * Utility functions
- *
- */
-
-
-/* Produce an IPv6 address following the 6bed4 structures.
- *  - The top half is taken from v6listen
- *  - The bottom contains IPv4 address and port from v4name
- *  - The last 14 bits are filled with the lanip parameter
- */
-void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
-       memcpy (&dst_ip6->s6_addr [0], &v6listen, 8);
-       dst_ip6->s6_addr32 [2] = v4name.sin_addr.s_addr;
-       dst_ip6->s6_addr16 [6] = v4name.sin_port;
-       dst_ip6->s6_addr  [14] = ((dst_ip6->s6_addr [8] & 0x03) << 6)
-                              | ((lanip >> 8) & 0x3f);
-       dst_ip6->s6_addr  [15] = (lanip & 0xff);
-       dst_ip6->s6_addr  [8] &= 0xfc;
-}
-
-/* Calculate the ICMPv6 checksum field
- */
-uint16_t icmp6_checksum (struct in6_addr *src6, struct in6_addr *dst6,
-                               uint16_t *payload, size_t payloadlen) {
-       uint16_t plenword = htons (payloadlen); // our ICMPv6 is small
-       uint16_t nxthword = htons (IPPROTO_ICMPV6);
-       uint16_t *area [] = { src6->s6_addr16, dst6->s6_addr16,
-                               &plenword, &nxthword,
-                               payload, &payload [2] };
-       uint8_t areawords [] = { 8, 8, 1, 1, 1, (payloadlen >> 1) - 2 };
-       uint32_t csum = 0;
-       u_int8_t i, j;
-       for (i=0; i < 6; i++) {
-               for (j=0; j<areawords [i]; j++) {
-                       csum += ntohs (area [i] [j]);
-               }
-       }
-       csum = (csum & 0xffff) + (csum >> 16);
-       csum = (csum & 0xffff) + (csum >> 16);
-       csum = htons (~csum);
-       return csum;
-}
-
-
-/* Mangle a packet by replacing an IPv6 address, correcting the checksum.
- * In all of ICMPv6, UDPv6, TCPv6, SCTPv6, the checksumming is a 1's cpl
- * of a sum of 16-bit words; the position of the words don't matter and
- * we can subtract the old address' words and add the new address' words.
- * Or, as a result of the one's complement, the opposite.  There is some
- * inclusion of the top half of a 32-bit sum, but that is also neutral.
- */
-void masquerade_address (struct in6_addr *var,
-                       const struct in6_addr *new,
-                       uint16_t *csum_field) {
-       int32_t csum = ntohs (~*csum_field);
-       uint8_t i;
-       for (i=0; i<8; i++) {
-               csum -= ntohs (var->s6_addr16 [i]);
-               var->s6_addr16 [i] = new->s6_addr16 [i];
-               csum += ntohs (var->s6_addr16 [i]);
-       }
-       csum = (csum & 0xffff) + (csum >> 16);
-       csum = (csum & 0xffff) + (csum >> 16);
-       *csum_field = htons (~csum);
-}
-
-
-/* Send an ICMPv6 reply.  This is constructed at the tunnel end, from
- * the incoming message.  The parameter indicates how many bytes the
- * ICMPv6 package counts after the ICMPv6 header.  It must be 4 (mod 8).
- *
- * Actions: v4/udp src becomes dest, set v4/udp/v6 src, len, cksum, send.
- *          reply is always to v4src6, except that if it starts with
- *         0x00,0x00 it will be replaced with allnodes_linklocal_address.
- */
-void icmp6_reply (size_t icmp6bodylen) {
-       v4v6hoplimit = 255;
-       if ((icmp6bodylen & 0x07) != 4) {
-               return;   /* illegal length, drop */
-       }
-       v4v6plen = htons (icmp6bodylen + 4);
-       memcpy (v4dst6,
-               (v4src6->s6_addr16 [0])
-                       ? (uint8_t *) v4src6
-                       : allnodes_linklocal_address,
-               16);
-       memcpy (v4src6, router_linklocal_address, 16);
-       v4v6icmpcksum = icmp6_checksum (v4src6, v4dst6,
-                               v4v6payload, ntohs (v4v6plen));
-       //
-       // Send the message to the IPv4 originator port
-printf ("Sending ICMPv6-IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %zd\n",
-((uint8_t *) &v4name.sin_addr.s_addr) [0],
-((uint8_t *) &v4name.sin_addr.s_addr) [1],
-((uint8_t *) &v4name.sin_addr.s_addr) [2],
-((uint8_t *) &v4name.sin_addr.s_addr) [3],
-ntohs (v4name.sin_port),
-       sendto (v4sox,
-                       v4data,
-                       sizeof (struct ip6_hdr) + 4 + icmp6bodylen,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, sizeof (v4name)));
-}
-
-
-/* Append the current prefix to an ICMPv6 message.  Incoming optidx
- * and return values signify original and new offset for ICMPv6 options.
- * The endlife parameter must be set to obtain zero lifetimes, thus
- * instructing the tunnel client to stop using an invalid prefix.
- */
-size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
-       v4v6icmpdata [optidx++] = 3;    // Type
-       v4v6icmpdata [optidx++] = 4;    // Length
-       v4v6icmpdata [optidx++] = 114;  // This is a /114 prefix
-       v4v6icmpdata [optidx++] = 0xc0; // L=1, A=1, Reserved1=0
-       memset (v4v6icmpdata + optidx, endlife? 0x00: 0xff, 8);
-       optidx += 8;
-                                       // Valid Lifetime: Zero / Infinite
-                                       // Preferred Lifetime: Zero / Infinite
-       memset (v4v6icmpdata + optidx, 0, 4);
-       optidx += 4;
-                                       // Reserved2=0
-       addr_6bed4 ((struct in6_addr *) (v4v6icmpdata + optidx), 0);
-                                       // Set IPv6 prefix
-       optidx += 16;
-       return optidx;
-}
-
-
-/*
- * TODO: DEPRECATED
- *
- * Append a Destination Link-Layer Address Option to an ICMPv6
- * message.  The address is comprised from the remote's UDP port
- * and IPv4 address, as seen by the router.  They are supplied
- * in that order, in network byte order.  The resulting address
- * is 6 bytes, but even though that makes it look like a MAC
- * address, it really is another beast.
- * Note that no effort is made in the router to recognise the
- * "illegal port number" 0x3333 -- the client needs a message
- * and will recognise it and act on it.
- */
-size_t icmp6_dest_linkaddr (size_t optidx) {
-       uint8_t typelen [2] = { ND_OPT_DESTINATION_LINKADDR, 1 };
-       memcpy (v4v6icmpdata + optidx + 0, &typelen, 2);
-       v4v6icmpdata [optidx + 2] = ntohs (v4name.sin_port) & 0xff;
-       v4v6icmpdata [optidx + 3] = ntohs (v4name.sin_port) >> 8;
-       memcpy (v4v6icmpdata + optidx + 4, &v4name.sin_addr, 4);
-       optidx += 8;
-       return optidx;
-}
-
-
-/*
- * Test if an address is a local override.  This function is compiled-in
- * to support hosts with a /64 from their own ISP and nothing more; they
- * need to access local IPv6 addresses.  We rely on the 6bed4 port being
- * 0 to decide that an address cannot be anything but a local override.
- * Define LOCAL_OVERRIDES_PORT0 to enable this feature.
- */
-#ifdef LOCAL_OVERRIDES_PORT0
-static inline bool is_local_override (struct in6_addr *ip6) {
-       return (ip6->s6_addr16 [6] == 0) && (memcmp (ip6->s6_addr, &v6listen, 8) == 0);
-}
-#else
-#define is_local_override(_) false
-#endif
-
-
-/*
- * Test if the provided IPv6 address matches the prefix used for 6bed4.
- *TODO: This is oversimplistic, it only cares for the Hetzner /64
- */
-static inline bool is_6bed4 (struct in6_addr *ip6) {
-       return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
-}
-
-/* Test if the provided IPv6 address matches the fc64::/16 prefix.
- * If so, the traffic may be bounced using 6bed4 traffic, but it
- * must not be relayed to the native IPv6 side.
- * TODO: Perhaps allow only configured <netid>, so fc64:<netid>::/32
- */
-static inline bool is_fc64 (struct in6_addr *ip6) {
-       return ip6->s6_addr16 [0] == htons (0xfc64);
-}
-
-/* Test if the src and destination share the same /114 and the destination
- * fills up the rest with zero bits; in other words, test that this is a
- * package targeted from a 6bed4peer node to its 6bed4router.  This is used
- * to distinguish special ICMPv6 messages, as well as masquerading targets.
- */
-static inline bool is_mine (struct in6_addr *src6, struct in6_addr *dst6) {
-       return ((dst6->s6_addr [15] == 0x00)
-               && ((dst6->s6_addr [14] & 0x3f) == 0x00)
-               && (memcmp (dst6->s6_addr, src6->s6_addr, 14) == 0));
-}
-
-
-/*
- * Validate the originator's IPv6 address.  It should match the
- * UDP/IPv4 coordinates of the receiving 6bed4 socket.  Also,
- * the /64 prefix (but not the /114 prefix!) must match v6listen.
- */
-bool validate_originator (struct in6_addr *ip6) {
-       uint32_t addr;
-       //
-       // Require non-local top halves to match our v6listen_linklocal address
-       // We will enforce our own fallback address (and fc64:<netid>::/32)
-       if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
-               if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
-                       return false;
-               }
-       }
-       //
-       // Require the sender port to appear in its IPv6 address
-       if (v4name.sin_port != ip6->s6_addr16 [6]) {
-               return false;
-       }
-       //
-       // Require the sender address to appear in its IPv6 address
-       addr = ntohl (ip6->s6_addr32 [2]) & 0xfcffffff;
-       addr |= ((uint32_t) (ip6->s6_addr [14] & 0xc0)) << (24-6);
-       if (addr != ntohl (v4name.sin_addr.s_addr)) {
-               return false;
-       }
-       //
-       // We passed with flying colours
-       return true;
-}
-
-
-
-/*
- * Major packet processing functions
- */
-
-
-/*
- * Respond to a Router Solicitation received over the 6bed4 Network.
- */
-void handle_6bed4_router_solicit (void) {
-       struct in6_addr observed;
-       v4v6icmptype = ND_ROUTER_ADVERT;
-       v4v6icmpdata [0] = 0;                   // Cur Hop Limit: unspec
-       v4v6icmpdata [1] = 0x18;                // M=0, O=0
-                                               // H=0, Prf=11=Low
-                                               // Reserved=0
-// TODO: wire says 0x44 for router_adv.flags
-       size_t writepos = 2;
-       memset (v4v6icmpdata+writepos, 0xff, 2+4+4);
-                                       // Router Lifetime: max, 18.2h
-                                       // Reachable Time: max
-                                       // Retrans Timer: max
-       writepos += 2+4+4;
-       writepos = icmp6_prefix (writepos, 0);
-       icmp6_reply (writepos);
-}
-
-
-/* Handle the IPv4 message pointed at by msg as a neighbouring command.
- *
- * Type        Code    ICMPv6 meaning                  Handling
- * ----        ----    -----------------------------   ----------------------------
- * 133 0       Router Solicitation             Send Router Advertisement
- * 134 0       Router Advertisement            Ignore
- * 135 0       Neighbour Solicitation          Send Neighbour Advertisement
- * 136 0       Neighbour Advertisement         Ignore
- * 137 0       Redirect                        Ignore
- */
-void handle_4to6_nd (ssize_t v4ngbcmdlen) {
-       uint16_t srclinklayer;
-       if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
-               return;
-       }
-       if (v4v6icmpcode != 0) {
-               return;
-       }
-       if (icmp6_checksum (v4src6, v4dst6,
-                               v4v6payload, v4ngbcmdlen-sizeof (struct ip6_hdr)
-                       ) != v4v6icmpcksum) {
-               return;
-       }
-       //
-       // Approved.  Perform neighbourly courtesy.
-       switch (v4v6icmptype) {
-       case ND_ROUTER_SOLICIT:
-               //
-               // Validate Router Solicitation
-               srclinklayer = 0;
-               if (v4ngbcmdlen == sizeof (struct ip6_hdr) + 8 + 8) {
-                       // One option is possible, the source link-layer address
-                       if (v4v6icmpdata [4] != 1 || v4v6icmpdata [5] != 1) {
-                               break;   /* bad opton, ignore */
-                       }
-                       // The source link-layer address is end-aligned
-                       srclinklayer = (v4v6icmpdata [10] << 8) | v4v6icmpdata [11];
-                       if (srclinklayer == 0) {
-                               break;   /* illegal, ignore */
-                       }
-               } else if (v4ngbcmdlen == sizeof (struct ip6_hdr) + 8) {
-                       srclinklayer = 0;   /* set for latter filling */
-               } else {
-                       break;   /* illegal length, drop */
-               }
-               //
-               // Having accepted the Router Advertisement, respond
-               handle_6bed4_router_solicit ();
-               break;
-       case ND_NEIGHBOR_SOLICIT:
-               //
-               // Validate Neigbour Solicitation
-               if (!validate_originator (v4src6)) {
-                       break;  /* bad source address, drop */
-               }
-               if ((v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) &&
-                   (v4ngbcmdlen != sizeof (struct ip6_hdr) + 24 + 8)) {
-                       break;   /* funny length, drop */
-               }
-               if ((memcmp (v4ngbsoltarget, v6listen_linklocal, 16) != 0) &&
-                    (memcmp (v4ngbsoltarget, v6listen_linklocal_complete, 16) != 0) &&
-                    (memcmp (v4ngbsoltarget, &v6listen_complete, 16) != 0)) {
-                       break;  /* target not known here, drop */
-               }
-               //
-               // Construct Neigbour Advertisement
-               v4v6icmptype = ND_NEIGHBOR_ADVERT;
-               v4v6icmpdata [0] = 0xc0;
-               v4v6icmpdata [1] =
-               v4v6icmpdata [2] =
-               v4v6icmpdata [3] = 0x00;        // R=1, S=1, O=1, Reserved=0
-               memcpy (v4v6icmpdata +  4, &v6listen_complete, 16);
-               // Append option: the target link-layer address
-               // Note: wire does not include target link-layer address
-               v4v6icmpdata [20] = 2;          // Type: Target Link-Layer Addr
-               v4v6icmpdata [21] = 1;          // Length: 1x 8 bytes
-               memcpy (v4v6icmpdata + 22, lladdr_6bed4, 6);
-               icmp6_reply (28);       // 28 is the ICMPv6 response length
-               break;
-       case ND_ROUTER_ADVERT:
-       case ND_NEIGHBOR_ADVERT:
-       case ND_REDIRECT:
-               break;   /* drop */
-       }
-}
-
-
-/*
- * Forward a message to the masquerading target, or drop it.  This is done
- * with non-6bed4 packets that are sent from a 6bed4peer node to its
- * 6bed4router address; see is_mine() for the definition.  The general idea
- * is that the address that is to be masqueraded can later be reconstructed
- * from the 6bed4peer's address.
- */
-void handle_4to6_masquerading (ssize_t v4datalen) {
-       fprintf (stderr, "Traffic to 6bed4router may be fit for masquerading\n");
-       uint16_t *portpairs = NULL;
-       uint16_t numpairs = 0;
-       uint16_t port = 0;
-       uint16_t *csum_field = NULL;
-       uint16_t sctp_offset = 0;
-       uint16_t skip_csum;
-       switch (v4v6nexthdr) {
-       case IPPROTO_SCTP:
-               portpairs = masqportpairs [0];  // 's'
-               numpairs  = num_masqportpairs [0];
-               port = ntohs (v4v6sctpdstport);
-               csum_field = &skip_csum;
-               sctp_offset = v4v6sctpofs;
-               break;
-       case IPPROTO_TCP:
-               portpairs = masqportpairs [1];  // 't'
-               numpairs  = num_masqportpairs [1];
-               port = ntohs (v4v6tcpdstport);
-               csum_field = &v4v6tcpcksum;
-               break;
-       case IPPROTO_UDP:
-               portpairs = masqportpairs [2];  // 'u'
-               numpairs  = num_masqportpairs [2];
-               port = ntohs (v4v6udpdstport);
-               csum_field = &v4v6udpcksum;
-               if ((port == 9899) && (v4v6udpsrcport == v4v6udpdstport)) {
-                       // SCTP tunneled over UDP (keep csum_field)
-                       portpairs = masqportpairs [0];
-                       numpairs = num_masqportpairs [0];
-                       port = ntohs (v4v6tunsctpdstport);
-                       sctp_offset = v4v6tunsctpofs;
-               }
-               break;
-       case IPPROTO_ICMPV6:
-               portpairs = icmp_portpairs;
-               numpairs = icmp_num_portpairs;
-               port = icmp_portpairs [0];
-               csum_field = &v4v6icmpcksum;
-               break;
-       default:
-               return;
-       }
-       fprintf (stderr, "DEBUG: Looking for masquerading of port %d in %d entries\n", port, numpairs);
-       while (numpairs-- > 0) {
-               if ((port >= portpairs [0]) && (port <= portpairs [1])) {
-                       //
-                       // Replace masqueraded address by 6bed4router's
-                       fprintf (stderr, "DEBUG: Passing traffic to masquerading address number %d\n", portpairs [2]);
-                       masquerade_address (v4dst6,
-                               (struct in6_addr *) masqhost [portpairs [2]],
-                               csum_field);
-                       if (sctp_offset > 0) {
-                               //TODO// Recompute SCTP's CRC-32 checksum
-                               // recompute_sctp_checksum (ip6hdr, sctp_offset, framelen);
-                               //BUT: SCTP checksum does not use pseudo IPhdr
-                               //SO:  Address masquerading goes undetected :-D
-                       }
-                       //
-                       // Forward immediately, and return from this function
-printf ("Writing Masqueraded IPv6, result = %zd\n",
-                       write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
-                       return;
-               }
-               portpairs += 3;
-       }
-       //
-       // Silently skip the offered packet
-}
-
-
-/* 
- * Forward a message received over the 6bed4 Network over IPv6.
- * Note that existing checksums will work well, as only the
- * Hop Limit has been altered, and this is not part of the
- * checksum calculations.
- */
-void handle_4to6_plain_unicast (ssize_t v4datalen) {
-printf ("Writing IPv6, result = %zd\n",
-       write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
-}
-
-
-/*
- * Forward a 6bed4 message to another 6bed4 destination address.
- * Local address prefixes fc64:<netid>:<ipv4>::/64 are also relayed.
- * Note that existing checksums will work well, as only the
- * Hop Limit has been altered, and this is not part of the
- * checksum calculations.
- */
-void relay_4to4_plain_unicast (uint8_t* data, ssize_t v4datalen, struct in6_addr *ip6) {
-       v4name.sin_port = htons (ip6->s6_addr [12] << 8 | ip6->s6_addr [13]);
-       uint8_t *addr = (uint8_t *) &v4name.sin_addr.s_addr;
-       addr [0] = (ip6->s6_addr [8] & 0xfc) | ip6->s6_addr [14] >> 6;
-       memcpy (addr + 1, ip6->s6_addr + 9, 3);
-printf ("Relaying over 6bed4 Network to %d.%d.%d.%d:%d, result = %zd\n",
-((uint8_t *) &v4name.sin_addr.s_addr) [0],
-((uint8_t *) &v4name.sin_addr.s_addr) [1],
-((uint8_t *) &v4name.sin_addr.s_addr) [2],
-((uint8_t *) &v4name.sin_addr.s_addr) [3],
-ntohs (v4name.sin_port),
-       sendto (v4sox,
-                       data, v4datalen,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, sizeof (v4name)));
-}
-
-
-/* Receive a tunnel package, and route it to either the handler for the
- * tunnel protocol, or to the handler that checks and then unpacks the
- * contained IPv6.
- */
-void handle_4to6 (void) {
-       uint8_t buf [1501];
-       ssize_t buflen;
-       socklen_t adrlen = sizeof (v4name);
-       //
-       // Receive IPv4 package, which may be tunneled or a tunnel request
-       buflen = recvfrom (v4sox,
-                       v4data, MTU,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, &adrlen
-               );
-       if (buflen == -1) {
-               printf ("%s: Error receiving IPv4-side package: %s",
-                               program, strerror (errno));
-               return;
-       }
-       if (buflen < sizeof (struct ip6_hdr)) {
-               return;
-       }
-       if ((v4data [0] & 0xf0) != 0x60) {
-               // Not an IPv6 packet
-               return;
-       }
-       //
-       // Handle the tunneled IPv6 package (dependent on its class)
-       if ((v4v6nexthdr == IPPROTO_ICMPV6) &&
-                       (v4v6icmptype >= 133) && (v4v6icmptype <= 137)) {
-               //
-               // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
-               if (v4v6hoplimit != 255) {
-                       return;
-               }
-               handle_4to6_nd (buflen);
-       } else if (is_mine (v4src6, v4dst6)) {
-               handle_4to6_masquerading (buflen);
-       } else if ((v4dst6->s6_addr [0] != 0xff) && !(v4dst6->s6_addr [8] & 0x01)) {
-               //
-               // Plain Unicast
-               if (is_local_override (v4dst6)) {
-                       handle_4to6_plain_unicast (buflen);
-               } else if (validate_originator (v4src6)) {
-                       if (v4v6hoplimit-- <= 1) {
-                               return;
-                       }
-                       if (is_6bed4 (v4dst6) || is_fc64 (v4dst6)) {
-                               relay_4to4_plain_unicast (v4data, buflen, v4dst6);
-                       } else {
-                               handle_4to6_plain_unicast (buflen);
-                       }
-               } else if (is_6bed4 (v4src6)) {
-                       // The sender must not have kept NAT/firewall holes
-                       // open and should be instructed about a change in
-                       // its 6bed4 Link-Local Address.
-                       handle_6bed4_router_solicit ();
-               }
-       } else {
-               //
-               // Plain Multicast
-               //OPTIONAL// validate_originator, hoplimit, relay_mcast.
-               return;
-       }
-}
-
-
-/* Receive an IPv6 package, check its address and pickup IPv4 address and
- * port, then package it as a tunnel message and forward it to IPv4:port.
- */
-void handle_6to4 (void) {
-       uint16_t mqh;
-       //
-       // Receive the IPv6 package and ensure a consistent size
-       size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
-       if (rawlen == -1) {
-               return;         /* error reading, drop */
-       }
-       if (rawlen < sizeof (struct tun_pi) + sizeof (struct ip6_hdr) + 1) {
-               return;         /* too small, drop */
-       }
-       if (v6tuncmd.proto != htons (ETH_P_IPV6)) {
-               return;         /* no IPv6, drop */
-       }
-       if ((v6nexthdr == IPPROTO_ICMPV6) &&
-                       (v6icmptype >= 133) && (v6icmptype <= 137)) {
-               return;         /* not plain IPv6, drop */
-       }
-       if (v6hoplimit-- <= 1) {
-               // TODO: Send back an ICMPv6 error message
-               return;         /* hop limit exceeded, drop */
-       }
-       if ((v6dst6->s6_addr [0] == 0xff) /* TODO:UDP_PORT_NOT_YET_FORCED_TO_EVEN || (v6dst6->s6_addr [8] & 0x01) */ ) {
-printf ("Received multicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
-               //OPTIONAL// handle_6to4_plain_multicast ()
-               return;         /* multicast, drop */
-       }
-printf ("Received plain unicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
-       //
-       // Replace masqueraded addresses configured with -m and its default ::1
-       //TODO// Better use the port number to find the find the masqhost
-       for (mqh=0; mqh<num_masqhost; mqh++) {
-               if (memcmp (v6src6, masqhost [mqh], 16) == 0) {
-printf ("Masqueraded sender address in 6to4 set to the client's 6bed4router address\n");
-                       uint16_t *csum_field = NULL;
-                       uint16_t fake_csum;
-                       struct in6_addr masq_src;
-                       uint16_t sctp_offset = 0;
-                       switch (v6nexthdr) {
-                       case IPPROTO_SCTP:
-                               csum_field = &fake_csum;
-                               sctp_offset = v6sctpofs;
-                               break;
-                       case IPPROTO_TCP:
-                               csum_field = &v6tcpcksum;
-                               break;
-                       case IPPROTO_UDP:
-                               csum_field = &v6udpcksum;
-                               if ((v6udpsrcport == v6udpdstport) &&
-                                               (v6udpsrcport == htons (9899))) {
-                                       // SCTP tunneled over UDP
-                                       sctp_offset = v6tunsctpofs;
-                               }
-                               break;
-                       case IPPROTO_ICMPV6:
-                               csum_field = &v6icmpcksum;
-                               break;
-                       default:
-                               continue;
-                       }
-                       //
-                       // Construct v6src6 from v6dst6, ending in 14 zero bits
-                       memcpy (&masq_src, v6dst6, 16);
-                       masq_src.s6_addr16 [7] &= htons (0xc000);
-                       fprintf (stderr, "Masquerading cksum.old = 0x%04x, ", ntohs (*csum_field));
-                       masquerade_address (v6src6, &masq_src, csum_field);
-                       if (sctp_offset > 0) {
-                               //TODO// Recompute SCTP's CRC-32 checksum
-                               // recompute_sctp_checksum (ip6hdr, sctp_offset, framelen);
-                               //BUT: SCTP checksum does not use pseudo IPhdr
-                               //SO:  Address masquerading goes undetected :-D
-                       }
-                       fprintf (stderr, "cksum.new = 0x%04x\n", ntohs (*csum_field));
-                       break;
-               }
-       }
-       //
-       // Ensure that the incoming IPv6 address is properly formatted
-       // Note that this avoids access to ::1/128, fe80::/10, fec0::/10
-#ifndef TODO_PERMIT_BOUNCE_FOR_32BIT_PREFIX
-       if (memcmp (v6dst6, &v6listen, 8) != 0) {
-               return;
-       }
-#else
-       if (v6dst6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
-               // Mismatch /32 so this is not going to fly (anywhere)
-               return;
-       } else if (v6dst6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
-               // Match /32 but mismatch /64 -- relay to proper fallback
-               // that fc64::/16 is not welcome in 6to4 processing
-               //TODO//OVER_6bed4// relay_6to6_plain_unicast (v6data, rawlen - sizeof (struct tun_pi), v6dst6);
-       }
-#endif
-       //
-       // Harvest socket address data from destination IPv6, then send
-       relay_4to4_plain_unicast (v6data, rawlen - sizeof (struct tun_pi), v6dst6);
-}
-
-
-/* Run the daemon core code, passing information between IPv4 and IPv6 and
- * responding to tunnel requests if so requested.
- */
-void run_daemon (void) {
-       fd_set io;
-       FD_ZERO (&io);
-       FD_SET (v4sox, &io);
-       FD_SET (v6sox, &io);
-       int nfds = (v4sox < v6sox)? (v6sox + 1): (v4sox + 1);
-       while (1) {
-               select (nfds, &io, NULL, NULL, NULL);
-               if (FD_ISSET (v4sox, &io)) {
-                       handle_4to6 ();
-               } else {
-                       FD_SET (v4sox, &io);
-               }
-               if (FD_ISSET (v6sox, &io)) {
-                       handle_6to4 ();
-               } else {
-                       FD_SET (v6sox, &io);
-               }
-fflush (stdout);
-       }
-}
-
-
-/* Option descriptive data structures */
-
-char *short_opt = "l:L:d:hit:u:s:m:";
-
-struct option long_opt [] = {
-       { "v4listen", 1, NULL, 'l' },
-       { "v6prefix", 1, NULL, 'L' },
-       { "tundev", 1, NULL, 'd' },
-       { "help", 0, NULL, 'h' },
-       { "icmp", 0, NULL, 'i' },
-       { "tcp", 1, NULL, 't' },
-       { "udp", 1, NULL, 'u' },
-       { "sctp", 1, NULL, 's' },
-       { "masqhost", 1, NULL, 'm' },
-       // { "fallback4", 1 NULL, 'f' },
-       // { "fallback6", 1, NULL, 'F' },
-       { NULL, 0, NULL, 0 }    /* Array termination */
-};
-
-/* Parse commandline arguments (and start to process them).
- * Return 1 on success, 0 on failure.
- */
-int process_args (int argc, char *argv []) {
-       int ok = 1;
-       int help = (argc == 1);
-       int done = 0;
-       int opt;
-       while (!done) {
-               switch (opt = getopt_long (argc, argv, short_opt, long_opt, NULL)) {
-               case -1:
-                       done = 1;
-                       if (optind != argc) {
-                               fprintf (stderr, "%s: Extra arguments not permitted: %s...\n", program, argv [optind]);
-                               ok = 0;
-                       }
-                       break;
-               case 'l':
-                       if (v4sox != -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Only one -l argument is permitted\n", program);
-                               break;
-                       }
-                       v4server = optarg;
-                       if (inet_pton (AF_INET, optarg, &v4name.sin_addr) <= 0) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to parse IPv4 address %s\n", program, optarg);
-                               break;
-                       }
-                       memcpy (&v4listen, &v4name.sin_addr, 4);
-                       v4sox = socket (AF_INET, SOCK_DGRAM, 0);
-                       if (v4sox == -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to allocate UDPv4 socket: %s\n", program, strerror (errno));
-                               break;
-                       }
-                       if (bind (v4sox, (struct sockaddr *) &v4name, sizeof (v4name)) != 0) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to bind to UDPv4 %s:%d: %s\n", program, optarg, ntohs (v4name.sin_port), strerror (errno));
-                               break;
-                       }
-                       break;
-               case 'L':
-                       if (v6server) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Only one -L argument is permitted\n", program);
-                               break;
-                       }
-                       char *slash64 = strchr (optarg, '/');
-                       if (!slash64 || strcmp (slash64, "/64") != 0) {
-                               ok = 0;
-                               fprintf (stderr, "%s: The -L argument must be an explicit /64 prefix and not %s\n", program, slash64? slash64: "implied");
-                               break;
-                       }
-                       *slash64 = 0;
-                       v6server = strdup (optarg);
-                       *slash64 = '/';
-                       v6prefix = optarg;
-                       if (!v6server || (inet_pton (AF_INET6, v6server, &v6listen) <= 0)) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to parse IPv6 prefix %s\n", program, optarg);
-                               break;
-                       }
-                       if (v6listen.s6_addr32 [2] || v6listen.s6_addr32 [3]) {
-                               ok = 0;
-                               fprintf (stderr, "%s: IPv6 prefix contains bits beyond its /64 prefix: %s\n", program, optarg);
-                               break;
-                       }
-                       break;
-               case 'd':
-                       if (v6sox != -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Multiple -d arguments are not permitted\n", program);
-                               break;
-                       }
-                       v6sox = open (optarg, O_RDWR);
-                       if (v6sox == -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to open tunnel device %s: %s\n", program, optarg, strerror (errno));
-                               break;
-                       }
-                       break;
-               case 's':
-               case 't':
-               case 'u':
-               case 'i':
-                       // Masqueraded port (range) for SCTP, TCP, UDP
-                       //TODO// Should we support ICMPv6 as well? [honeypots]
-                       if (num_masqhost == 0) {
-                               //TODO// Reference v6listen instead of ::1
-                               inet_pton (AF_INET6, "::1", masqhost [0]);
-                               num_masqhost = 1;
-                       }
-                       // Temporary variables in local scope
-                       if (opt == 'i') {
-                               if (icmp_num_portpairs > 0) {
-                                       fprintf (stderr, "%s: Only one ICMP masquerading setting is possible\n", program);
-                                       ok = 0;
-                                       break;
-                               }
-                               icmp_portpairs [2] = num_masqhost - 1;
-                               icmp_num_portpairs++;
-                       } else {
-                               unsigned long fromport, toport;
-                               uint16_t *portpairs;
-                               errno = 0;
-                               if (*optarg != ':') {
-                                       fromport = strtoul (optarg, &optarg, 10);
-                               } else {
-                                       fromport = 1;
-                               }
-                               if (*optarg != ':') {
-                                       toport = fromport;
-                               } else if (*++optarg) {
-                                       toport = strtoul (optarg, &optarg, 10);
-                               } else {
-                                       toport = 65535;
-                               }
-                               if (errno || *optarg) {
-                                       fprintf (stderr, "%s: Failed to parse port or port:port\n", program);
-                                       ok = 0;
-                                       break;
-                               }
-                               if ((fromport < 1) || (fromport > toport) || (toport > 65535)) {
-                                       fprintf (stderr, "%s: Invalid port or port range\n", program);
-                                       ok = 0;
-                                       break;
-                               }
-                               if (++num_masqportpairs [opt-'s'] >= MAXNUM_PORTPAIRS) {
-                                       fprintf (stderr, "%s: You cannot define so many ports / port pairs\n", program);
-                                       ok = 0;
-                                       break;
-                               }
-                               portpairs = &masqportpairs [opt-'s'][3*(num_masqportpairs [opt-'s']-1)];
-                               portpairs [0] = fromport;
-                               portpairs [1] = toport;
-                               portpairs [2] = num_masqhost-1;
-                       }
-                       break;
-               case 'm':
-                       // Masqueraded host for TCP, UDP, SCTP (default is ::1)
-                       if (++num_masqhost >= MAXNUM_MASQHOST) {
-                               fprintf (stderr, "%s: No more than %d masquering hosts can be specified\n", program, MAXNUM_MASQHOST);
-                               ok = 0;
-                               break;
-                       }
-                       if (inet_pton (AF_INET6, optarg, masqhost [num_masqhost]) != 1) {
-                               fprintf (stderr, "%s: Unsupported masquerading host \"%s\"\n", program, optarg);
-                               ok = 0;
-                               break;
-                       }
-                       num_masqhost++;
-                       break;
-               // case 'f':
-               // case 'F':
-               //      // Fallback addresses for IPv4, IPv6
-               //      ok = 0; //TODO:IMPLEMENT//
-               //      help = 1;
-               //      break;
-               default:
-                       ok = 0;
-                       /* continue into 'h' to produce usage information */
-               case 'h':
-                       help = 1;
-                       break;
-               }
-               if (help || !ok) {
-                       done = 1;
-               }
-       }
-       if (help) {
-#ifdef HAVE_SETUP_TUNNEL
-               fprintf (stderr, "Usage: %s [-d /dev/tunX] -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
-               fprintf (stderr, "\tUse -s|-t|-u to masquerade a port (range) to last -m host or ::1\n");
-#else
-               fprintf (stderr, "Usage: %s -d /dev/tunX -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
-               fprintf (stderr, "\tUse -s|-t|-u to masquerade a port (range) to last -m host of ::1\n");
-#endif
-               return ok;
-       }
-       if (!ok) {
-               return 0;
-       }
-       if (v4sox == -1) {
-               fprintf (stderr, "%s: Use -l to specify an IPv4 address for the tunnel interface\n", program);
-               return 0;
-       }
-       if (!v6server) {
-               fprintf (stderr, "%s: Use -L to specify a /64 prefix on the IPv6 side\n", program);
-               return 0;
-       }
-#ifdef HAVE_SETUP_TUNNEL
-       if (v6sox == -1) {
-               if (geteuid () != 0) {
-                       fprintf (stderr, "%s: You should be root, or use -d to specify an accessible tunnel device\n", program);
-                       return 0;
-               }
-               ok = setup_tunnel ();
-       }
-#else /* ! HAVE_SETUP_TUNNEL */
-       if (v6sox == -1) {
-               fprintf (stderr, "%s: You must specify a tunnel device with -d that is accessible to you\n", program);
-               return 0;
-       }
-#endif /* HAVE_SETUP_TUNNEL */
-       return ok;
-}
-
-
-/* The main program parses commandline arguments and forks off the daemon
- */
-int main (int argc, char *argv []) {
-       //
-       // Initialise
-       program = argv [0];
-       memset (&v4name, 0, sizeof (v4name));
-       memset (&v6name, 0, sizeof (v6name));
-       v4name.sin_family  = AF_INET ;
-       v6name.sin6_family = AF_INET6;
-       v4name.sin_port = htons (UDP_PORT_6BED4);   /* 6BED4 standard port */
-       v4tunpi6.flags = 0;
-       v4tunpi6.proto = htons (ETH_P_IPV6);
-       //
-       // Parse commandline arguments
-       if (!process_args (argc, argv)) {
-               exit (1);
-       }
-       //
-       // Setup a few addresses for later comparison/reproduction
-       //  * lladdr_6bed4 is the 6bed4 Link-Local Address
-       //  * v6listen_complete is the router's full IPv6 address (with if-id)
-       //  * v6listen_linklocal_complete is fe80::/64 plus the router's if-id
-       // A few others have already been setup at this time
-       //  * v6listen is the router's 6bed4 prefix ending in 64 zero bits
-       //  * v6listen_linklocal is the address fe80::/128
-       //
-       lladdr_6bed4 [0] = UDP_PORT_6BED4 & 0xff;
-       lladdr_6bed4 [1] = UDP_PORT_6BED4 >> 8;
-       memcpy (lladdr_6bed4 + 2, (uint8_t *) &v4name.sin_addr, 4);
-       addr_6bed4 (&v6listen_complete, 0);
-
-       memcpy (v6listen_linklocal_complete, v6listen_linklocal, 8);
-       memcpy (v6listen_linklocal_complete + 8, &v6listen_complete.s6_addr [8], 8);
-printf ("LISTEN lladdr_6bed4 = %02x:%02x:%02x:%02x:%02x:%02x\n", lladdr_6bed4 [0], lladdr_6bed4 [1], lladdr_6bed4 [2], lladdr_6bed4 [3], lladdr_6bed4 [4], lladdr_6bed4 [5]);
-printf ("LISTEN v6listen = %x:%x:%x:%x:%x:%x:%x:%x\n", htons (v6listen.s6_addr16 [0]), htons (v6listen.s6_addr16 [1]), htons (v6listen.s6_addr16 [2]), htons (v6listen.s6_addr16 [3]), htons (v6listen.s6_addr16 [4]), htons (v6listen.s6_addr16 [5]), htons (v6listen.s6_addr16 [6]), htons (v6listen.s6_addr16 [7]));
-printf ("LISTEN v6listen_complete = %x:%x:%x:%x:%x:%x:%x:%x\n", htons (v6listen_complete.s6_addr16 [0]), htons (v6listen_complete.s6_addr16 [1]), htons (v6listen_complete.s6_addr16 [2]), htons (v6listen_complete.s6_addr16 [3]), htons (v6listen_complete.s6_addr16 [4]), htons (v6listen_complete.s6_addr16 [5]), htons (v6listen_complete.s6_addr16 [6]), htons (v6listen_complete.s6_addr16 [7]));
-printf ("LISTEN v6listen_linklocal = %x:%x:%x:%x:%x:%x:%x:%x\n", htons (((uint16_t *) v6listen_linklocal) [0]), htons (((uint16_t *) v6listen_linklocal) [1]), htons (((uint16_t *) v6listen_linklocal) [2]), htons (((uint16_t *) v6listen_linklocal) [3]), htons (((uint16_t *) v6listen_linklocal) [4]), htons (((uint16_t *) v6listen_linklocal) [5]), htons (((uint16_t *) v6listen_linklocal) [6]), htons (((uint16_t *) v6listen_linklocal) [7]));
-printf ("LISTEN v6listen_linklocal_complete = %x:%x:%x:%x:%x:%x:%x:%x\n", htons (((uint16_t *) v6listen_linklocal_complete) [0]), htons (((uint16_t *) v6listen_linklocal_complete) [1]), htons (((uint16_t *) v6listen_linklocal_complete) [2]), htons (((uint16_t *) v6listen_linklocal_complete) [3]), htons (((uint16_t *) v6listen_linklocal_complete) [4]), htons (((uint16_t *) v6listen_linklocal_complete) [5]), htons (((uint16_t *) v6listen_linklocal_complete) [6]), htons (((uint16_t *) v6listen_linklocal_complete) [7]));
-       //
-       // Start the main daemon process
-#ifdef SKIP_TESTING_KLUDGE_IN_FOREGROUND
-       switch (fork ()) {
-       case -1:                /* Error forking */
-               fprintf (stderr, "%s: Failed to fork: %s\n", program, strerror (errno));
-               exit (1);
-       case 0:                 /* Child process */
-               close (0);
-               //TODO: tmp.support for ^printf// close (1);
-               close (2);
-               setsid ();
-               run_daemon ();
-               break;
-       default:                /* Parent process */
-               close (v4sox);
-               close (v6sox);
-               break;
-       }
-#else
-       run_daemon ();
-#endif
-       //
-       // Report successful creation of the daemon
-       return 0;
-}
diff --git a/6bed4router.man b/6bed4router.man
deleted file mode 100644 (file)
index db939a7..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-.TH 6BED4 8 "Februari 1, 2011"
-.\" Please adjust this date whenever revising the manpage.
-.\"
-.\" Some roff macros, for reference:
-.\" .nh        disable hyphenation
-.\" .hy        enable hyphenation
-.\" .ad l      left justify
-.\" .ad b      justify to both left and right margins
-.\" .nf        disable filling
-.\" .fi        enable filling
-.\" .br        insert line break
-.\" .sp <n>    insert n+1 empty lines
-.\" for manpage-specific macros, see man(7)
-.SH NAME
-6bed4router \- server-side daemon for 6bed4 service
-.SH SYNOPSYS
-.B 6bed4router
-[\fB\-d\fR \fI/dev/tunX\fR] \fB\-l\fR \fIv4addr\fR \fB\-L\fR \fIv6prefix/64\fR [\fB\-m\fR/\fB\-i\fR/\fBs\fR/\fBt\fR/\fBu\fR \fI...\fR]
-.PP
-.B 6bed4router
-[\fB\-h\fR]
-.SH DESCRIPTION
-.PP
-Through a \fB6bed4router\fR daemon, it is possible to make a small range of IPv6
-addresses available to a potentially large number of IPv4-only users.  This means that even an IPv4-only
-network can host IPv6-only applications, as long as they can fall back on
-a tunnel based on this profile.
-.PP
-Use the \fB6bed4router\fR on nodes that support \fB6bed4peer\fR clients
-anywhere; instead, use the \fB6bed4node\fR on nodes that want to provide
-native IPv6 on a local network.
-.PP
-These tunnels are intended for mobile and embedded devices, to assist them
-in instant changeover from being IPv4-only to eventually being IPv6-only.
-Given the
-restricted resources in embedded applications, this is likely to improve
-the speed of transitioning to IPv6.  To avoid cluttered access, these
-tunnels should be reserved for resource-hampered devices.  For routers,
-transitioning mechanisms like 6to4 and 6in4 are more suitable.  For
-desktops, tunnelling routers or a direct link to a subscription tunnel
-service is a better solution.
-.PP
-The NAT traversing aspect of this profile is needed to support the normal
-openness of IPv6 connections.  This is achieved by packing IPv6 into UDP
-and then into IPv4.  This is a guaranteed manner of traversing NAT,
-provided that this daemon listens to a public IPv4 address.  The daemon
-currently does not support any other modes of operation.
-.PP
-The public-use aspect of this profile means that there is no requirement for
-clients to register.  However, this does not mean that the use of \fB6bed4router\fR
-makes it possible to browse IPv6 networks anonymously; in order to
-ensure traceability of abusive behaviour, the IPv4 address and UDP port
-of the tunnel client are mentioned in the IPv6 address.  See ADDRESS FORMAT
-below for details, and SECURITY CHECKS for further precautions against abuse.
-.PP
-The intended application of clients under this transitionary mechanism are
-IPv6-only devices that need a transport over an IPv4-only network.  The
-reason for defining IPv6-only device can be simplicity, or making IPv6
-more efficient and/or more useful.  To avoid making IPv6 a breaking
-requirement in roll-outs, devices for such an IPv6-only service could
-implement this mechanism to provide a simple backward-compatible mode for
-IPv4-only network nodes, without impairing all advantages of being IPv6-only.
-.PP
-This daemon is in fact a stateless translation between an IPv4 client
-and the general IPv6 world; to configure an IPv6 address on the tunnel
-client side it will perform autoconfiguration over the tunnel.  The
-assigned prefix will be a /112, with 16 bits of interface identifiers
-to fill in.  The interface identifier 0 is reserved for the router,
-or in other words, the tunnel server.
-.PP
-The tunnel server implementation listens to a UDP socket on port 3653
-on the IPv4 side, and to a
-tunnel endpoint to capture all traffic matching an IPv6 /64 prefix.
-It basically tosses traffic back and forth between these interfaces,
-but not without performing the SECURITY CHECKS desribed below
-on what is tossed at it.
-.SH OPTIONS
-.TP
-\fB\-d\fR \fI/dev/tunX\fR
-.TP
-\fB\-\-tundev\fR \fI/dev/tunX\fR
-Instead of creating a tunnel for the duration that \fB6bed4router\fR runs,
-use one that already exists and that has already been setup with
-the proper IPv6 prefix.  This option makes it possible for
-non-root users to run \fB6bed4router\fR.  All that is required is acccess to
-the tunnel device by the user that runs \fB6bed4router\fR.  Optional on Linux.
-.TP
-\fB\-l\fR \fIv4addr\fR
-.TP
-\fB\-\-v4listen\fR \fIv4addr\fR
-Bind to the given local IPv4 address and listen for incoming IPv6
-neighbour discovery packages as well as general IPv6 traffic.  Required.
-.TP
-\fB\-L\fR \fIv6prefix/64\fR
-.TP
-\fB\-\-v6prefix\fR \fIv6prefix/64\fR
-Bind to the given IPv6 address range through a tunnel device, and
-forward incoming IPv6 messages to IPv4-based UDP tunnel endpoints.
-See ADDRESS FORMAT below for an explanation of the lower half of
-the IPv6 addresses.  Required.
-.IP
-If no \fB\-d\fR option is given, a tunnel will be created for the time that
-\fB6bed4router\fR is running, and the \fIv6prefix/64\fR is used as a router address
-on that interface.  Routing table entries will not be setup by \fB6bed4router\fR,
-nor will the general ablity to forward IPv6 traffic.
-.TP
-\fB\-m\fR \fIv6addr\fR
-.TP
-\fB\-\-masqhost\fR \fIv6addr\fR
-Use the given IPv6 address as the target for masqueraded operation.  This
-means that any ports setup with \fB\-\-sctp\fR, \fB\-\-tcp\fR or \fB\-\-udp\fR
-are forwarded
-to this address when addressed on the client's idea of the 6bed4 router
-address.  Note that the router only uses a few ICMPv6 messages, so the use
-of these additional ports can benefit various applications, ranging from
-authentication services to honeypots.
-.TP
-\fB\-s\fR \fIport\fR[\fB:\fIport\fR]
-.TP
-\fB\-t\fR \fIport\fR[\fB:\fIport\fR]
-.TP
-\fB\-u\fR \fIport\fR[\fB:\fIport\fR]
-.TP
-\fB\-sctp\fR \fIport\fR[\fB:\fIport\fR]
-.TP
-\fB\-tcp\fR \fIport\fR[\fB:\fIport\fR]
-.TP
-\fB\-udp\fR \fIport\fR[\fB:\fIport\fR]
-Apply the masquerading port or port range for SCTP, TCP or UDP to the
-masquerading host set by the last preceding \fB\-\-masqhost\fR option,
-or use the tunnel's IPv6-side address
-if none was set yet.  Ports that have been previously bound will not make it,
-so it is possible to first relay a port with \fB\-t 22\fR to a service and
-then pass the rest to a honeypot with \fB\-t 1:65535\fR.
-.TP
-\fB\-i\fR
-Apply masquerading for ICMPv6 to the masquerading host set by the last
-preceding \fB\-\-masqhost\fR option, or use the tunnel's IPv6-side address
-if none was set yet.  The ICMPv6 messages with a special meaning to 6bed4
-are exempted (Router and Neighbor Solicitation and Advertisement, as well
-as Redirect) but other messages will be masqueraded.
-.TP
-\fB\-h\fR
-.TP
-\fB\-\-help\fR
-Print usage information and exit.
-.SH ADDRESS FORMAT
-.PP
-An IPv6 address used from \fB6bed4router\fR reveals the IPv4 address and UDP port
-used by the tunnel endpoint.  This format is checked on sending from
-the IPv4 tunnel to IPv6, and used to reconstruct the IPv4 tunnel access
-information for traffic from IPv6 to the IPv4 tunnel.
-.PP
-The format of the IPv6 addresses managed by \fB6bed4router\fR are:
-.PP
-\fIv6prefix\fR + \fIv4addr\fR + \fIudp-port\fR + \fIinterfaceidentifier\fR
-.PP
-In this format, the \fIv6prefix\fR is configured with the \fB\-L\fR option,
-and the \fIv4addr\fR with the \fB\-l\fR option.  The \fIudp-port\fR is noted on
-arrival of a packet on the IPv4 tunnel side of \fB6bed4router\fR.
-.PP
-The \fIinterfaceidentifier\fR is always 0 on the router side, and may be set
-to other values to distinguish 65,535 different client addresses.  As
-the main application foreseen for \fB6bed4router\fR is to get IPv6-only tools and
-devices working on an IPv4-only network, it is very likely that the clients
-will pick a fixed \fIinterfaceidentifier\fR such as 1 and hard-code it.
-.PP
-Due to the IPv6 practice of assigning link-local names composed of \fBfe80::\fR
-and the \fIinterfaceidentifier\fR, the router-side of a tunnel can always
-be addressed as \fBfe80::0\fR and clients can be found at addresses ranging
-from \fBfe80::1\fR to \fBfe80::ffff\fR.
-.PP
-Incoming IPv6 traffic destined for a serviced address is first checked
-as specified under SECURITY CHECKS, and then forwarded to \fIudp-port\fR at
-\fIv4addr\fR.  In doing so, the IPv6 packet is embedded in whole inside
-the UDP packet.  The IPv6 addresses are not altered, but only used
-to derive IPv4 contact information.
-.PP
-Outgoing IPv6 traffic arriving on the IPv4 tunnel side of \fB6bed4router\fR will
-be checked to have been sent from the right \fIv6prefix\fR and mention
-the \fIv4addr\fR and \fIudp-port\fR matching the client's public side.  That
-is, NAT may translate the IPv4 address and UDP port used, but these
-parts of the IPv6 address should show how it is forwarded to \fB6bed4router\fR.
-Note that autonegotiation protocol provides this necessary information at the
-time the \fB6bed4router\fR daemon starts.  If the NAT mapping changes during the uptime
-of the tunnel, a new Router Advertisement is sent from tunnel server to
-client, to notify it of the new prefix to use.  The original message is
-then discarded.
-.PP
-If it is desired to keep the same IPv6 address for longer periods, it
-is recommended that the client keeps NAT state intact by regularly
-sending over the UDP port to the tunnel endpoint.  For example, a regular
-ping could do that.  Alternatively, a client-mode only daemon could
-ensure that it is sending regularly during the times that an outside
-party might wish to send to it.  This is under the assumption that no
-explicit mapping in NAT overtakes this responsibility of an active
-mapping between the internal and external address space.
-.SH SECURITY CHECKS
-.PP
-Not everything will be passed through \fB6bed4router\fR, even if this would be
-technically possible.  A few security checks are applied to silently
-drop traffic that looks evil.
-.PP
-Packets should be long enough to at least contain the IPv6 traffic
-and a minimal payload size.  Also, it should not exceed a predefined
-MTU of 1280 bytes for IPv6.
-.PP
-IPv6 traffic uploaded through the IPv4 side should reveal the proper
-IPv4 settings in the IPv6 source address, as specified under
-ADDRESS FORMAT above.  This is basically the tunnel aspect of egress
-filtering.
-.PP
-Tunnel commands should adhere to the format of RFC 5722 and may not
-contain any NUL characters.
-.SH BUGS
-Currently, \fB6bed4router\fR does not use ICMP notifications at the IPv4
-level to provide smart feedback to an IPv6 client.  It is undecided
-at this point if this would add value.
-.PP
-To be able to fallback to this TSP profile, an IPv6-only application
-needs to find a \fB6bed4router\fR or similar service.  A general naming
-or numbering scheme is needed to make that straightforward.  The
-\fB6bed4router\fR service could be setup privately and configured in
-individual IPv6-only nodes, but it could accelerate the introduction
-of IPv6-only nodes if this were organised by network providers.
-.PP
-Ideally, \fB6bed4router\fR would be near all heavily connected nodes
-of the Internet.  There, they would improve connectivity without
-being a detour for the traffic.  Alternatively, it would be located
-in various uplinks.  To optimise routing, it is possible to assign
-a fixed IPv4 address and IPv6 prefix for \fB6bed4router\fR running
-anywhere; its stateless operation means that traffic going back and
-forth can go through different instances of \fB6bed4router\fR without
-posing problems.
-.PP
-The \fB6bed4router\fR daemon is a piece of highly efficient code,
-and it should be able to handle very high bandwidths.  A stress
-test has not been conducted yet.
-.PP
-This daemon does not pass on QoS headers as it should according to the
-specification.
-.SH LICENSE
-Released under a BSD-style license without advertisement clause.
-.SH SEE ALSO
-The 0cpm project is an example of an IPv6-only SIP application
-that can use \fB6bed4router\fR and comparable TSP tunnel services to
-demonstrate the advantages of IPv6 to end users.  It is also
-a typical example of a transitionary need for something like
-\fB6bed4router\fR.
-.PP
-http://0cpm.org/ \- the homepage of the 0cpm project.
-.PP
-http://devel.0cpm.org/6bed4/ \- the homepage of \fB6bed4\fR.
-.PP
-RFC 5722 \- the authoritative description of TSP, of which \fB6bed4\fR
-implements a specific profile for public service under NAT traversal.
-.SH AUTHOR
-\fB6bed4router\fR was written by Rick van Rein from OpenFortress.
-It was created to support the 0cpm project.
index db48711..ca67fd5 100644 (file)
-PROJECT(6bed4 C)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
-SET(VERSION "0.0.1")
+project (6bed4 C)
+cmake_minimum_required (VERSION 2.6)
+set (VERSION "0.0.1")
+cmake_policy(SET CMP0053 NEW)
 
-ADD_DEFINITIONS(-DLINUX)
+add_definitions (-DLINUX)
 
-# maybe they should be renamed to 6bed4client and 6bed4server
-# or something like that...
-ADD_EXECUTABLE(6bed4client "client.c")
-ADD_EXECUTABLE(6bed4server "router.c")
+add_executable (6bed4client "src/6bed4peer.c")
+add_executable (6bed4server "src/6bed4router.c")
 
-INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/6bed4client DESTINATION sbin)
-INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/6bed4server DESTINATION sbin)
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/6bed4server.man DESTINATION ${SHARE_INSTALL_PREFIX}/man/man8)
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/6bed4client.man DESTINATION ${SHARE_INSTALL_PREFIX}/man/man8)
+install (PROGRAMS
+       ${CMAKE_BINARY_DIR}/6bed4client
+       ${CMAKE_BINARY_DIR}/6bed4server
+       DESTINATION sbin)
+install (FILES
+       ${CMAKE_SOURCE_DIR}/doc/man/6bed4server.man
+       ${CMAKE_SOURCE_DIR}/doc/man/6bed4client.man
+       DESTINATION ${SHARE_INSTALL_PREFIX}/man/man8)
 
-INCLUDE(CPack)
+
+#
+# Setup a number of parameters.  Until we get 6bed4 standardised,
+# these are preliminary values that MUST NOT be assumed static.
+#
+# This is why CMake will present a warning, but it will still
+# go ahead with the values.  They can be changed at will using
+# any tool that interacts with the CMakeCache.txt file or that
+# overrides the settings at build time.
+#
+# Please beware that the values contain the information in a
+# number of different forms.  In a perfect world, we would
+# derive values from one another.  In a portable world, for
+# now at least, we resort to letting you maintain consistency.
+#
+
+if (NOT ND_OPT_DESTINATION_LINKADDR)
+       message ("Stealing an experimental Neighbor Discovery Option Type 253 for Destination Link-Layer Address -- please configure your local override")
+       set (ND_OPT_DESTINATION_LINKADDR 253
+               CACHE STRING "Neighbor Discovery Option Type for Destination Link-Layer Address; Configure an unused value until this is standardised"
+       )
+else ()
+       message (WARNING
+               "Using experimental Neighbour Discovery Option Type ${ND_OPT_DESTINATION_LINKADDR}")
+endif ()
+
+if (NOT UDP_PORT_6BED4)
+       message ("Stealing an experimental UDP port number 25790 or 0x64be -- please configure your local override")
+       set (UDP_PORT_6BED4 25790
+               CACHE STRING "Decimal UDP Port to use in this 6bed4 setup; Must be consistent with other settings that mention it in different forms; Configure an unused value until this is standardised"
+       )
+       set (SERVER_6BED4_PORT_TXT "\"25790\""
+               CACHE STRING "Decimal string representation of the UDP Port to use; Must be consistent with UDP_PORT_6BED4"
+       )
+       set (SERVER_6BED4_PORT_BINSTR "\"\\xbe\\x64\""
+               CACHE STRING "Binary string representation of the UDP Port to use; Unsigned 16 bits in network byte order; Must be consistent with UDP_PORT_6BED4"
+       )
+else ()
+       message (WARNING
+               "Using experimental UDP port number ${UDP_PORT_6BED4}")
+endif ()
+
+if (NOT SERVER_6BED4_IPV4_TXT)
+       message ("Stealing an expermental 6bed4 server IP address 145.136.0.1 -- please configure your local override")
+       set (SERVER_6BED4_IPV4_TXT "\"145.136.0.1\""
+               CACHE STRING "String representation of decimal IP address of the 6bed4 server to use; Must be consistent with other settings that mention it in different forms"
+       )
+       set (SERVER_6BED4_IPV4_INT32 252183167
+               CACHE STRING "Unsinged 32-bit integer form of the IP address of the 6bed4 server; Higher bits are for coarser routing; Must be consistent with SERVER_6BED4_IPV4_TXT"
+       )
+       set (SERVER_6BED4_IPV4_INT0 145
+               CACHE STRING "Decimal form of the byte 1/4 of the IP address of the 6bed4 server; Must be consistent with SERVER_6BED4_IPV4_TXT"
+       )
+       set (SERVER_6BED4_IPV4_INT1 136
+               CACHE STRING "Decimal form of the byte 2/4 of the IP address of the 6bed4 server; Must be consistent with SERVER_6BED4_IPV4_TXT"
+       )
+       set (SERVER_6BED4_IPV4_INT2 0
+               CACHE STRING "Decimal form of the byte 3/4 of the IP address of the 6bed4 server; Must be consistent with SERVER_6BED4_IPV4_TXT"
+       )
+       set (SERVER_6BED4_IPV4_INT3 1
+               CACHE STRING "Decimal form of the byte 4/4 of the IP address of the 6bed4 server; Must be consistent with SERVER_6BED4_IPV4_TXT"
+       )
+       set (SERVER_6BED4_IPV4_BINSTR "\"\\x91\\x88\\x00\\x01\""
+               CACHE STRING "Binary string representation of the IP address of the 6bed4 server; Must be consistent with SERVER_6BED4_IPV4_TXT"
+       )
+else ()
+       message (WARNING
+               "Using experimental server IP address ${SERVER_6BED4_IPV4_TXT}")
+endif ()
+
+set (SERVER_6BED4_IPV4_PORT_TXT "\"145.136.0.1:25790\""
+       CACHE STRING "Textual representation of the 6bed4 server IPv4 address and port number; Must be consistent with SERVER_6BED4_IPV4_TXT and UDP_PORT_6BED4"
+)
+set (SERVER_6BED4_PORT_IPV4_ADDRSTR "\"\\xbe\\x64\\x91\\x88\\x00\\x01\""
+       CACHE STRING "Binary string representation of the 6bed4 server port number followed by the IPv4 address; Must be the concatenation of SERVER_6BED4_PORT_BINSTR and SERVER_6BED4_IPV4_BINSTR"
+)
+set (SERVER_6BED4_PORT_IPV4_MACSTR "\"\\xbc\\x64\\x91\\x88\\x00\\x01\""
+       CACHE STRING "Binary string representation of the 6bed4 server port number and IPv4 address as a MAC address; Must be equal to SERVER_6BED4_PORT_IPV4_ADDRSTR except for an applied XOR with 0x02 on the first byte"
+)
+
+set (LOCAL_OVERRIDES_PORT0 yes)
+
+set (INTERFACE_NAME_6BED4 "6bed4")
+
+
+configure_file (
+       ${CMAKE_SOURCE_DIR}/src/nonstd.h.in
+       ${CMAKE_BINARY_DIR}/src/nonstd.h
+       IMMEDIATE @ONLY
+)
+
+include_directories (${CMAKE_BINARY_DIR}/src)
+
+
+#
+# Packaging
+#
+include (CPack)
diff --git a/configure b/configure
deleted file mode 100755 (executable)
index 39e8476..0000000
--- a/configure
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-echo I am too old for autotools -- perhaps someone is interesting in putting it in?
-
-if [ -r Makefile.in ]
-then
-       echo 'Writing Makefile...'
-       cp Makefile.in Makefile
-fi
-
-if [ -r nonstd.h.in ]
-then
-       echo 'Writing nonstd.h (with mindlessness exclusion)'
-       cp nonstd.h.in nonstd.h
-fi
-
diff --git a/doc/man/6bed4peer.man b/doc/man/6bed4peer.man
new file mode 100644 (file)
index 0000000..d58807a
--- /dev/null
@@ -0,0 +1,258 @@
+.TH 6BED4CLIENT 8 "Februari 1, 2011"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+6bed4client \- client-side daemon for instant-on IPv6 service
+.SH SYNOPSYS
+.B 6bed4client
+[\fB\-d\fR \fI/dev/tunX\fR] [\fB\-r\fR] [\fB\-f\fR] [\fB\-l\fR \fIv4addr\fR] [\fB\-p\fR \fIport\fR] [\fB\-t\fR \fIhops\fR] [\fB-s \fIv4addr\fR]
+.PP
+.B 6bed4client
+[\fB\-h\fR]
+.SH DESCRIPTION
+.PP
+The \fB6bed4client\fR creates an instant-on, zero-config IPv6
+communication facility.  It is designed to work behind NAT and
+firewalls, and to find the shortest possible route to a communications
+peer.
+.PP
+The command usually works through a 6bed4 interface, often a tunnel,
+through which commands are passed to this daemon, which encapsulates
+the traffic into UDP and IPv4 before sending it.  Return UDP/IPv4
+traffic is decapsulated and offered through the 6bed4 interface.
+.SH OPTIMAL ROUTING
+The \fB6bed4client\fR goes through lengths to achieve optimal routing
+for all packets.  The existence of a public server ensures that
+IPv6 connections are always possible, but anything more direct is
+of course better.
+.PP
+Note that the structure of a 6bed4 IPv6 address is such that it
+reveals a host's public IPv4 address and an external UDP port used
+for the 6bed4 tunneling protocol.  This information can be used to
+derive both local addressing information, as well as remote.  This
+will only work for addresses that start with the standard prefix
+under which 6bed4 addresses are created.
+.PP
+If traffic is intended for the same public IPv4 address as the local
+node, then it is likely to be a host on the same local network.  In
+such cases, a Neighbor Solicitation is sent to the IPv4 all-hosts multicast
+address in an attempt to find a direct route on the LAN.  This may not
+always work, for instance in the presence of subnets without multicast
+forwarding between their segments.
+.PP
+More generally though, a remote peer has an IPv4 address and a UDP
+port over which it once commenced 6bed4 towards the public server,
+to obtain its IPv6 address.  In an attempt to find a direct route,
+the \fB6bed4client\fR will try to find a direct route to that
+endpoint.  If it succeeds to send a Neighbor Solicitation and
+receives back a Neighbor Advertisement, it has established a direct
+channel for IPv6 communications, and it can continue to use that
+instead of going through the public server.
+.PP
+Direct connections to an IPv4/UDP address will only fail if the
+remote system is behind symmetric NAT or a similar firewall.  In
+this case, an initiative from that remote system to contact the
+local system may still succeed, and give rise to a seconde attempt
+towards the remote peer, which should then succeed.  Only if both
+local and remote peers use symmetric NAT, will it be necessary
+to continue to communicate through the public 6bed4 server.
+.PP
+In general, local network traffic is preferred over anything
+else.  Second-best is direct traffic to a public IPv4/UDP address,
+and the public 6bed4 server would be the last resort.
+.SH SPECIAL CASES
+A system with access to native IPv6 can still use 6bed4, although
+it would not want to setup a default route over it.  The use of
+doing this is twofold: At first it unloads the public server from
+having to make the connection, and secondly it makes the connection
+between the local and remote host as direct as is possible over
+IPv4.  The mixed setup of native IPv6 and 6bed4 will not lead to
+any trouble, as 6bed4 traffic is easily recognised by the target
+address prefix, and the interface is setup to handle this.
+.PP
+It is possible to allocate a fixed 6bed4 address for a server, and
+publish it in DNS.  This would be as constant as the IPv4 address
+and UDP port assigned to the \fB6bed4client\fR, but most NAT and
+firewalls support port forwarding; the \fB\-p\fR option on the client
+can be used to support reception of incoming 6bed4 traffic on the
+forwarded port.
+.SH OPTIONS
+.TP
+\fB\-d\fR \fI/dev/tunX\fR
+.TP
+\fB\-\-tundev\fR \fI/dev/tunX\fR
+Instead of creating a tunnel for the duration that \fB6bed4server\fR runs,
+use one that already exists and that has already been setup with
+the proper IPv6 prefix.  This option makes it possible for
+non-root users to run \fB6bed4server\fR.  All that is required is acccess to
+the tunnel device by the user that runs \fB6bed4server\fR.  Optional on Linux.
+.TP
+\fB\-r\fR
+.TP
+\fB\-\-default\-route\fR
+Create a default route through the 6bed4 interface.  This means that the
+entire IPv6 Internet can be accessed through the 6bed4 interface.  This is
+not setup by default, as 6bed4 might also be used as an add-on interface
+that connects more directly to other 6bed4 hosts.
+.TP
+\fB\-l\fR \fIv4addr\fR
+.TP
+\fB\-\-listen\fR \fIv4addr\fR
+Listen for 6bed4 messages on the specified IPv4 address.  This will also
+be the address from which the traffic is sent.  This setting may be
+used together with \fB\-p\fR to control the daemon's behaviour such that
+it can be the target of a port forwarding setup in NAT or firewall.
+.TP
+\fB\-p\fR \fIport\fR
+.TP
+\fB\-\-port\fR \fIport\fR
+Let the 6bed4 daemon listen to the given UDP port.  This will also be
+the port from which the traffic is sent.  This setting may be used
+together with \fB\-l\fR to control the daemon's behaviour such that it
+can be the target of a port forwarding setup in NAT or firewall.
+.TP
+\fB\-s\fR
+.TP
+\fB\-\-v4server\fR \fIv4addr\fR
+Use the given IPv4 address as the fallback 6bed4 server instead of the
+default public server.  This is an experimental facility; it may lead to
+network inefficiencies or instabilities if the public server address cannot
+be found by comparison.  Use with caution, and please report any problems
+that you run into when using this option.  Do not assume this feature will
+always be present.
+.TP
+\fB\-f\fR
+.TP
+\fB\-\-foreground\fR
+.TP
+\fB\-\-fork\-not\fR
+Do not fork to the background.  Instead, stay on the foreground and listen
+to break signals.  This is primarily useful for testing, including while
+rolling out 6bed4 on a site.
+.TP
+\fB\-e\fR
+.TP
+\fB\-\-error\-console\fR
+Normally, debugging messages are sent tot syslog.  With this setting, the
+messages are instead printed to the console.
+On systems supporting the non-POSIX syslog extension LOG_PERROR, the output will be sent to stderr.
+On systems without that extension, the system console is used.
+.TP
+\fB\-k\fR \fItime\fR[,\fIreach\fR]
+.TP
+\fB\-\-keepalive \fItime\fR[,\fIreach\fR]
+This setting defines the keepalives sent to the Public 6bed4 Service.
+The \fItime\fR indicates the number of seconds between keepalives, the
+\fIreach\fR indicates the TTL to use on the traffic where supported;
+it is only needed to get outside NAT and firewalls, but not to reach
+the central infrastructure.  The default is \fB\-\-keepalive 30,3\fR
+and may be automatically determined in future versions.
+.TP
+\fB\-t\fR \fIhops\fR
+.TP
+\fB\-\-ttl\fR \fIhops\fR
+Set the multicast radius to the given number of hops, the default being 1.
+This number is used as the TTL on multicast messages, thereby determining
+whether routers are permitted to forward these packets.  The value 0
+indicates that no multicast should be used.  Values above 1 run the risk
+of deregulating the performance of 6bed4 on an unsuitable network, please
+read \fBLOCAL NETWORKING\fR below for details.
+.TP
+\fB\-u\fI
+.TP
+\fB\-\-udp-variability\fR
+TODO - argue, and maybe implement.
+Accept variations in remote UDP ports when comparing 6bed4 address with
+each other, or with an IPv6 address.  This reduces the security of the
+tunnel mechanism by permitting different processes, and possibly different
+users, on the remote end to take over from each other.  It also means that
+remote symmetric routers stand a chance of contacting this node over direct
+peering traffic.  This option is not helpful if the local node is a
+symmetric router; and if both peers run a symmetric router then there is
+never going to be direct traffic between the peers.
+.PP
+This option sets up support for remote peers that run a NAT router that is
+inherently unsuitable for peer-to-peer traffic.  The default setup is not
+kind to those routers, but it sends a much clearer signal about the origin
+of the problems, namely at the symmetric NAT router.  Being able to
+pinpoint the cause of a problem is probably more helpful than trying to
+deal with a few situations but fail on certain connections, where each
+end concludes that the other end must be at fault because direct connections
+only fail with that other party.
+.SH LOCAL NETWORKING
+Whenever possible, 6bed4 connections are connected directly over the locally
+attached network.  This optimises the traffic by not passing it through an
+external router.  But it also implies trust in the peers on a local network;
+for this reason, it is possible to set \fB\-\-ttl 0\fR and thereby
+disable the attempts to find peers locally.
+.PP
+The mechanism used to find peers locally is through multicast.  It is
+assumed that all hosts that can be reached over multicast can also be
+reached over unicast, given that their direct address were known.  The
+response to a multicast query through Neighbor Solicitation is a unicast
+response through Neighbor Advertisement, in both cases encapsulated in
+UDP and IPv4.
+.PP
+The default setting \fB\-\-ttl 1\fR works only on locally attached
+subnets.  This is generally safe, as this network is normally unfiltered.
+In places where filtering is applied within a subnet, the administrative
+staff should be prepared to stop confusion of network nodes; in case of
+6bed4, this means setting \fB\-\-ttl 0\fR to avoid relying on an open
+locally attached subnet.  This setting implies that the daemon does not
+listen for incoming queries over multicast.  The standards specify that
+multicast support is optional, so this does not break any standards.
+.PP
+Settings of \fB\-\-ttl 2\fR and beyond are more dangerous; it could
+lead to asymmetric routes if not properly configured on a network.  The
+problem of asymmetric routes being that one half might go through a
+hole in NAT, which closes when traffic does not flow through bidirectionally.
+The daemon goes through lengths to avoid this situation, and to that end it
+may generate Neighbor Solicitations and Redirects in response to every
+packet exchanged.  If you see this pattern, you almost certainly have an
+asymmetric routing situation.
+.PP
+To avoid asymmetric routes, all nodes should be able to find each other
+through multicast in both directions; if A can find B, then B should be
+able to find A.  Plain 6bed4 traffic should be able to pass in both
+directions as well as multicast traffic.  Note that multicast traffic is
+always sent to default UDP port 25788, but unicast traffic may be sent
+to any UDP port.  These additional requirements are the reason why the
+default settings are limited to the locally attached subnets.
+.SH BUGS
+This daemon does not pass on QoS headers as it should according to the
+specification.
+.PP
+The daemon needs to access the neighbor cache to be able to compare routes
+in both directions and ensure their symmetry.  It does this by accessing
+the AF_NETLINK(7) interface, more specifically NETLINK_ROUTE(7).  This
+introduces a number of potential problems.
+.PP
+First, the AF_NETLINK/NETLINK_ROUTE facility may limit portability to non-Linux platforms.
+The AF_NETLINK is the closest bet to a standard approach, and similar
+constructions exist on other platforms, so there may be no problem in
+reality.
+.PP
+Second, the AF_NETLINK/NETLINK_ROUTE documentation is incomplete, and unclear at some
+points.  This means that the current code may not work on all platforms;
+notably, the proper use of macros is insufficiently documented to support
+reliable porting to other platforms and newer kernel versions.  Another
+point of concern is whether message breakdown into partial messages has
+been covered accurately, as that process also has not been specified fully.
+.PP
+Thirdly, AF_NETLINK/NETLINK_ROUTE queries are not cached.  Every Neighbor Discovery
+that is accepted from a remote origin will trigger the process of
+comparing routes.  This may lead to scaling problems on very active
+nodes with lots of peers to communicate with simultaneously.
+.SH AUTHOR
+\fB6bed4client\fR was written by Rick van Rein from OpenFortress.
+It was created to support the 0cpm project.
diff --git a/doc/man/6bed4router.man b/doc/man/6bed4router.man
new file mode 100644 (file)
index 0000000..db939a7
--- /dev/null
@@ -0,0 +1,259 @@
+.TH 6BED4 8 "Februari 1, 2011"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+6bed4router \- server-side daemon for 6bed4 service
+.SH SYNOPSYS
+.B 6bed4router
+[\fB\-d\fR \fI/dev/tunX\fR] \fB\-l\fR \fIv4addr\fR \fB\-L\fR \fIv6prefix/64\fR [\fB\-m\fR/\fB\-i\fR/\fBs\fR/\fBt\fR/\fBu\fR \fI...\fR]
+.PP
+.B 6bed4router
+[\fB\-h\fR]
+.SH DESCRIPTION
+.PP
+Through a \fB6bed4router\fR daemon, it is possible to make a small range of IPv6
+addresses available to a potentially large number of IPv4-only users.  This means that even an IPv4-only
+network can host IPv6-only applications, as long as they can fall back on
+a tunnel based on this profile.
+.PP
+Use the \fB6bed4router\fR on nodes that support \fB6bed4peer\fR clients
+anywhere; instead, use the \fB6bed4node\fR on nodes that want to provide
+native IPv6 on a local network.
+.PP
+These tunnels are intended for mobile and embedded devices, to assist them
+in instant changeover from being IPv4-only to eventually being IPv6-only.
+Given the
+restricted resources in embedded applications, this is likely to improve
+the speed of transitioning to IPv6.  To avoid cluttered access, these
+tunnels should be reserved for resource-hampered devices.  For routers,
+transitioning mechanisms like 6to4 and 6in4 are more suitable.  For
+desktops, tunnelling routers or a direct link to a subscription tunnel
+service is a better solution.
+.PP
+The NAT traversing aspect of this profile is needed to support the normal
+openness of IPv6 connections.  This is achieved by packing IPv6 into UDP
+and then into IPv4.  This is a guaranteed manner of traversing NAT,
+provided that this daemon listens to a public IPv4 address.  The daemon
+currently does not support any other modes of operation.
+.PP
+The public-use aspect of this profile means that there is no requirement for
+clients to register.  However, this does not mean that the use of \fB6bed4router\fR
+makes it possible to browse IPv6 networks anonymously; in order to
+ensure traceability of abusive behaviour, the IPv4 address and UDP port
+of the tunnel client are mentioned in the IPv6 address.  See ADDRESS FORMAT
+below for details, and SECURITY CHECKS for further precautions against abuse.
+.PP
+The intended application of clients under this transitionary mechanism are
+IPv6-only devices that need a transport over an IPv4-only network.  The
+reason for defining IPv6-only device can be simplicity, or making IPv6
+more efficient and/or more useful.  To avoid making IPv6 a breaking
+requirement in roll-outs, devices for such an IPv6-only service could
+implement this mechanism to provide a simple backward-compatible mode for
+IPv4-only network nodes, without impairing all advantages of being IPv6-only.
+.PP
+This daemon is in fact a stateless translation between an IPv4 client
+and the general IPv6 world; to configure an IPv6 address on the tunnel
+client side it will perform autoconfiguration over the tunnel.  The
+assigned prefix will be a /112, with 16 bits of interface identifiers
+to fill in.  The interface identifier 0 is reserved for the router,
+or in other words, the tunnel server.
+.PP
+The tunnel server implementation listens to a UDP socket on port 3653
+on the IPv4 side, and to a
+tunnel endpoint to capture all traffic matching an IPv6 /64 prefix.
+It basically tosses traffic back and forth between these interfaces,
+but not without performing the SECURITY CHECKS desribed below
+on what is tossed at it.
+.SH OPTIONS
+.TP
+\fB\-d\fR \fI/dev/tunX\fR
+.TP
+\fB\-\-tundev\fR \fI/dev/tunX\fR
+Instead of creating a tunnel for the duration that \fB6bed4router\fR runs,
+use one that already exists and that has already been setup with
+the proper IPv6 prefix.  This option makes it possible for
+non-root users to run \fB6bed4router\fR.  All that is required is acccess to
+the tunnel device by the user that runs \fB6bed4router\fR.  Optional on Linux.
+.TP
+\fB\-l\fR \fIv4addr\fR
+.TP
+\fB\-\-v4listen\fR \fIv4addr\fR
+Bind to the given local IPv4 address and listen for incoming IPv6
+neighbour discovery packages as well as general IPv6 traffic.  Required.
+.TP
+\fB\-L\fR \fIv6prefix/64\fR
+.TP
+\fB\-\-v6prefix\fR \fIv6prefix/64\fR
+Bind to the given IPv6 address range through a tunnel device, and
+forward incoming IPv6 messages to IPv4-based UDP tunnel endpoints.
+See ADDRESS FORMAT below for an explanation of the lower half of
+the IPv6 addresses.  Required.
+.IP
+If no \fB\-d\fR option is given, a tunnel will be created for the time that
+\fB6bed4router\fR is running, and the \fIv6prefix/64\fR is used as a router address
+on that interface.  Routing table entries will not be setup by \fB6bed4router\fR,
+nor will the general ablity to forward IPv6 traffic.
+.TP
+\fB\-m\fR \fIv6addr\fR
+.TP
+\fB\-\-masqhost\fR \fIv6addr\fR
+Use the given IPv6 address as the target for masqueraded operation.  This
+means that any ports setup with \fB\-\-sctp\fR, \fB\-\-tcp\fR or \fB\-\-udp\fR
+are forwarded
+to this address when addressed on the client's idea of the 6bed4 router
+address.  Note that the router only uses a few ICMPv6 messages, so the use
+of these additional ports can benefit various applications, ranging from
+authentication services to honeypots.
+.TP
+\fB\-s\fR \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB\-t\fR \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB\-u\fR \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB\-sctp\fR \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB\-tcp\fR \fIport\fR[\fB:\fIport\fR]
+.TP
+\fB\-udp\fR \fIport\fR[\fB:\fIport\fR]
+Apply the masquerading port or port range for SCTP, TCP or UDP to the
+masquerading host set by the last preceding \fB\-\-masqhost\fR option,
+or use the tunnel's IPv6-side address
+if none was set yet.  Ports that have been previously bound will not make it,
+so it is possible to first relay a port with \fB\-t 22\fR to a service and
+then pass the rest to a honeypot with \fB\-t 1:65535\fR.
+.TP
+\fB\-i\fR
+Apply masquerading for ICMPv6 to the masquerading host set by the last
+preceding \fB\-\-masqhost\fR option, or use the tunnel's IPv6-side address
+if none was set yet.  The ICMPv6 messages with a special meaning to 6bed4
+are exempted (Router and Neighbor Solicitation and Advertisement, as well
+as Redirect) but other messages will be masqueraded.
+.TP
+\fB\-h\fR
+.TP
+\fB\-\-help\fR
+Print usage information and exit.
+.SH ADDRESS FORMAT
+.PP
+An IPv6 address used from \fB6bed4router\fR reveals the IPv4 address and UDP port
+used by the tunnel endpoint.  This format is checked on sending from
+the IPv4 tunnel to IPv6, and used to reconstruct the IPv4 tunnel access
+information for traffic from IPv6 to the IPv4 tunnel.
+.PP
+The format of the IPv6 addresses managed by \fB6bed4router\fR are:
+.PP
+\fIv6prefix\fR + \fIv4addr\fR + \fIudp-port\fR + \fIinterfaceidentifier\fR
+.PP
+In this format, the \fIv6prefix\fR is configured with the \fB\-L\fR option,
+and the \fIv4addr\fR with the \fB\-l\fR option.  The \fIudp-port\fR is noted on
+arrival of a packet on the IPv4 tunnel side of \fB6bed4router\fR.
+.PP
+The \fIinterfaceidentifier\fR is always 0 on the router side, and may be set
+to other values to distinguish 65,535 different client addresses.  As
+the main application foreseen for \fB6bed4router\fR is to get IPv6-only tools and
+devices working on an IPv4-only network, it is very likely that the clients
+will pick a fixed \fIinterfaceidentifier\fR such as 1 and hard-code it.
+.PP
+Due to the IPv6 practice of assigning link-local names composed of \fBfe80::\fR
+and the \fIinterfaceidentifier\fR, the router-side of a tunnel can always
+be addressed as \fBfe80::0\fR and clients can be found at addresses ranging
+from \fBfe80::1\fR to \fBfe80::ffff\fR.
+.PP
+Incoming IPv6 traffic destined for a serviced address is first checked
+as specified under SECURITY CHECKS, and then forwarded to \fIudp-port\fR at
+\fIv4addr\fR.  In doing so, the IPv6 packet is embedded in whole inside
+the UDP packet.  The IPv6 addresses are not altered, but only used
+to derive IPv4 contact information.
+.PP
+Outgoing IPv6 traffic arriving on the IPv4 tunnel side of \fB6bed4router\fR will
+be checked to have been sent from the right \fIv6prefix\fR and mention
+the \fIv4addr\fR and \fIudp-port\fR matching the client's public side.  That
+is, NAT may translate the IPv4 address and UDP port used, but these
+parts of the IPv6 address should show how it is forwarded to \fB6bed4router\fR.
+Note that autonegotiation protocol provides this necessary information at the
+time the \fB6bed4router\fR daemon starts.  If the NAT mapping changes during the uptime
+of the tunnel, a new Router Advertisement is sent from tunnel server to
+client, to notify it of the new prefix to use.  The original message is
+then discarded.
+.PP
+If it is desired to keep the same IPv6 address for longer periods, it
+is recommended that the client keeps NAT state intact by regularly
+sending over the UDP port to the tunnel endpoint.  For example, a regular
+ping could do that.  Alternatively, a client-mode only daemon could
+ensure that it is sending regularly during the times that an outside
+party might wish to send to it.  This is under the assumption that no
+explicit mapping in NAT overtakes this responsibility of an active
+mapping between the internal and external address space.
+.SH SECURITY CHECKS
+.PP
+Not everything will be passed through \fB6bed4router\fR, even if this would be
+technically possible.  A few security checks are applied to silently
+drop traffic that looks evil.
+.PP
+Packets should be long enough to at least contain the IPv6 traffic
+and a minimal payload size.  Also, it should not exceed a predefined
+MTU of 1280 bytes for IPv6.
+.PP
+IPv6 traffic uploaded through the IPv4 side should reveal the proper
+IPv4 settings in the IPv6 source address, as specified under
+ADDRESS FORMAT above.  This is basically the tunnel aspect of egress
+filtering.
+.PP
+Tunnel commands should adhere to the format of RFC 5722 and may not
+contain any NUL characters.
+.SH BUGS
+Currently, \fB6bed4router\fR does not use ICMP notifications at the IPv4
+level to provide smart feedback to an IPv6 client.  It is undecided
+at this point if this would add value.
+.PP
+To be able to fallback to this TSP profile, an IPv6-only application
+needs to find a \fB6bed4router\fR or similar service.  A general naming
+or numbering scheme is needed to make that straightforward.  The
+\fB6bed4router\fR service could be setup privately and configured in
+individual IPv6-only nodes, but it could accelerate the introduction
+of IPv6-only nodes if this were organised by network providers.
+.PP
+Ideally, \fB6bed4router\fR would be near all heavily connected nodes
+of the Internet.  There, they would improve connectivity without
+being a detour for the traffic.  Alternatively, it would be located
+in various uplinks.  To optimise routing, it is possible to assign
+a fixed IPv4 address and IPv6 prefix for \fB6bed4router\fR running
+anywhere; its stateless operation means that traffic going back and
+forth can go through different instances of \fB6bed4router\fR without
+posing problems.
+.PP
+The \fB6bed4router\fR daemon is a piece of highly efficient code,
+and it should be able to handle very high bandwidths.  A stress
+test has not been conducted yet.
+.PP
+This daemon does not pass on QoS headers as it should according to the
+specification.
+.SH LICENSE
+Released under a BSD-style license without advertisement clause.
+.SH SEE ALSO
+The 0cpm project is an example of an IPv6-only SIP application
+that can use \fB6bed4router\fR and comparable TSP tunnel services to
+demonstrate the advantages of IPv6 to end users.  It is also
+a typical example of a transitionary need for something like
+\fB6bed4router\fR.
+.PP
+http://0cpm.org/ \- the homepage of the 0cpm project.
+.PP
+http://devel.0cpm.org/6bed4/ \- the homepage of \fB6bed4\fR.
+.PP
+RFC 5722 \- the authoritative description of TSP, of which \fB6bed4\fR
+implements a specific profile for public service under NAT traversal.
+.SH AUTHOR
+\fB6bed4router\fR was written by Rick van Rein from OpenFortress.
+It was created to support the 0cpm project.
diff --git a/nonstd.h.in b/nonstd.h.in
deleted file mode 100644 (file)
index 05dd584..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/* The error line below is here to satisfy RFC requirements: We cannot
- * distribute code that has non-standard protocol numbers built in as
- * defaults.  So you must read this and understand that you are running
- * a non-compliant, experimental code version.  When you agree, please
- * change the error directives to warnings -- then, the code will build.
- *
- * Sincerely, Rick van Rein, OpenFortress.
- */
-
-#error "Build uses experimental Neighbor Discovery Option Type 253 for Destination Link-Layer Address"
-
-#define ND_OPT_DESTINATION_LINKADDR 253
-
-
-#error "Build uses experimental UDP port number 25790 or 0x64be"
-
-#define UDP_PORT_6BED4 25790
-
-
-#warning "Build uses temporary IPv4 address information"
-
-#define SERVER_6BED4_IPV4_TXT                  "145.136.0.1"
-#define SERVER_6BED4_IPV4_INT32                        ( (145L << 24) | (136L << 16) | (0L << 8) | 1L )
-#define SERVER_6BED4_IPV4_INT0                 145
-#define SERVER_6BED4_IPV4_INT1                 136
-#define SERVER_6BED4_IPV4_INT2                 0
-#define SERVER_6BED4_IPV4_INT3                 1
-#define SERVER_6BED4_IPV4_BINSTR               "\x91\x88\x00\x01"
-
-#define SERVER_6BED4_PORT_TXT                  "25790"
-#define SERVER_6BED4_PORT_BINSTR               "\xbe\x64"
-
-#define SERVER_6BED4_IPV4_PORT_TXT             "145.136.0.1:25790"
-#define SERVER_6BED4_PORT_IPV4_ADDRSTR         "\xbe\x64\x91\x88\x00\x01"
-#define SERVER_6BED4_PORT_IPV4_MACSTR          "\xbe\x64\x91\x88\x00\x01"
-
-/* Define LOCAL_OVERRIDES_PORT0 to forcefully detect 6bed4 port 0 as an
- * override to a local address.  This is used when a host uses its own /64
- * and has no other addresses available.
- */
-// #define LOCAL_OVERRIDES_PORT0 yes
-
-/* Possibly override the name of the interface from the default "6bed4"
- */
-// #define INTERFACE_NAME_6BED4 "6bed4"
diff --git a/src/6bed4peer.c b/src/6bed4peer.c
new file mode 100644 (file)
index 0000000..b4670f9
--- /dev/null
@@ -0,0 +1,2007 @@
+/* 6bed4/peer.c -- Peer-to-Peer IPv6-anywhere with 6bed4 -- peer.c
+ *
+ * This is an implementation of neighbour and router discovery over a
+ * tunnel that packs IPv6 inside UDP/IPv4.  This tunnel mechanism is
+ * so efficient that the server administrators need not mind if it is
+ * distributed widely.
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include <syslog.h>
+#ifndef LOG_PERROR
+#define LOG_PERROR LOG_CONS            /* LOG_PERROR is non-POSIX, LOG_CONS is */
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+
+#include <asm/types.h>
+//#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_ether.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+/* The following will initially fail, due to an IANA obligation to avoid
+ * default builds with non-standard options.
+ */
+#include "nonstd.h"
+
+
+#define MTU 1280
+#define PREFIX_SIZE 114
+
+typedef enum {
+       METRIC_LOW,
+       METRIC_MEDIUM,
+       METRIC_HIGH
+} metric_t;
+
+/*
+ * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
+ * the -d option leads to an error, or to an attempt to setup the tunnel.
+ * The setup_tunnel() function used for that is defined per platform, such
+ * as for LINUX.  Remember to maintain the manpage's optionality for -d.
+ */
+#undef HAVE_SETUP_TUNNEL
+
+
+/* Global variables */
+
+char *program;
+
+int rtsox = -1;
+int v4sox = -1;
+int v6sox = -1;
+int v4mcast = -1;
+
+uint8_t v4qos = 0;             /* Current QoS setting on UDP/IPv4 socket */
+uint32_t v6tc = 0;             /* Current QoS used by the IPv6 socket */
+uint8_t v4ttl = 64;            /* Default TTL setting on UDP/IPv4 socket */
+int v4ttl_mcast = -1;          /* Multicast TTL for LAN explorations */
+
+char *v4server = NULL;
+char *v6server = NULL;
+char v6prefix [INET6_ADDRSTRLEN];
+uint8_t v6lladdr [6];
+
+const uint8_t v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+uint8_t v6listen_linklocal_complete [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+struct sockaddr_nl rtname;
+struct sockaddr_nl rtkernel;
+
+struct sockaddr_in  v4name;
+struct sockaddr_in  v4peer;
+struct sockaddr_in6 v6name;
+
+struct sockaddr_in v4bind;
+struct sockaddr_in v4allnodes;
+
+struct in6_addr v6listen;
+//TODO:NEEDNOT// struct in6_addr v6listen_complete;
+struct in_addr  v4listen;
+
+
+struct {
+#ifdef PEER_USE_TAP
+       struct ethhdr eth;
+#else
+       struct tun_pi tun;
+#endif
+       union {
+               struct {
+                       struct ip6_hdr v6hdr;
+                       uint8_t data [MTU - sizeof (struct ip6_hdr)];
+               } idata;
+               struct {
+                       struct ip6_hdr v6hdr;
+                       struct icmp6_hdr v6icmphdr;
+               } ndata;
+       } udata;
+} __attribute__((packed)) v4data6;
+
+#ifdef PEER_USE_TAP
+#define v4ether        (v4data6.eth)
+#else
+#define v4tunpi6       (v4data6.tun)
+#endif
+#define v4data         ((uint8_t *) &v4data6.udata)
+#define v4hdr6         (&v4data6.udata.idata.v6hdr)
+#define v4src6         (&v4data6.udata.idata.v6hdr.ip6_src)
+#define v4dst6         (&v4data6.udata.idata.v6hdr.ip6_dst)
+
+#define v4v6plen       (v4data6.udata.ndata.v6hdr.ip6_plen)
+#define v4v6nexthdr    (v4data6.udata.ndata.v6hdr.ip6_nxt)
+#define v4v6hoplimit   (v4data6.udata.ndata.v6hdr.ip6_hops)
+
+#define v4icmp6                (&v4data6.udata.ndata.v6icmphdr)
+#define v4v6icmpdata   (v4data6.udata.ndata.v6icmphdr.icmp6_data8)
+#define v4v6icmptype   (v4data6.udata.ndata.v6icmphdr.icmp6_type)
+#define v4v6icmpcode   (v4data6.udata.ndata.v6icmphdr.icmp6_code)
+#define v4v6icmpcksum  (v4data6.udata.ndata.v6icmphdr.icmp6_cksum)
+#define v4v6ndtarget   (&v4data6.udata.ndata.v6icmphdr.icmp6_data8 [4])
+
+
+struct {
+#ifdef PEER_USE_TAP
+       struct ethhdr eth;
+#else
+       struct tun_pi tun;
+#endif
+       union {
+               uint8_t data [MTU];
+               struct {
+                       struct ip6_hdr v6hdr;
+                       struct icmp6_hdr v6icmp;
+               } __attribute__((packed)) ndata;
+       } udata;
+}  __attribute__((packed)) v6data6;
+
+#ifdef PEER_USE_TAP
+#define v6ether                (v6data6.eth)
+#else
+#define v6tuncmd       (v6data6.tun)
+#endif
+#define v6data         (v6data6.udata.data)
+#define v6hdr6         (&v6data6.udata.ndata.v6hdr)
+#define v6hops         (v6data6.udata.ndata.v6hdr.ip6_hops)
+#define v6type         (v6data6.udata.ndata.v6hdr.ip6_nxt)
+#define v6plen         (v6data6.udata.ndata.v6hdr.ip6_plen)
+#define v6src6         (&v6data6.udata.ndata.v6hdr.ip6_src)
+#define v6dst6         (&v6data6.udata.ndata.v6hdr.ip6_dst)
+#define v6icmp6type    (v6data6.udata.ndata.v6icmp.icmp6_type)
+#define v6icmp6code    (v6data6.udata.ndata.v6icmp.icmp6_code)
+#define v6icmp6data    (v6data6.udata.ndata.v6icmp.icmp6_data8)
+#define v6icmp6csum    (v6data6.udata.ndata.v6icmp.icmp6_cksum)
+#define v6ndtarget     (&v6data6.udata.ndata.v6icmp.icmp6_data16[2])
+
+#ifdef PEER_USE_TAP
+#define HDR_SIZE        (sizeof(struct ethhdr))
+#else
+#define HDR_SIZE        (sizeof(struct tun_pi))
+#endif
+
+/* Structure for tasks in neighbor discovery queues
+ */
+struct ndqueue {
+       struct ndqueue *next;
+       struct timeval tv;
+       struct in6_addr source;
+       struct in6_addr target;
+       uint8_t source_lladdr [6];
+       uint8_t todo_lancast, todo_direct;
+};
+
+/* Round-robin queue for regular tasks, starting at previous value */
+struct ndqueue *ndqueue = NULL;
+struct ndqueue *freequeue = NULL;
+uint32_t freequeue_items = 100;
+
+/* The time for the next scheduled maintenance: routersol or keepalive.
+ * The milliseconds are always 0 for maintenance tasks.
+ */
+time_t maintenance_time_sec;
+time_t maintenance_time_cycle = 0;
+time_t maintenance_time_cycle_max = 30;
+bool got_lladdr = false;
+time_t keepalive_period = 30;
+time_t keepalive_ttl = -1;
+
+/* The network packet structure of a 6bed4 Router Solicitation */
+
+uint8_t ipv6_router_solicitation [] = {
+       // IPv6 header
+       0x60, 0x00, 0x00, 0x00,
+       16 >> 8, 16 & 0xff, IPPROTO_ICMPV6, 255,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,          // unspecd src
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
+       // ICMPv6 header: router solicitation
+       ND_ROUTER_SOLICIT, 0, 0x7a, 0xae,       // Checksum courtesy of WireShark :)
+       // ICMPv6 body: reserved
+       0, 0, 0, 0,
+       // ICMPv6 option: source link layer address 0x0001 (end-aligned)
+       0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
+};
+
+uint8_t ipv6_defaultrouter_neighbor_advertisement [] = {
+       // IPv6 header
+       0x60, 0x00, 0x00, 0x00,
+       32 >> 8, 32 & 0xff, IPPROTO_ICMPV6, 255,
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // src is default router
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,// dst is all-nodes multicast, portable?
+       // ICMPv6 header: neighbor solicitation
+       ND_NEIGHBOR_ADVERT, 0, 0x36, 0xf2,              // Checksum courtesy of WireShark :)
+       // ICMPv6 Neighbor Advertisement: flags
+       0x40, 0, 0, 0,
+       // Target: fe80::
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // the targeted neighbor
+       // ICMPv6 option: target link layer address
+       2, 1,
+       UDP_PORT_6BED4 & 0xff, UDP_PORT_6BED4 >> 8,
+       SERVER_6BED4_IPV4_INT0, SERVER_6BED4_IPV4_INT1, SERVER_6BED4_IPV4_INT2, SERVER_6BED4_IPV4_INT3
+};
+
+uint8_t router_linklocal_address [] = {
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
+};
+
+//TODO// Complete with the if-id of the 6bed4 Router:
+uint8_t router_linklocal_address_complete [] = {
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
+};
+
+uint8_t client1_linklocal_address [] = {
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
+};
+
+uint8_t allnodes_linklocal_address [] = {
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
+};
+
+uint8_t allrouters_linklocal_address [] = {
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x02,
+};
+
+uint8_t solicitednodes_linklocal_prefix [13] = {
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff
+};
+
+bool default_route = false;
+
+bool foreground = false;
+
+bool log_to_stderr = false;
+
+bool multicast = true;
+
+
+/*
+ *
+ * Driver routines
+ *
+ */
+
+#ifndef INTERFACE_NAME_6BED4
+#define INTERFACE_NAME_6BED4 "6bed4"
+#endif
+
+#ifdef LINUX
+#define HAVE_SETUP_TUNNEL
+static struct ifreq ifreq;
+static bool have_tunnel = false;
+/* Implement the setup_tunnel() command for Linux.
+ * Return true on success, false on failure.
+ */
+bool setup_tunnel (void) {
+       if (v6sox == -1) {
+               v6sox = open ("/dev/net/tun", O_RDWR);
+       }
+       if (v6sox == -1) {
+               syslog (LOG_ERR, "%s: Failed to access tunnel driver on /dev/net/tun: %s\n", program, strerror (errno));
+               return 0;
+       }
+       bool ok = true;
+       int flags = fcntl (v6sox, F_GETFL, 0);
+       if (flags == -1) {
+               syslog (LOG_CRIT, "Failed to retrieve flags for the tunnel file descriptor: %s\n", strerror (errno));
+               ok = false;
+       }
+       if (!have_tunnel) {
+               memset (&ifreq, 0, sizeof (ifreq));
+               strncpy (ifreq.ifr_name, INTERFACE_NAME_6BED4, IFNAMSIZ);
+#ifdef PEER_USE_TAP
+               ifreq.ifr_flags = IFF_TAP | IFF_NO_PI;
+#else
+               ifreq.ifr_flags = IFF_TUN;
+#endif
+               if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
+                       syslog (LOG_CRIT, "Failed to set interface name: %s\n", strerror (errno));
+                       ok = false;
+               } else {
+                       have_tunnel = ok;
+               }
+               ifreq.ifr_name [IFNAMSIZ] = 0;
+               ifreq.ifr_ifindex = if_nametoindex (ifreq.ifr_name);
+syslog (LOG_DEBUG, "Found Interface Index %d for name %s\n", ifreq.ifr_ifindex, ifreq.ifr_name);
+               ok = ok & (ifreq.ifr_ifindex != 0);
+       }
+       char cmd [512+1];
+       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.forwarding=0", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               fprintf (stderr, "Failed command: %s\n", cmd);
+               ok = false;
+       }
+       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.accept_dad=0", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               fprintf (stderr, "Failed command: %s\n", cmd);
+               ok = false;
+       }
+       if (!ok) {
+               close (v6sox);  /* This removes the tunnel interface */
+               v6sox = -1;
+       }
+       return ok;
+}
+
+bool setup_tunnel_address (void) {
+       bool ok = have_tunnel;
+       char cmd [512+1];
+
+       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.autoconf=0", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = 0;
+       }
+       snprintf (cmd, 512, "/sbin/ip link set %s up mtu %d", ifreq.ifr_name, MTU);
+       if (ok && system (cmd) != 0) {
+               fprintf (stderr, "Failed command: %s\n", cmd);
+               ok = false;
+       }
+       snprintf (cmd, 512, "/sbin/ip -6 addr add %s/114 dev %s", v6prefix, ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               fprintf (stderr, "Failed command: %s\n", cmd);
+               ok = false;
+       }
+       if (default_route) {
+               snprintf (cmd, 512, "/sbin/ip -6 route add default via fe80:: dev %s metric 1042", ifreq.ifr_name);
+               if (ok && system (cmd) != 0) {
+                       fprintf (stderr, "Failed command: %s\n", cmd);
+                       ok = false;
+               }
+       }
+       return ok;
+}
+#endif /* LINUX */
+
+
+/*
+ *
+ * Utility functions
+ *
+ */
+
+
+
+/* Produce an IPv6 address following the 6bed4 structures.
+ *  - The top half is taken from v6listen
+ *  - The bottom contains IPv4 address and port from v4name
+ *  - The last 14 bits are filled with the lanip parameter
+ */
+void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
+       memcpy (&dst_ip6->s6_addr [0], &v6listen, 8);
+       dst_ip6->s6_addr32 [2] = v4name.sin_addr.s_addr;
+       dst_ip6->s6_addr16 [6] = v4name.sin_port;
+       dst_ip6->s6_addr  [14] = ((dst_ip6->s6_addr [8] & 0x03) << 6)
+                              | ((lanip >> 8) & 0x3f);
+       dst_ip6->s6_addr  [15] = (lanip & 0xff);
+       dst_ip6->s6_addr  [8] &= 0xfc;
+}
+
+/* Look for an entry in the 50ms-cycled Neighbor Discovery queue.
+ * Match the target address.  Return the entry found, or NULL.
+ */
+struct ndqueue *findqueue (struct in6_addr *target) {
+       struct ndqueue *ndq = ndqueue;
+       if (ndq) {
+               do {
+                       if (memcmp (target, &ndq->target, 16) == 0) {
+                               return ndq;
+                       }
+                       ndq = ndq->next;
+               } while (ndq != ndqueue);
+       }
+       return NULL;
+}
+
+/* Enter an item in the 50ms-cycled Neighbor Discovery queue.
+ * Retrieve its storage space from the free queue.
+ * TODO: Avoid double entries by looking up entries first -> "requeue?"
+ */
+static int TODO_qlen;
+void enqueue (struct in6_addr *target, struct in6_addr *v6src, uint8_t *source_lladdr) {
+       //
+       // Refuse to create double entries
+       if (findqueue (target)) {
+               return;
+       }
+       //
+       // Allocate a free item to enqueue
+       struct ndqueue *new = freequeue;
+       if (!new) {
+               // Temporarily overflown with ND -> drop the request
+               return;
+       }
+char tgt [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, target, tgt, sizeof (tgt));
+syslog (LOG_DEBUG, "Queue++ => %d, looking for %s\n", ++TODO_qlen, tgt);
+       freequeue = freequeue->next;
+       //
+       // Setup the new entry with target details
+       memcpy (&new->target, target, sizeof (new->target));
+       memcpy (&new->source, v6src, sizeof (new->source));
+       memcpy (&new->source_lladdr, source_lladdr, sizeof (new->source_lladdr));
+       new->todo_lancast = (v4mcast == -1)? 0: 2;
+       new->todo_direct = 3;
+       //
+       // Time the new item to run instantly
+       new->tv.tv_sec = 0;
+       //
+       // Enqueue the new item in front of the queue
+       if (ndqueue) {
+               new->next = ndqueue->next;
+               ndqueue->next = new;
+       } else {
+               new->next = new;
+               ndqueue = new;
+       }
+}
+
+/* Remove an item from the 50ms-cycled Neighbor Discovery queue.
+ * Enter its storage space in the free queue.
+ */
+void dequeue (struct ndqueue *togo) {
+       struct ndqueue *prev = ndqueue;
+       do {
+               if (prev->next == togo) {
+                       if (togo->next != togo) {
+                               prev->next = togo->next;
+                               if (ndqueue == togo) {
+                                       ndqueue = togo->next;
+                               }
+                       } else {
+                               // Must be the only queued item
+                               ndqueue = NULL;
+                       }
+                       togo->next = freequeue;
+                       freequeue = togo;
+syslog (LOG_DEBUG, "Queue-- => %d\n", --TODO_qlen);
+                       return;
+               }
+               prev = prev->next;
+       } while (prev != ndqueue);
+}
+
+
+/*
+ * Calculate the ICMPv6 checksum field
+ */
+uint16_t icmp6_checksum (uint8_t *ipv6hdr, size_t payloadlen) {
+       uint16_t plenword = htons (payloadlen);
+       uint16_t nxthword = htons (IPPROTO_ICMPV6);
+       uint16_t *areaptr [] = { (uint16_t *) &ipv6hdr [8], (uint16_t *) &ipv6hdr [24], &plenword, &nxthword, (uint16_t *) &ipv6hdr [40], (uint16_t *) &ipv6hdr [40 + 4] };
+       uint8_t areawords [] = { 8, 8, 1, 1, 1, payloadlen/2 - 2 };
+       uint32_t csum = 0;
+       u_int8_t i, j;
+       for (i=0; i < 6; i++) {
+               uint16_t *area = areaptr [i];
+               for (j=0; j<areawords [i]; j++) {
+                       csum += ntohs (area [j]);
+               }
+       }
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = htons (~csum);
+       return csum;
+}
+
+
+/*
+ * Send a Redirect reply.  This is in response to a v4v6data message,
+ * and is directed straight at the origin's address but sent with a
+ * lower metric.
+ *
+ * Note: Although the packet arrived in v4data6, the reply is built
+ *       in v6data6 and sent from there as though it had come from
+ *       the IPv6 stack.
+ */
+void redirect_reply (uint8_t *ngbc_llremote, metric_t ngbc_metric) {
+       void handle_6to4_plain_unicast (const ssize_t pktlen, const uint8_t *lladdr);
+       v6icmp6type = ND_REDIRECT;
+       v6icmp6code = 0;
+       v6icmp6data [0] =
+       v6icmp6data [1] =
+       v6icmp6data [2] =
+       v6icmp6data [3] = 0;            // Reserved
+       memcpy (v6icmp6data + 4, &v6listen, 16);
+                                       // Target IPv6 address
+       switch (ngbc_metric) {
+                                       // Destination Address suggestion
+       case METRIC_LOW:
+               //
+               // Redirect to the local-subnet IPv4 address
+               memcpy (v6icmp6data + 4 + 16, v6listen_linklocal, 8);
+               v6icmp6data [4 + 16 + 8 ] = v4peer.sin_port & 0x00ff;
+               v6icmp6data [4 + 16 + 9 ] = v4peer.sin_port >> 8;
+               memcpy (v6icmp6data + 4 + 16 + 12, &v4peer.sin_addr, 4);
+               v6icmp6data [4 + 16 + 10] = v4v6icmpdata [4 + 16 + 12];
+               v6icmp6data [4 + 16 + 11] = 0xff;
+               v6icmp6data [4 + 16 + 12] = 0xfe;
+               break;
+       case METRIC_MEDIUM:
+               memcpy (v6icmp6data + 4 + 16, v6listen_linklocal_complete, 16);
+               break;
+       case METRIC_HIGH:
+       default:
+               return;         /* no cause for Redirect, drop */
+       }
+       v6type = IPPROTO_ICMPV6;
+       v6plen = htons (8 + 16 + 16);
+       memcpy (v6src6, &v6listen, 16);
+       memcpy (v6dst6, v4src6, 16);
+       v6icmp6csum = icmp6_checksum ((uint8_t *) v4hdr6, 8 + 16 + 16);
+       handle_6to4_plain_unicast (HDR_SIZE + 40 + 8 + 16 + 16, ngbc_llremote);
+} 
+
+
+/* Append the current prefix to an ICMPv6 message.  Incoming optidx
+ * and return values signify original and new offset for ICMPv6 options.
+ * The endlife parameter must be set to obtain zero lifetimes, thus
+ * instructing the tunnel client to stop using an invalid prefix.
+ */
+size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
+       v6icmp6data [optidx++] = 3;     // Type
+       v6icmp6data [optidx++] = 4;     // Length
+       v6icmp6data [optidx++] = 114;   // This is a /114 prefix
+#ifndef COMPENSATE_FOR_AUTOCONF
+       v6icmp6data [optidx++] = 0xc0;  // L=1, A=1, Reserved1=0
+#else
+       //TODO// Temporary fix: "ip -6 addr add .../64 dev 6bed4"
+       v6icmp6data [optidx++] = 0x80;  // L=1, A=0, Reserved1=0
+#endif
+       memset (v6icmp6data + optidx, endlife? 0x00: 0xff, 8);
+       optidx += 8;
+                                       // Valid Lifetime: Zero / Infinite
+                                       // Preferred Lifetime: Zero / Infinite
+       memset (v6icmp6data + optidx, 0, 4);
+       optidx += 4;
+                                       // Reserved2=0
+       addr_6bed4 ((struct in6_addr *) (v6icmp6data + optidx), 0);
+                                       // Set IPv6 prefix
+       optidx += 16;
+       return optidx;
+}
+
+
+/*
+ * Construct a Neighbor Advertisement message, providing the
+ * Public 6bed4 Service as the link-local address.
+ *
+ * This is done immediately when the IPv6 stack requests the link-local
+ * address for fe80:: through Router Solicition.  In addition, it is the
+ * fallback response used when attempts to contact the remote peer at its
+ * direct IPv4 address and UDP port (its 6bed4 address) fails repeatedly.
+ *
+ * This routine is called with info==NULL to respond to an fe80::
+ * Neighbor Solicitation, otherwise with an info pointer containing
+ * a target IPv6 address to service.
+ */
+void advertise_6bed4_public_service (struct ndqueue *info) {
+#ifdef PEER_USE_TAP
+
+       if (info) {
+               memcpy (v6ether.h_dest, info->source_lladdr, 6);
+       } else {
+               memcpy (v6ether.h_dest, v6ether.h_source, 6);
+       }
+       memcpy (v6ether.h_source, SERVER_6BED4_PORT_IPV4_MACSTR, 6);
+#endif
+       memcpy (v6data, ipv6_defaultrouter_neighbor_advertisement, 8);
+       if (info) {
+               memcpy (v6dst6, &info->source, 16);
+       } else {
+               memcpy (v6dst6, v6src6, 16);
+       }
+       if (info) {
+               memcpy (v6src6, &info->target, 16);
+       } else {
+               memcpy (v6src6, router_linklocal_address_complete, 16);
+       }
+       //TODO:OVERWROTE// memcpy (v6data + 8, ipv6_defaultrouter_neighbor_advertisement + 8, 16);
+       memcpy (v6data + 8 + 16 + 16, ipv6_defaultrouter_neighbor_advertisement + 8 + 16 + 16, sizeof (ipv6_defaultrouter_neighbor_advertisement) - 8 - 16 - 16);
+       if (info) {
+               // Overwrite target only for queued requests
+               memcpy (&v6icmp6data [4], &info->target, 16);
+       }
+       v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 32);
+       int sent = write (v6sox, &v6data6, HDR_SIZE + sizeof (ipv6_defaultrouter_neighbor_advertisement));
+       if (info) {
+               syslog (LOG_DEBUG, "TODO: Neighbor Discovery failed to contact directly -- standard response provided\n");
+       } else {
+               syslog (LOG_DEBUG, "TODO: Neighbor Discovery for Public 6bed4 Service -- standard response provided\n");
+       }
+}
+
+
+/*
+ * Validate the originator's IPv6 address.  It should match the
+ * UDP/IPv4 coordinates of the receiving 6bed4 socket.  Also,
+ * the /64 prefix (but not the /114 prefix!) must match v6listen.
+ */
+bool validate_originator (struct in6_addr *ip6) {
+       uint32_t addr;
+       //
+       // Communication from the configured router is welcomed
+       //TODO// Why should we trust the ip6 address at face value?
+       if ((v4name.sin_addr.s_addr == v4peer.sin_addr.s_addr)
+                       && (v4name.sin_port == v4peer.sin_port)) {
+               return true;
+       }
+       //
+       // Require non-local top halves to match our v6listen_linklocal address
+       //TODO// When do we receive local top halves?
+       //TODO// We should really be more flexible and allow fallback addrs
+       if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
+               if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
+                       return false;
+               }
+       }
+       //
+       // Require the sender port to appear in its IPv6 address
+       if (v4name.sin_port != ip6->s6_addr16 [6]) {
+               return false;
+       }
+       //
+       // Require the sender address to appear in its IPv6 address
+       addr = ntohl (ip6->s6_addr32 [2]) & 0xfcffffff;
+       addr |= ((uint32_t) (ip6->s6_addr [14] & 0xc0)) << (24-6);
+       if (addr != ntohl (v4name.sin_addr.s_addr)) {
+               return false;
+       }
+       //
+       // We passed with flying colours
+       return true;
+}
+
+
+/*
+ * Translate a Link-Local Address to its metric.  The metrics are
+ * numbered so that a higher number indicates a more costly path
+ * over which to connect.  The values of the metric should not be
+ * published, but be treated as an opaque value with a complete
+ * ordering (that is: <, <=, >=, > relations) defined on it.
+ */
+metric_t lladdr_metric (uint8_t *lladdr) {
+       uint32_t ipv4 = * (uint32_t *) (lladdr + 2);
+       //
+       // Metric 2: The 6bed4 Router address
+       if (ipv4 == v4peer.sin_addr.s_addr) {
+               return METRIC_HIGH;
+       }
+       //
+       // Metric 0: Private Addresses, as per RFC 1918
+       if ((ipv4 & 0xff000000) == 0x0a000000) {
+               return METRIC_LOW;      /* 10.0.0./8 */
+       }
+       if ((ipv4 & 0xffff0000) == 0xc0a80000) {
+               return METRIC_LOW;      /* 192.168.0.0/16 */
+       }
+       if ((ipv4 & 0xfff00000) == 0xac100000) {
+               return METRIC_LOW;      /* 172.16.0.0/12 */
+       }
+       //
+       // Metric 1: Direct IPv4 contact is any other address
+       //           Correctness should be checked elsewhere
+       return METRIC_MEDIUM;
+}
+
+
+/*
+ * Retrieve the Link-Local Address, if any, for a given 6bed4 Peer.
+ * Return true on success, false on failure to find it.  The lladdr
+ * parameter is only overwritten in case of success.
+ *
+ * Obviously, there is a point where it is more efficient to not
+ * lookup the cache for every request, but to cache it locally
+ * and limit the lookup frequency.  This low-traffic optimal version
+ * is used here for initial simplicity, and because this is a peer
+ * daemon and a reference implementation.  But who knows what people
+ * will submit as patches...
+ *
+ * Note: This code is specific to Linux, but note that BSD also has a
+ *       NetLink concept, so it may port without needing to resort to
+ *       shell commands running slowly in separate processes.
+ * Note: The interface for Linux is under-documented.  Work may be
+ *       needed to handle exception situations, such as going over
+ *       invisible boundaries on the number of neighbours.  Similarly,
+ *       the use of alignment macros is rather unclear.  This is not
+ *       how I prefer to write code, but it's the best I can do now.
+ */
+bool lookup_neighbor (uint8_t *ipv6, uint8_t *lladdr) {
+       struct mymsg {
+               struct nlmsghdr hd;
+               struct ndmsg nd;
+               uint8_t arg [16384];
+       } msg;
+       memset (&msg, 0, sizeof (struct nlmsghdr) + sizeof (struct ndmsg));
+       msg.hd.nlmsg_len = NLMSG_LENGTH (sizeof (msg.nd));
+       msg.hd.nlmsg_type = RTM_GETNEIGH;
+       msg.hd.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT /* | NLM_F_MATCH */;
+       msg.hd.nlmsg_pid = rtname.nl_pid;
+       msg.nd.ndm_family = AF_INET6;
+       msg.nd.ndm_state = NUD_REACHABLE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT | NUD_STALE;   // Ignored by the kernel?
+       msg.nd.ndm_ifindex = ifreq.ifr_ifindex; // Ignored by the kernel?
+       // How to select an IPv6 address?  Ignored by the kernel?
+#if 0
+       struct rtattr *ra1 = (struct rtattr *) (((char *) &msg) + sizeof (struct nlmsghdr) + sizeof (struct ndmsg));
+       ra1->rta_type = NDA_DST;        // lookup IPv6 address
+       ra1->rta_len = RTA_LENGTH(16);
+       msg.hd.nlmsg_len = NLMSG_ALIGN (msg.hd.nlmsg_len) + RTA_LENGTH (16);
+       memcpy (RTA_DATA (ra1), ipv6, 16);
+#endif
+       if (send (rtsox, &msg, msg.hd.nlmsg_len, MSG_DONTWAIT) == -1) {
+               return false;
+       }
+       ssize_t recvlen;
+       uint16_t pos = 0;
+{ char buf [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, ipv6, buf, sizeof (buf)); syslog (LOG_DEBUG, "Looking up v6addr %s\n", buf); }
+       while (recvlen = recv (rtsox, ((char *) &msg) + pos, sizeof (msg) - pos, MSG_DONTWAIT), recvlen > 0) {
+syslog (LOG_DEBUG, "Message of %zd bytes from neighbor cache, total is now %zd\n", recvlen, pos + recvlen);
+               recvlen += pos;
+               pos = 0;
+               struct mymsg *resp;
+               while (resp = (struct mymsg *) (((char *) &msg) + pos),
+                               (pos + sizeof (struct nlmsghdr) <= recvlen) &&
+                               (pos + resp->hd.nlmsg_len <= recvlen)) {
+                       bool ok = true, match = false;
+                       uint8_t *result = NULL;
+                       if (resp->hd.nlmsg_type == NLMSG_DONE) {
+                               return false;
+                       } else if (resp->hd.nlmsg_type != RTM_NEWNEIGH) {
+                               syslog (LOG_ERR, "Kernel sent an unexpected nlmsg_type 0x%02x, ending neighbor interpretation", resp->hd.nlmsg_type);
+                               ok = false;
+                       } else if (resp->nd.ndm_ifindex != ifreq.ifr_ifindex) {
+                               syslog (LOG_ERR, "Kernel sent an unexpected interface index");
+                               ok = false;
+                       } else if (resp->nd.ndm_family != AF_INET6) {
+                               syslog (LOG_ERR, "Kernel reported unknown neighbor family %d\n", resp->nd.ndm_family);
+                               ok = false;
+                       } else
+                       if (!(resp->nd.ndm_state & (NUD_REACHABLE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT | NUD_STALE))) {
+                               syslog (LOG_ERR, "Kernel sent a funnily flagged interface state");
+                               ok = false;
+                       }
+                       struct rtattr *ra = (struct rtattr *) ((char *) &resp + pos + sizeof (struct nlmsghdr) + sizeof (struct ndmsg) + 8);
+                       ssize_t rapos = 0;
+                       while (ok && (rapos + ra->rta_len <= resp->hd.nlmsg_len)) {
+                               switch (ra->rta_type) {
+                               case NDA_DST:
+{ char buf [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, RTA_DATA (ra), buf, sizeof (buf)); syslog (LOG_DEBUG, "Comparing against %s\n", buf); }
+                                       if (memcmp (ipv6, RTA_DATA (ra), 16) == 0) {
+                                               match = true;
+                                       }
+                                       break;
+                               case NDA_LLADDR:
+                                       result = RTA_DATA (ra);
+                                       break;
+                               case NDA_PROBES:
+                               case NDA_CACHEINFO:
+                               default:
+                                       break;  /* not of interest, skip */
+                               }
+                               rapos += ((ra->rta_len - 1) | 0x00000003) + 1;
+                               ra = (struct rtattr *) (((char *) ra) + (((ra->rta_len - 1) | 0x0000003) + 1));
+                       }
+                       if (ok && match && result) {
+                               memcpy (lladdr, result, 6);
+                               return true;    /* Yippy! Erfolg! */
+                       }
+                       pos += resp->hd.nlmsg_len;
+               }
+               // Copy remaining partial message to the beginning, continue from there
+               memcpy (&msg, ((char *) &msg) + pos, recvlen - pos);
+               pos = recvlen - pos;
+       }
+       return false;
+}
+
+
+/*
+ * Major packet processing functions
+ */
+
+
+/* Handle the IPv4 message pointed at by msg, checking if (TODO:HUH?) the IPv4:port
+ * data matches the lower half of the IPv6 sender address.  Drop silently
+ * if this is not the case.  TODO: or send ICMP?
+ */
+void handle_4to6_plain (ssize_t v4datalen, struct sockaddr_in *sin) {
+       //
+       // Send the unwrapped IPv6 message out over v6sox
+#ifdef PEER_USE_TAP
+       v4ether.h_proto = htons (ETH_P_IPV6);
+       memcpy (v4ether.h_dest,   v6lladdr, 6);
+       v4ether.h_source [0] = ntohs (sin->sin_port) & 0xff;
+       v4ether.h_source [1] = ntohs (sin->sin_port) >> 8;
+       memcpy (v4ether.h_source + 2, &sin->sin_addr, 4);
+#endif
+syslog (LOG_INFO, "Writing IPv6, result = %zd\n",
+       write (v6sox, &v4data6, HDR_SIZE + v4datalen)
+)
+       ;
+}
+
+
+/* Handle the IPv4 message pointed at by msg as a neighbouring command.
+ *
+ * Type        Code    ICMPv6 meaning                  Handling
+ * ----        ----    -----------------------------   ----------------------------
+ * 133 0       Router Solicitation             Ignore
+ * 134 0       Router Advertisement            Setup Tunnel with Prefix
+ * 135 0       Neighbour Solicitation          Send Neighbour Advertisement
+ * 136 0       Neighbour Advertisement         Ignore
+ * 137 0       Redirect                        Ignore
+ */
+void handle_4to6_nd (struct sockaddr_in *sin, ssize_t v4ngbcmdlen) {
+       uint16_t srclinklayer;
+       uint8_t *destprefix = NULL;
+#ifdef TODO_DEPRECATED
+       uint8_t *destlladdr = NULL;
+#endif
+       struct ndqueue *ndq;
+       if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
+               return;
+       }
+       //
+       if (v4v6icmpcode != 0) {
+               return;
+       }
+       if (icmp6_checksum (v4data, v4ngbcmdlen - sizeof (struct ip6_hdr)) != v4v6icmpcksum) {
+               return;
+       }
+       //
+       // Approved.  Perform neighbourly courtesy.
+       switch (v4v6icmptype) {
+       case ND_ROUTER_SOLICIT:
+               return;         /* this is not a router, drop */
+       case ND_ROUTER_ADVERT:
+               //
+               // Validate Router Advertisement
+               if (ntohs (v4v6plen) < sizeof (struct icmp6_hdr) + 16) {
+                       return;   /* strange length, drop */
+               }
+               if (v4v6icmpdata [1] & 0x80 != 0x00) {
+                       return;   /* indecent proposal to use DHCPv6, drop */
+               }
+               if (memcmp (&v4src6->s6_addr, router_linklocal_address, 16) != 0) {
+                       return;   /* not from router, drop */
+               }
+               if (memcmp (&v4dst6->s6_addr, client1_linklocal_address, 8) != 0) {
+                       if (memcmp (&v4dst6->s6_addr, allnodes_linklocal_address, 16) != 0) {
+                               return;   /* no address setup for me, drop */
+                       }
+               }
+               if (v4dst6->s6_addr [8] & 0x01) {
+                       syslog (LOG_WARNING, "TODO: Ignoring (by accepting) an odd public UDP port revealed in a Router Advertisement -- this could cause confusion with multicast traffic\n");
+               }
+               size_t rdofs = 12;
+               //TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
+               while (rdofs + 4 < ntohs (v4v6plen)) {
+                       if (v4v6icmpdata [rdofs + 1] == 0) {
+                               return;   /* zero length option */
+                       }
+#ifdef TODO_DEPRACATED
+                       if ((v4v6icmpdata [rdofs + 0] == ND_OPT_DESTINATION_LINKADDR) && (v4v6icmpdata [rdofs + 1] == 1)) {
+                               if (v4v6icmpdata [rdofs + 2] & 0x01) {
+                                       syslog (LOG_WARNING, "TODO: Ignoring an odd UDP port offered in a Router Advertisement over 6bed4\n");
+                               }
+                               syslog (LOG_INFO, "TODO: Set tunnel link-local address to %02x:%02x:%02x:%02x:%02x:%02x\n", v4v6icmpdata [rdofs + 2], v4v6icmpdata [rdofs + 3], v4v6icmpdata [rdofs + 4], v4v6icmpdata [rdofs + 5], v4v6icmpdata [rdofs + 6], v4v6icmpdata [rdofs + 7]);
+                               destlladdr = &v4v6icmpdata [rdofs + 2];
+                               /* continue with next option */
+                       } else
+#endif
+                       if (v4v6icmpdata [rdofs + 0] != ND_OPT_PREFIX_INFORMATION) {
+                               /* skip to next option */
+                       } else if (v4v6icmpdata [rdofs + 1] != 4) {
+                               return;   /* bad length field */
+                       } else if (rdofs + (v4v6icmpdata [rdofs + 1] << 3) > ntohs (v4v6plen) + 4) {
+                               return;   /* out of packet length */
+                       } else if (v4v6icmpdata [rdofs + 3] & 0xc0 != 0xc0) {
+                               /* no on-link autoconfig prefix */
+                       } else if (v4v6icmpdata [rdofs + 2] != PREFIX_SIZE) {
+                               /* not a /114 prefix, so no 6bed4 offer */
+                               return;
+                       } else {
+                               destprefix = &v4v6icmpdata [rdofs + 16];
+                       }
+                       rdofs += (v4v6icmpdata [rdofs + 1] << 3);
+               }
+               if (destprefix) {
+                       memcpy (v6listen.s6_addr + 0, destprefix, 16);
+                       v6listen.s6_addr [14] &= 0xc0;
+                       v6listen.s6_addr [15]  = 0x01;  // choose client 1
+                       memcpy (v6listen_linklocal_complete + 0, v6listen_linklocal, 8);
+                       memcpy (v6listen_linklocal_complete + 8, v6listen.s6_addr + 8, 8);
+                       memcpy (v6lladdr, destprefix + 8, 6);
+                       //TODO// Is v6lladdr useful?  Should it include lanip?
+                       v6lladdr [0] &= 0xfc;
+                       v6lladdr [0] |= (destprefix [14] >> 6);
+                       inet_ntop (AF_INET6,
+                               &v6listen,
+                               v6prefix,
+                               sizeof (v6prefix));
+                       syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
+                       setup_tunnel_address ();  //TODO// parameters?
+                       got_lladdr = true;
+                       maintenance_time_cycle = maintenance_time_cycle_max;
+                       maintenance_time_sec = time (NULL) + maintenance_time_cycle;
+               }
+               return;
+       case ND_NEIGHBOR_SOLICIT:
+               //
+               // Validate Neigbour Solicitation (trivial)
+               //
+               // Replicate the message over the IPv6 Link (like plain IPv6)
+               if (v4ngbcmdlen < 24) {
+                       return;         /* too short, drop */
+               }
+               syslog (LOG_DEBUG, "%s: Replicating Neighbor Solicatation from 6bed4 to the IPv6 Link\n", program);
+char buf [INET6_ADDRSTRLEN]; uint8_t ll [6]; if ((memcmp (v4src6, v6listen_linklocal, 8) != 0) && (memcmp (v4src6, &v6listen, 8) != 0)) { inet_ntop (AF_INET6, v4src6, buf, sizeof (buf)); syslog (LOG_DEBUG, "Source IPv6 address %s from wrong origin\n", buf); } else { uint8_t pfaddr [16]; memcpy (pfaddr, v6listen.s6_addr, 8); memcpy (pfaddr + 8, v4src6->s6_addr + 8, 8); inet_ntop (AF_INET6, pfaddr, buf, sizeof (buf)); if (lookup_neighbor (pfaddr, ll)) { syslog (LOG_DEBUG, "Source IPv6 %s has Link-Local Address %02x:%02x:%02x:%02x:%02x:%02x with metric %d\n", buf, ll [0], ll [1], ll [2], ll [3], ll [4], ll [5], lladdr_metric (ll)); } else { syslog (LOG_DEBUG, "Source IPv6 %s is unknown to me\n", buf); } }
+uint8_t optofs = 4 + 16;
+#if 0
+uint8_t *srcll = NULL; /* TODO -- use 6bed4 Network sender instead! */
+while ((40 + 4 + optofs + 2 < v4ngbcmdlen) && (40 + 4 + optofs + 8 * v4v6icmpdata [optofs + 1] <= v4ngbcmdlen)) {
+if (v4v6icmpdata [optofs] == 1) {
+srcll = v4v6icmpdata + optofs + 2;
+}
+optofs += 8 * v4v6icmpdata [optofs + 1];
+}
+if (srcll) { syslog (LOG_DEBUG, "ND-contained Source Link-Layer Address %02x:%02x:%02x:%02x:%02x:%02x has metric %d\n", srcll [0], srcll [1], srcll [2], srcll [3], srcll [4], srcll [5], lladdr_metric (srcll)); }
+#endif
+               //
+               // We should attach a Source Link-Layer Address, but
+               // we cannot automatically trust the one provided remotely.
+               // Also, we want to detect if routes differ, and handle it.
+               //
+               // 0. if no entry in the ngb.cache
+               //    then use 6bed4 server in ND, initiate ngb.sol to src.ll
+               //         impl: use 6bed4-server lladdr, set highest metric
+               // 1. if metric (ngb.cache) < metric (src.ll)
+               //    then retain ngb.cache, send Redirect to source
+               // 2. if metric (ngb.cache) > metric (src.ll)
+               //    then retain ngb.cache, initiate ngb.sol to src.ll
+               // 3. if metric (ngb.cache) == metric (src.ll)
+               //    then retain ngb.cache
+               //
+               uint8_t src_lladdr [6];
+               src_lladdr [0] = ntohs (v4name.sin_port) & 0x00ff;
+               src_lladdr [1] = ntohs (v4name.sin_port) >> 8;
+               memcpy (src_lladdr + 2, &v4name.sin_addr, 4);
+               metric_t src_metric = lladdr_metric (src_lladdr);
+               v4v6icmpdata [4+16+0] = 1;      /* Option: Source LLaddr */
+               v4v6icmpdata [4+16+1] = 1;      /* Length: 1x 8 bytes */
+               uint8_t *ngbc_lladdr = v4v6icmpdata + 4+16+2;
+               uint8_t ngbc_ipv6 [16];
+               if (memcmp (v4src6, v6listen_linklocal, 8)) {
+                       memcpy (ngbc_ipv6 + 0, &v6listen, 8);
+                       memcpy (ngbc_ipv6 + 8, v4src6 + 8, 8);
+               } else {
+                       memcpy (ngbc_ipv6, v4src6, 16);
+               }
+               bool ngbc_cached = lookup_neighbor (ngbc_ipv6, ngbc_lladdr);
+               metric_t ngbc_metric;
+               if (ngbc_cached) {
+                       ngbc_metric = lladdr_metric (ngbc_lladdr);
+               } else {
+                       ngbc_metric = METRIC_HIGH; /* trigger local ngbsol */
+                       memcpy (ngbc_lladdr, SERVER_6BED4_PORT_IPV4_MACSTR, 6);
+syslog (LOG_DEBUG, "Failed to find neighbor in cache, initialising it with the high metric\n");
+               }
+               syslog (LOG_DEBUG, "Metric analysis: source lladdr %02x:%02x:%02x:%02x:%02x:%02x metric %d, neighbor cache lladdr %02x:%02x:%02x:%02x:%02x:%02x metric %d\n", src_lladdr [0], src_lladdr [1], src_lladdr [2], src_lladdr [3], src_lladdr [4], src_lladdr [5], src_metric, ngbc_lladdr [0], ngbc_lladdr [1], ngbc_lladdr [2], ngbc_lladdr [3], ngbc_lladdr [4], ngbc_lladdr [5], ngbc_metric);
+               //
+               // Replicate the ngb.sol with the selected ngbc-lladdr
+               v4v6icmpcksum = icmp6_checksum ((uint8_t *) v4hdr6, 8 + 16 + 8);
+               handle_4to6_plain (40 + 24 + 8, &v4name);
+               //
+               // If needed, initiate Neigbor Solicitation to the source
+               // Note: Also when !ngbc_cached as the router is then cached
+               if (ngbc_metric > src_metric) {
+syslog (LOG_DEBUG, "Trying to find the more direct route that the remote peer seems to be using\n");
+                       enqueue ((struct in6_addr *) v4src6, &v6listen, v6lladdr);
+               }
+               //
+               // If needed, ask the source to redo Neighbor Solicitation
+               if (ngbc_metric < src_metric) {
+syslog (LOG_DEBUG, "Redirecting the remote peer to the more efficient route that I am using\n");
+                       redirect_reply (ngbc_lladdr, ngbc_metric);
+               }
+               return;
+       case ND_NEIGHBOR_ADVERT:
+               //
+               // Process Neighbor Advertisement coming in over 6bed4
+               // First, make sure it is against an item in the ndqueue
+               ndq = findqueue ((struct in6_addr *) v4v6ndtarget);
+               if (!ndq) {
+                       // Ignore advertisement -- it may be an attack
+                       return;
+               }
+               // Remove the matching item from the ndqueue
+               dequeue (ndq);
+               // Replicate the Neigbor Advertisement over the IPv6 Link (like plain IPv6)
+               v4v6icmpdata [0] |= 0xe0;       /* Router, Solicited, Override */
+               v4v6icmpdata [20] = 2;          /* Target Link-Layer Address */
+               v4v6icmpdata [21] = 1;          /* Length: 1x 8 bytes */
+               v4v6icmpdata [22] = ntohs (v4name.sin_port) & 0xff;
+               v4v6icmpdata [23] = ntohs (v4name.sin_port) >> 8;
+               memcpy (v4v6icmpdata + 24, &v4name.sin_addr, 4);
+               v4v6plen = htons (24 + 8);
+               v4v6icmpcksum = icmp6_checksum ((uint8_t *) v4hdr6, 24 + 8);
+               handle_4to6_plain (sizeof (struct ip6_hdr) + 24 + 8, &v4name);
+               return;
+       case ND_REDIRECT:
+               //
+               // Redirect indicates that a more efficient bypass exists than
+               // the currently used route.  The remote peer has established
+               // this and wants to share that information to retain a
+               // symmetric communication, which is helpful in keeping holes
+               // in NAT and firewalls open.
+               //
+               //TODO// BE EXTREMELY SELECTIVE BEFORE ACCEPTING REDIRECT!!!
+               //TODO:NOTYET// enqueue ((struct in6_addr *) v4v6ndtarget, &v6listen, v6lladdr);
+               return;
+       }
+}
+
+
+/* Receive a tunnel package, and route it to either the handler for the
+ * tunnel protocol, or to the handler that checks and then unpacks the
+ * contained IPv6.
+ */
+void handle_4to6 (int v4in) {
+       uint8_t buf [1501];
+       ssize_t buflen;
+       socklen_t adrlen = sizeof (v4name);
+       //
+       // Receive IPv4 package, which may be tunneled or a tunnel request
+       buflen = recvfrom (v4in,
+                       v4data, MTU,
+                       MSG_DONTWAIT,
+                       (struct sockaddr *) &v4name, &adrlen
+               );
+       if (buflen == -1) {
+               syslog (LOG_INFO, "%s: WARNING: Error receiving IPv4-side package: %s\n",
+                               program, strerror (errno));
+               return;         /* receiving error, drop */
+       }
+       if (buflen <= sizeof (struct ip6_hdr)) {
+               return;         /* received too little data, drop */
+       }
+       if ((v4data [0] & 0xf0) != 0x60) {
+               return;         /* not an IPv6 packet, drop */
+       }
+       if (!validate_originator (v4src6)) {
+               return;         /* source appears fake, drop */
+       }
+       /*
+        * Distinguish types of traffic:
+        * Non-plain, Plain Unicast, Plain Multicast
+        */
+       if ((v4v6nexthdr == IPPROTO_ICMPV6) &&
+                       (v4v6icmptype >= 133) && (v4v6icmptype <= 137)) {
+               //
+               // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+               if (v4v6hoplimit != 255) {
+                       return;
+               }
+               handle_4to6_nd (&v4name, buflen);
+       } else {
+               //
+               // Plain Unicast or Plain Multicast (both may enter)
+               if (v4v6hoplimit-- <= 1) {
+                       return;
+               }
+               handle_4to6_plain (buflen, &v4name);
+       }
+}
+
+
+/*
+ * Relay an IPv6 package to 6bed4, using the link-local address as it
+ * is found in the Ethernet header.  Trust the local IPv6 stack to have
+ * properly obtained this destination address through Neighbor Discovery
+ * over 6bed4.
+ */
+void handle_6to4_plain_unicast (const ssize_t pktlen, const uint8_t *lladdr) {
+       struct sockaddr_in v4dest;
+       memset (&v4dest, 0, sizeof (v4dest));
+       v4dest.sin_family = AF_INET;
+       v4dest.sin_port = htons (lladdr [0] | (lladdr [1] << 8));
+       memcpy (&v4dest.sin_addr, lladdr + 2, 4);
+       if (v6tc != (v6hdr6->ip6_vfc & htonl (0x0ff00000))) {
+               v6tc = v6hdr6->ip6_vfc & htonl (0x0ff00000);
+               v4qos = (ntohl (v6hdr6->ip6_vfc) & 0x0ff00000) >> 24;
+               if (setsockopt (v4sox, IPPROTO_IP, IP_TOS, &v4qos, sizeof (v4qos)) == -1) {
+                       syslog (LOG_ERR, "Failed to switch IPv4 socket to QoS setting 0x%02x\n", v4qos);
+               }
+       }
+       syslog (LOG_DEBUG, "%s: Sending IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %zd\n", program,
+       ((uint8_t *) &v4dest.sin_addr.s_addr) [0],
+       ((uint8_t *) &v4dest.sin_addr.s_addr) [1],
+       ((uint8_t *) &v4dest.sin_addr.s_addr) [2],
+       ((uint8_t *) &v4dest.sin_addr.s_addr) [3],
+       ntohs (v4dest.sin_port),
+               sendto (v4sox,
+                               v6data,
+                               pktlen - HDR_SIZE,
+                               MSG_DONTWAIT,
+                               (struct sockaddr *) &v4dest,
+                               sizeof (struct sockaddr_in))
+       )
+                               ;
+}
+
+
+/*
+ * Handle a request for Neighbor Discovery over the 6bed4 Link.
+ */
+void handle_6to4_nd (ssize_t pktlen) {
+       uint8_t lldest [6];
+       //
+       // Validate ICMPv6 message -- trivial, trust local generation
+       //
+       // Handle the message dependent on its type
+       switch (v6icmp6type) {
+       case ND_ROUTER_SOLICIT:
+               v6icmp6type = ND_ROUTER_ADVERT;
+               v6icmp6code = 0;
+               v6icmp6data [0] = 0;            // Cur Hop Limit: unspec
+               v6icmp6data [1] = 0x18;         // M=0, O=0,
+                                               // H=0, Prf=11=Low
+                                               // Reserved=0
+               //TODO: wire says 0x44 for router_adv.flags
+               size_t writepos = 2;
+               memset (v6icmp6data + writepos,
+                               default_route? 0xff: 0x00,
+                               2);             // Router Lifetime
+               writepos += 2;
+               memcpy (v6icmp6data + writepos,
+                               "\x00\x00\x80\x00",
+                               4);             // Reachable Time: 32s
+               writepos += 4;
+               memcpy (v6icmp6data + writepos,
+                               "\x00\x00\x01\x00",
+                               4);             // Retrans Timer: .25s
+               writepos += 4;
+               writepos = icmp6_prefix (writepos, 0);
+               v6plen = htons (4 + writepos);
+               memcpy (v6dst6, v6src6, 16);
+               memcpy (v6src6, v6listen_linklocal_complete, 16);
+               v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 4 + writepos);
+#ifdef PEER_USE_TAP
+               v6ether.h_proto = htons (ETH_P_IPV6);
+               memcpy (v6ether.h_dest, v6ether.h_source, 6);
+               memcpy (v6ether.h_source, v6lladdr, 6);
+#endif
+               syslog (LOG_INFO, "Replying Router Advertisement to the IPv6 Link, result = %zd\n",
+                       write (v6sox, &v6data6, HDR_SIZE + sizeof (struct ip6_hdr) + 4 + writepos)
+               )
+                       ;
+               break;
+       case ND_ROUTER_ADVERT:
+               return;         /* the IPv6 Link is no router, drop */
+       case ND_NEIGHBOR_SOLICIT:
+               //
+               // Neighbor Solicitation is treated depending on its kind:
+               //  - the 6bed4 Router address is answered immediately
+               //  - discovery for the local IPv6 address is dropped
+               //  - discovery for fe80::/64 addresses is answered
+               //  - other peers start a process in the ndqueue
+               if ((memcmp (v6ndtarget, router_linklocal_address, 16) == 0) ||
+                   (memcmp (v6ndtarget, router_linklocal_address_complete, 16) == 0)) {
+                       advertise_6bed4_public_service (NULL);
+               } else if (memcmp (v6ndtarget, &v6listen, 16) == 0) {
+                       return;         /* yes you are unique, drop */
+               } else if (memcmp (v6ndtarget, v6listen_linklocal, 8) == 0) {
+                       //
+                       // Construct response for fe80::/64
+                       v6icmp6type = ND_NEIGHBOR_ADVERT;
+                       v6icmp6data [0] = 0x60; /* Solicited, Override */
+                       v6icmp6data [20] = 2;   /* Target Link-Layer Address */
+                       v6icmp6data [21] = 1;   /* Length is 1x 8 bytes */
+                       v6icmp6data [22] = v6icmp6data [12] ^ 0x02;
+                       v6icmp6data [23] = v6icmp6data [13];
+                       v6icmp6data [24] = v6icmp6data [14];
+                       v6icmp6data [25] = v6icmp6data [17];
+                       v6icmp6data [26] = v6icmp6data [18];
+                       v6icmp6data [27] = v6icmp6data [19];
+                       v6plen = htons (4 + 28);
+                       memcpy (v6dst6, v6src6, 16);
+                       memcpy (v6src6, &v6listen, 16);
+#ifdef PEER_USE_TAP
+                       memcpy (v6ether.h_dest, v6ether.h_source, 6);
+                       memcpy (v6ether.h_source, v6lladdr, 6);
+#endif
+                       v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 4 + 28);
+syslog (LOG_DEBUG, "Sending trivial reply to fe80::/64 type query\n");
+                       write (v6sox, &v6data6, HDR_SIZE + sizeof (struct ip6_hdr) + 4 + 28);
+                       return;
+               } else {
+// TODO
+#ifdef PEER_USE_TAP
+                       enqueue ((struct in6_addr *) v6ndtarget, (struct in6_addr *) v6src6, v6ether.h_source);
+#endif
+               }
+               break;
+       case ND_NEIGHBOR_ADVERT:
+               lldest [0] = v6dst6->s6_addr [8] ^ 0x02;
+               lldest [1] = v6dst6->s6_addr [9];
+               lldest [2] = v6dst6->s6_addr [10];
+               lldest [3] = v6dst6->s6_addr [13];
+               lldest [4] = v6dst6->s6_addr [14];
+               lldest [5] = v6dst6->s6_addr [15];
+               handle_6to4_plain_unicast (pktlen, lldest);
+               break;
+       case ND_REDIRECT:
+               //TODO:NOT_IMPLEMENTED_YET:ND_REDIRECT_FROM_6BED4//
+               //
+               // Redirect indicates that a more efficient bypass exists than
+               // the currently used route.  The remote peer has established
+               // this and wants to share that information to retain a
+               // symmetric communication, which is helpful in keeping holes
+               // in NAT and firewalls open.
+               //
+               return;
+       }
+}
+
+/*
+ * Receive an IPv6 package, check its address and pickup IPv4 address and
+ * port, then package it as a tunnel message and forward it to IPv4:port.
+ * Rely on the proper formatting of the incoming IPv6 packet, as it is
+ * locally generated.
+ */
+void handle_6to4 (void) {
+#ifndef PEER_USE_TAP
+       static uint8_t lladdr [] =
+       {
+               // UDP port
+               UDP_PORT_6BED4 & 0xff,
+               UDP_PORT_6BED4 >> 8,
+               // IPv4 address
+               SERVER_6BED4_IPV4_INT0,
+               SERVER_6BED4_IPV4_INT1,
+               SERVER_6BED4_IPV4_INT2,
+               SERVER_6BED4_IPV4_INT3
+       };
+#endif
+       //
+       // Receive the IPv6 package and ensure a consistent size
+       size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
+       if (rawlen == -1) {
+               return;         /* failure to read, drop */
+       }
+       if (rawlen < HDR_SIZE + sizeof (struct ip6_hdr) + 1) {
+               return;         /* packet too small, drop */
+       }
+#ifdef PEER_USE_TAP
+       if (v6ether.h_proto != htons (ETH_P_IPV6)) {
+               return;         /* not IPv6, drop */
+       }
+//TODO// syslog (LOG_DEBUG, "Packet from IPv6 stack, target %02x:%02x:%02x:%02x:%02x:%02x\n", v6ether.h_dest [0], v6ether.h_dest [1], v6ether.h_dest [2], v6ether.h_dest [3], v6ether.h_dest [4], v6ether.h_dest [5]);
+       //
+       // Ignore messages from the IPv6 stack to itself
+       if (memcmp (v6ether.h_dest, v6ether.h_source, 6) == 0) {
+               syslog (LOG_DEBUG, "TODO: Self-to-self messaging in IPv6 stack ignored\n");
+               return;
+       }
+#endif
+       /*
+        * Distinguish types of traffic:
+        * Non-plain, Plain Unicast, Plain Multicast
+        */
+       if ((v6type == IPPROTO_ICMPV6) &&
+                       (v6icmp6type >= 133) && (v6icmp6type <= 137)) {
+               //
+               // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+syslog (LOG_DEBUG, "Forwarding non-plain unicast from IPv6 to 6bed4\n");
+               handle_6to4_nd (rawlen);
+       } else if ((v6dst6->s6_addr [0] != 0xff) && !(v6dst6->s6_addr [8] & 0x01)) {
+               //
+               // Plain Unicast
+               if (v6hops-- <= 1) {
+                       return;
+               }
+syslog (LOG_DEBUG, "Forwarding plain unicast from IPv6 to 6bed4\n");
+#ifdef PEER_USE_TAP
+               handle_6to4_plain_unicast (rawlen, v6ether.h_dest);
+#else
+               handle_6to4_plain_unicast (rawlen, lladdr);
+#endif
+
+       } else {
+               //
+               // Plain Multicast
+               //TODO:IGNORE_MULTICAST//
+               //TODO// syslog (LOG_DEBUG, "%s: Plain multicast from 6bed4 Link to 6bed4 Network is not supported\n", program);
+       }
+}
+
+
+/*
+ * Send a single Neighbor Solicitation message over 6bed4.  This will
+ * be sent to the given 6bed4 address, and is usually part of a series
+ * of attempts to find a short-cut route to the 6bed4 peer.
+ */
+void solicit_6bed4_neighbor (const struct ndqueue *info, const uint8_t *addr6bed4) {
+       memcpy (v6src6, &info->source, 16);
+       memcpy (v6dst6, &info->target, 16);
+       v6type = IPPROTO_ICMPV6;
+       v6hops = 255;
+       v6icmp6type = ND_NEIGHBOR_SOLICIT;
+       v6icmp6code = 0;
+       v6icmp6data [0] =
+       v6icmp6data [1] =
+       v6icmp6data [2] =
+       v6icmp6data [3] = 0x00;
+       memcpy (v6icmp6data + 4, &info->target, 16);
+       v6icmp6data [20] = 1;   // option type is Source Link-Layer Address
+       v6icmp6data [21] = 1;   // option length is 1x 8 bytes
+       memcpy (v6icmp6data + 22, v6lladdr, 6);
+       uint16_t pktlen = sizeof (struct ip6_hdr) + 4 + 28;
+       //OLD// v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 28 + 8);
+       v6plen = htons (4 + 28);
+       v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 4 + 28);
+       handle_6to4_plain_unicast (sizeof (struct ip6_hdr) + 8 + 28 + 10, addr6bed4);
+       //TODO// Why these +8 and +10 are needed, I don't know yet!
+}
+
+
+/*
+ * Find a neighbor's 6bed4 address.  This is coordinated by the ndqueue,
+ * which schedules such tasks and makes them repeat.  Furthermore, a few
+ * attempts may be scheduled on the local network before attempts
+ * shift to the direct target IPv4/UDP addresses.  Of course the local
+ * network will only be scheduled if the public IPv4 address matches
+ * the one for the local node.
+ *
+ * This process is dequeued by reverse Neighbor Advertisements.  If none
+ * comes back in spite of the various Neighbor Solicitations sent, then
+ * the final action is to send a Neighbor Advertisement to the host with
+ * the Public 6bed4 Service as its target of last resort.  In case of
+ * this last resort, the process is not continued any further; the
+ * return value indicates whether the queue entry should be kept for
+ * another round.
+ */
+bool chase_neighbor_6bed4_address (struct ndqueue *info) {
+       uint8_t addr6bed4 [6];
+       static const uint8_t addr6bed4_lancast [8] = {
+               UDP_PORT_6BED4 & 0xff, UDP_PORT_6BED4 >> 8,
+               224, 0, 0, 1
+       };
+       if (info->todo_lancast > 0) {
+               // Attempt 1. Send to LAN multicast address (same public IP)
+               info->todo_lancast--;
+               solicit_6bed4_neighbor (info, addr6bed4_lancast);
+               return true;
+       } else if (info->todo_direct > 0) {
+               // Attempt 2. Send to target's direct IP address / UDP port
+               info->todo_direct--;
+               addr6bed4 [0] = info->target.s6_addr [8] ^ 0x02;
+               addr6bed4 [1] = info->target.s6_addr [9];
+               addr6bed4 [2] = info->target.s6_addr [10];
+               addr6bed4 [3] = info->target.s6_addr [13];
+               addr6bed4 [4] = info->target.s6_addr [14];
+               addr6bed4 [5] = info->target.s6_addr [15];
+               solicit_6bed4_neighbor (info, addr6bed4);
+               return true;
+       } else {
+               // Attempt 3. Respond with Public 6bed4 Service
+               syslog (LOG_INFO, "%s: Failed to find a bypass, passing back the 6bed4 Router\n", program);
+               advertise_6bed4_public_service (info);
+               return false;
+       }
+}
+
+
+/*
+ * Perform Router Solicitation.  This is the usual mechanism that is used
+ * on ethernet links as well, except that the 6bed4 permits fixed interface
+ * identifiers; for this client, the interface identifier will be 0x0001.
+ * The router always has interface identifier 0x0000 but it will now be
+ * addressed at the all-routers IPv6 address 0xff02::2 with the general
+ * source IPv6 address ::
+ */
+void solicit_router (void) {
+       v4name.sin_family = AF_INET;
+       memcpy (&v4name.sin_addr.s_addr, &v4listen, 4);
+       v4name.sin_port = htons (UDP_PORT_6BED4);
+       int done = 0;
+       int secs = 1;
+// syslog (LOG_DEBUG, "%s: Sending RouterSolicitation-IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %d\n", program,
+// ((uint8_t *) &v4name.sin_addr.s_addr) [0],
+// ((uint8_t *) &v4name.sin_addr.s_addr) [1],
+// ((uint8_t *) &v4name.sin_addr.s_addr) [2],
+// ((uint8_t *) &v4name.sin_addr.s_addr) [3],
+// ntohs (v4name.sin_port),
+(
+       sendto (v4sox,
+                       ipv6_router_solicitation,
+                       sizeof (ipv6_router_solicitation),
+                       MSG_DONTWAIT,
+                       (struct sockaddr *) &v4name, sizeof (v4name)));
+}
+
+
+/*
+ * Send a KeepAlive message.  This is an UDP/IPv4 message with no contents.
+ * The router will not respond, but that is okay; outgoing traffic is the
+ * way to keep holes in NAT and firewalls open.
+ */
+void keepalive (void) {
+       v4name.sin_family = AF_INET;
+       memcpy (&v4name.sin_addr.s_addr, &v4listen, 4);
+       v4name.sin_port = htons (UDP_PORT_6BED4);
+       int done = 0;
+       int secs = 1;
+       setsockopt (v4sox, IPPROTO_IP, IP_TTL, &keepalive_ttl, sizeof (keepalive_ttl));
+       sendto (v4sox,
+                       "",
+                       0,
+                       MSG_DONTWAIT,
+                       (struct sockaddr *) &v4name, sizeof (v4name));
+       setsockopt (v4sox, IPPROTO_IP, IP_TTL, &v4ttl, sizeof (v4ttl));
+}
+
+
+/* Regular maintenance is a routine that runs regularly to do one of two
+ * generic tasks: either it sends Router Solicitation messages to the
+ * Public 6bed4 Service, or it sends an empty UDP message somewhat in its
+ * direction to keep NAT/firewall holes open.
+ */
+void regular_maintenance (void) {
+       if (!got_lladdr) {
+               solicit_router ();
+               maintenance_time_cycle <<= 1;
+               maintenance_time_cycle += 1;
+               if (maintenance_time_cycle > maintenance_time_cycle_max) {
+                       maintenance_time_cycle = maintenance_time_cycle_max;
+               }
+               syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %ld seconds\n", maintenance_time_cycle);
+       } else {
+               syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
+               keepalive ();
+               maintenance_time_cycle = maintenance_time_cycle_max;
+       }
+       maintenance_time_sec = time (NULL) + maintenance_time_cycle;
+}
+
+
+/* Run the daemon core code, passing information between IPv4 and IPv6 and
+ * responding to tunnel requests if so requested.
+ */
+void run_daemon (void) {
+       fd_set io;
+       bool keep;
+       maintenance_time_sec = 0;       // trigger Router Solicitation
+       FD_ZERO (&io);
+       FD_SET (v4sox, &io);
+       FD_SET (v6sox, &io);
+       int nfds = (v4sox < v6sox)? (v6sox + 1): (v4sox + 1);
+       if (v4mcast != -1) {
+               FD_SET (v4mcast, &io);
+               if (v4mcast >= nfds) {
+                       nfds = v4mcast + 1;
+               }
+       }
+       while (1) {
+               struct timeval tout;
+               struct timeval now;
+               gettimeofday (&now, NULL);
+               if (maintenance_time_sec <= now.tv_sec) {
+                       regular_maintenance ();
+               }
+               tout.tv_sec = maintenance_time_sec - now.tv_sec;
+               tout.tv_usec = 0;
+               while (ndqueue && (
+                               ((ndqueue->next->tv.tv_sec == now.tv_sec)
+                                 && (ndqueue->next->tv.tv_usec <= now.tv_usec))
+                               || (ndqueue->next->tv.tv_sec < now.tv_sec))) {
+                       //
+                       // Run the entry's handler code
+                       syslog (LOG_DEBUG, "Queue at %ld.%03ld: Timed for %ld.%03ld", now.tv_sec, now.tv_usec / 1000, ndqueue->next->tv.tv_sec, ndqueue->next->tv.tv_usec / 1000);
+                       keep = chase_neighbor_6bed4_address (ndqueue->next);
+                       if (!keep) {
+                               dequeue (ndqueue->next);
+                               continue;
+                       }
+                       //
+                       // Make ndqueue point to the entry to run
+                       ndqueue = ndqueue->next;
+                       //
+                       // Add 50ms to the running time
+                       if (now.tv_usec < 950000) {
+                               ndqueue->tv.tv_usec = now.tv_usec +   50000;
+                               ndqueue->tv.tv_sec  = now.tv_sec  + 0;
+                       } else {
+                               ndqueue->tv.tv_usec = now.tv_usec -  950000;
+                               ndqueue->tv.tv_sec  = now.tv_sec  + 1;
+                       }
+               }
+               if (ndqueue && ((ndqueue->next->tv.tv_sec - now.tv_sec) < tout.tv_sec)) {
+                       tout.tv_sec  = ndqueue->next->tv.tv_sec  - now.tv_sec ;
+                       tout.tv_usec = ndqueue->next->tv.tv_usec - now.tv_usec;
+                       if (tout.tv_usec < 0) {
+                               tout.tv_usec += 1000000;
+                               tout.tv_sec  -= 1;
+                       }
+               }
+               if (select (nfds, &io, NULL, NULL, &tout) == -1) {
+                       syslog (LOG_ERR, "Select failed: %s\n", strerror (errno));
+               }
+               if (FD_ISSET (v4sox, &io)) {
+syslog (LOG_DEBUG, "Got unicast input\n");
+                       handle_4to6 (v4sox);
+               } else {
+                       FD_SET (v4sox, &io);
+               }
+               if (FD_ISSET (v6sox, &io)) {
+                       handle_6to4 ();
+               } else {
+                       FD_SET (v6sox, &io);
+               }
+               if (v4mcast != -1) {
+                       if (FD_ISSET (v4mcast, &io)) {
+syslog (LOG_DEBUG, "WOW: Got multicast input\n");
+                               handle_4to6 (v4mcast);
+                       } else {
+                               FD_SET (v4mcast, &io);
+                       }
+               }
+//fflush (stdout);
+       }
+}
+
+
+/* Option descriptive data structures */
+
+char *short_opt = "s:t:d:l:p:rk:feh";
+
+struct option long_opt [] = {
+       { "v4server", 1, NULL, 's' },
+       { "tundev", 1, NULL, 'd' },
+       { "default-route", 0, NULL, 'r' },
+       { "listen", 1, NULL, 'l' },
+       { "port", 1, NULL, 'p' },
+       { "ttl", 1, NULL, 't' },
+       { "foreground", 0, NULL, 'f' },
+       { "fork-not", 0, NULL, 'f' },
+       { "keepalive", 1, NULL, 'k' },
+       { "keepalive-period-ttl", 1, NULL, 'k' },
+       { "error-console", 0, NULL, 'e' },
+       { "help", 0, NULL, 'h' },
+       { NULL, 0, NULL, 0 }    /* Array termination */
+};
+
+
+/* Parse commandline arguments (and start to process them).
+ * Return 1 on success, 0 on failure.
+ */
+int process_args (int argc, char *argv []) {
+       int ok = 1;
+       int help = 0;
+       int done = 0;
+       unsigned long tmpport;
+       char *endarg;
+       default_route = false;
+       while (!done) {
+               switch (getopt_long (argc, argv, short_opt, long_opt, NULL)) {
+               case -1:
+                       done = 1;
+                       if (optind != argc) {
+                               fprintf (stderr, "%s: Extra arguments not permitted: %s...\n", program, argv [optind]);
+                               ok = 0;
+                       }
+                       break;
+               case 's':
+                       if (v4sox != -1) {
+                               ok = 0;
+                               fprintf (stderr, "%s: You can only specify a single server address\n", program);
+                               continue;
+                       }
+                       v4server = optarg;
+                       if (inet_pton (AF_INET, optarg, &v4peer.sin_addr) <= 0) {
+                               ok = 0;
+                               fprintf (stderr, "%s: Failed to parse IPv4 address %s\n", program, optarg);
+                               break;
+                       }
+                       memcpy (&v4listen, &v4peer.sin_addr, 4);
+                       v4sox = socket (AF_INET, SOCK_DGRAM, 0);
+                       if (v4sox == -1) {
+                               ok = 0;
+                               fprintf (stderr, "%s: Failed to allocate UDPv4 socket: %s\n", program, strerror (errno));
+                               break;
+                       }
+                       break;
+               case 'd':
+                       if (v6sox != -1) {
+                               ok = 0;
+                               fprintf (stderr, "%s: Multiple -d arguments are not permitted\n", program);
+                               break;
+                       }
+                       v6sox = open (optarg, O_RDWR);
+                       if (v6sox == -1) {
+                               ok = 0;
+                               fprintf (stderr, "%s: Failed to open tunnel device %s: %s\n", program, optarg, strerror (errno));
+                               break;
+                       }
+                       break;
+               case 'r':
+                       if (default_route) {
+                               fprintf (stderr, "%s: You can only request default route setup once\n", program);
+                               ok = 0;
+                               break;
+                       }
+                       default_route = true;
+                       break;
+               case 'l':
+                       if (inet_pton (AF_INET, optarg, &v4bind.sin_addr.s_addr) != 1) {
+                               fprintf (stderr, "%s: IPv4 address %s is not valid\n", program, optarg);
+                               ok = 0;
+                               break;
+                       }
+                       break;
+               case 'p':
+                       tmpport = strtoul (optarg, &endarg, 10);
+                       if ((*endarg) || (tmpport > 65535)) {
+                               fprintf (stderr, "%s: UDP port number %s is not valid\n", program, optarg);
+                               ok = 0;
+                               break;
+                       }
+                       if (tmpport & 0x0001) {
+                               fprintf (stderr, "%s: UDP port number %ld is odd, which is not permitted\n", program, tmpport);
+                               ok = 0;
+                               break;
+                       }
+                       v4bind.sin_port = htons (tmpport);
+                       break;
+               case 'f':
+                       if (foreground) {
+                               fprintf (stderr, "%s: You can only request foreground operation once\n", program);
+                               ok = 0;
+                               break;
+                       }
+                       foreground = true;
+                       break;
+               case 'e':
+                       if (log_to_stderr) {
+                               fprintf (stderr, "%s: You can only specify logging to stderr once\n", program);
+                               ok = 0;
+                               break;
+                       }
+                       log_to_stderr = true;
+                       break;
+               case 't':
+                       if (v4ttl_mcast != -1) {
+                               fprintf (stderr, "%s: You can set the ttl for multicast once\n", program);
+                               ok = 0;
+                               break;
+                       }
+                       char *zero;
+                       long setting = strtol(optarg, &zero, 10);
+                       if (*zero || (setting < 0) || (setting > 255)) {
+                               fprintf (stderr, "%s: Multicast radius must be a number in the range 0 to 255, inclusive, not %s\n", program, optarg);
+                               ok = 0;
+                               break;
+                       }
+                       v4ttl_mcast = setting;
+                       break;
+               case 'k':
+                       if (keepalive_ttl != -1) {
+                               fprintf (stderr, "%s: You can only set the keepalive period and TTL once\n", program);
+                               ok = 0;
+                               break;
+                       }
+                       char *rest;
+                       keepalive_period = strtol (optarg, &rest, 10);
+                       if (*rest == ',') {
+                               rest++;
+                               keepalive_ttl = strtol (rest, &rest, 10);
+                               if ((keepalive_ttl < 0) || (keepalive_ttl > 255)) {
+                                       fprintf (stderr, "%s: The keepalive TTL must be in the range 0 to 255, inclusive\n", program);
+                                       ok = 0;
+                                       break;
+                               }
+                       } else {
+                               keepalive_ttl = 3;
+                       }
+                       if (*rest) {
+                               fprintf (stderr, "%s: The format for keepalive configuration is 'period,ttl' or just 'period', but not %s\n", program, optarg);
+                               exit (1);
+                       }
+                       break;
+               default:
+                       ok = 0;
+                       help = 1;
+                       /* continue into 'h' to produce usage information */
+               case 'h':
+                       help = 1;
+                       break;
+               }
+               if (help || !ok) {
+                       done = 1;
+               }
+       }
+       if (help) {
+#ifdef HAVE_SETUP_TUNNEL
+               fprintf (stderr, "Usage: %s [-r] [-d /dev/tunX]\n       %s -h\n", program, program);
+#else
+               fprintf (stderr, "Usage: %s [-r] -d /dev/tunX\n       %s -h\n", program, program);
+#endif
+               return 0;
+       }
+       if (!ok) {
+               return 0;
+       }
+       if (keepalive_ttl != -1) {
+               maintenance_time_cycle_max = keepalive_period;
+       } else {
+               keepalive_ttl = 3;
+       }
+#ifdef HAVE_SETUP_TUNNEL
+       if (v6sox == -1) {
+               if (geteuid () != 0) {
+                       fprintf (stderr, "%s: You should be root, or use -d to specify an accessible tunnel device\n", program);
+                       return 0;
+               } else {
+                       ok = setup_tunnel ();
+                       if (!ok) {
+                               fprintf (stderr, "Failed to setup tunnel\n");
+                       }
+               }
+       }
+#else /* ! HAVE_SETUP_TUNNEL */
+       if (v6sox == -1) {
+               fprintf (stderr, "%s: You must specify a tunnel device with -d that is accessible to you\n", program);
+               return 0;
+       }
+#endif /* HAVE_SETUP_TUNNEL */
+       return ok;
+}
+
+
+/* The main program parses commandline arguments and forks off the daemon
+ */
+int main (int argc, char *argv []) {
+       //
+       // Initialise
+       program = strrchr (argv [0], '/');
+       if (program) {
+               program++;
+       } else {
+               program = argv [0];
+       }
+       memset (&v4name, 0, sizeof (v4name));
+       memset (&v4peer, 0, sizeof (v4peer));
+       memset (&v6name, 0, sizeof (v6name));
+       v4name.sin_family  = AF_INET ;
+       v4peer.sin_family  = AF_INET ;
+       v6name.sin6_family = AF_INET6;
+       // Fixed public server data, IPv4 and UDP:
+       v4server = SERVER_6BED4_IPV4_TXT;
+       v4peer.sin_addr.s_addr = htonl (SERVER_6BED4_IPV4_INT32);
+       v4name.sin_port = htons (UDP_PORT_6BED4);
+       v4peer.sin_port = htons (UDP_PORT_6BED4);
+       memcpy (&v4listen, &v4peer.sin_addr, 4);
+       memset (&v4bind, 0, sizeof (v4bind));
+       v4bind.sin_family = AF_INET;
+#ifndef PEER_USE_TAP
+       v4tunpi6.flags = 0;
+       v4tunpi6.proto = htons (ETH_P_IPV6);
+#endif
+       //
+       // Parse commandline arguments
+       openlog (program, LOG_NDELAY | LOG_PID | LOG_PERROR, LOG_DAEMON);
+       if (!process_args (argc, argv)) {
+               fprintf (stderr, "Argument processing failed\n");
+               exit (1);
+       }
+       if (!log_to_stderr) {
+               closelog ();
+               openlog (program, LOG_NDELAY | LOG_PID, LOG_DAEMON);
+       }
+       //
+       // Construct the 6bed4 Router's complete link-layer address
+       router_linklocal_address_complete [8] = (ntohs (v4peer.sin_port) & 0xff) ^ 0x02;
+       router_linklocal_address_complete [9] = ntohs (v4peer.sin_port) >> 8;
+       router_linklocal_address_complete [10] = ntohl (v4peer.sin_addr.s_addr) >> 24;
+       router_linklocal_address_complete [11] = 0xff;
+       router_linklocal_address_complete [12] = 0xfe;
+       memcpy (router_linklocal_address_complete + 13, &((uint8_t *) &v4peer.sin_addr) [1], 3);
+       //
+       // Create memory for the freequeue buffer
+       freequeue = calloc (freequeue_items, sizeof (struct ndqueue));
+       if (!freequeue) {
+               syslog (LOG_CRIT, "%s: Failed to allocate %d queue items\n", program, freequeue_items);
+               exit (1);
+       }
+       int i;
+       for (i = 1; i < freequeue_items; i++) {
+               freequeue [i].next = &freequeue [i-1];
+       }
+       freequeue = &freequeue [freequeue_items - 1];
+       //
+       // Create socket for normal outgoing (and return) 6bed4 traffic
+       if (v4sox == -1) {
+               v4sox = socket (AF_INET, SOCK_DGRAM, 0);
+               if (v4sox == -1) {
+                       syslog (LOG_CRIT, "%s: Failed to open a local IPv4 socket -- does your system still support IPv4?\n", program);
+                       exit (1);
+               }
+       }
+       struct sockaddr_in tmpaddr;
+       memset (&tmpaddr, 0, sizeof (tmpaddr));
+       tmpaddr.sin_family = AF_INET;
+       srand (getpid ());
+       uint16_t portn = rand () & 0x3ffe;
+       uint16_t port0 = portn + 16384;
+       //TODO// Move port iteration + allocation to separate function
+       while (portn < port0) {
+               tmpaddr.sin_port = htons ((portn & 0x3ffe) + 49152);
+               if (bind (v4sox, (struct sockaddr *) &tmpaddr, sizeof (tmpaddr)) == 0) {
+                       break;
+               }
+               portn += 2;
+       }
+       if (portn < port0) {
+               syslog (LOG_DEBUG, "Bound to UDP port %d\n", ntohs (tmpaddr.sin_port));
+       } else {
+               fprintf (stderr, "%s: All even dynamic ports rejected binding: %s\n", program, strerror (errno));
+               exit (1);
+       }
+       //
+       // Setup fragmentation, QoS and TTL options
+       u_int yes = 1;
+       u_int no = 0;
+#if defined(IP_DONTFRAG)
+       if (setsockopt (v4sox, IPPROTO_IP, IP_DONTFRAG, no, sizeof (no)) == -1) {
+               syslog (LOG_ERR, "Failed to permit fragmentation -- not all peers may be accessible with MTU 1280");
+       }
+#elif defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+       int pmtuflag = IP_PMTUDISC_DONT;
+       if (setsockopt (v4sox, IP_MTU_DISCOVER, IP_MTU_DISCOVER, &pmtuflag, sizeof (pmtuflag)) == -1) {
+               syslog (LOG_ERR, "Failed to permit fragmentation -- not all peers may be accessible with MTU 1280");
+       }
+#else
+#warning "Target system lacks support for controlling packet fragmentation"
+#endif
+       socklen_t soxlen = sizeof (v4qos);
+       if (getsockopt (v4sox, IPPROTO_IP, IP_TOS, &v4qos, &soxlen) == -1) {
+               syslog (LOG_ERR, "Quality of Service is not supported by the IPv4-side socket");
+               v4qos = 0;
+       }
+       v6tc = htonl (v4qos << 20);
+       soxlen = sizeof (v4ttl);
+       if (getsockopt (v4sox, IPPROTO_IP, IP_TTL, &v4ttl, &soxlen) == -1) {
+               syslog (LOG_ERR, "Time To Live cannot be varied on the IPv4 socket");
+               v4ttl = 64;
+       }
+       //
+       // Bind to the IPv4 all-nodes local multicast address
+       memset (&v4allnodes, 0, sizeof (v4allnodes));
+       v4allnodes.sin_family = AF_INET;
+       v4allnodes.sin_port = htons (UDP_PORT_6BED4);
+       v4allnodes.sin_addr.s_addr = htonl ( INADDR_ANY );
+       if (multicast) {
+               v4mcast = socket (AF_INET, SOCK_DGRAM, 0);
+               if (v4mcast != -1) {
+                       struct ip_mreq mreq;
+                       mreq.imr_multiaddr.s_addr = htonl ( (224L << 24) | 1L);
+                       mreq.imr_multiaddr.s_addr = htonl ( INADDR_ANY );
+                       if (bind (v4mcast, (struct sockaddr *) &v4allnodes, sizeof (v4allnodes)) != 0) {
+                               close (v4mcast);
+                               v4mcast = -1;
+                               syslog (LOG_ERR, "No LAN bypass: Failed to bind to IPv4 all-nodes: %s\n", strerror (errno));
+                       } else if (setsockopt (v4mcast, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (yes)) == -1) {
+                               close (v4mcast);
+                               v4mcast = -1;
+                               syslog (LOG_ERR, "No LAN bypass: Failed to share multicast port: %s\n", strerror (errno));
+                       } else if (setsockopt (v4mcast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) {
+                               close (v4mcast);
+                               v4mcast = -1;
+                               syslog (LOG_ERR, "No LAN bypass: Failed to setup multicast: %s\n", strerror (errno));
+                       } else if ((v4ttl_mcast != -1) && (setsockopt (v4mcast, IPPROTO_IP, IP_MULTICAST_TTL, &v4ttl_mcast, sizeof (v4ttl_mcast)) == -1)) {
+                               close (v4mcast);
+                               v4mcast = -1;
+                               syslog (LOG_ERR, "No LAN bypass: Failed to configure the multicast radius: %s\n", strerror (errno));
+                       }
+#if 0
+                       if (bind (v4mcast, (struct sockaddr *) &v4allnodes, sizeof (v4allnodes)) != 0) {
+                               close (v4mcast);
+                               v4mcast = -1;
+                               syslog (LOG_ERR, "%s: No LAN bypass: Failed to bind to IPv4 all-nodes\n", program);
+                       } else if (listen (v4mcast, 10) != 0) {
+                               close (v4mcast);
+                               v4mcast = -1;
+                               syslog (LOG_ERR, "%s: No LAN bypass: Failed to listen to IPv4 all-nodes\n", program);
+                       }
+#endif
+               }
+       } else {
+               syslog (LOG_INFO, "%s: No LAN bypass: Not desired\n", program);
+       }
+       //
+       // Construct an rtnetlink socket for neighbor cache interaction
+       rtsox = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       if (rtsox == -1) {
+               syslog (LOG_CRIT, "Failed to gain access to the neighbor cache: %s\n", strerror (errno));
+               exit (1);
+       }
+       memset (&rtname,   0, sizeof (rtname  ));
+       memset (&rtkernel, 0, sizeof (rtkernel));
+       rtname.nl_family = rtkernel.nl_family = AF_NETLINK;
+       rtname.nl_pid = getpid ();
+       if (bind (rtsox, (struct sockaddr *) &rtname, sizeof (rtname)) == -1) {
+               syslog (LOG_CRIT, "Failed to bind to the neighbor cache socket: %s\n", strerror (errno));
+               exit (1);
+       }
+       if (connect (rtsox, (struct sockaddr *) &rtkernel, sizeof (rtkernel)) == -1) {
+               syslog (LOG_CRIT, "Failed to connect to the neighbor cachr in the kernel; %s\n", strerror (errno));
+               exit (1);
+       }
+{ uint8_t testll [6];
+uint8_t test_address [] = { 0xfe, 0x80, 0,0,0,0,0,0, 0xc2, 0x25, 0x06, 0xff, 0xfe, 0xb0, 0x7e, 0xa6 };
+if (lookup_neighbor (test_address, testll)) {
+syslog (LOG_INFO, "Successfully retrieved LL: %02x:%02x:%02x:%02x:%02x:%02x\n", testll [0], testll [1], testll [2], testll [3], testll [4], testll [5]);
+} else { syslog (LOG_INFO, "Failed to find LL\n"); } }
+       //
+       // If port and/or listen arguments were provided, bind to them
+       if ((v4bind.sin_addr.s_addr != INADDR_ANY) || (v4bind.sin_port != 0)) {
+               if (bind (v4sox, (struct sockaddr *) &v4bind, sizeof (v4bind)) != 0) {
+                       syslog (LOG_CRIT, "%s: Failed to bind to local socket -- did you specify both address and port?\n", program);
+                       exit (1);
+               }
+       }
+       //
+       // Run the daemon
+       if (foreground) {
+               run_daemon ();
+       } else {
+               if (setsid () != -1) {
+                       syslog (LOG_CRIT, "%s: Failure to detach from parent session: %s\n", program, strerror (errno));
+                       exit (1);
+               }
+               switch (fork ()) {
+               case -1:
+                       syslog (LOG_CRIT, "%s: Failure to fork daemon process: %s\n", program, strerror (errno));
+                       exit (1);
+               case 0:
+                       close (0);
+                       if (! log_to_stderr) {
+                               close (1);
+                               close (2);
+                       }
+                       run_daemon ();
+                       break;
+               default:
+                       break;
+               }
+       }
+       //
+       // Report successful creation of the daemon
+       closelog ();
+       exit (0);
+}
+
diff --git a/src/6bed4router.c b/src/6bed4router.c
new file mode 100644 (file)
index 0000000..63b099d
--- /dev/null
@@ -0,0 +1,1261 @@
+/* 6bed4/router.c -- traffic forwarding daemon for public TSP service
+ *
+ * This is an implementation of the profile that makes TSP service publicly
+ * usable, that is without authentication.  However to avoid abuse of such
+ * a service, it is not anonymous -- IPv6 addresses contain the IPv4 address
+ * and port.
+ *
+ * This is an implementation of neighbour and router discovery over a
+ * tunnel that packs IPv6 inside UDP/IPv4.  This tunnel mechanism is
+ * targeted specifically at embedded devices that are to function on
+ * any network, including IPv4-only, while being designed as IPv6-only
+ * devices with a fallback to this tunnel.
+ *
+ * Interestingly, as a side-effect of this design the router daemon can be
+ * stateless.  Any further requirements that are stateful are most likely
+ * filtering, and that can be solved in stateful firewall configuration.
+ *
+ * The intention of TSP is to enable IPv4-only hosts to connecto to
+ * IPv6 services; the public TSP profile adds to that the ability to
+ * do it in a temporary manner.
+ *
+ * TODO: Should we translate ICMPv4 --> ICMPv6?
+ *
+ * From: Rick van Rein <rick@openfortress.nl>
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/icmp6.h>
+#include <arpa/inet.h>
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_ether.h>
+
+
+/* The following will initially fail, due to an IANA obligation to avoid
+ * default builds with non-standard options.
+ */
+#include "nonstd.h"
+
+
+#define MTU 1280
+
+/*
+ * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
+ * the -d option leads to an error, or to an attempt to setup the tunnel.
+ * The setup_tunnel() function used for that is defined per platform, such
+ * as for LINUX.  Remember to maintain the manpage's optionality for -d.
+ */
+#undef HAVE_SETUP_TUNNEL
+
+
+/* Global variables */
+
+/* SCTP structures are far from standardised, so we define our own header */
+struct my_sctphdr {
+       uint16_t source, dest;
+       uint32_t vtag;
+       uint32_t cksum;
+};
+
+char *program;
+
+int v4sox = -1;
+int v6sox = -1;
+
+char *v4server = NULL;
+char *v6server = NULL;
+char *v6prefix = NULL;
+
+const uint8_t v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+uint8_t v6listen_linklocal_complete [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+uint8_t lladdr_6bed4 [6];
+
+struct sockaddr_in  v4name;
+struct sockaddr_in6 v6name;
+
+struct in6_addr v6listen;
+struct in6_addr v6listen_complete;
+struct in_addr  v4listen;
+
+
+struct {
+       struct tun_pi tun;
+       union {
+               struct {
+                       struct ip6_hdr v6hdr;
+                       uint8_t data [MTU - sizeof (struct ip6_hdr)];
+               } idata;
+               struct v4frame {
+                       struct ip6_hdr v6hdr;
+                       union {
+                               struct icmp6_hdr  v6icmphdr;
+                               struct my_sctphdr v6sctphdr;
+                               struct tcphdr     v6tcphdr ;
+                               struct udphdr     v6udphdr ;
+                               struct {
+                                       struct udphdr     v6udphdr;
+                                       struct my_sctphdr v6sctphdr;
+                               } tdata;
+                       } adata;
+               } ndata;
+       } udata;
+} v4data6;
+
+#define v4tunpi6       ( v4data6.tun)
+#define v4data         ((uint8_t *) &v4data6.udata)
+#define v4hdr6         (&v4data6.udata.idata.v6hdr)
+#define v4src6         (&v4data6.udata.idata.v6hdr.ip6_src)
+#define v4dst6         (&v4data6.udata.idata.v6hdr.ip6_dst)
+
+#define v4v6payload    ((uint16_t *) &v4data6.udata.ndata.adata)
+#define v4v6plen       ( v4data6.udata.ndata.v6hdr.ip6_plen)
+#define v4v6nexthdr    ( v4data6.udata.ndata.v6hdr.ip6_nxt)
+#define v4v6hoplimit   ( v4data6.udata.ndata.v6hdr.ip6_hops)
+
+#define v4v6icmpdata   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_data8)
+#define v4v6icmptype   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_type)
+#define v4v6icmpcode   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_code)
+#define v4v6icmpcksum  ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_cksum)
+
+#define v4v6sctpofs    (offsetof (struct v4frame,adata.v6sctphdr))
+#define v4v6sctpsrcport        ( v4data6.udata.ndata.adata.v6sctphdr.source)
+#define v4v6sctpdstport        ( v4data6.udata.ndata.adata.v6sctphdr.dest)
+#define v4v6sctpcksum  ( v4data6.udata.ndata.adata.v6sctphdr.cksum)
+
+#define v4v6tunsctpofs (offsetof (struct v4frame,adata.tdata.v6sctphdr))
+#define v4v6tunsctpsrcport ( v4data6.udata.ndata.adata.tdata.v6sctphdr.source)
+#define v4v6tunsctpdstport ( v4data6.udata.ndata.adata.tdata.v6sctphdr.dest)
+#define v4v6tunsctpcksum   ( v4data6.udata.ndata.adata.tdata.v6sctphdr.cksum)
+
+#define v4v6tcpsrcport ( v4data6.udata.ndata.adata.v6tcphdr.source)
+#define v4v6tcpdstport ( v4data6.udata.ndata.adata.v6tcphdr.dest)
+#define v4v6tcpcksum   ( v4data6.udata.ndata.adata.v6tcphdr.check)
+
+#define v4v6udpsrcport ( v4data6.udata.ndata.adata.v6udphdr.source)
+#define v4v6udpdstport ( v4data6.udata.ndata.adata.v6udphdr.dest)
+#define v4v6udpcksum   ( v4data6.udata.ndata.adata.v6udphdr.check)
+
+#define v4ngbsoltarget (&v4data6.udata.ndata.adata.v6icmphdr.icmp6_data8 [4])
+
+
+struct {
+       struct tun_pi tun;
+       union {
+               uint8_t data [MTU];
+               struct v6frame {
+                       struct ip6_hdr v6hdr;
+                       union {
+                               struct icmp6_hdr  v6icmphdr;
+                               struct my_sctphdr v6sctphdr;
+                               struct tcphdr     v6tcphdr ;
+                               struct udphdr     v6udphdr ;
+                               struct {
+                                       struct udphdr     v6udphdr;
+                                       struct my_sctphdr v6sctphdr;
+                               } tdata;
+                       } adata;
+               } ndata;
+       } udata;
+       uint8_t zero;
+} v6data6;
+
+#define v6tuncmd       ( v6data6.tun)
+#define v6data         ( v6data6.udata.data)
+#define v6hdr6         (&v6data6.udata.ndata.v6hdr)
+#define v6src6         (&v6data6.udata.ndata.v6hdr.ip6_src)
+#define v6dst6         (&v6data6.udata.ndata.v6hdr.ip6_dst)
+#define v6hoplimit     ( v6data6.udata.ndata.v6hdr.ip6_hops)
+#define v6nexthdr      ( v6data6.udata.ndata.v6hdr.ip6_nxt)
+
+#define v6icmptype     ( v6data6.udata.ndata.adata.v6icmphdr.icmp6_type)
+#define v6icmpcksum    ( v6data6.udata.ndata.adata.v6icmphdr.icmp6_cksum)
+
+#define v6sctpcksum    ( v6data6.udata.ndata.adata.v6sctphdr.cksum)
+#define v6tunsctpcksum ( v6data6.udata.ndata.adata.tdata.v6sctphdr.cksum)
+
+#define v6sctpofs      (offsetof(struct v6frame,adata.v6sctphdr))
+#define v6tunsctpofs   (offsetof(struct v6frame,adata.tdata.v6sctphdr))
+
+#define v6tcpcksum     ( v6data6.udata.ndata.adata.v6tcphdr.check)
+
+#define v6udpcksum     ( v6data6.udata.ndata.adata.v6udphdr.check)
+#define v6udpdstport   ( v6data6.udata.ndata.adata.v6udphdr.dest)
+#define v6udpsrcport   ( v6data6.udata.ndata.adata.v6udphdr.source)
+
+
+uint8_t router_linklocal_address [] = {
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
+};
+
+uint8_t democlient_linklocal_address [] = {
+       0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
+};
+
+uint8_t allnodes_linklocal_address [] = {
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
+};
+
+uint8_t allrouters_linklocal_address [] = {
+       0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x02,
+};
+
+
+
+#ifndef MAXNUM_MASQHOST
+#define MAXNUM_MASQHOST 4
+#endif
+
+uint16_t num_masqhost = 0;
+uint8_t masqhost [MAXNUM_MASQHOST][16];
+
+// ports for 's', 't', 'u' -- SCTP, TCP, UDP have increasing numbers
+
+#ifndef MAXNUM_PORTPAIRS
+#define MAXNUM_PORTPAIRS 10
+#endif
+
+// masqportpairs holds triples <lowport,highport,masqhostnr>
+uint16_t num_masqportpairs [3] = { 0, 0, 0 };
+uint16_t masqportpairs [3][3*MAXNUM_PORTPAIRS];
+
+// same for ICMPv6
+uint16_t icmp_num_portpairs = 0;
+uint16_t icmp_portpairs [3] = { 1, 1, 0 };
+
+
+
+/*
+ *
+ * Driver routines
+ *
+ */
+
+#ifndef INTERFACE_NAME_6BED4
+#define INTERFACE_NAME_6BED4 "6bed4"
+#endif
+
+#ifdef LINUX
+#define HAVE_SETUP_TUNNEL
+/* Implement the setup_tunnel() command for Linux.
+ * Return 1 on success, 0 on failure.
+ */
+int setup_tunnel (void) {
+       v6sox = open ("/dev/net/tun", O_RDWR);
+       if (v6sox == -1) {
+               fprintf (stderr, "%s: Failed to access tunnel driver on /dev/net/tun: %s\n", program, strerror (errno));
+               return 0;
+       }
+       int ok = 1;
+       struct ifreq ifreq;
+       memset (&ifreq, 0, sizeof (ifreq));
+       strncpy (ifreq.ifr_name, INTERFACE_NAME_6BED4, IFNAMSIZ);
+       ifreq.ifr_flags = IFF_TUN;
+       if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
+               ok = 0;
+       }
+       ifreq.ifr_name [IFNAMSIZ] = 0;
+       char cmd [512+1];
+       snprintf (cmd, 512, "/sbin/ip -6 addr flush dev %s", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = 0;
+       }
+       snprintf (cmd, 512, "/sbin/ip addr add fe80::0 dev %s scope link", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = 0;
+       }
+       snprintf (cmd, 512, "/sbin/ip -6 addr add %s dev %s", v6prefix, ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = 0;
+       }
+       snprintf (cmd, 512, "/sbin/ip link set %s up mtu 1280", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = 0;
+       }
+       if (!ok) {
+               close (v6sox);  /* This removes the tunnel interface */
+               fprintf (stderr, "Failed to setup tunnel \"%s\"\n", INTERFACE_NAME_6BED4);
+       }
+       return ok;
+}
+#endif /* LINUX */
+
+
+/*
+ *
+ * Utility functions
+ *
+ */
+
+
+/* Produce an IPv6 address following the 6bed4 structures.
+ *  - The top half is taken from v6listen
+ *  - The bottom contains IPv4 address and port from v4name
+ *  - The last 14 bits are filled with the lanip parameter
+ */
+void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
+       memcpy (&dst_ip6->s6_addr [0], &v6listen, 8);
+       dst_ip6->s6_addr32 [2] = v4name.sin_addr.s_addr;
+       dst_ip6->s6_addr16 [6] = v4name.sin_port;
+       dst_ip6->s6_addr  [14] = ((dst_ip6->s6_addr [8] & 0x03) << 6)
+                              | ((lanip >> 8) & 0x3f);
+       dst_ip6->s6_addr  [15] = (lanip & 0xff);
+       dst_ip6->s6_addr  [8] &= 0xfc;
+}
+
+/* Calculate the ICMPv6 checksum field
+ */
+uint16_t icmp6_checksum (struct in6_addr *src6, struct in6_addr *dst6,
+                               uint16_t *payload, size_t payloadlen) {
+       uint16_t plenword = htons (payloadlen); // our ICMPv6 is small
+       uint16_t nxthword = htons (IPPROTO_ICMPV6);
+       uint16_t *area [] = { src6->s6_addr16, dst6->s6_addr16,
+                               &plenword, &nxthword,
+                               payload, &payload [2] };
+       uint8_t areawords [] = { 8, 8, 1, 1, 1, (payloadlen >> 1) - 2 };
+       uint32_t csum = 0;
+       u_int8_t i, j;
+       for (i=0; i < 6; i++) {
+               for (j=0; j<areawords [i]; j++) {
+                       csum += ntohs (area [i] [j]);
+               }
+       }
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = htons (~csum);
+       return csum;
+}
+
+
+/* Mangle a packet by replacing an IPv6 address, correcting the checksum.
+ * In all of ICMPv6, UDPv6, TCPv6, SCTPv6, the checksumming is a 1's cpl
+ * of a sum of 16-bit words; the position of the words don't matter and
+ * we can subtract the old address' words and add the new address' words.
+ * Or, as a result of the one's complement, the opposite.  There is some
+ * inclusion of the top half of a 32-bit sum, but that is also neutral.
+ */
+void masquerade_address (struct in6_addr *var,
+                       const struct in6_addr *new,
+                       uint16_t *csum_field) {
+       int32_t csum = ntohs (~*csum_field);
+       uint8_t i;
+       for (i=0; i<8; i++) {
+               csum -= ntohs (var->s6_addr16 [i]);
+               var->s6_addr16 [i] = new->s6_addr16 [i];
+               csum += ntohs (var->s6_addr16 [i]);
+       }
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = (csum & 0xffff) + (csum >> 16);
+       *csum_field = htons (~csum);
+}
+
+
+/* Send an ICMPv6 reply.  This is constructed at the tunnel end, from
+ * the incoming message.  The parameter indicates how many bytes the
+ * ICMPv6 package counts after the ICMPv6 header.  It must be 4 (mod 8).
+ *
+ * Actions: v4/udp src becomes dest, set v4/udp/v6 src, len, cksum, send.
+ *          reply is always to v4src6, except that if it starts with
+ *         0x00,0x00 it will be replaced with allnodes_linklocal_address.
+ */
+void icmp6_reply (size_t icmp6bodylen) {
+       v4v6hoplimit = 255;
+       if ((icmp6bodylen & 0x07) != 4) {
+               return;   /* illegal length, drop */
+       }
+       v4v6plen = htons (icmp6bodylen + 4);
+       memcpy (v4dst6,
+               (v4src6->s6_addr16 [0])
+                       ? (uint8_t *) v4src6
+                       : allnodes_linklocal_address,
+               16);
+       memcpy (v4src6, router_linklocal_address, 16);
+       v4v6icmpcksum = icmp6_checksum (v4src6, v4dst6,
+                               v4v6payload, ntohs (v4v6plen));
+       //
+       // Send the message to the IPv4 originator port
+printf ("Sending ICMPv6-IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %zd\n",
+((uint8_t *) &v4name.sin_addr.s_addr) [0],
+((uint8_t *) &v4name.sin_addr.s_addr) [1],
+((uint8_t *) &v4name.sin_addr.s_addr) [2],
+((uint8_t *) &v4name.sin_addr.s_addr) [3],
+ntohs (v4name.sin_port),
+       sendto (v4sox,
+                       v4data,
+                       sizeof (struct ip6_hdr) + 4 + icmp6bodylen,
+                       MSG_DONTWAIT,
+                       (struct sockaddr *) &v4name, sizeof (v4name)));
+}
+
+
+/* Append the current prefix to an ICMPv6 message.  Incoming optidx
+ * and return values signify original and new offset for ICMPv6 options.
+ * The endlife parameter must be set to obtain zero lifetimes, thus
+ * instructing the tunnel client to stop using an invalid prefix.
+ */
+size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
+       v4v6icmpdata [optidx++] = 3;    // Type
+       v4v6icmpdata [optidx++] = 4;    // Length
+       v4v6icmpdata [optidx++] = 114;  // This is a /114 prefix
+       v4v6icmpdata [optidx++] = 0xc0; // L=1, A=1, Reserved1=0
+       memset (v4v6icmpdata + optidx, endlife? 0x00: 0xff, 8);
+       optidx += 8;
+                                       // Valid Lifetime: Zero / Infinite
+                                       // Preferred Lifetime: Zero / Infinite
+       memset (v4v6icmpdata + optidx, 0, 4);
+       optidx += 4;
+                                       // Reserved2=0
+       addr_6bed4 ((struct in6_addr *) (v4v6icmpdata + optidx), 0);
+                                       // Set IPv6 prefix
+       optidx += 16;
+       return optidx;
+}
+
+
+/*
+ * TODO: DEPRECATED
+ *
+ * Append a Destination Link-Layer Address Option to an ICMPv6
+ * message.  The address is comprised from the remote's UDP port
+ * and IPv4 address, as seen by the router.  They are supplied
+ * in that order, in network byte order.  The resulting address
+ * is 6 bytes, but even though that makes it look like a MAC
+ * address, it really is another beast.
+ * Note that no effort is made in the router to recognise the
+ * "illegal port number" 0x3333 -- the client needs a message
+ * and will recognise it and act on it.
+ */
+size_t icmp6_dest_linkaddr (size_t optidx) {
+       uint8_t typelen [2] = { ND_OPT_DESTINATION_LINKADDR, 1 };
+       memcpy (v4v6icmpdata + optidx + 0, &typelen, 2);
+       v4v6icmpdata [optidx + 2] = ntohs (v4name.sin_port) & 0xff;
+       v4v6icmpdata [optidx + 3] = ntohs (v4name.sin_port) >> 8;
+       memcpy (v4v6icmpdata + optidx + 4, &v4name.sin_addr, 4);
+       optidx += 8;
+       return optidx;
+}
+
+
+/*
+ * Test if an address is a local override.  This function is compiled-in
+ * to support hosts with a /64 from their own ISP and nothing more; they
+ * need to access local IPv6 addresses.  We rely on the 6bed4 port being
+ * 0 to decide that an address cannot be anything but a local override.
+ * Define LOCAL_OVERRIDES_PORT0 to enable this feature.
+ */
+#ifdef LOCAL_OVERRIDES_PORT0
+static inline bool is_local_override (struct in6_addr *ip6) {
+       return (ip6->s6_addr16 [6] == 0) && (memcmp (ip6->s6_addr, &v6listen, 8) == 0);
+}
+#else
+#define is_local_override(_) false
+#endif
+
+
+/*
+ * Test if the provided IPv6 address matches the prefix used for 6bed4.
+ *TODO: This is oversimplistic, it only cares for the Hetzner /64
+ */
+static inline bool is_6bed4 (struct in6_addr *ip6) {
+       return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
+}
+
+/* Test if the provided IPv6 address matches the fc64::/16 prefix.
+ * If so, the traffic may be bounced using 6bed4 traffic, but it
+ * must not be relayed to the native IPv6 side.
+ * TODO: Perhaps allow only configured <netid>, so fc64:<netid>::/32
+ */
+static inline bool is_fc64 (struct in6_addr *ip6) {
+       return ip6->s6_addr16 [0] == htons (0xfc64);
+}
+
+/* Test if the src and destination share the same /114 and the destination
+ * fills up the rest with zero bits; in other words, test that this is a
+ * package targeted from a 6bed4peer node to its 6bed4router.  This is used
+ * to distinguish special ICMPv6 messages, as well as masquerading targets.
+ */
+static inline bool is_mine (struct in6_addr *src6, struct in6_addr *dst6) {
+       return ((dst6->s6_addr [15] == 0x00)
+               && ((dst6->s6_addr [14] & 0x3f) == 0x00)
+               && (memcmp (dst6->s6_addr, src6->s6_addr, 14) == 0));
+}
+
+
+/*
+ * Validate the originator's IPv6 address.  It should match the
+ * UDP/IPv4 coordinates of the receiving 6bed4 socket.  Also,
+ * the /64 prefix (but not the /114 prefix!) must match v6listen.
+ */
+bool validate_originator (struct in6_addr *ip6) {
+       uint32_t addr;
+       //
+       // Require non-local top halves to match our v6listen_linklocal address
+       // We will enforce our own fallback address (and fc64:<netid>::/32)
+       if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
+               if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
+                       return false;
+               }
+       }
+       //
+       // Require the sender port to appear in its IPv6 address
+       if (v4name.sin_port != ip6->s6_addr16 [6]) {
+               return false;
+       }
+       //
+       // Require the sender address to appear in its IPv6 address
+       addr = ntohl (ip6->s6_addr32 [2]) & 0xfcffffff;
+       addr |= ((uint32_t) (ip6->s6_addr [14] & 0xc0)) << (24-6);
+       if (addr != ntohl (v4name.sin_addr.s_addr)) {
+               return false;
+       }
+       //
+       // We passed with flying colours
+       return true;
+}
+
+
+
+/*
+ * Major packet processing functions
+ */
+
+
+/*
+ * Respond to a Router Solicitation received over the 6bed4 Network.
+ */
+void handle_6bed4_router_solicit (void) {
+       struct in6_addr observed;
+       v4v6icmptype = ND_ROUTER_ADVERT;
+       v4v6icmpdata [0] = 0;                   // Cur Hop Limit: unspec
+       v4v6icmpdata [1] = 0x18;                // M=0, O=0
+                                               // H=0, Prf=11=Low
+                                               // Reserved=0
+// TODO: wire says 0x44 for router_adv.flags
+       size_t writepos = 2;
+       memset (v4v6icmpdata+writepos, 0xff, 2+4+4);
+                                       // Router Lifetime: max, 18.2h
+                                       // Reachable Time: max
+                                       // Retrans Timer: max
+       writepos += 2+4+4;
+       writepos = icmp6_prefix (writepos, 0);
+       icmp6_reply (writepos);
+}
+
+
+/* Handle the IPv4 message pointed at by msg as a neighbouring command.
+ *
+ * Type        Code    ICMPv6 meaning                  Handling
+ * ----        ----    -----------------------------   ----------------------------
+ * 133 0       Router Solicitation             Send Router Advertisement
+ * 134 0       Router Advertisement            Ignore
+ * 135 0       Neighbour Solicitation          Send Neighbour Advertisement
+ * 136 0       Neighbour Advertisement         Ignore
+ * 137 0       Redirect                        Ignore
+ */
+void handle_4to6_nd (ssize_t v4ngbcmdlen) {
+       uint16_t srclinklayer;
+       if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
+               return;
+       }
+       if (v4v6icmpcode != 0) {
+               return;
+       }
+       if (icmp6_checksum (v4src6, v4dst6,
+                               v4v6payload, v4ngbcmdlen-sizeof (struct ip6_hdr)
+                       ) != v4v6icmpcksum) {
+               return;
+       }
+       //
+       // Approved.  Perform neighbourly courtesy.
+       switch (v4v6icmptype) {
+       case ND_ROUTER_SOLICIT:
+               //
+               // Validate Router Solicitation
+               srclinklayer = 0;
+               if (v4ngbcmdlen == sizeof (struct ip6_hdr) + 8 + 8) {
+                       // One option is possible, the source link-layer address
+                       if (v4v6icmpdata [4] != 1 || v4v6icmpdata [5] != 1) {
+                               break;   /* bad opton, ignore */
+                       }
+                       // The source link-layer address is end-aligned
+                       srclinklayer = (v4v6icmpdata [10] << 8) | v4v6icmpdata [11];
+                       if (srclinklayer == 0) {
+                               break;   /* illegal, ignore */
+                       }
+               } else if (v4ngbcmdlen == sizeof (struct ip6_hdr) + 8) {
+                       srclinklayer = 0;   /* set for latter filling */
+               } else {
+                       break;   /* illegal length, drop */
+               }
+               //
+               // Having accepted the Router Advertisement, respond
+               handle_6bed4_router_solicit ();
+               break;
+       case ND_NEIGHBOR_SOLICIT:
+               //
+               // Validate Neigbour Solicitation
+               if (!validate_originator (v4src6)) {
+                       break;  /* bad source address, drop */
+               }
+               if ((v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) &&
+                   (v4ngbcmdlen != sizeof (struct ip6_hdr) + 24 + 8)) {
+                       break;   /* funny length, drop */
+               }
+               if ((memcmp (v4ngbsoltarget, v6listen_linklocal, 16) != 0) &&
+                    (memcmp (v4ngbsoltarget, v6listen_linklocal_complete, 16) != 0) &&
+                    (memcmp (v4ngbsoltarget, &v6listen_complete, 16) != 0)) {
+                       break;  /* target not known here, drop */
+               }
+               //
+               // Construct Neigbour Advertisement
+               v4v6icmptype = ND_NEIGHBOR_ADVERT;
+               v4v6icmpdata [0] = 0xc0;
+               v4v6icmpdata [1] =
+               v4v6icmpdata [2] =
+               v4v6icmpdata [3] = 0x00;        // R=1, S=1, O=1, Reserved=0
+               memcpy (v4v6icmpdata +  4, &v6listen_complete, 16);
+               // Append option: the target link-layer address
+               // Note: wire does not include target link-layer address
+               v4v6icmpdata [20] = 2;          // Type: Target Link-Layer Addr
+               v4v6icmpdata [21] = 1;          // Length: 1x 8 bytes
+               memcpy (v4v6icmpdata + 22, lladdr_6bed4, 6);
+               icmp6_reply (28);       // 28 is the ICMPv6 response length
+               break;
+       case ND_ROUTER_ADVERT:
+       case ND_NEIGHBOR_ADVERT:
+       case ND_REDIRECT:
+               break;   /* drop */
+       }
+}
+
+
+/*
+ * Forward a message to the masquerading target, or drop it.  This is done
+ * with non-6bed4 packets that are sent from a 6bed4peer node to its
+ * 6bed4router address; see is_mine() for the definition.  The general idea
+ * is that the address that is to be masqueraded can later be reconstructed
+ * from the 6bed4peer's address.
+ */
+void handle_4to6_masquerading (ssize_t v4datalen) {
+       fprintf (stderr, "Traffic to 6bed4router may be fit for masquerading\n");
+       uint16_t *portpairs = NULL;
+       uint16_t numpairs = 0;
+       uint16_t port = 0;
+       uint16_t *csum_field = NULL;
+       uint16_t sctp_offset = 0;
+       uint16_t skip_csum;
+       switch (v4v6nexthdr) {
+       case IPPROTO_SCTP:
+               portpairs = masqportpairs [0];  // 's'
+               numpairs  = num_masqportpairs [0];
+               port = ntohs (v4v6sctpdstport);
+               csum_field = &skip_csum;
+               sctp_offset = v4v6sctpofs;
+               break;
+       case IPPROTO_TCP:
+               portpairs = masqportpairs [1];  // 't'
+               numpairs  = num_masqportpairs [1];
+               port = ntohs (v4v6tcpdstport);
+               csum_field = &v4v6tcpcksum;
+               break;
+       case IPPROTO_UDP:
+               portpairs = masqportpairs [2];  // 'u'
+               numpairs  = num_masqportpairs [2];
+               port = ntohs (v4v6udpdstport);
+               csum_field = &v4v6udpcksum;
+               if ((port == 9899) && (v4v6udpsrcport == v4v6udpdstport)) {
+                       // SCTP tunneled over UDP (keep csum_field)
+                       portpairs = masqportpairs [0];
+                       numpairs = num_masqportpairs [0];
+                       port = ntohs (v4v6tunsctpdstport);
+                       sctp_offset = v4v6tunsctpofs;
+               }
+               break;
+       case IPPROTO_ICMPV6:
+               portpairs = icmp_portpairs;
+               numpairs = icmp_num_portpairs;
+               port = icmp_portpairs [0];
+               csum_field = &v4v6icmpcksum;
+               break;
+       default:
+               return;
+       }
+       fprintf (stderr, "DEBUG: Looking for masquerading of port %d in %d entries\n", port, numpairs);
+       while (numpairs-- > 0) {
+               if ((port >= portpairs [0]) && (port <= portpairs [1])) {
+                       //
+                       // Replace masqueraded address by 6bed4router's
+                       fprintf (stderr, "DEBUG: Passing traffic to masquerading address number %d\n", portpairs [2]);
+                       masquerade_address (v4dst6,
+                               (struct in6_addr *) masqhost [portpairs [2]],
+                               csum_field);
+                       if (sctp_offset > 0) {
+                               //TODO// Recompute SCTP's CRC-32 checksum
+                               // recompute_sctp_checksum (ip6hdr, sctp_offset, framelen);
+                               //BUT: SCTP checksum does not use pseudo IPhdr
+                               //SO:  Address masquerading goes undetected :-D
+                       }
+                       //
+                       // Forward immediately, and return from this function
+printf ("Writing Masqueraded IPv6, result = %zd\n",
+                       write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
+                       return;
+               }
+               portpairs += 3;
+       }
+       //
+       // Silently skip the offered packet
+}
+
+
+/* 
+ * Forward a message received over the 6bed4 Network over IPv6.
+ * Note that existing checksums will work well, as only the
+ * Hop Limit has been altered, and this is not part of the
+ * checksum calculations.
+ */
+void handle_4to6_plain_unicast (ssize_t v4datalen) {
+printf ("Writing IPv6, result = %zd\n",
+       write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
+}
+
+
+/*
+ * Forward a 6bed4 message to another 6bed4 destination address.
+ * Local address prefixes fc64:<netid>:<ipv4>::/64 are also relayed.
+ * Note that existing checksums will work well, as only the
+ * Hop Limit has been altered, and this is not part of the
+ * checksum calculations.
+ */
+void relay_4to4_plain_unicast (uint8_t* data, ssize_t v4datalen, struct in6_addr *ip6) {
+       v4name.sin_port = htons (ip6->s6_addr [12] << 8 | ip6->s6_addr [13]);
+       uint8_t *addr = (uint8_t *) &v4name.sin_addr.s_addr;
+       addr [0] = (ip6->s6_addr [8] & 0xfc) | ip6->s6_addr [14] >> 6;
+       memcpy (addr + 1, ip6->s6_addr + 9, 3);
+printf ("Relaying over 6bed4 Network to %d.%d.%d.%d:%d, result = %zd\n",
+((uint8_t *) &v4name.sin_addr.s_addr) [0],
+((uint8_t *) &v4name.sin_addr.s_addr) [1],
+((uint8_t *) &v4name.sin_addr.s_addr) [2],
+((uint8_t *) &v4name.sin_addr.s_addr) [3],
+ntohs (v4name.sin_port),
+       sendto (v4sox,
+                       data, v4datalen,
+                       MSG_DONTWAIT,
+                       (struct sockaddr *) &v4name, sizeof (v4name)));
+}
+
+
+/* Receive a tunnel package, and route it to either the handler for the
+ * tunnel protocol, or to the handler that checks and then unpacks the
+ * contained IPv6.
+ */
+void handle_4to6 (void) {
+       uint8_t buf [1501];
+       ssize_t buflen;
+       socklen_t adrlen = sizeof (v4name);
+       //
+       // Receive IPv4 package, which may be tunneled or a tunnel request
+       buflen = recvfrom (v4sox,
+                       v4data, MTU,
+                       MSG_DONTWAIT,
+                       (struct sockaddr *) &v4name, &adrlen
+               );
+       if (buflen == -1) {
+               printf ("%s: Error receiving IPv4-side package: %s",
+                               program, strerror (errno));
+               return;
+       }
+       if (buflen < sizeof (struct ip6_hdr)) {
+               return;
+       }
+       if ((v4data [0] & 0xf0) != 0x60) {
+               // Not an IPv6 packet
+               return;
+       }
+       //
+       // Handle the tunneled IPv6 package (dependent on its class)
+       if ((v4v6nexthdr == IPPROTO_ICMPV6) &&
+                       (v4v6icmptype >= 133) && (v4v6icmptype <= 137)) {
+               //
+               // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+               if (v4v6hoplimit != 255) {
+                       return;
+               }
+               handle_4to6_nd (buflen);
+       } else if (is_mine (v4src6, v4dst6)) {
+               handle_4to6_masquerading (buflen);
+       } else if ((v4dst6->s6_addr [0] != 0xff) && !(v4dst6->s6_addr [8] & 0x01)) {
+               //
+               // Plain Unicast
+               if (is_local_override (v4dst6)) {
+                       handle_4to6_plain_unicast (buflen);
+               } else if (validate_originator (v4src6)) {
+                       if (v4v6hoplimit-- <= 1) {
+                               return;
+                       }
+                       if (is_6bed4 (v4dst6) || is_fc64 (v4dst6)) {
+                               relay_4to4_plain_unicast (v4data, buflen, v4dst6);
+                       } else {
+                               handle_4to6_plain_unicast (buflen);
+                       }
+               } else if (is_6bed4 (v4src6)) {
+                       // The sender must not have kept NAT/firewall holes
+                       // open and should be instructed about a change in
+                       // its 6bed4 Link-Local Address.
+                       handle_6bed4_router_solicit ();
+               }
+       } else {
+               //
+               // Plain Multicast
+               //OPTIONAL// validate_originator, hoplimit, relay_mcast.
+               return;
+       }
+}
+
+
+/* Receive an IPv6 package, check its address and pickup IPv4 address and
+ * port, then package it as a tunnel message and forward it to IPv4:port.
+ */
+void handle_6to4 (void) {
+       uint16_t mqh;
+       //
+       // Receive the IPv6 package and ensure a consistent size
+       size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
+       if (rawlen == -1) {
+               return;         /* error reading, drop */
+       }
+       if (rawlen < sizeof (struct tun_pi) + sizeof (struct ip6_hdr) + 1) {
+               return;         /* too small, drop */
+       }
+       if (v6tuncmd.proto != htons (ETH_P_IPV6)) {
+               return;         /* no IPv6, drop */
+       }
+       if ((v6nexthdr == IPPROTO_ICMPV6) &&
+                       (v6icmptype >= 133) && (v6icmptype <= 137)) {
+               return;         /* not plain IPv6, drop */
+       }
+       if (v6hoplimit-- <= 1) {
+               // TODO: Send back an ICMPv6 error message
+               return;         /* hop limit exceeded, drop */
+       }
+       if ((v6dst6->s6_addr [0] == 0xff) /* TODO:UDP_PORT_NOT_YET_FORCED_TO_EVEN || (v6dst6->s6_addr [8] & 0x01) */ ) {
+printf ("Received multicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
+               //OPTIONAL// handle_6to4_plain_multicast ()
+               return;         /* multicast, drop */
+       }
+printf ("Received plain unicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
+       //
+       // Replace masqueraded addresses configured with -m and its default ::1
+       //TODO// Better use the port number to find the find the masqhost
+       for (mqh=0; mqh<num_masqhost; mqh++) {
+               if (memcmp (v6src6, masqhost [mqh], 16) == 0) {
+printf ("Masqueraded sender address in 6to4 set to the client's 6bed4router address\n");
+                       uint16_t *csum_field = NULL;
+                       uint16_t fake_csum;
+                       struct in6_addr masq_src;
+                       uint16_t sctp_offset = 0;
+                       switch (v6nexthdr) {
+                       case IPPROTO_SCTP:
+&