Added SCTP handling for masquerading
[6bed4] / 6bed4router.c
index 7de998b..63b099d 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 
+#include <stddef.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -45,6 +46,7 @@
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
+#include <netinet/tcp.h>
 #include <netinet/udp.h>
 #include <netinet/icmp6.h>
 #include <arpa/inet.h>
 
 /*
  * 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 -d option leads to an error, or to an attempt to setup the tunnel.
  * The setup_tunnel() function used for that is defined per platform, such
- * as for LINUX.  Remember to maintain the manpage's optionality for -t.
+ * as for LINUX.  Remember to maintain the manpage's optionality for -d.
  */
 #undef HAVE_SETUP_TUNNEL
 
 
 /* Global variables */
 
+/* SCTP structures are far from standardised, so we define our own header */
+struct my_sctphdr {
+       uint16_t source, dest;
+       uint32_t vtag;
+       uint32_t cksum;
+};
+
 char *program;
 
 int v4sox = -1;
@@ -102,9 +111,18 @@ struct {
                        struct ip6_hdr v6hdr;
                        uint8_t data [MTU - sizeof (struct ip6_hdr)];
                } idata;
-               struct {
+               struct v4frame {
                        struct ip6_hdr v6hdr;
-                       struct icmp6_hdr v6icmphdr;
+                       union {
+                               struct icmp6_hdr  v6icmphdr;
+                               struct my_sctphdr v6sctphdr;
+                               struct tcphdr     v6tcphdr ;
+                               struct udphdr     v6udphdr ;
+                               struct {
+                                       struct udphdr     v6udphdr;
+                                       struct my_sctphdr v6sctphdr;
+                               } tdata;
+                       } adata;
                } ndata;
        } udata;
 } v4data6;
