This version appears to be working fine -- specifically, peer2peer
authorRick van Rein <vanrein@air.opera>
Tue, 20 Mar 2012 05:13:34 +0000 (06:13 +0100)
committerRick van Rein <vanrein@air.opera>
Tue, 20 Mar 2012 05:13:34 +0000 (06:13 +0100)
It supports QoS (untested) and keepalive parameterisation (default -k 30,3)

12 files changed:
6bed4.man [deleted file]
6bed4peer.man [new file with mode: 0644]
6bed4router.man [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
Makefile.in
configure
democlient.c [deleted file]
fedora/6bed4.spec [new file with mode: 0644]
inetdraft.rst [deleted file]
peer.c [new file with mode: 0644]
pubtsp.man [deleted file]
router.c

diff --git a/6bed4.man b/6bed4.man
deleted file mode 100644 (file)
index 2716feb..0000000
--- a/6bed4.man
+++ /dev/null
@@ -1,212 +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
-6bed4 \- server-side daemon for 6bed4 service
-.SH SYNOPSYS
-.B 6bed4
-[\fB\-t\fR \fI/dev/tunX\fR] \fB\-l\fR \fIv4addr\fR \fB\-L\fR \fIv6prefix/64\fR
-.PP
-.B 6bed4
-[\fB\-h\fR]
-.SH DESCRIPTION
-.PP
-Through a \fB6bed4\fR daemon, it is possible to make a small range of IPv6
-addresses available to 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
-These tunnels are primarily intended for embedded devices, to assist them
-in instant changeover from being IPv4-only to 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 \fB6bed4\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\-t\fR \fI/dev/tunX\fR
-.TP
-\fB\-\-tundev\fR \fI/dev/tunX\fR
-Instead of creating a tunnel for the duration that \fB6bed4\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 \fB6bed4\fR.  All that is required is acccess to
-the tunnel device by the user that runs \fB6bed4\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 TSP
-tunnel commands and packaged-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\-t\fR option is given, a tunnel will be created for the time that
-\fB6bed4\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 \fBpublicTSP\fR,
-nor will the general ablity to forward IPv6 traffic.
-.TP
-\fB\-h\fR
-.TP
-\fB\-\-help\fR
-Print usage information and exit.
-.SH ADDRESS FORMAT
-.PP
-An IPv6 address used from \fB6bed4\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 \fB6bed4\fR are:
-.PP
-\fIv6prefix\fR + \fIv4addr\fR + \fIudp-port\fR + \fIlocalnet\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 \fB6bed4\fR.
-.PP
-The \fIlocalnet\fR defaults to 0, but may be used to differentiate up to
-65,536 different hosts accessible through the same TSP client.  As
-the main application foreseen for \fB6bed4\fR is to get IPv6-only tools and
-devices working on an IPv4-only network, this is not a probable setup,
-but it is nevertheless possible.  The current version of \fB6bed4\fR does
-not actually hand this over as a subnet.
-.PP
-The router address that \fB6bed4\fR chooses for itself consists of the
-\fIv6prefix\fR with zeroes appended; this falls outside the setup above,
-because neither IPv4 addresses nor UDP port numbers are not permitted
-te be zero-valued.
-.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 \fB6bed4\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 \fB6bed4\fR.
-Note that the TSP protocol provides this necessary information at the
-time the TSP tunnel is created.
-.PP
-It is recommended to keep NAT state in tact by regularly sending over
-the UDP port to the tunnel endpoint.  At the very least, 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 \fB6bed4\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, \fB6bed4\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 \fB6bed4\fR or similar service.  A general naming
-or numbering scheme is needed to make that straightforward.  The
-\fB6bed4\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, \fB6bed4\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 \fB6bed4\fR running
-anywhere; its stateless operation means that traffic going back and
-forth can go through different instances of \fB6bed4\fR without
-posing problems.
-.PP
-The \fB6bed4\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.
-.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 \fB6bed4\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
-\fB6bed4\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 is
-implements a specific profile for public service under NAT traversal.
-.SH AUTHOR
-\fB6bed4\fR was written by Rick van Rein from OpenFortress.
-It was created to support the 0cpm project.
diff --git a/6bed4peer.man b/6bed4peer.man
new file mode 100644 (file)
index 0000000..5f3139c
--- /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\-t\fR \fI/dev/tunX\fR] [\fB\-d\fR] [\fB\-f\fR] [\fB\-l\fR \fIv4addr\fR] [\fB\-p\fR \fIport\fR] [\fB\-r\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\-t\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\-d\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\-r\fR \fIhops\fR
+.TP
+\fB\-\-radius\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\-\-radius 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\-\-radius 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\-\-radius 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\-\-radius 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.man b/6bed4router.man
new file mode 100644 (file)
index 0000000..6a6f75e
--- /dev/null
@@ -0,0 +1,218 @@
+.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
+6bed4server \- server-side daemon for 6bed4 service
+.SH SYNOPSYS
+.B 6bed4server
+[\fB\-t\fR \fI/dev/tunX\fR] \fB\-l\fR \fIv4addr\fR \fB\-L\fR \fIv6prefix/64\fR
+.PP
+.B 6bed4server
+[\fB\-h\fR]
+.SH DESCRIPTION
+.PP
+Through a \fB6bed4server\fR daemon, it is possible to make a small range of IPv6
+addresses available to 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
+These tunnels are primarily intended for embedded devices, to assist them
+in instant changeover from being IPv4-only to 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 \fB6bed4server\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\-t\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\-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\-t\fR option is given, a tunnel will be created for the time that
+\fB6bed4server\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 \fB6bed4server\fR,
+nor will the general ablity to forward IPv6 traffic.
+.TP
+\fB\-h\fR
+.TP
+\fB\-\-help\fR
+Print usage information and exit.
+.SH ADDRESS FORMAT
+.PP
+An IPv6 address used from \fB6bed4server\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 \fB6bed4server\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 \fB6bed4server\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 \fB6bed4server\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 \fB6bed4server\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 \fB6bed4server\fR.
+Note that autonegotiation protocol provides this necessary information at the
+time the 6bed4server 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 \fB6bed4server\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, \fB6bed4server\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 \fB6bed4server\fR or similar service.  A general naming
+or numbering scheme is needed to make that straightforward.  The
+\fB6bed4server\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, \fB6bed4server\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 \fB6bed4server\fR running
+anywhere; its stateless operation means that traffic going back and
+forth can go through different instances of \fB6bed4server\fR without
+posing problems.
+.PP
+The \fB6bed4server\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 \fB6bed4server\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
+\fB6bed4server\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
+\fB6bed4server\fR was written by Rick van Rein from OpenFortress.
+It was created to support the 0cpm project.
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..db48711
--- /dev/null
@@ -0,0 +1,17 @@
+PROJECT(6bed4 C)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(VERSION "0.0.1")
+
+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")
+
+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)
+
+INCLUDE(CPack)
index f246f1c..56f7301 100644 (file)
@@ -1,8 +1,16 @@
-all: 6bed4_plus_pubtsp strictlydemoclient
+all: 6bed4router 6bed4peer
 
-6bed4_plus_pubtsp: router.c
+6bed4router: router.c nonstd.h
        gcc -DLINUX -ggdb3 -o $@ $<
 
-strictlydemoclient: democlient.c
+6bed4peer: peer.c nonstd.h
        gcc -DLINUX -ggdb3 -o $@ $<
 
+tags: router.c peer.c
+       ctags router.c peer.c nonstd.h
+
+clean:
+       rm -f 6bed4router 6bed4peer tags
+
+distclean: clean
+       rm -f Makefile
index 510994c..39e8476 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,7 +1,16 @@
 #!/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/democlient.c b/democlient.c