@@ -115,38 +133,80 @@ struct {
 #define v4src6         (&v4data6.udata.idata.v6hdr.ip6_src)
 #define v4dst6         (&v4data6.udata.idata.v6hdr.ip6_dst)
 
+#define v4v6payload    ((uint16_t *) &v4data6.udata.ndata.adata)
 #define v4v6plen       ( v4data6.udata.ndata.v6hdr.ip6_plen)
 #define v4v6nexthdr    ( v4data6.udata.ndata.v6hdr.ip6_nxt)
 #define v4v6hoplimit   ( v4data6.udata.ndata.v6hdr.ip6_hops)
 
-#define 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 v4v6icmpdata   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_data8)
+#define v4v6icmptype   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_type)
+#define v4v6icmpcode   ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_code)
+#define v4v6icmpcksum  ( v4data6.udata.ndata.adata.v6icmphdr.icmp6_cksum)
+
+#define v4v6sctpofs    (offsetof (struct v4frame,adata.v6sctphdr))
+#define v4v6sctpsrcport        ( v4data6.udata.ndata.adata.v6sctphdr.source)
+#define v4v6sctpdstport        ( v4data6.udata.ndata.adata.v6sctphdr.dest)
+#define v4v6sctpcksum  ( v4data6.udata.ndata.adata.v6sctphdr.cksum)
+
+#define v4v6tunsctpofs (offsetof (struct v4frame,adata.tdata.v6sctphdr))
+#define v4v6tunsctpsrcport ( v4data6.udata.ndata.adata.tdata.v6sctphdr.source)
+#define v4v6tunsctpdstport ( v4data6.udata.ndata.adata.tdata.v6sctphdr.dest)
+#define v4v6tunsctpcksum   ( v4data6.udata.ndata.adata.tdata.v6sctphdr.cksum)
+
+#define v4v6tcpsrcport ( v4data6.udata.ndata.adata.v6tcphdr.source)
+#define v4v6tcpdstport ( v4data6.udata.ndata.adata.v6tcphdr.dest)
+#define v4v6tcpcksum   ( v4data6.udata.ndata.adata.v6tcphdr.check)
 
-#define v4ngbsoltarget (&v4data6.udata.ndata.v6icmphdr.icmp6_data8 [4])
+#define v4v6udpsrcport ( v4data6.udata.ndata.adata.v6udphdr.source)
+#define v4v6udpdstport ( v4data6.udata.ndata.adata.v6udphdr.dest)
+#define v4v6udpcksum   ( v4data6.udata.ndata.adata.v6udphdr.check)
+
+#define v4ngbsoltarget (&v4data6.udata.ndata.adata.v6icmphdr.icmp6_data8 [4])
 
 
 struct {
        struct tun_pi tun;
        union {
                uint8_t data [MTU];
-               struct ip6_hdr v6hdr;
-               struct icmp6_hdr v6icmp;
+               struct v6frame {
+                       struct ip6_hdr v6hdr;
+                       union {
+                               struct icmp6_hdr  v6icmphdr;
+                               struct my_sctphdr v6sctphdr;
+                               struct tcphdr     v6tcphdr ;
+                               struct udphdr     v6udphdr ;
+                               struct {
+                                       struct udphdr     v6udphdr;
+                                       struct my_sctphdr v6sctphdr;
+                               } tdata;
+                       } adata;
+               } ndata;
        } udata;
        uint8_t zero;
 } v6data6;
 
 #define v6tuncmd       ( v6data6.tun)
 #define v6data         ( v6data6.udata.data)
-#define v6hdr6         (&v6data6.udata.v6hdr)
-#define v6src6         (&v6data6.udata.v6hdr.ip6_src)
-#define v6dst6         (&v6data6.udata.v6hdr.ip6_dst)
-#define v6hoplimit     ( v6data6.udata.v6hdr.ip6_hops)
+#define v6hdr6         (&v6data6.udata.ndata.v6hdr)
+#define v6src6         (&v6data6.udata.ndata.v6hdr.ip6_src)
+#define v6dst6         (&v6data6.udata.ndata.v6hdr.ip6_dst)
+#define v6hoplimit     ( v6data6.udata.ndata.v6hdr.ip6_hops)
+#define v6nexthdr      ( v6data6.udata.ndata.v6hdr.ip6_nxt)
+
+#define v6icmptype     ( v6data6.udata.ndata.adata.v6icmphdr.icmp6_type)
+#define v6icmpcksum    ( v6data6.udata.ndata.adata.v6icmphdr.icmp6_cksum)
+
+#define v6sctpcksum    ( v6data6.udata.ndata.adata.v6sctphdr.cksum)
+#define v6tunsctpcksum ( v6data6.udata.ndata.adata.tdata.v6sctphdr.cksum)
 
-#define v6nexthdr      ( v6data6.udata.v6hdr.ip6_nxt)
-#define v6icmptype     ( v6data6.udata.v6icmp.icmp6_type)
+#define v6sctpofs      (offsetof(struct v6frame,adata.v6sctphdr))
+#define v6tunsctpofs   (offsetof(struct v6frame,adata.tdata.v6sctphdr))
+
+#define v6tcpcksum     ( v6data6.udata.ndata.adata.v6tcphdr.check)
+
+#define v6udpcksum     ( v6data6.udata.ndata.adata.v6udphdr.check)
+#define v6udpdstport   ( v6data6.udata.ndata.adata.v6udphdr.dest)
+#define v6udpsrcport   ( v6data6.udata.ndata.adata.v6udphdr.source)
 
 
 uint8_t router_linklocal_address [] = {
@@ -166,6 +226,30 @@ uint8_t allrouters_linklocal_address [] = {
 };
 
 
+
+#ifndef MAXNUM_MASQHOST
+#define MAXNUM_MASQHOST 4
+#endif
+
+uint16_t num_masqhost = 0;
+uint8_t masqhost [MAXNUM_MASQHOST][16];
+
+// ports for 's', 't', 'u' -- SCTP, TCP, UDP have increasing numbers
+
+#ifndef MAXNUM_PORTPAIRS
+#define MAXNUM_PORTPAIRS 10
+#endif
+
+// masqportpairs holds triples <lowport,highport,masqhostnr>
+uint16_t num_masqportpairs [3] = { 0, 0, 0 };
+uint16_t masqportpairs [3][3*MAXNUM_PORTPAIRS];
+
+// same for ICMPv6
+uint16_t icmp_num_portpairs = 0;
+uint16_t icmp_portpairs [3] = { 1, 1, 0 };
+
+
+
 /*
  *
  * Driver routines
@@ -246,11 +330,14 @@ void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
 
 /* Calculate the ICMPv6 checksum field
  */
-uint16_t icmp6_checksum (size_t payloadlen) {
-       uint16_t plenword = htons (payloadlen);
+uint16_t icmp6_checksum (struct in6_addr *src6, struct in6_addr *dst6,
+                               uint16_t *payload, size_t payloadlen) {
+       uint16_t plenword = htons (payloadlen); // our ICMPv6 is small
        uint16_t nxthword = htons (IPPROTO_ICMPV6);
-       uint16_t *area [] = { (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 };
+       uint16_t *area [] = { src6->s6_addr16, dst6->s6_addr16,
+                               &plenword, &nxthword,
+                               payload, &payload [2] };
+       uint8_t areawords [] = { 8, 8, 1, 1, 1, (payloadlen >> 1) - 2 };
        uint32_t csum = 0;
        u_int8_t i, j;
        for (i=0; i < 6; i++) {
@@ -265,6 +352,29 @@ uint16_t icmp6_checksum (size_t payloadlen) {
 }
 
 
+/* Mangle a packet by replacing an IPv6 address, correcting the checksum.
+ * In all of ICMPv6, UDPv6, TCPv6, SCTPv6, the checksumming is a 1's cpl
+ * of a sum of 16-bit words; the position of the words don't matter and
+ * we can subtract the old address' words and add the new address' words.
+ * Or, as a result of the one's complement, the opposite.  There is some
+ * inclusion of the top half of a 32-bit sum, but that is also neutral.
+ */
+void masquerade_address (struct in6_addr *var,
+                       const struct in6_addr *new,
+                       uint16_t *csum_field) {
+       int32_t csum = ntohs (~*csum_field);
+       uint8_t i;
+       for (i=0; i<8; i++) {
+               csum -= ntohs (var->s6_addr16 [i]);
+               var->s6_addr16 [i] = new->s6_addr16 [i];
+               csum += ntohs (var->s6_addr16 [i]);
+       }
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = (csum & 0xffff) + (csum >> 16);
+       *csum_field = htons (~csum);
+}
+
+
 /* Send an ICMPv6 reply.  This is constructed at the tunnel end, from
  * the incoming message.  The parameter indicates how many bytes the
  * ICMPv6 package counts after the ICMPv6 header.  It must be 4 (mod 8).
@@ -285,7 +395,8 @@ void icmp6_reply (size_t icmp6bodylen) {
                        : allnodes_linklocal_address,
                16);
        memcpy (v4src6, router_linklocal_address, 16);
-       v4v6icmpcksum = icmp6_checksum (ntohs (v4v6plen));
+       v4v6icmpcksum = icmp6_checksum (v4src6, v4dst6,
+                               v4v6payload, ntohs (v4v6plen));
        //
        // Send the message to the IPv4 originator port
 printf ("Sending ICMPv6-IPv6-UDP-IPv4 to %d.%d.%d.%d:%d, result = %zd\n",
@@ -359,12 +470,13 @@ size_t icmp6_dest_linkaddr (size_t optidx) {
  */
 #ifdef LOCAL_OVERRIDES_PORT0
 static inline bool is_local_override (struct in6_addr *ip6) {
-       return ip6->s6_addr16 [6] == 0;
+       return (ip6->s6_addr16 [6] == 0) && (memcmp (ip6->s6_addr, &v6listen, 8) == 0);
 }
 #else
 #define is_local_override(_) false
 #endif
 
+
 /*
  * Test if the provided IPv6 address matches the prefix used for 6bed4.
  *TODO: This is oversimplistic, it only cares for the Hetzner /64
@@ -373,6 +485,26 @@ static inline bool is_6bed4 (struct in6_addr *ip6) {
        return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
 }
 
+/* Test if the provided IPv6 address matches the fc64::/16 prefix.
+ * If so, the traffic may be bounced using 6bed4 traffic, but it
+ * must not be relayed to the native IPv6 side.
+ * TODO: Perhaps allow only configured <netid>, so fc64:<netid>::/32
+ */
+static inline bool is_fc64 (struct in6_addr *ip6) {
+       return ip6->s6_addr16 [0] == htons (0xfc64);
+}
+
+/* Test if the src and destination share the same /114 and the destination
+ * fills up the rest with zero bits; in other words, test that this is a
+ * package targeted from a 6bed4peer node to its 6bed4router.  This is used
+ * to distinguish special ICMPv6 messages, as well as masquerading targets.
+ */
+static inline bool is_mine (struct in6_addr *src6, struct in6_addr *dst6) {
+       return ((dst6->s6_addr [15] == 0x00)
+               && ((dst6->s6_addr [14] & 0x3f) == 0x00)
+               && (memcmp (dst6->s6_addr, src6->s6_addr, 14) == 0));
+}
+
 
 /*
  * Validate the originator's IPv6 address.  It should match the
@@ -383,7 +515,7 @@ bool validate_originator (struct in6_addr *ip6) {
        uint32_t addr;
        //
        // Require non-local top halves to match our v6listen_linklocal address
-       // We will enforce our own fallback address (and fc64:<port>)
+       // We will enforce our own fallback address (and fc64:<netid>::/32)
        if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
                if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
                        return false;
@@ -407,6 +539,7 @@ bool validate_originator (struct in6_addr *ip6) {
 }
 
 
+
 /*
  * Major packet processing functions
  */
@@ -452,7 +585,9 @@ void handle_4to6_nd (ssize_t v4ngbcmdlen) {
        if (v4v6icmpcode != 0) {
                return;
        }
-       if (icmp6_checksum (v4ngbcmdlen - sizeof (struct ip6_hdr)) != v4v6icmpcksum) {
+       if (icmp6_checksum (v4src6, v4dst6,
+                               v4v6payload, v4ngbcmdlen-sizeof (struct ip6_hdr)
+                       ) != v4v6icmpcksum) {
                return;
        }
        //
@@ -519,6 +654,85 @@ void handle_4to6_nd (ssize_t v4ngbcmdlen) {
 }
 
 
+/*
+ * Forward a message to the masquerading target, or drop it.  This is done
+ * with non-6bed4 packets that are sent from a 6bed4peer node to its
+ * 6bed4router address; see is_mine() for the definition.  The general idea
+ * is that the address that is to be masqueraded can later be reconstructed
+ * from the 6bed4peer's address.
+ */
+void handle_4to6_masquerading (ssize_t v4datalen) {
+       fprintf (stderr, "Traffic to 6bed4router may be fit for masquerading\n");
+       uint16_t *portpairs = NULL;
+       uint16_t numpairs = 0;
+       uint16_t port = 0;
+       uint16_t *csum_field = NULL;
+       uint16_t sctp_offset = 0;
+       uint16_t skip_csum;
+       switch (v4v6nexthdr) {
+       case IPPROTO_SCTP:
+               portpairs = masqportpairs [0];  // 's'
+               numpairs  = num_masqportpairs [0];
+               port = ntohs (v4v6sctpdstport);
+               csum_field = &skip_csum;
+               sctp_offset = v4v6sctpofs;
+               break;
+       case IPPROTO_TCP:
+               portpairs = masqportpairs [1];  // 't'
+               numpairs  = num_masqportpairs [1];
+               port = ntohs (v4v6tcpdstport);
+               csum_field = &v4v6tcpcksum;
+               break;
+       case IPPROTO_UDP:
+               portpairs = masqportpairs [2];  // 'u'
+               numpairs  = num_masqportpairs [2];
+               port = ntohs (v4v6udpdstport);
+               csum_field = &v4v6udpcksum;
+               if ((port == 9899) && (v4v6udpsrcport == v4v6udpdstport)) {
+                       // SCTP tunneled over UDP (keep csum_field)
+                       portpairs = masqportpairs [0];
+                       numpairs = num_masqportpairs [0];
+                       port = ntohs (v4v6tunsctpdstport);
+                       sctp_offset = v4v6tunsctpofs;
+               }
+               break;
+       case IPPROTO_ICMPV6:
+               portpairs = icmp_portpairs;
+               numpairs = icmp_num_portpairs;
+               port = icmp_portpairs [0];
+               csum_field = &v4v6icmpcksum;
+               break;
+       default:
+               return;
+       }
+       fprintf (stderr, "DEBUG: Looking for masquerading of port %d in %d entries\n", port, numpairs);
+       while (numpairs-- > 0) {
+               if ((port >= portpairs [0]) && (port <= portpairs [1])) {
+                       //
+                       // Replace masqueraded address by 6bed4router's
+                       fprintf (stderr, "DEBUG: Passing traffic to masquerading address number %d\n", portpairs [2]);
+                       masquerade_address (v4dst6,
+                               (struct in6_addr *) masqhost [portpairs [2]],
+                               csum_field);
+                       if (sctp_offset > 0) {
+                               //TODO// Recompute SCTP's CRC-32 checksum
+                               // recompute_sctp_checksum (ip6hdr, sctp_offset, framelen);
+                               //BUT: SCTP checksum does not use pseudo IPhdr
+                               //SO:  Address masquerading goes undetected :-D
+                       }
+                       //
+                       // Forward immediately, and return from this function
+printf ("Writing Masqueraded IPv6, result = %zd\n",
+                       write (v6sox, &v4data6, sizeof (struct tun_pi) + v4datalen));
+                       return;
+               }
+               portpairs += 3;
+       }
+       //
+       // Silently skip the offered packet
+}
+
+
 /* 
  * Forward a message received over the 6bed4 Network over IPv6.
  * Note that existing checksums will work well, as only the
@@ -533,11 +747,12 @@ printf ("Writing IPv6, result = %zd\n",
 
 /*
  * Forward a 6bed4 message to another 6bed4 destination address.
+ * Local address prefixes fc64:<netid>:<ipv4>::/64 are also relayed.
  * Note that existing checksums will work well, as only the
  * Hop Limit has been altered, and this is not part of the
  * checksum calculations.
  */
-void relay_6bed4_plain_unicast (uint8_t* data, ssize_t v4datalen, struct in6_addr *ip6) {
+void relay_4to4_plain_unicast (uint8_t* data, ssize_t v4datalen, struct in6_addr *ip6) {
        v4name.sin_port = htons (ip6->s6_addr [12] << 8 | ip6->s6_addr [13]);
        uint8_t *addr = (uint8_t *) &v4name.sin_addr.s_addr;
        addr [0] = (ip6->s6_addr [8] & 0xfc) | ip6->s6_addr [14] >> 6;
@@ -592,6 +807,8 @@ void handle_4to6 (void) {
                        return;
                }
                handle_4to6_nd (buflen);
+       } else if (is_mine (v4src6, v4dst6)) {
+               handle_4to6_masquerading (buflen);
        } else if ((v4dst6->s6_addr [0] != 0xff) && !(v4dst6->s6_addr [8] & 0x01)) {
                //
                // Plain Unicast
@@ -601,8 +818,8 @@ void handle_4to6 (void) {
                        if (v4v6hoplimit-- <= 1) {
                                return;
                        }
-                       if (is_6bed4 (v4dst6)) {
-                               relay_6bed4_plain_unicast (v4data, buflen, v4dst6);
+                       if (is_6bed4 (v4dst6) || is_fc64 (v4dst6)) {
+                               relay_4to4_plain_unicast (v4data, buflen, v4dst6);
                        } else {
                                handle_4to6_plain_unicast (buflen);
                        }
@@ -625,6 +842,7 @@ void handle_4to6 (void) {
  * port, then package it as a tunnel message and forward it to IPv4:port.
  */
 void handle_6to4 (void) {
+       uint16_t mqh;
        //
        // Receive the IPv6 package and ensure a consistent size
        size_t rawlen = read (v6sox, &v6data6, sizeof (v6data6));
@@ -652,14 +870,73 @@ printf ("Received multicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.f
        }
 printf ("Received plain unicast IPv6 data, flags=0x%04x, proto=0x%04x\n", v6tuncmd.flags, v6tuncmd.proto);
        //
+       // Replace masqueraded addresses configured with -m and its default ::1
+       //TODO// Better use the port number to find the find the masqhost
+       for (mqh=0; mqh<num_masqhost; mqh++) {
+               if (memcmp (v6src6, masqhost [mqh], 16) == 0) {
+printf ("Masqueraded sender address in 6to4 set to the client's 6bed4router address\n");
+                       uint16_t *csum_field = NULL;
+                       uint16_t fake_csum;
+                       struct in6_addr masq_src;
+                       uint16_t sctp_offset = 0;
+                       switch (v6nexthdr) {
+                       case IPPROTO_SCTP:
+                               csum_field = &fake_csum;
+                               sctp_offset = v6sctpofs;
+                               break;
+                       case IPPROTO_TCP:
+                               csum_field = &v6tcpcksum;
+                               break;
+                       case IPPROTO_UDP:
+                               csum_field = &v6udpcksum;
+                               if ((v6udpsrcport == v6udpdstport) &&
+                                               (v6udpsrcport == htons (9899))) {
+                                       // SCTP tunneled over UDP
+                                       sctp_offset = v6tunsctpofs;
+                               }
+                               break;
+                       case IPPROTO_ICMPV6:
+                               csum_field = &v6icmpcksum;
+                               break;
+                       default:
+                               continue;
+                       }
+                       //
+                       // Construct v6src6 from v6dst6, ending in 14 zero bits
+                       memcpy (&masq_src, v6dst6, 16);
+                       masq_src.s6_addr16 [7] &= htons (0xc000);
+                       fprintf (stderr, "Masquerading cksum.old = 0x%04x, ", ntohs (*csum_field));
+                       masquerade_address (v6src6, &masq_src, csum_field);
+                       if (sctp_offset > 0) {
+                               //TODO// Recompute SCTP's CRC-32 checksum
+                               // recompute_sctp_checksum (ip6hdr, sctp_offset, framelen);
+                               //BUT: SCTP checksum does not use pseudo IPhdr
+                               //SO:  Address masquerading goes undetected :-D
+                       }
+                       fprintf (stderr, "cksum.new = 0x%04x\n", ntohs (*csum_field));
+                       break;
+               }
+       }
+       //
        // Ensure that the incoming IPv6 address is properly formatted
        // Note that this avoids access to ::1/128, fe80::/10, fec0::/10
+#ifndef TODO_PERMIT_BOUNCE_FOR_32BIT_PREFIX
        if (memcmp (v6dst6, &v6listen, 8) != 0) {
                return;
        }
+#else
+       if (v6dst6->s6_addr32 [0] != v6listen.s6_addr32 [0]) {
+               // Mismatch /32 so this is not going to fly (anywhere)
+               return;
+       } else if (v6dst6->s6_addr32 [1] != v6listen.s6_addr32 [1]) {
+               // Match /32 but mismatch /64 -- relay to proper fallback
+               // that fc64::/16 is not welcome in 6to4 processing
+               //TODO//OVER_6bed4// relay_6to6_plain_unicast (v6data, rawlen - sizeof (struct tun_pi), v6dst6);
+       }
+#endif
        //
        // Harvest socket address data from destination IPv6, then send
-       relay_6bed4_plain_unicast (v6data, rawlen - sizeof (struct tun_pi), v6dst6);
+       relay_4to4_plain_unicast (v6data, rawlen - sizeof (struct tun_pi), v6dst6);
 }
 
 
@@ -691,13 +968,20 @@ fflush (stdout);
 
 /* Option descriptive data structures */
 
-char *short_opt = "l:L:t:h";
+char *short_opt = "l:L:d:hit:u:s:m:";
 
 struct option long_opt [] = {
        { "v4listen", 1, NULL, 'l' },
        { "v6prefix", 1, NULL, 'L' },
-       { "tundev", 1, NULL, 't' },
+       { "tundev", 1, NULL, 'd' },
        { "help", 0, NULL, 'h' },
+       { "icmp", 0, NULL, 'i' },
+       { "tcp", 1, NULL, 't' },
+       { "udp", 1, NULL, 'u' },
+       { "sctp", 1, NULL, 's' },
+       { "masqhost", 1, NULL, 'm' },
+       // { "fallback4", 1 NULL, 'f' },
+       // { "fallback6", 1, NULL, 'F' },
        { NULL, 0, NULL, 0 }    /* Array termination */
 };
 
@@ -708,8 +992,9 @@ int process_args (int argc, char *argv []) {
        int ok = 1;
        int help = (argc == 1);
        int done = 0;
+       int opt;
        while (!done) {
-               switch (getopt_long (argc, argv, short_opt, long_opt, NULL)) {
+               switch (opt = getopt_long (argc, argv, short_opt, long_opt, NULL)) {
                case -1:
                        done = 1;
                        if (optind != argc) {
@@ -758,7 +1043,7 @@ int process_args (int argc, char *argv []) {
                        v6server = strdup (optarg);
                        *slash64 = '/';
                        v6prefix = optarg;
-                       if (!v6server || inet_pton (AF_INET6, v6server, &v6listen) <= 0) {
+                       if (!v6server || (inet_pton (AF_INET6, v6server, &v6listen) <= 0)) {
                                ok = 0;
                                fprintf (stderr, "%s: Failed to parse IPv6 prefix %s\n", program, optarg);
                                break;
@@ -769,10 +1054,10 @@ int process_args (int argc, char *argv []) {
                                break;
                        }
                        break;
-               case 't':
+               case 'd':
                        if (v6sox != -1) {
                                ok = 0;
-                               fprintf (stderr, "%s: Multiple -t arguments are not permitted\n", program);
+                               fprintf (stderr, "%s: Multiple -d arguments are not permitted\n", program);
                                break;
                        }
                        v6sox = open (optarg, O_RDWR);
@@ -782,9 +1067,85 @@ int process_args (int argc, char *argv []) {
                                break;
                        }
                        break;
+               case 's':
+               case 't':
+               case 'u':
+               case 'i':
+                       // Masqueraded port (range) for SCTP, TCP, UDP
+                       //TODO// Should we support ICMPv6 as well? [honeypots]
+                       if (num_masqhost == 0) {
+                               //TODO// Reference v6listen instead of ::1
+                               inet_pton (AF_INET6, "::1", masqhost [0]);
+                               num_masqhost = 1;
+                       }
+                       // Temporary variables in local scope
+                       if (opt == 'i') {
+                               if (icmp_num_portpairs > 0) {
+                                       fprintf (stderr, "%s: Only one ICMP masquerading setting is possible\n", program);
+                                       ok = 0;
+                                       break;
+                               }
+                               icmp_portpairs [2] = num_masqhost - 1;
+                               icmp_num_portpairs++;
+                       } else {
+                               unsigned long fromport, toport;
+                               uint16_t *portpairs;
+                               errno = 0;
+                               if (*optarg != ':') {
+                                       fromport = strtoul (optarg, &optarg, 10);
+                               } else {
+                                       fromport = 1;
+                               }
+                               if (*optarg != ':') {
+                                       toport = fromport;
+                               } else if (*++optarg) {
+                                       toport = strtoul (optarg, &optarg, 10);
+                               } else {
+                                       toport = 65535;
+                               }
+                               if (errno || *optarg) {
+                                       fprintf (stderr, "%s: Failed to parse port or port:port\n", program);
+                                       ok = 0;
+                                       break;
+                               }
+                               if ((fromport < 1) || (fromport > toport) || (toport > 65535)) {
+                                       fprintf (stderr, "%s: Invalid port or port range\n", program);
+                                       ok = 0;
+                                       break;
+                               }
+                               if (++num_masqportpairs [opt-'s'] >= MAXNUM_PORTPAIRS) {
+                                       fprintf (stderr, "%s: You cannot define so many ports / port pairs\n", program);
+                                       ok = 0;
+                                       break;
+                               }
+                               portpairs = &masqportpairs [opt-'s'][3*(num_masqportpairs [opt-'s']-1)];
+                               portpairs [0] = fromport;
+                               portpairs [1] = toport;
+                               portpairs [2] = num_masqhost-1;
+                       }
+                       break;
+               case 'm':
+                       // Masqueraded host for TCP, UDP, SCTP (default is ::1)
+                       if (++num_masqhost >= MAXNUM_MASQHOST) {
+                               fprintf (stderr, "%s: No more than %d masquering hosts can be specified\n", program, MAXNUM_MASQHOST);
+                               ok = 0;
+                               break;
+                       }
+                       if (inet_pton (AF_INET6, optarg, masqhost [num_masqhost]) != 1) {
+                               fprintf (stderr, "%s: Unsupported masquerading host \"%s\"\n", program, optarg);
+                               ok = 0;
+                               break;
+                       }
+                       num_masqhost++;
+                       break;
+               // case 'f':
+               // case 'F':
+               //      // Fallback addresses for IPv4, IPv6
+               //      ok = 0; //TODO:IMPLEMENT//
+               //      help = 1;
+               //      break;
                default:
                        ok = 0;
-                       help = 1;
                        /* continue into 'h' to produce usage information */
                case 'h':
                        help = 1;
@@ -796,9 +1157,11 @@ int process_args (int argc, char *argv []) {
        }
        if (help) {
 #ifdef HAVE_SETUP_TUNNEL
-               fprintf (stderr, "Usage: %s [-t /dev/tunX] -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
+               fprintf (stderr, "Usage: %s [-d /dev/tunX] -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
+               fprintf (stderr, "\tUse -s|-t|-u to masquerade a port (range) to last -m host or ::1\n");
 #else
-               fprintf (stderr, "Usage: %s -t /dev/tunX -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
+               fprintf (stderr, "Usage: %s -d /dev/tunX -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
+               fprintf (stderr, "\tUse -s|-t|-u to masquerade a port (range) to last -m host of ::1\n");
 #endif
                return ok;
        }
@@ -816,14 +1179,14 @@ int process_args (int argc, char *argv []) {
 #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);
+                       fprintf (stderr, "%s: You should be root, or use -d to specify an accessible tunnel device\n", program);
                        return 0;
                }
                ok = setup_tunnel ();
        }
 #else /* ! HAVE_SETUP_TUNNEL */
        if (v6sox == -1) {
-               fprintf (stderr, "%s: You must specify a tunnel device with -t that is accessible to you\n", program);
+               fprintf (stderr, "%s: You must specify a tunnel device with -d that is accessible to you\n", program);
                return 0;
        }
 #endif /* HAVE_SETUP_TUNNEL */