deleted file mode 100644 (file)
index d27b2ff..0000000
+++ /dev/null
@@ -1,789 +0,0 @@
-/* 6bed4/democlient.c -- IPv6-anywhere demo-only client for 6bed4
- *
- * 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.
- *
- * Because of the emphasis on embedded devices, this code or derivatives
- * SHOULD NOT be distributed as a desktop application.  Variations are
- * possible for network providers who intend to host IPv6 support as a
- * local network service, available on a non-standard IPv4 address and
- * a non-standard IPv6 /64 prefix.
- *
- * The software is ONLY available for experimentation purposes, and as
- * a foundation for embedded code.  This status will only be changed
- * when the tunnel hosting parties agree that desktop use of their
- * tunnels is permitted.  This may happen when the tunnels are widely
- * spread accross the Internet, like 6to4 is now.
- *
- * From: Rick van Rein <rick@openfortress.nl>
- */
-
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdio.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/udp.h>
-#include <netinet/icmp6.h>
-#include <arpa/inet.h>
-
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_ether.h>
-
-
-struct tsphdr {
-       uint32_t seqnum;
-       uint32_t timestamp;
-};
-
-
-#define TUNNEL_CAPABILITIES "CAPABILITY TUNNEL=V6UDPV4 AUTH=ANONYMOUS"
-
-#define MTU 1280
-
-/*
- * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
- * the -t 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 -t.
- */
-#undef HAVE_SETUP_TUNNEL
-
-
-/* Global variables */
-
-char *program;
-
-int v4sox = -1;
-int v6sox = -1;
-
-char *v4server = NULL;
-char *v6server = NULL;
-char v6prefix [INET6_ADDRSTRLEN];
-
-const char v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-struct sockaddr_in  v4name;
-struct sockaddr_in  v4peer;
-struct sockaddr_in6 v6name;
-
-struct in6_addr v6listen;
-struct in_addr  v4listen;
-
-
-struct {
-       struct tun_pi tun;
-       union {
-               struct {
-                       struct tsphdr tsp;
-                       uint8_t cmd [MTU];
-                       uint8_t zerobyte;
-               } cdata;
-               struct {
-                       struct ip6_hdr v6hdr;
-                       uint8_t data [MTU];
-               } idata;
-               struct {
-                       struct ip6_hdr v6hdr;
-                       struct icmp6_hdr v6icmphdr;
-               } ndata;
-       } udata;
-} v4data6;
-
-#define v4tunpi6       (v4data6.tun)
-#define v4data         ((uint8_t *) &v4data6.udata)
-#define v4tsphdr       (&v4data6.udata.cdata.tsp)
-#define v4tspcmd       (v4data6.udata.cdata.cmd)
-#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)
-
-
-struct {
-       struct tun_pi tun;
-       union {
-               uint8_t data [MTU];
-               struct ip6_hdr v6hdr;
-       } udata;
-       uint8_t zero;
-} v6data6;
-
-#define v6data         (v6data6.udata.data)
-#define v6tuncmd       (v6data6.tun)
-#define v6hdr6         (&v6data6.udata.v6hdr)
-#define v6src6         (&v6data6.udata.v6hdr.ip6_src)
-#define v6dst6         (&v6data6.udata.v6hdr.ip6_dst)
-
-
-uint8_t ipv6_router_solicitation [] = {
-       // IPv6 header
-       0x60, 0x00, 0x00, 0x00,
-       16 / 256, 16 % 256, 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 from 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 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,
-};
-
-
-/*
- *
- * Driver routines
- *
- */
-
-#ifdef LINUX
-#define HAVE_SETUP_TUNNEL
-/* Implement the setup_tunnel() command for Linux.
- * Return 1 on success, 0 on failure.
- */
-int setup_tunnel (void) {
-       if (v6sox == -1) {
-               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;
-       static struct ifreq ifreq;
-       static int have_tunnel = 0;
-       if (!have_tunnel) {
-               memset (&ifreq, 0, sizeof (ifreq));
-               ifreq.ifr_flags = IFF_TUN;
-               if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
-                       ok = 0;
-               } else {
-                       have_tunnel = 1;
-               }
-               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 -6 route flush dev %s", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       snprintf (cmd, 512, "/sbin/ip addr add fe80::1 dev %s scope link", ifreq.ifr_name);
-       if (ok && system (cmd) != 0) {
-               ok = 0;
-       }
-       if (* (uint16_t *) v6prefix != htons (0x0000)) {
-               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 -6 route add %s/112 mtu 1280 dev %s", v6prefix, ifreq.ifr_name);
-               if (ok && system (cmd) != 0) {
-                       ok = 0;
-               }
-#if 0
-               snprintf (cmd, 512, "/sbin/ip -6 rule add from %s/112 table 64", v6prefix);
-               if (ok && system (cmd) != 0) {
-                       ok = 0;
-               }
-#endif
-               snprintf (cmd, 512, "/sbin/ip -6 route flush table 64");
-               if (ok && system (cmd) != 0) {
-                       ok = 0;
-               }
-               snprintf (cmd, 512, "/sbin/ip -6 route add table 64 default via %s dev %s metric 512", 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 */
-       }
-       return ok;
-}
-#endif /* LINUX */
-
-
-/*
- *
- * Command functions
- *
- */
-
-
-/* Calculate the ICMPv6 checksum field
- */
-uint16_t icmp6_checksum (size_t payloadlen) {
-       uint16_t plenword = htons (payloadlen);
-       uint16_t nxthword = htons (IPPROTO_ICMPV6);
-       uint16_t *area [] = { (uint16_t *) v4src6, (uint16_t *) v4dst6, &plenword, &nxthword, (uint16_t *) v4icmp6, (uint16_t *) v4v6icmpdata };
-       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++) {
-               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;
-}
-
-
-/* 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/v6 src becomes dest, set v4/udp/v6 src, len, cksum, send.
- */
-void icmp6_reply (size_t icmp6bodylen) {
-       size_t v6iphdr_msglen = sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr) + icmp6bodylen;
-       size_t v4iphdr_msglen = sizeof (struct iphdr) + sizeof (struct udphdr) + v6iphdr_msglen;
-       v4v6hoplimit = 255;
-       icmp6bodylen += 4;
-       icmp6bodylen >>= 3;
-       ; //TODO: icmp6 reply construction (src==any => tgt=multicast-node)
-}
-
-
-/* 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++] = 112;  // This is a /112 prefix
-       v4v6icmpdata [optidx++] = 0x40; // L=0, 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
-       memcpy (v4v6icmpdata + optidx +  0, &v6listen, 8);
-       memcpy (v4v6icmpdata + optidx +  8, &v4listen, 4);
-       //memcpy (v4v6icmpdata + optidx + 12, &v4port,   2);
-       * ((uint16_t *) (v4v6icmpdata + optidx + 12)) = htons (3653);
-       memset (v4v6icmpdata + optidx + 14, 0,         2);
-                                       // Set IPv6 prefix
-       optidx += 16;
-       return optidx;
-}
-
-
-/* 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_ngb (ssize_t v4ngbcmdlen) {
-       uint16_t srclinklayer;
-       //
-       // Ensure that the packet is large enough
-       if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
-               return;
-       }
-       //
-       // Ensure that the packet is an ICMPv6 packet is otherwise okay
-       if (v4v6nexthdr != IPPROTO_ICMPV6 || v4v6icmpcode != 0 || v4v6hoplimit < 255) {
-               return;
-       }
-       if (icmp6_checksum (v4ngbcmdlen - sizeof (struct ip6_hdr)) != v4v6icmpcksum) {
-               return;
-       }
-       //
-       // TODO? Ensure that the packet hops indicate that it is local traffic
-       //
-       // Approved.  Perform neighbourly courtesy.
-       switch (v4v6icmptype) {
-       case ND_ROUTER_ADVERT:
-               //
-               // Validate Router Advertisement
-               if (memcmp (v4src6, router_linklocal_address, 16) != 0) {
-                       return;   /* not from router, ignore */
-               }
-               if (memcmp (v4dst6, democlient_linklocal_address, 16) != 0 &&
-                   memcmp (v4dst6,   allnodes_linklocal_address, 16) != 0) {
-                       return;   /* not for me, ignore */
-               }
-               if (v4v6hoplimit != 255) {
-                       return;   /* hops made, ignore */
-               }
-               if (ntohs (v4v6plen) < sizeof (struct icmp6_hdr) + 16) {
-                       return;   /* strange length, return */
-               }
-               if (v4v6icmpdata [1] & 0x80 != 0x00) {
-                       return;   /* indecent proposal: DHCPv6 */
-               }
-               size_t rdofs = 12;
-               while (rdofs <= ntohs (v4v6plen) + 4) {
-                       if (v4v6icmpdata [rdofs + 1] == 0) {
-                               return;   /* zero length option */
-                       }
-                       if (v4v6icmpdata [rdofs + 0] != 3) {
-                               break;    /* 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) {
-                               break;    /* no on-link autoconfig prefix */
-                       } else if (v4v6icmpdata [rdofs + 2] != 112) {
-                               break;    /* wrong prefix length */
-                       } else {
-                               //
-                               // Process prefix Information option
-                               memcpy (&v6listen, v4v6icmpdata + rdofs+16, 16);
-                               v6listen.s6_addr16 [7] = htons (0x0001);
-                               inet_ntop (AF_INET6,
-                                       &v6listen,
-                                       v6prefix,
-                                       sizeof (v6prefix));
-                               printf ("Assigning address %s to tunnel\n", v6prefix);
-                               setup_tunnel ();
-                       }
-                       rdofs += (v4v6icmpdata [rdofs + 1] << 3);
-               }
-       case ND_NEIGHBOR_SOLICIT:
-               //
-               // Validate Neigbour Solicitation
-               if (v4dst6->s6_addr16 [0] == htons (0xff02)) {
-                       break;   /* drop */
-               }
-               if (v4src6->s6_addr16 [9] == htons (0x0000)) {
-                       // TODO: 24 ---> 24 + bytes_voor_srclinklayaddr
-                       if (v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) {
-                               break;   /* drop */
-                       }
-               } else {
-                       if (v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) {
-                               break;   /* 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, 8);        // prefix /64
-               memcpy (v4v6icmpdata + 12, &v4name.sin_addr, 4); // IPv4
-               memcpy (v4v6icmpdata + 16, &v4name.sin_port, 2); // UDPport
-               v4v6icmpdata [18] =
-               v4v6icmpdata [19] = 0x00;                        // router if-id
-               // 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
-               memset (v4v6icmpdata + 22, 0x00, 6); // Link-layer addr is 0
-               // Total length of ICMPv6 body is 28 bytes
-               icmp6_reply (28);
-               break;
-       default:
-               break;   /* drop */
-       }
-}
-
-
-/* Handle the IPv4 message pointed at by msg, checking if 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_payload (ssize_t v4datalen) {
-       //
-       // Ensure that the lower half of the IPv6 sender address is ok
-#if 0
-       if (v4dst6->s6_addr32 [2] != v4peer.sin_addr.s_addr) {
-               return;
-       }
-       if (v4dst6->s6_addr16 [6] != v4peer.sin_port) {
-               return;
-       }
-#endif
-       if (v4dst6->s6_addr16 [7] == htons (0x0000)) {
-               return;
-       }
-       //
-       // Ensure that the top half of the IPv6 address is ok
-       // Note that this implies rejection of ::1/128, fe80::/10 and fec0::/10
-       if (memcmp (v4dst6, &v6listen, 8) != 0) {
-               return;
-       }
-       if (v4dst6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
-               return;
-       }
-       if (v4dst6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
-               return;
-       }
-       //
-       // Send the unwrapped IPv6 message out over v6sox
-       memcpy (&v6name.sin6_addr, v4dst6, sizeof (v6name.sin6_addr));
-printf ("Writing IPv6, result = %d\n",
-       write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
-}
-
-/* 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, sizeof (struct tsphdr) + MTU,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, &adrlen
-               );
-       if (buflen == -1) {
-               printf ("%s: Error receiving IPv4-side package: %s\n",
-                               program, strerror (errno));
-               return;
-       }
-       if (buflen < sizeof (struct tsphdr)) {
-               return;
-       }
-       /* Handle as a tunneled IPv6 package */
-       if (buflen > sizeof (struct ip6_hdr) + 1) {
-               uint16_t dst = v4src6->s6_addr16 [0];
-               if ((v4dst6->s6_addr16 [0] == htons (0xff02)) ||
-                   (v4dst6->s6_addr16 [0] == htons (0xfe80))) {
-                       handle_4to6_ngb (buflen);
-               } else {
-                       handle_4to6_payload (buflen);
-               }
-       }
-}
-
-
-/* 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) {
-       //
-       // Receive the IPv6 package and ensure a consistent size
-       size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
-       if (rawlen == -1) {
-               return;
-       }
-       if (rawlen < sizeof (struct tun_pi) + sizeof (struct ip6_hdr) + 1) {
-               return;
-       }
-       if (v6tuncmd.proto != htons (ETH_P_IPV6)) {
-               return;
-       }
-printf ("Received IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
-       //
-       // Ensure that the incoming IPv6 address is properly formatted
-       // Note that this avoids access to ::1/128, fe80::/10, fec0::/10
-       // TODO: v6src6 or v6dst6?!?
-       if (memcmp (v6src6, &v6listen, 8) != 0) {
-               return;
-       }
-       if (v6src6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
-               return;
-       }
-       if (v6src6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
-               return;
-       }
-       if (v6src6->s6_addr16 [7] == htons (0x0000)) {
-               return;
-       }
-       //
-       // Harvest socket address data from destination IPv6, then send
-socklen_t v4namelen = sizeof (v4name);
-printf ("Sending IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %d\n",
-((uint8_t *) &v4peer.sin_addr.s_addr) [0],
-((uint8_t *) &v4peer.sin_addr.s_addr) [1],
-((uint8_t *) &v4peer.sin_addr.s_addr) [2],
-((uint8_t *) &v4peer.sin_addr.s_addr) [3],
-ntohs (v4peer.sin_port),
-       send (v4sox,
-                       v6data,
-                       rawlen - sizeof (struct tun_pi),
-                       MSG_DONTWAIT));
-}
-
-
-/* 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_routers (void) {
-       v4name.sin_family = AF_INET;
-       memcpy (&v4name.sin_addr.s_addr, &v4listen, 4);
-       v4name.sin_port = htons (3653);
-       int done = 0;
-       int secs = 1;
-       while (!done) {
-printf ("Sending RouterSolicitation-IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %d\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,
-                               ipv6_router_solicitation,
-                               sizeof (ipv6_router_solicitation),
-                               MSG_DONTWAIT,
-                               (struct sockaddr *) &v4name, sizeof (v4name)));
-               fd_set wait4me;
-               FD_ZERO (&wait4me);
-               FD_SET (v4sox, &wait4me);
-               struct timeval tout = { secs, 0 };
-               done = select (v4sox+1, &wait4me, NULL, NULL, &tout) > 0;
-               if (secs < 60) {
-                       secs <<= 1;
-               }
-       }
-       printf ("Got a response, liberally assuming it is an offer\n");
-}
-
-
-/* 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 = "s:t:h";
-
-struct option long_opt [] = {
-       { "v4server", 1, NULL, 's' },
-       { "tundev", 1, NULL, 't' },
-       { "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 = (argc == 1);
-       int done = 0;
-       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: Only one -s argument is permitted\n");
-                               break;
-                       }
-                       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;
-                       }
-                       if (connect (v4sox, (struct sockaddr *) &v4peer, sizeof (v4peer)) != 0) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Failed to bind to UDPv4 %s:%d: %s\n", program, optarg, ntohs (v4peer.sin_port), strerror (errno));
-                               break;
-                       }
-                       break;
-               case 't':
-                       if (v6sox != -1) {
-                               ok = 0;
-                               fprintf (stderr, "%s: Multiple -t arguments are not permitted\n");
-                               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;
-               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 [-t /dev/tunX] -s <v4server>\n       %s -h\n", program, program);
-#else
-               fprintf (stderr, "Usage: %s -t /dev/tunX -s <v4server>\n       %s -h\n", program, program);
-#endif
-               return 0;
-       }
-       if (!ok) {
-               return 0;
-       }
-       if (v4sox == -1) {
-               fprintf (stderr, "%s: Use -s to specify an IPv4 address for the tunnel interface\n", program);
-               return 0;
-       }
-#ifdef HAVE_SETUP_TUNNEL
-       if (v6sox == -1) {
-               if (geteuid () != 0) {
-                       fprintf (stderr, "%s: You should be root, or use -t to specify an accessible tunnel device\n", program);
-                       return 0;
-               } else {
-                       setup_tunnel ();
-               }
-       }
-#else /* ! HAVE_SETUP_TUNNEL */
-       if (v6sox == -1) {
-               fprintf (stderr, "%s: You must specify a tunnel device with -t 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 (&v4peer, 0, sizeof (v4peer));
-       memset (&v6name, 0, sizeof (v6name));
-       v4name.sin_family  = AF_INET ;
-       v4peer.sin_family  = AF_INET ;
-       v6name.sin6_family = AF_INET6;
-       v4name.sin_port = htons (3653); /* TSP standard port */
-       v4peer.sin_port = htons (3653); /* TSP standard port */
-       v4tunpi6.flags = 0;
-       v4tunpi6.proto = htons (ETH_P_IPV6);
-       //
-       // Parse commandline arguments
-       if (!process_args (argc, argv)) {
-               exit (1);
-       }
-       //
-       // Inform the user about the DEMO-ONLY status of this tool
-       if (!isatty (fileno (stdin)) || !isatty (fileno (stdout))) {
-               fprintf (stderr, "This tool can only be started with terminal I/O\n");
-               exit (1);
-       }
-       printf ("\nThis tunnel client is ONLY FOR DEMONSTRATION PURPOSES.\n\nUntil there are plenty of tunnels and tunnel hosting parties agree, it is\nnot permitted to rolll out this application on desktops.  Please acknowledge\nthat by entering the word demonstrate to the following prompt.\n\nExceptions are made for roll-outs on local networks, where the tunnel service\nis used from a non-standard IPv4 address and IPv6 /64 prefix.\n\nType the word from the text to proceed: ");
-       fflush (stdout);
-       char demobuf [100];
-       if (fgets (demobuf, sizeof (demobuf)-1, stdin) == NULL
-           || strcmp (demobuf, "demonstrate\n") != 0) {
-               fprintf (stderr, "Please read the instructions and try again.\n");
-               exit (1);
-       }
-       //
-       // Start the main daemon process
-       solicit_routers ();     // DEMO -- only once
-       run_daemon ();
-       //
-       // Report successful creation of the daemon
-       return 0;
-}
diff --git a/fedora/6bed4.spec b/fedora/6bed4.spec
new file mode 100644 (file)
index 0000000..dcb555d
--- /dev/null
@@ -0,0 +1,60 @@
+Name:          6bed4
+Version:       0.0.1
+Release:       0.1%{?dist}
+Summary:       IPv6 tunneling technique over UDP and IPv4
+
+Group:         Applications/Internet
+License:       BSD
+URL:           http://devel.0cpm.org/6bed4/
+Source0:       6bed4-%{version}.tar.bz2
+BuildRoot:     %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+
+BuildRequires: cmake
+
+%description
+Given the limited resources available to a lot of embedded systems,
+dual-stack solutions are not always feasible for such hosts.  A
+mechanism that supports a direct transition from IPv4-only to
+IPv6-only may prove beneficial in getting the smallest hosts to make
+a transition to IPv6 at a much earlier stage than would otherwise be
+possible.  This calls for tunnels, but no current tunnel technique
+appears to be optimal for embedded systems.
+
+This specification details an IPv6 tunneling technique over UDP and
+IPv4.  The technique is specifically designed to benefit embedded
+systems, and to work without end user configuration.  The working
+principle for obtaining a routable IPv6 address is through stateless
+autoconfiguration from an anycast tunnel service.
+
+%prep
+%setup -q
+
+
+%build
+%cmake .
+make %{?_smp_mflags}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-,root,root,-)
+%doc HISTORY LICENSE doc
+%{_sbindir}/6bed4peer
+%{_sbindir}/6bed4router
+%{_mandir}/man8/*
+
+%changelog
+* Thu mar  8 2012 Rick van Rein
+- rearranged command and man file names
+
+* Sat Oct 22 2011 Fran├žois Kooman - 0.0.1-0.1
+- initial version
+
+
diff --git a/inetdraft.rst b/inetdraft.rst
deleted file mode 100644 (file)
index 8feccc0..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-============================================
-IPv6 tunnelling for embedded devices (6bed4)
-============================================
-
-::
-
-        From: Rick van Rein <rick@openfortress.nl>
-
-The current set of transitioning techniques provide no suitable mechanism
-for embedded devices that wish to support IPv6.  Given the limitation on
-resources in most embedded applications, dual-stack solutions are not
-usually possible, so a mechanism is needed that supports the transition
-from IPv4-only to IPv6-only.
-
-If an embedded device is to be IPv6-only, and if it is to work without
-effort in that mode, it requires a method to access IPv6 from any
-current network, including IPv4-only networks behind NAT routers.
-The nature of these applications makes it desirable that such access
-can happen without configuration of credentials for access to the
-IPv6 network.  At the same time, the mechanism must support tracing
-of abusers based on their IPv6 network use.
-
-
-Protocol description
-====================
-
-The 6bed4 mechanism is a tunnel that encapsulates IPv6 packets in
-UDP, and then into IPv4.  The mechanism assumes a remote tunnel service
-at a well-known IPv4 address and UDP port, effectively making the tunnel
-independent of DNS, and capable of traversing NAT.  The local IPv4 address
-is acquired through common techniques such as DHCPv4, and the local
-UDP port can be picked in any way that makes sense locally.
-
-The tunnel service can be implemented at many locations, each announcing a
-route to their well-known IPv4 address over BGP,
-basically following the anycast principle.  The routing infrastructure
-will then forward tunnel traffic to a nearby instance of that service.
-
-Embedded devices obtain a routeable IPv6 address over the tunnel through
-autoconfiguration.  The router, always with interface identifier 0,
-can be reached on the fixed link-local IPv6 address fe80:: or on the
-all-routers address ff02::2 or on the all-nodes address ff02::1.  The
-tunnel client, which is the only other participant in the tunnel, can
-pick non-zero interface identifiers at will to complete autoconfiguration.
-
-The routeable prefix offered over the tunnel includes the IPv4 address
-and UDP port as seen from the Internet.  Any NAT layer crossed by the
-tunnel may influence the client-side IPv4 address and UDP port, which is
-why the tunnel server will provide this data.  Autoconfiguration serves
-to learn how the tunnel client looks from the perspective of the Internet.
-
-
-
-Well-known addresses
-====================
-
-The setup described here allocates a number of well-known numbers for
-addressing.  These numbers can be fixated into an embedded device, and
-serve to address an anycast-published tunnel as a fallback for local
-IPv6 facilities.
-
-IPv4 address for the remote tunnel service:    TODO
-IPv6 prefix for the remote tunnel service:     TODO::/64
-UDP port for the remote tunnel service:                TODO
-
-The well-known UDP port is only needed for routing UDP traffic
-tofor the well-known IPv4 address of the remote tunnel service.
-
-
-
-Link-local profile for 6bed4 tunnels
-====================================
-
-The 6bed4 tunnels add detail with respect to the autoconfiguration
-mechanism described in RFC 2462.  Most importantly, the parameter
-N for the number of bits in the interface identifier is 16.  The
-preceeding 112 bits are filled with a /64 prefix for the IPv6-side
-of the tunnel service, 32 bits worth of IPv4 address and 16 bits
-of UDP port number::
-
-  +-------------------------------+---------------+-------+-------+
-  |      IPv6-side /64 PREFIX     | Public v4addr |UDPport| if-id |
-  +-------------------------------+---------------+-------+-------+
-
-Or, seen from the tunnel client perspective::
-
-  +-------------------------------------------------------+-------+
-  |                 IPv6-side /112 PREFIX                 | if-id |
-  +-------------------------------------------------------+-------+
-
-The interface identifier for the router is always 0 and only 0, which
-means that it can always be reached at its link-local address fe80::/128.
-The tunnel client can choose its own interface identifier(s) at will from
-the range 1-65535.  The tunnel client MAY fixate the interface identifier
-in its firmware.
-
-The point-to-point nature makes it possible for tunnel clients and
-servers to ignore the interface identifier altogether without
-malfunctioning.  Upon sending however, a node MUST use a valid
-interface identifier to accommodate peers that do check it.
-
-The value of DupAddrDetectTransmits defaults to 0 for this kind of link,
-meaning that no neighbour discovery is required for 6bed4 links.
-This is possible because of the static allocation of interface
-identifiers to endpoint.  Note that RFC 2462 requires the tunnel client
-and server provide a facility for overriding this setting.  For this
-reason, the tunnel endpoints MUST support neighbouring requests.
-
-
-Tunnelling traffic
-==================
-
-The following describes how to pass traffic down from the IPv6-side
-of the tunnel to the IPv4-tunnelled side; how to pass it up in the
-opposite direction; and, as an optimisation, how a tunnel may
-directly connect two tunnels by bouncing traffic to the other side.
-
-
-Handling traffic sent down the tunnel
--------------------------------------
-
-Traffic that is to be sent down through the tunnel, is routed to a
-tunnel server by routing to its IPv6 /64 prefix.  If this is the
-well-known prefix, then many BGP speakers may be announcing it; the
-routing infrastructure would then find a suitable tunnel server
-nearby the IPv6 sender.
-
-If an entering IPv6 packet has a destination address with a different
-/64 prefix than the prefix setup for the tunnel, then that packet MUST
-be dropped or rejected with an ICMPv6 message.
-
-If an IPv6 packet is passed down through the tunnel, its time-to-live
-MUST be decremented.  If this is not possible because the time-to-live
-is already 0, then the packet MUST be rejected with TODO:ICMPv6-TTL.
-
-The IPv6 address to which the message is sent contains an IPv4 address
-right after its /64 prefix, followed by 16 bits of client UDP port.
-The distination side of an UDP header and IPv4 header can be reconstructed
-from that, and the source side can be filled with the well-known values
-that have been defined for 6bed4.  At some point before shipping this
-message, the last 16 bits of the address, holding the interface identifier,
-MUST be checked to be non-zero.  If the value is zero, the incoming
-traffic MUST be silently dropped.
-
-
-Handling traffic sent up the tunnel
------------------------------------
-
-The embedded device can send IPv6 packets through a tunnel as soon as it
-has an assigned IPv6 address.  To do this, it will prefix an UDP header
-with the well-known UDP port as a destination and the UDP port used
-locally for the tunnel as a source.  It will then prefix an IPv4 header,
-containing the well-known IPv4 address of the tunnel as destination,
-and the classically obtained local IPv4 address as the sender.
-
-This is shipped over the IPv4 network, may pass NAT routers, and will
-arrive on the tunnel server with possibly altered sender information in
-the IPv4 and UDP headers.
-
-When traffic is sent to the all-routers multicast address ff02::2, the
-all-nodes multicast address ff02::1 or to the router's link-local
-address fe80::/128 it is subject to local handling according to RFC 2461
-and RFC 2462.  If however, the time-to-live is not 255, the packet
-MUST be dropped or rejected with ICMPv6:TTL.  Specifically, router
-sollication will result in sending a router advertisement, and neighbour
-sollicitations are handled as usual.
-
-Before passing traffic through the tunnel, the time-to-live in the IPv6
-packet MUST be decremented.  If this is not possible because it is already
-0, then the packet MUST be rejected and an ICMPv6:TTL sent in response.
-
-The tunnel server then verifies the correctness of the sending IPv6 address:
-The first 64 bits should match the fixed prefix assigned to the tunnel
-server; the following 32 bits should match the IPv4 address according
-to the incoming message; the following 16 bits should match the UDP
-port from which the incoming message came; the final 16 bits with the
-interface identifier may not be zero, as that is always the tunnel
-server's address.
-
-If the match is good, the IPv6 payload will be taken out of its
-IPv4/UDP wrapper, and forwarded as normal traffic over the IPv6
-network.  A few exceptional forms of deliver (local, or IPv4) are
-handled in later sections of this specification.
-
-If the match is false, the tunnel server will send an unsollicited
-router advertisement.  This advertisement will revoke the prefix
-used by the tunnel client by setting its preferred and valid lifetimes
-to 0.  In the same message, it will advertise the new prefix that
-holds the external IPv4 address and UDP port of the client, and
-assign it an infinite preferred and valid lifetime value 0xffffffff.
-
-Upon reception of a router advertisement, the tunnel client MUST
-immediately update its IPv6 addresses and it MUST NOT send out
-any further messages using the old IPv6 address.  It MAY resend
-any unacknowledged messages that are being processed.
-
-
-Bouncing traffic on the tunnel's IPv4 side
-------------------------------------------
-
-If a tunnel receives a tunnelled package destined for an IPv6
-address that begins with the tunnel's well-known IPv6 /64 prefix,
-it MAY optimise the flow of traffic by forwarding it as
-tunnelled traffic to the IPv4 address and UDP port found in the
-remainder of the IPv6 destination address.
-
-In doing so, it MUST NOT bypass the comparison of the IPv6 prefix,
-IPv4 address and UDP port as mentioned in the source IPv6 address.
-If this fails, the traffic MUST be treated as traffic trying to pass
-up through the tunnel with an incorrectly set IPv6 address.
-
-
-Tunnel service profiles
-=======================
-
-Two different service profiles are expcted to be useful for this tunnel
-mechanism.  The first is public, available to users anywhere in the World.
-The other profile is local, intended to serve only a part of the Internet,
-such as the network of a particular provider.
-
-
-Public tunnel server profile
-----------------------------
-
-The public tunnel server profile is announced over BGP and MUST be made
-available to all partners that are allowed to route general traffic
-through the autonomous system announcing the tunnel server.  The
-announcement over BGP of the tunnel service SHOULD publish both sides
-of the service, that is the IPv4 address as well as the IPv6 /64 prefix.
-
-The traffic flowing through the tunnel server MUST NOT be logged or
-analysed for any other reason than the correct functioning of networks.
-That includes blockage of abusive patterns, but traffic MUST NOT be
-logged for reasons of packet inspection by/for government policies.
-If a policy exists that mandates any such forms of inspection then,
-as a result of the anycast mechanism, the risk could arise that foreign
-privacy-depriving laws would be applied to parties that communicate
-from a region that is subject to more privacy.
-
-Under the public profile, the tunnel server MUST use the well-known IPv6
-prefix for this tunnel service.  This makes the tunnel servers fully
-replace each other.
-
-
-Local tunnel server profile
----------------------------
-
-Under the local tunnel server profile, the well-known IPv4 address for
-this server is not exported to non-local parts of the network.  In the
-local part of the network, all traffic sent to the well-known IPv4 address
-is handled by an internal tunnel server.
-
-The IPv6 prefix used for this tunnel server MUST NOT be the well-known
-prefix; it is allocated with the intention of direct routeability
-to the local network.  The service provider MUST register an abuse
-contact address for the IPv6 /64 prefix, and MAY explain how abuse can
-be traced to IPv4 abusers, in the regional internet registry's whois
-database.
-
-The traffic flowing through the local tunnel server MUST NOT perform
-more tapping than the weakest required form of tapping on the local
-network.  In cases where a local network spans different jurisdictions,
-it is therefore not possible to apply stronger privacy-depriving
-standards to network users that are used to more privacy.
-
-
-NAT traversal issues
-====================
-
-Very often, a 6bed4 tunnel will have to pass through a layer of
-netwerk address translation, or NAT.  This layer will rewrite IPv4
-and UDP source addresses.  The 6bed4 tunnel protocol has been designed
-to accommodate that situation.
-
-A problem with NAT for UDP is that it has no connection status, and
-its translation must therefore be flushed at some point.  Although the
-tunnel itself will recover quickly if that happens, the higher protocols
-may not be as accommodating; notably, TCP connections over IPv6 would
-break if the translation changed suddenly.
-
-For this reason, it may be needed to send messages for the purpose
-of keeping the address translation active in any on-route NAT routers.  
-This can either be achieved by an actively communicating protocol, or by
-explicit keep-alive messages.
-
-Explicit keep-alive messages MAY be complete neighbour discovery messages
-sent to the tunnel service, but there usually is no need to go that far.
-Any such messages MUST NOT be sent if there is no application need for
-keeping the same IPv6 address on which the tunnel client can be reached;
-and in any case it MUST NOT be sent more often than once in 30 seconds.
-Furthermore, randomization of the keep-alive message interval is important
-so as to offload the tunnel server from synchronisation of keep-alives
-after things like power outages.
-
-In general, it suffices to send messages to the first IPv4 router with an
-address that is not defined in RFC TODO for local use.  This address can
-be easily obtained by using the traceroute procedure: send UDP traffic
-with increasing TTL values and wait for the ICMP return traffic that
-reveal router addresses.  A tunnel client SHOULD attempt to obtain this
-nearer-by address and use it for explicit keep-alive messages, so as to
-offload the Internet and specifically the 6bed4 service.
-
-Note that a device MAY use other means to achieve the longevity of an
-open link.  If it can communicate its wish for an open UDP port directed
-to its local endpoint directly, this is a much simpler method.  The only
-disadvantage of this approach is usually that it cannot be relied upon
-as a general mechanism; but if it does, it will save energy and bandwidth,
-so it is certainly recommended.
-
-
-IANA Issues
-===========
-
-Well-known IPv4 address, well-known IPv6 /64 prefix, well-known UDP port.
-
-
-Security issues
-===============
-
-Any party that can convince a network of being the router for a given
-range of addresses will be able to attract the traffic for 6bed4 tunnels.
-This could open up such protocols for man-in-the-middle attacks.
-
-The foreseeable means of doing this are either through BGP advertisements
-on the Internet, or through router advertisements on a local network.
-The issue of BGP advertisements is a general problem and is not generally
-thought of as being hazardous; notwithstanding that, work is being done
-to solve the general problems at that level.  At the local level, the
-problem is not much different from DHCP hijacking, a risk that is usually
-dealt with locally by monitoring nodes to behave as clients, or by strict
-control over network accessibility.
-
-Although symmetric signatures are possible over neighbour discovery
-protocols, this is not usable for the 6bed4 system, because it is a
-global protocol and includes too many parties to be able to protect
-te secret keys used.  Any signature mechanism for 6bed4 would have
-to be asymmetrical.
-
diff --git a/peer.c b/peer.c
new file mode 100644 (file)
index 0000000..ab21fa0
--- /dev/null
+++ b/peer.c
@@ -0,0 +1,1916 @@
+/* 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 <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 <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
+
+typedef enum {
+       METRIC_LOW,
+       METRIC_MEDIUM,
+       METRIC_HIGH
+} metric_t;
+
+/*
+ * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
+ * the -t 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 -t.
+ */
+#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 {
+       struct ethhdr eth;
+       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;
+
+#define v4ether        (v4data6.eth)
+#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 {
+       struct ethhdr eth;
+       union {
+               uint8_t data [MTU];
+               struct {
+                       struct ip6_hdr v6hdr;
+                       struct icmp6_hdr v6icmp;
+               } __attribute__((packed)) ndata;
+       } udata;
+}  __attribute__((packed)) v6data6;
+
+#define v6ether                (v6data6.eth)
+#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])
+
+
+/* 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 / 256, 16 % 256, 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 / 256, 32 % 256, 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 % 256, UDP_PORT_6BED4 / 256,
+       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 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,
+};
+
+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
+ *
+ */
+
+#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, "6bed4", IFNAMSIZ);
+               ifreq.ifr_flags = IFF_TAP | IFF_NO_PI;
+               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];
+#if 0
+       snprintf (cmd, 512, "/sbin/ip addr add fe80::1 dev %s scope link", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = false;
+       }
+#endif 
+       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.forwarding=0", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = false;
+       }
+       snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.accept_dad=0", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               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/ifconfig %s hw ether 98:1e:53:a4:cf:6e", ifreq.ifr_name, MTU);
+       snprintf (cmd, 512, "/sbin/ip link set %s address %02x:%02x:%02x:%02x:%02x:%02x", ifreq.ifr_name, v6lladdr [0], v6lladdr [1], v6lladdr [2], v6lladdr [3], v6lladdr [4], v6lladdr [5]);
+       if (ok && system (cmd) != 0) {
+syslog (LOG_CRIT, "Bad news!\n");
+               ok = false;
+       }
+       snprintf (cmd, 512, "/sbin/ip link set %s up mtu %d", ifreq.ifr_name, MTU);
+       if (ok && system (cmd) != 0) {
+               ok = false;
+       }
+#ifdef TODO_BELIEVE_THAT_ROUTES_SHOULD_BE_ADDED_STATICALLY
+       snprintf (cmd, 512, "/sbin/ip -6 route add 2001:610:188:2001::/64 mtu 1280 dev %s", ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = false;
+       }
+#endif
+#ifdef TODO_COMPENSATE_FOR_AUTOCONFIG
+       snprintf (cmd, 512, "/sbin/ip -6 addr add %s/64 dev %s", v6prefix, ifreq.ifr_name);
+       if (ok && system (cmd) != 0) {
+               ok = false;
+       }
+#endif
+#if 0
+       if (default_route) {
+               snprintf (cmd, 512, "/sbin/ip -6 route add default via fe80:: dev %s", ifreq.ifr_name);
+               if (ok && system (cmd) != 0) {
+                       ok = false;
+               }
+       }
+#endif
+       return ok;
+}
+#endif /* LINUX */
+
+
+/*
+ *
+ * Utility functions
+ *
+ */
+
+
+/* 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 (sizeof (struct ethhdr) + 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++] = 64;    // This is a /64 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
+       memcpy (v6icmp6data + optidx + 0, &v6listen, 8);
+       memset (v6icmp6data + optidx + 8, 0, 8);
+                                       // 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) {
+       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);
+       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, sizeof (struct ethhdr) + 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");
+       }
+}
+
+
+/*
+ * Test if the provided IPv6 address matches the prefix used for 6bed4.
+ */
+static inline bool prefix_6bed4 (struct in6_addr *ip6) {
+       return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
+}
+
+
+/*
+ * Validate the originator's IPv6 address.  It should match the
+ * UDP/IPv4 coordinates of the receiving 6bed4 socket.  Also,
+ * the /64 prefix must match that of v6listen.
+ */
+bool validate_originator (struct sockaddr_in *sin, struct in6_addr *ip6) {
+       if ((sin->sin_addr.s_addr == v4peer.sin_addr.s_addr) && (sin->sin_port == v4peer.sin_port)) {
+               return true;
+       }
+       uint16_t port = ntohs (sin->sin_port);
+       uint32_t addr = ntohl (sin->sin_addr.s_addr);
+if (memcmp (ip6, v6listen_linklocal, 8) != 0)
+       if (!prefix_6bed4 (ip6)) {
+               return false;
+       }
+       if ((port % 256) != (ip6->s6_addr [8] ^ 0x02)) {
+               return false;
+       }
+       if ((port / 256) != ip6->s6_addr [9]) {
+               return false;
+       }
+       if ((addr >> 24) != ip6->s6_addr [10]) {
+               return false;
+       }
+       if ((addr & 0x00ffffff) != (htonl (ip6->s6_addr32 [3]) & 0x00ffffff)) {
+               return false;
+       }
+       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 %d bytes from neighbor cache, total is now %d\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\n", resp->hd.nlmsg_type);
+                               ok = false;
+                       } else if (resp->nd.ndm_ifindex != ifreq.ifr_ifindex) {
+                               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))) {
+                               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
+       v4ether.h_proto = htons (ETH_P_IPV6);
+       memcpy (v4ether.h_dest,   v6lladdr, 6);
+       v4ether.h_source [0] = ntohs (sin->sin_port) % 256;
+       v4ether.h_source [1] = ntohs (sin->sin_port) / 256;
+       memcpy (v4ether.h_source + 2, &sin->sin_addr, 4);
+syslog (LOG_INFO, "Writing IPv6, result = %d\n",
+       write (v6sox, &v4data6, sizeof (struct ethhdr) + 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, router_linklocal_address, 16) != 0) {
+                       return;   /* not from router, drop */
+               }
+               if (memcmp (v4dst6, democlient_linklocal_address, 8) != 0) {
+                       return;   /* no address setup for me, drop */
+               }
+               if ((v4dst6->s6_addr [11] != 0xff) || (v4dst6->s6_addr [12] != 0xfe)) {
+                       return;   /* funny interface identifier, 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] != 64) {
+                               return;
+                       } else {
+                               destprefix = &v4v6icmpdata [rdofs + 16];
+                       }
+                       rdofs += (v4v6icmpdata [rdofs + 1] << 3);
+               }
+#ifdef TODO_DEPRECATED
+               if (destprefix && destlladdr) {
+                       memcpy (v6lladdr, destlladdr, 6);
+                       memcpy (&v6listen.s6_addr [0], destprefix, 8);
+                       v6listen.s6_addr [8] = destlladdr [0] ^ 0x02;
+                       v6listen.s6_addr [9] = destlladdr [1];
+                       v6listen.s6_addr [10] = destlladdr [2];
+                       v6listen.s6_addr [11] = 0xff;
+                       v6listen.s6_addr [12] = 0xfe;
+                       v6listen.s6_addr [13] = destlladdr [3];
+                       v6listen.s6_addr [14] = destlladdr [4];
+                       v6listen.s6_addr [15] = destlladdr [5];
+                       inet_ntop (AF_INET6,
+                               &v6listen,
+                               v6prefix,
+                               sizeof (v6prefix));
+                       syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
+                       if (!setup_tunnel_address ()) {
+                               syslog (LOG_CRIT, "Failed to setup tunnel address\n");
+                               exit (1);
+                       }
+                       got_lladdr = true;
+                       maintenance_time_cycle = maintenance_time_cycle_max;
+                       maintenance_time_sec = time (NULL) + maintenance_time_cycle;
+               }
+#else
+               if (destprefix) {
+                       memcpy (v6listen.s6_addr + 0, destprefix, 8);
+                       memcpy (v6listen.s6_addr + 8, v4dst6->s6_addr + 8, 8);
+                       memcpy (v6listen_linklocal_complete, v4dst6, 16);
+                       v6lladdr [0] = v6listen_linklocal_complete [8] ^ 0x02;
+                       v6lladdr [1] = v6listen_linklocal_complete [9];
+                       v6lladdr [2] = v6listen_linklocal_complete [10];
+                       v6lladdr [3] = v6listen_linklocal_complete [13];
+                       v6lladdr [4] = v6listen_linklocal_complete [14];
+                       v6lladdr [5] = v6listen_linklocal_complete [15];
+                       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;
+               }
+#endif
+               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"); } 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) % 256;
+               v4v6icmpdata [23] = ntohs (v4name.sin_port) / 256;
+               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 (&v4name, 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 = %d\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 - sizeof (struct ethhdr),
+                               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);
+               v6ether.h_proto = htons (ETH_P_IPV6);
+               memcpy (v6ether.h_dest, v6ether.h_source, 6);
+               memcpy (v6ether.h_source, v6lladdr, 6);
+               syslog (LOG_INFO, "Replying Router Advertisement to the IPv6 Link, result = %d\n",
+                       write (v6sox, &v6data6, sizeof (struct ethhdr) + 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) {
+                       return; //TODO// Construct response for fe80::/64
+               } else {
+                       enqueue ((struct in6_addr *) v6ndtarget, (struct in6_addr *) v6src6, v6ether.h_source);
+               }
+               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) {
+       //
+       // 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 < sizeof (struct ethhdr) + sizeof (struct ip6_hdr) + 1) {
+               return;         /* packet too small, drop */
+       }
+       if (v6ether.h_proto != htons (ETH_P_IPV6)) {
+               return;         /* not IPv6, drop */
+       }
+//TODO// syslog (LOG_DEBUG, "TODO: 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;
+       }
+       /*
+        * 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
+               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, "TODO: Forwarding plain unicast from IPv6 to 6bed4\n");
+               handle_6to4_plain_unicast (rawlen, v6ether.h_dest);
+       } 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 % 256, UDP_PORT_6BED4 / 256,
+               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 %d 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 %d.%03d: Timed for %d.%03d", 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:dl:p:r:k:feh";
+
+struct option long_opt [] = {
+       { "v4server", 1, NULL, 's' },
+       { "tundev", 1, NULL, 't' },
+       { "default-route", 0, NULL, 'd' },
+       { "listen", 1, NULL, 'l' },
+       { "port", 1, NULL, 'p' },
+       { "radius-multicast", 1, NULL, 'r' },
+       { "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");
+                               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 't':
+                       if (v6sox != -1) {
+                               ok = 0;
+                               fprintf (stderr, "%s: Multiple -t arguments are not permitted\n");
+                               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 'd':
+                       if (default_route) {
+                               fprintf (stderr, "%s: You can only request default route setup once\n", program);
+                               exit (1);
+                       }
+                       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);
+                               exit (1);
+                       }
+                       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);
+                               exit (1);
+                       }
+                       if (tmpport & 0x0001) {
+                               fprintf (stderr, "%s: UDP port number %d is odd, which is not permitted\n", program, tmpport);
+                               exit (1);
+                       }
+                       v4bind.sin_port = htons (tmpport);
+                       break;
+               case 'f':
+                       if (foreground) {
+                               fprintf (stderr, "%s: You can only request foreground operation once\n", program);
+                               exit (1);
+                       }
+                       foreground = true;
+                       break;
+               case 'e':
+                       if (log_to_stderr) {
+                               fprintf (stderr, "%s: You can only specify logging to stderr once\n", program);
+                               exit (1);
+                       }
+                       log_to_stderr = true;
+                       break;
+               case 'r':
+                       if (v4ttl_mcast != -1) {
+                               fprintf (stderr, "%s: You can set the radius for multicast once\n", program);
+                               exit (1);
+                       }
+                       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);
+                               exit (1);
+                       }
+                       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);
+                               exit (1);
+                       }
+                       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);
+                                       exit (1);
+                               }
+                       } 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 [-d] [-t /dev/tunX]\n       %s -h\n", program, program);
+#else
+               fprintf (stderr, "Usage: %s [-d] -t /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 -t to specify an accessible tunnel device\n", program);
+                       return false;
+               } else {
+                       return setup_tunnel ();
+               }
+       }
+#else /* ! HAVE_SETUP_TUNNEL */
+       if (v6sox == -1) {
+               fprintf (stderr, "%s: You must specify a tunnel device with -t 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;
+       //
+       // Parse commandline arguments
+       if (!process_args (argc, argv)) {
+               exit (1);
+       }
+       //
+       // Construct the 6bed4 Router's complete link-layer address
+       router_linklocal_address_complete [8] = (ntohs (v4peer.sin_port) % 256) ^ 0x02;
+       router_linklocal_address_complete [9] = ntohs (v4peer.sin_port) / 256;
+       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);
+       //
+       // Open the syslog channel
+       openlog (program, LOG_NDELAY | LOG_PID | ( log_to_stderr? LOG_PERROR: 0), LOG_DAEMON);
+       //
+       // Create memory for the freequeue buffer
+       freequeue = calloc (freequeue_items, sizeof (struct ndqueue));
+       if (!freequeue) {
+               syslog (LOG_CRIT, "%d: 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;
+#ifdef 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");
+       }
+#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;
+                               sysctl (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;
+                               sysctl (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;
+                               sysctl (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");
+       }
+       //
+       // 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/pubtsp.man b/pubtsp.man
deleted file mode 100644 (file)
index 9b11cd1..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-.TH PUBTSP 8 "December 12, 2010"
-.\" 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
-pubtsp \- server-side daemon for public TSP service
-.SH SYNOPSYS
-.B pubtsp
-[\fB\-t\fR \fI/dev/tunX\fR] \fB\-l\fR \fIv4addr\fR \fB\-L\fR \fIv6prefix/64\fR
-.PP
-.B pubtsp
-\fB\-h\fR
-.SH DESCRIPTION
-.PP
-Through a \fBpubTSP\fR daemon, it is possible to make an IPv6 range of
-addresses available to 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
-TSP, or the Tunnel Setup Protocol, is documented in RFC 5722.  It defines
-several methods for encapsulating IPv6 traffic in IPv4 packets.  It also
-defines SASL as its generic authentication layer.
-.PP
-This daemon implements a specific profile for the server side of TSP.
-The profile of RFC 5722 implemented by this daemon supports NAT traversal
-for public use.  The purpose of this profile is to provide a fallback
-mode to IPv6-only devices running on an IPv4-only network; the idea is
-to make such a fallback possible without needing to configure a tunnel.
-.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.  In terms of SASL, this uses the ANONYMOUS method of
-authentication.  However, this does not mean that the use of \fBpubTSP\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 foreseen application of servers under this profile of TSP are
-IPv6-only services that are accessed from an IPv4-only network.  The pivotal
-point of defining IPv6-only service can be simplicity, or making IPv6
-more efficient and/or more useful.  To avoid making IPv6 a breaking
-requirement, clients for such an IPv6-only service could implement this
-profile of TSP 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; the interactions defined by TSP are all
-handled as required by RFC 5722, but some are strictly speaking not
-required for \fBpubTSP\fR.  We emphesise that their specification should still
-be met in all clients, so as to avoid dependency on this one
-implementation of TSP, and/or its current version.
-.PP
-The implementation listens to a UDP socket on TSP standard port 3653
-on the IPv4 side, and to a
-tunnel endpoint to capture all traffic matching an IPv6 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\-t\fR \fI/dev/tunX\fR
-.TP
-\fB\-\-tundev\fR \fI/dev/tunX\fR
-Instead of creating a tunnel for the duration that \fBpubTSP\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 \fBpubTSP\fR.  All that is required is acccess to
-the tunnel device by the user that runs \fBpubTSP\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 TSP
-tunnel commands and packaged-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\-t\fR option is given, a tunnel will be created for the time that
-\fBpubTSP\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 \fBpublicTSP\fR,
-nor will the general ablity to forward IPv6 traffic.
-.TP
-\fB\-h\fR
-.TP
-\fB\-\-help\fR
-Print usage information and exit.
-.SH ADDRESS FORMAT
-.PP
-An IPv6 address used from \fBpubTSP\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 \fBpubTSP\fR are:
-.PP
-\fIv6prefix\fR + \fIv4addr\fR + \fIudp-port\fR + \fIlocalnet\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 \fBpubTSP\fR.
-.PP
-The \fIlocalnet\fR defaults to 0, but may be used to differentiate up to
-65,536 different hosts accessible through the same TSP client.  As
-the main application foreseen for \fBpubTSP\fR is to get IPv6-only tools and
-devices working on an IPv4-only network, this is not a probable setup,
-but it is nevertheless possible.  The current version of \fBpubTSP\fR does
-not actually hand this over as a subnet.
-.PP
-The router address that \fBpubTSP\fR chooses for itself consists of the
-\fIv6prefix\fR with zeroes appended; this falls outside the setup above,
-because neither IPv4 addresses nor UDP port numbers are not permitted
-te be zero-valued.
-.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 \fBpubTSP\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 \fBpubTSP\fR.
-Note that the TSP protocol provides this necessary information at the
-time the TSP tunnel is created.
-.PP
-It is recommended to keep NAT state in tact by regularly sending over
-the UDP port to the tunnel endpoint.  At the very least, 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 \fBpubTSP\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, \fBpubTSP\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 \fBpubTSP\fR or similar service.  A general naming
-or numbering scheme is needed to make that straightforward.  The
-\fBpubTSP\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, \fBpubTSP\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 \fBpubTSP\fR running
-anywhere; its stateless operation means that traffic going back and
-forth can go through different instances of \fBpubTSP\fR without
-posing problems.
-.PP
-The \fBpubTSP\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.
-.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 \fBpubTSP\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
-\fBpubTSP\fR.
-.PP
-http://0cpm.org/ \- the homepage of the 0cpm project.
-.PP
-http://devel.0cpm.org/pubtsp/ \- the homepage of \fBpubTSP\fR.
-.PP
-RFC 5722 \- the authoritative description of TSP, of which \fBpubTSP\fR is
-implements a specific profile for public service under NAT traversal.
-.SH AUTHOR
-\fBpubTSP\fR was written by Rick van Rein from OpenFortress.
-It was created to support the 0cpm project.
index d51df01..7aa8f3c 100644 (file)
--- a/router.c
+++ b/router.c
@@ -28,6 +28,8 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <stdbool.h>
+
 #include <errno.h>
 #include <unistd.h>
 #include <string.h>
 #include <linux/if_ether.h>
 
 
-struct tsphdr {
-       uint32_t seqnum;
-       uint32_t timestamp;
-};
-
+/* The following will initially fail, due to an IANA obligation to avoid
+ * default builds with non-standard options.
+ */
+#include "nonstd.h"
 
-#define TUNNEL_CAPABILITIES "CAPABILITY TUNNEL=V6UDPV4 AUTH=ANONYMOUS"
 
 #define MTU 1280
 
@@ -82,12 +82,16 @@ char *v4server = NULL;
 char *v6server = NULL;
 char *v6prefix = NULL;
 
-const char v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+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;
 
 
@@ -95,13 +99,8 @@ struct {
        struct tun_pi tun;
        union {
                struct {
-                       struct tsphdr tsp;
-                       uint8_t cmd [MTU];
-                       uint8_t zerobyte;
-               } cdata;
-               struct {
                        struct ip6_hdr v6hdr;
-                       uint8_t data [MTU];
+                       uint8_t data [MTU - sizeof (struct ip6_hdr)];
                } idata;
                struct {
                        struct ip6_hdr v6hdr;
@@ -112,8 +111,6 @@ struct {
 
 #define v4tunpi6       ( v4data6.tun)
 #define v4data         ((uint8_t *) &v4data6.udata)
-#define v4tsphdr       (&v4data6.udata.cdata.tsp)
-#define v4tspcmd       (v4data6.udata.cdata.cmd)
 #define v4hdr6         (&v4data6.udata.idata.v6hdr)
 #define v4src6         (&v4data6.udata.idata.v6hdr.ip6_src)
 #define v4dst6         (&v4data6.udata.idata.v6hdr.ip6_dst)
@@ -128,12 +125,15 @@ struct {
 #define v4v6icmpcode   ( v4data6.udata.ndata.v6icmphdr.icmp6_code)
 #define v4v6icmpcksum  ( v4data6.udata.ndata.v6icmphdr.icmp6_cksum)
 
+#define v4ngbsoltarget (&v4data6.udata.ndata.v6icmphdr.icmp6_data8 [4])
+
 
 struct {
        struct tun_pi tun;
        union {
                uint8_t data [MTU];
                struct ip6_hdr v6hdr;
+               struct icmp6_hdr v6icmp;
        } udata;
        uint8_t zero;
 } v6data6;
@@ -145,6 +145,9 @@ struct {
 #define v6dst6         (&v6data6.udata.v6hdr.ip6_dst)
 #define v6hoplimit     ( v6data6.udata.v6hdr.ip6_hops)
 
+#define v6nexthdr      ( v6data6.udata.v6hdr.ip6_nxt)
+#define v6icmptype     ( v6data6.udata.v6icmp.icmp6_type)
+
 
 uint8_t router_linklocal_address [] = {
        0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
@@ -183,7 +186,7 @@ int setup_tunnel (void) {
        int ok = 1;
        struct ifreq ifreq;
        memset (&ifreq, 0, sizeof (ifreq));
-       strncpy (ifreq.ifr_name, "6bed4", IFNAMSIZ);
+       strncpy (ifreq.ifr_name, "6bed4_rfc", IFNAMSIZ);
        ifreq.ifr_flags = IFF_TUN;
        if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
                ok = 0;
@@ -216,27 +219,11 @@ int setup_tunnel (void) {
 
 /*
  *
- * Command functions
+ * Utility functions
  *
  */
 
 
-/* Send a reply to a tunnel command back to the most recent sender.
- * This is a textual protocol, so reply is a NUL-terminated string
- * and "\r\n" will be postfixed.
- */
-void tspcmd_reply (char *reply) {
-       if (reply != (char *) v4tspcmd) {
-               strncpy (v4tspcmd, reply, MTU-1);
-       } else {
-               v4tspcmd [MTU-1] = 0;
-       }
-       strncat (v4tspcmd, "\r\n", MTU+1);
-printf ("Reply =%s=\n", v4tspcmd);
-       sendto (v4sox, v4data, sizeof (struct tsphdr) + strlen (v4tspcmd),                      MSG_DONTWAIT, (struct sockaddr *) &v4name, sizeof (v4name));
-}
-
-
 /* Calculate the ICMPv6 checksum field
  */
 uint16_t icmp6_checksum (size_t payloadlen) {
@@ -262,18 +249,20 @@ uint16_t icmp6_checksum (size_t payloadlen) {
  * 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/v6 src becomes dest, set v4/udp/v6 src, len, cksum, send.
+ * Actions: v4/udp src becomes dest, set v4/udp/v6 src, len, cksum, send.
+ *          v6 dest is provided (usually v4src6) but if it starts with
+ *         0x00,0x00 it will be replaced with allnodes_linklocal_address.
  */
-void icmp6_reply (size_t icmp6bodylen) {
+void icmp6_reply (size_t icmp6bodylen, struct in6_addr *dest) {
        v4v6hoplimit = 255;
        if ((icmp6bodylen & 0x07) != 4) {
                return;   /* illegal length, drop */
        }
        v4v6plen = htons (icmp6bodylen + 4);
-       if ((* (uint16_t *) v4src6) == htons (0x0000)) {
+       if ((* (uint16_t *) dest) == htons (0x0000)) {
                memcpy (v4dst6, allnodes_linklocal_address, 16);
        } else {
-               memcpy (v4dst6, v4src6, 16);
+               memcpy (v4dst6, dest, 16);
        }
        memcpy (v4src6, router_linklocal_address, 16);
        v4v6icmpcksum = icmp6_checksum (ntohs (v4v6plen));
@@ -301,8 +290,8 @@ ntohs (v4name.sin_port),
 size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
        v4v6icmpdata [optidx++] = 3;    // Type
        v4v6icmpdata [optidx++] = 4;    // Length
-       v4v6icmpdata [optidx++] = 112;  // This is a /112 prefix
-       v4v6icmpdata [optidx++] = 0x40; // L=0, A=1, Reserved1=0
+       v4v6icmpdata [optidx++] = 64;   // This is a /64 prefix
+       v4v6icmpdata [optidx++] = 0xc0; // L=1, A=1, Reserved1=0
        memset (v4v6icmpdata + optidx, endlife? 0x00: 0xff, 8);
        optidx += 8;
                                        // Valid Lifetime: Zero / Infinite
@@ -310,104 +299,104 @@ size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
        memset (v4v6icmpdata + optidx, 0, 4);
        optidx += 4;
                                        // Reserved2=0
-       memcpy (v4v6icmpdata + optidx +  0, &v6listen, 8);
-       memcpy (v4v6icmpdata + optidx +  8, &v4name.sin_addr, 4);
-       memcpy (v4v6icmpdata + optidx + 12, &v4name.sin_port, 2);
-       memset (v4v6icmpdata + optidx + 14, 0,                2);
-                                       // Set IPv6 /112 prefix
+       memcpy (v4v6icmpdata + optidx + 0, &v6listen, 8);
+       memset (v4v6icmpdata + optidx + 8, 0, 8);
        optidx += 16;
        return optidx;
 }
 
 
-/* Send an info message, in response to a creation request on the
- * tunnel.  The contents of this message are constant, in support of
- * the stateless implementation of this daemon.  The address and port
- * of the IPv4 sender are taken into account, but the information sent
- * over XML is not.
+/*
+ * 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.
  */
-void tspcmd_create (void) {
-       char v4client [INET_ADDRSTRLEN];
-       char v6client [INET6_ADDRSTRLEN+1];
-       inet_ntop (AF_INET, &v4name.sin_addr, v4client, sizeof (v4client));
-       snprintf (v6client, sizeof (v6client)-1,
-               "%x:%x:%x:%x:%x:%x:%x::",
-                       ntohs (v6listen.s6_addr16 [0]),
-                       ntohs (v6listen.s6_addr16 [1]),
-                       ntohs (v6listen.s6_addr16 [2]),
-                       ntohs (v6listen.s6_addr16 [3]),
-                       ntohl (v4name.sin_addr.s_addr) >> 16,
-                       ntohl (v4name.sin_addr.s_addr) & 0x0000ffff,
-                       ntohs (v4name.sin_port));
-       snprintf (v4tspcmd, MTU-1,
-"Content-length: 0000\r\n"
-"200 OK\r\n"
-"<tunnel action=\"info\" type=\"v6udpv4\" lifetime=\"86400\">\r\n"
-"  <server>\r\n"
-"    <address type=\"ipv4\">%s</address>\r\n"
-"    <address type=\"ipv6\">%s</address>\r\n"
-"  </server>\r\n"
-"  <client>\r\n"
-"    <address type=\"ipv4\">%s</address>\r\n"
-"    <address type=\"ipv6\">%s</address>\r\n"
-"    <keepalive interval=\"30\">\r\n"
-"      <address type=\"ipv6\">%s</address>\r\n"
-"    </keepalive>\r\n"
-"  </client>\r\n"
-"</tunnel>"
-               , v4server, v6server, v4client, v6client, v6server);
-       char contlen [6];
-       snprintf (contlen, 5, "%04d", strlen (v4tspcmd) - 22);
-printf ("strlen = %d, contlen = \"%s\"\n", strlen (v4tspcmd), contlen);
-       memcpy (v4tspcmd + 16, contlen, 4);
-       tspcmd_reply (v4tspcmd);
+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) % 256;
+       v4v6icmpdata [optidx + 3] = ntohs (v4name.sin_port) / 256;
+       memcpy (v4v6icmpdata + optidx + 4, &v4name.sin_addr, 4);
+       optidx += 8;
+       return optidx;
 }
 
 
-/* Handle the IPv4 message pointed at by msg as a tunnel command.
+/*
+ * Test if the provided IPv6 address matches the prefix used for 6bed4.
  */
-void handle_4to6_tspcmd (ssize_t v4tspcmdlen) {
-       //
-       // Tunnel data is textual, append '\0' and ensure that's all
-       v4tspcmd [v4tspcmdlen] = 0;
-       if (strlen (v4tspcmd) != v4tspcmdlen) {
-               // Tricky package contains '\0' -- drop silently
-               return;
+static inline bool prefix_6bed4 (struct in6_addr *ip6) {
+       return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
+}
+
+
+/*
+ * Validate the originator's IPv6 address.  It should match the
+ * UDP/IPv4 coordinates of the receiving 6bed4 socket.  Also,
+ * the /64 prefix must match that of v6listen.
+ */
+bool validate_originator (struct sockaddr_in *sin, struct in6_addr *ip6) {
+       uint16_t port = ntohs (sin->sin_port);
+       uint32_t addr = ntohl (sin->sin_addr.s_addr);
+       if (!prefix_6bed4 (ip6)) {
+               return false;
        }
-       //
-       // Handle VERSION= interaction
-printf ("CMD=%s=\n", v4tspcmd);
-       if (strncmp (v4tspcmd, "VERSION=", 8) == 0) {
-               if (strcmp (v4tspcmd + 8, "2.0.0\r\n") == 0) {
-                       tspcmd_reply (TUNNEL_CAPABILITIES);
-               } else {
-                       tspcmd_reply ("302 Unsupported client version");
-               }
+       if ((port % 256) != (ip6->s6_addr [8] ^ 0x02)) {
+               return false;
        }
-       //
-       // Handle AUTHENTICATE command
-       else if (strncmp (v4tspcmd, "AUTHENTICATE ", 13) == 0) {
-               if (strcmp (v4tspcmd + 13, "ANONYMOUS\r\n") == 0) {
-                       tspcmd_reply ("200 Success\r\n");
-               } else {
-                       tspcmd_reply ("300 Only ANONYMOUS authentication supported");
-               }
+       if ((port / 256) != ip6->s6_addr [9]) {
+               return false;
        }
-       //
-       // Handle XML prefixed with "content-length:"
-       else if (strncasecmp (v4tspcmd, "content-length:", 15) == 0) {
-               // Hoping to get away with not parsing XML:
-               if (strstr (v4tspcmd, "create")) {
-                       tspcmd_create ();
-               } else {
-                       tspcmd_reply ("200 Success");
-               }
+       if ((addr >> 24) != ip6->s6_addr [10]) {
+               return false;
        }
-       //
-       // Reject any further commands loudly
-       else {
-               tspcmd_reply ("310 Go away");
+       if ((addr & 0x00ffffff) != (htonl (ip6->s6_addr32 [3]) & 0x00ffffff)) {
+               return false;
        }
+       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);
+       //TODO:DEPRECATED// writepos = icmp6_dest_linkaddr (writepos);
+       memcpy (&observed, v6listen_linklocal, 8);
+       observed.s6_addr [8] = htons (v4name.sin_port) % 256 ^ 0x02;
+       observed.s6_addr [9] = htons (v4name.sin_port) / 256;
+       memcpy (&observed.s6_addr32 [3], &v4name.sin_addr, 4);
+       observed.s6_addr [10] = observed.s6_addr [12];
+       observed.s6_addr [11] = 0xff;
+       observed.s6_addr [12] = 0xfe;
+       icmp6_reply (writepos, &observed);
 }
 
 
@@ -421,24 +410,18 @@ printf ("CMD=%s=\n", v4tspcmd);
  * 136 0       Neighbour Advertisement         Ignore
  * 137 0       Redirect                        Ignore
  */
-void handle_4to6_ngb (ssize_t v4ngbcmdlen) {
+void handle_4to6_nd (ssize_t v4ngbcmdlen) {
        uint16_t srclinklayer;
-       //
-       // Ensure that the packet is large enough
        if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
                return;
        }
-       //
-       // Ensure that the packet is an ICMPv6 packet is otherwise okay
-       if (v4v6nexthdr != IPPROTO_ICMPV6 || v4v6icmpcode != 0 || v4v6hoplimit < 255) {
+       if (v4v6icmpcode != 0) {
                return;
        }
        if (icmp6_checksum (v4ngbcmdlen - sizeof (struct ip6_hdr)) != v4v6icmpcksum) {
                return;
        }
        //
-       // TODO? Ensure that the packet hops indicate that it is local traffic
-       //
        // Approved.  Perform neighbourly courtesy.
        switch (v4v6icmptype) {
        case ND_ROUTER_SOLICIT:
@@ -461,37 +444,23 @@ void handle_4to6_ngb (ssize_t v4ngbcmdlen) {
                        break;   /* illegal length, drop */
                }
                //
-               // Construct Router Advertisement
-               v4v6icmptype = ND_ROUTER_ADVERT;
-               v4v6icmpdata [0] = 0;                   // Cur Hop Limit: unspec
-               v4v6icmpdata [1] = 0x18;                // M=0, O=0
-                                                       // H=0, Prf=11
-                                                       // 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);
+               // Having accepted the Router Advertisement, respond
+               handle_6bed4_router_solicit ();
                break;
        case ND_NEIGHBOR_SOLICIT:
                //
                // Validate Neigbour Solicitation
-               if (v4dst6->s6_addr16 [0] == htons (0xff02)) {
-                       break;   /* drop */
+               if (!validate_originator (&v4name, v4src6)) {
+                       break;  /* bad source address, drop */
                }
-               if (v4src6->s6_addr16 [9] == htons (0x0000)) {
-                       // TODO: 24 ---> 24 + bytes_voor_srclinklayaddr
-                       if (v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) {
-                               break;   /* drop */
-                       }
-               } else {
-                       if (v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) {
-                               break;   /* 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
@@ -500,67 +469,58 @@ void handle_4to6_ngb (ssize_t v4ngbcmdlen) {
                v4v6icmpdata [1] =
                v4v6icmpdata [2] =
                v4v6icmpdata [3] = 0x00;        // R=1, S=1, O=1, Reserved=0
-               memcpy (v4v6icmpdata +  4, &v6listen, 8);        // prefix /64
-               memcpy (v4v6icmpdata + 12, &v4name.sin_addr, 4); // IPv4
-               memcpy (v4v6icmpdata + 16, &v4name.sin_port, 2); // UDPport
-               v4v6icmpdata [18] =
-               v4v6icmpdata [19] = 0x00;                        // router if-id
+               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
-               memset (v4v6icmpdata + 22, 0x00, 6); // Link-layer addr is 0
-               // Total length of ICMPv6 body is 28 bytes
-               icmp6_reply (28);
+               memcpy (v4v6icmpdata + 22, lladdr_6bed4, 6);
+               icmp6_reply (28, v4src6);       // 28 is the ICMPv6 response length
                break;
-       default:
+       case ND_ROUTER_ADVERT:
+       case ND_NEIGHBOR_ADVERT:
+       case ND_REDIRECT:
                break;   /* drop */
        }
 }
 
 
-/* Handle the IPv4 message pointed at by msg, checking if 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?
+/* 
+ * 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_payload (ssize_t v4datalen) {
-       //
-       // Ensure that the lower half of the IPv6 sender address is ok
-       if (v4src6->s6_addr32 [2] != v4name.sin_addr.s_addr) {
-               return;
-       }
-       if (v4src6->s6_addr16 [6] != v4name.sin_port) {
-               return;
-       }
-#if 0
-       if (v4src6->s6_addr16 [7] != htons (0x0000)) {
-               return;
-       }
-#endif
-       //
-       // Ensure that the top half of the IPv6 address is ok
-       // Note that this implies rejection of ::1/128, fe80::/10 and fec0::/10
-       if (memcmp (v4src6, &v6listen, 8) != 0) {
-               return;
-       }
-       if (v4src6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
-               return;
-       }
-       if (v4src6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
-               return;
-       }
-       //
-       // Send the unwrapped IPv6 message out over v6sox
-       memcpy (&v6name.sin6_addr, v4dst6, sizeof (v6name.sin6_addr));
-printf ("Sending IPv6, result = %d\n",
-       sendto (v6sox,
-                       &v4data6, sizeof (struct tun_pi) + v4datalen,
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v6name, sizeof (v6name)));
+void handle_4to6_plain_unicast (ssize_t v4datalen) {
 printf ("Writing IPv6, result = %d\n",
        write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
 }
 
+
+/*
+ * Forward a 6bed4 message to another 6bed4 destination address.
+ * 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_6bed4_plain_unicast (ssize_t v4datalen, struct in6_addr *ip6) {
+       v4name.sin_port = htons (ip6->s6_addr [9] << 8 | ip6->s6_addr [8] ^ 0x02);
+       uint8_t *addr = (uint8_t *) &v4name.sin_addr.s_addr;
+       addr [0] = ip6->s6_addr [10];
+       memcpy (addr + 1, ip6->s6_addr + 13, 3);
+printf ("Relaying over 6bed4 Network to %d.%d.%d.%d:%d, result = %d\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, 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.
@@ -572,7 +532,7 @@ void handle_4to6 (void) {
        //
        // Receive IPv4 package, which may be tunneled or a tunnel request
        buflen = recvfrom (v4sox,
-                       v4data, sizeof (struct tsphdr) + MTU,
+                       v4data, MTU,
                        MSG_DONTWAIT,
                        (struct sockaddr *) &v4name, &adrlen
                );
@@ -581,36 +541,45 @@ void handle_4to6 (void) {
                                program, strerror (errno));
                return;
        }
-       if (buflen < sizeof (struct tsphdr)) {
+       if (buflen < sizeof (struct ip6_hdr)) {
                return;
        }
-       if (v4v6hoplimit <= 1) {
-               // TODO: Send back an ICMPv6 error message
+       if ((v4data [0] & 0xf0) != 0x60) {
+               // Not an IPv6 packet
                return;
        }
-       int flag = v4data [0] & 0xf0;
-       switch (flag) {
-       case 0xf0:
-               /* Handle as a tunnel command package */
-               if (buflen > sizeof (struct tsphdr) + 1) {
-                       handle_4to6_tspcmd (buflen - sizeof (struct tsphdr));
+       //
+       // 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;
                }
-               return;
-       case 0x60:
-               /* Handle as a tunneled IPv6 package */
-               if (buflen > sizeof (struct ip6_hdr) + 1) {
-                       uint16_t dst = v4src6->s6_addr16 [0];
-                       if ((v4dst6->s6_addr16 [0] == htons (0xff02)) ||
-                           (v4dst6->s6_addr16 [0] == htons (0xfe80))) {
-                               handle_4to6_ngb (buflen);
+               handle_4to6_nd (buflen);
+       } else if ((v4dst6->s6_addr [0] != 0xff) && !(v4dst6->s6_addr [8] & 0x01)) {
+               //
+               // Plain Unicast
+               if (validate_originator (&v4name, v4src6)) {
+                       if (v4v6hoplimit-- <= 1) {
+                               return;
+                       }
+                       if (prefix_6bed4 (v4dst6)) {
+                               relay_6bed4_plain_unicast (buflen, v4dst6);
                        } else {
-                               v4v6hoplimit--;
-                               handle_4to6_payload (buflen);
+                               handle_4to6_plain_unicast (buflen);
                        }
+               } else if (prefix_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 ();
                }
-               return;
-       default:
-               /* Silently ignore wrong types of packages */
+       } else {
+               //
+               // Plain Multicast
+               //OPTIONAL// validate_originator, hoplimit, relay_mcast.
                return;
        }
 }
@@ -624,52 +593,36 @@ void handle_6to4 (void) {
        // Receive the IPv6 package and ensure a consistent size
        size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
        if (rawlen == -1) {
-               return;
+               return;         /* error reading, drop */
        }
        if (rawlen < sizeof (struct tun_pi) + sizeof (struct ip6_hdr) + 1) {
-               return;
+               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;
+               return;         /* hop limit exceeded, drop */
        }
-       if (v6tuncmd.proto != htons (ETH_P_IPV6)) {
-               return;
+       if ((v6dst6->s6_addr [0] == 0xff) /* TODO:UDP_PORT_NOT_YET_FORCED_TO_EVEN || (v6dst6->s6_addr [8] & 0x01) */ ) {
+               //OPTIONAL// handle_6to4_plain_multicast ()
+               return;         /* multicast, drop */
        }
-printf ("Received IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
+printf ("Received plain unicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
        //
        // Ensure that the incoming IPv6 address is properly formatted
        // Note that this avoids access to ::1/128, fe80::/10, fec0::/10
        if (memcmp (v6dst6, &v6listen, 8) != 0) {
                return;
        }
-       if (v6dst6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
-               return;
-       }
-       if (v6dst6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
-               return;
-       }
-#if 0
-       if (v6dst6->s6_addr16 [7] != htons (0x0000)) {
-               return;
-       }
-#endif
        //
        // Harvest socket address data from destination IPv6, then send
-       v4name.sin_family = AF_INET;
-       v4name.sin_addr.s_addr = v6dst6->s6_addr32 [2];
-       v4name.sin_port = v6dst6->s6_addr16 [6];
-printf ("Sending IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %d\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,
-                       v6data,
-                       rawlen - sizeof (struct tun_pi),
-                       MSG_DONTWAIT,
-                       (struct sockaddr *) &v4name, sizeof (v4name)));
+       relay_6bed4_plain_unicast (rawlen - sizeof (struct tun_pi), v6dst6);
 }
 
 
@@ -730,7 +683,7 @@ int process_args (int argc, char *argv []) {
                case 'l':
                        if (v4sox != -1) {
                                ok = 0;
-                               fprintf (stderr, "%s: Only one -l argument is permitted\n");
+                               fprintf (stderr, "%s: Only one -l argument is permitted\n", program);
                                break;
                        }
                        v4server = optarg;
@@ -755,7 +708,7 @@ int process_args (int argc, char *argv []) {
                case 'L':
                        if (v6server) {
                                ok = 0;
-                               fprintf (stderr, "%s: Only one -L argument is permitted\n");
+                               fprintf (stderr, "%s: Only one -L argument is permitted\n", program);
                                break;
                        }
                        char *slash64 = strchr (optarg, '/');
@@ -770,7 +723,7 @@ int process_args (int argc, char *argv []) {
                        v6prefix = optarg;
                        if (!v6server || inet_pton (AF_INET6, v6server, &v6listen) <= 0) {
                                ok = 0;
-                               fprintf (stderr, "%s: Failed to parse IPv6 prefix %s\n", optarg);
+                               fprintf (stderr, "%s: Failed to parse IPv6 prefix %s\n", program, optarg);
                                break;
                        }
                        if (v6listen.s6_addr32 [2] || v6listen.s6_addr32 [3]) {
@@ -782,7 +735,7 @@ int process_args (int argc, char *argv []) {
                case 't':
                        if (v6sox != -1) {
                                ok = 0;
-                               fprintf (stderr, "%s: Multiple -t arguments are not permitted\n");
+                               fprintf (stderr, "%s: Multiple -t arguments are not permitted\n", program);
                                break;
                        }
                        v6sox = open (optarg, O_RDWR);
@@ -851,7 +804,7 @@ int main (int argc, char *argv []) {
        memset (&v6name, 0, sizeof (v6name));
        v4name.sin_family  = AF_INET ;
        v6name.sin6_family = AF_INET6;
-       v4name.sin_port = htons (3653); /* TSP standard port */
+       v4name.sin_port = htons (UDP_PORT_6BED4);   /* 6BED4 standard port */
        v4tunpi6.flags = 0;
        v4tunpi6.proto = htons (ETH_P_IPV6);
        //
@@ -860,7 +813,34 @@ int main (int argc, char *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 % 256;
+       lladdr_6bed4 [1] = UDP_PORT_6BED4 / 256;
+       memcpy (lladdr_6bed4 + 2, (uint8_t *) &v4name.sin_addr, 4);
+       memcpy (&v6listen_complete, &v6listen, 8);
+       v6listen_complete.s6_addr [8] = lladdr_6bed4 [0] ^ 0x02;
+       v6listen_complete.s6_addr [9] =  lladdr_6bed4 [1];
+       v6listen_complete.s6_addr [10] =  lladdr_6bed4 [2];
+       v6listen_complete.s6_addr [11] = 0xff;
+       v6listen_complete.s6_addr [12] = 0xfe;
+       memcpy (&v6listen_complete.s6_addr [13], lladdr_6bed4 + 3, 3);
+       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));
@@ -877,6 +857,9 @@ int main (int argc, char *argv []) {
                close (v6sox);
                break;
        }
+#else
+       run_daemon ();
+#endif
        //
        // Report successful creation of the daemon
        return 0;