Issue #5, Describe routable prefix, the -R option
[6bed4] / src / 6bed4router.c
index e3d99e4..9303f4d 100644 (file)
 
 #define MTU 1280
 
+
+#ifndef MAX_ROUTABLE_PREFIXES
+#define MAX_ROUTABLE_PREFIXES 10
+#endif
+
+
 /*
  * The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
  * the -d option leads to an error, or to an attempt to setup the tunnel.
@@ -96,13 +102,18 @@ uint8_t v6listen_linklocal_complete [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
 
 uint8_t lladdr_6bed4 [6];
 
-struct sockaddr_in  v4name;
 struct sockaddr_in6 v6name;
+struct sockaddr_in  v4name;
+struct sockaddr_in  v4name_xlate;
 
 struct in6_addr v6listen;
 struct in6_addr v6listen_complete;
 struct in_addr  v4listen;
 
+struct in6_addr v6routable_prefix    [MAX_ROUTABLE_PREFIXES];
+uint8_t         v6routable_prefixlen [MAX_ROUTABLE_PREFIXES];
+int             v6routable_prefixcount = 0;
+
 
 struct {
        struct tun_pi tun;
@@ -418,7 +429,7 @@ ntohs (v4name.sin_port),
  * 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) {
+size_t icmp6_prefix_local (size_t optidx, uint8_t endlife) {
        v4v6icmpdata [optidx++] = 3;    // Type
        v4v6icmpdata [optidx++] = 4;    // Length
        v4v6icmpdata [optidx++] = 114;  // This is a /114 prefix
@@ -437,6 +448,31 @@ size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
 }
 
 
+/* Append an extra routed 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_routed (size_t optidx, uint8_t endlife, struct in6_addr *prefix, uint8_t prefix_len) {
+       v4v6icmpdata [optidx++] = 3;    // Type
+       v4v6icmpdata [optidx++] = 4;    // Length
+       v4v6icmpdata [optidx++] = prefix_len;
+                                       // Prefix Length
+       v4v6icmpdata [optidx++] = 0x00; // L=0, A=0, 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, prefix, 16);
+                                       // Set IPv6 prefix
+       optidx += 16;
+       return optidx;
+}
+
+
 /*
  * TODO: DEPRECATED
  *
@@ -562,7 +598,12 @@ void handle_6bed4_router_solicit (void) {
                                        // Reachable Time: max
                                        // Retrans Timer: max
        writepos += 2+4+4;
-       writepos = icmp6_prefix (writepos, 0);
+       writepos = icmp6_prefix_local (writepos, 0);
+       for (int i = 0; i < v6routable_prefixcount ; i++) {
+               writepos = icmp6_prefix_routed (writepos, 0,
+                               &v6routable_prefix    [i],
+                                v6routable_prefixlen [i]);
+       }
        icmp6_reply (writepos);
 }
 
@@ -972,11 +1013,12 @@ fflush (stdout);
 
 /* Option descriptive data structures */
 
-char *short_opt = "l:L:d:hit:u:s:m:";
+char *short_opt = "l:L:R:d:hit:u:s:m:x:";
 
 struct option long_opt [] = {
        { "v4listen", 1, NULL, 'l' },
        { "v6prefix", 1, NULL, 'L' },
+       { "v6route", 1, NULL, 'R' },
        { "tundev", 1, NULL, 'd' },
        { "help", 0, NULL, 'h' },
        { "icmp", 0, NULL, 'i' },
@@ -984,6 +1026,7 @@ struct option long_opt [] = {
        { "udp", 1, NULL, 'u' },
        { "sctp", 1, NULL, 's' },
        { "masqhost", 1, NULL, 'm' },
+       { "xlate", 1, NULL, 'x' },
        // { "fallback4", 1 NULL, 'f' },
        // { "fallback6", 1, NULL, 'F' },
        { NULL, 0, NULL, 0 }    /* Array termination */
@@ -1007,11 +1050,12 @@ int process_args (int argc, char *argv []) {
                        }
                        break;
                case 'l':
-                       if (v4sox != -1) {
+                       if (v4name.sin_family != 0) {
                                ok = 0;
                                fprintf (stderr, "%s: Only one -l argument is permitted\n", program);
                                break;
                        }
+                       v4name.sin_family = AF_INET;
                        v4server = optarg;
                        if (inet_pton (AF_INET, optarg, &v4name.sin_addr) <= 0) {
                                ok = 0;
@@ -1019,17 +1063,25 @@ int process_args (int argc, char *argv []) {
                                break;
                        }
                        memcpy (&v4listen, &v4name.sin_addr, 4);
-                       v4sox = socket (AF_INET, SOCK_DGRAM, 0);
-                       if (v4sox == -1) {
+                       break;
+               case 'x':
+                       if (v4name_xlate.sin_family != 0) {
                                ok = 0;
-                               fprintf (stderr, "%s: Failed to allocate UDPv4 socket: %s\n", program, strerror (errno));
+                               fprintf (stderr, "%s: At most one -x argument is permitted\n", program);
                                break;
                        }
-                       if (bind (v4sox, (struct sockaddr *) &v4name, sizeof (v4name)) != 0) {
+                       v4name_xlate.sin_family = AF_INET;
+                       ((uint8_t *) &v4name_xlate.sin_addr.s_addr) [0] = 127;
+                       ((uint8_t *) &v4name_xlate.sin_addr.s_addr) [1] = 0;
+                       ((uint8_t *) &v4name_xlate.sin_addr.s_addr) [2] = 0;
+                       ((uint8_t *) &v4name_xlate.sin_addr.s_addr) [3] = 1;
+                       int port = atoi (optarg);
+                       if ((port <= 0) || (port > 65535)) {
                                ok = 0;
-                               fprintf (stderr, "%s: Failed to bind to UDPv4 %s:%d: %s\n", program, optarg, ntohs (v4name.sin_port), strerror (errno));
+                               fprintf (stderr, "%s: Port number for -x out of range\n", program);
                                break;
                        }
+                       v4name_xlate.sin_port = htons (port);
                        break;
                case 'L':
                        if (v6server) {
@@ -1043,7 +1095,7 @@ int process_args (int argc, char *argv []) {
                                fprintf (stderr, "%s: The -L argument must be an explicit /64 prefix and not %s\n", program, slash64? slash64: "implied");
                                break;
                        }
-                       *slash64 = 0;
+                       *slash64 = '\0';
                        v6server = strdup (optarg);
                        *slash64 = '/';
                        v6prefix = optarg;
@@ -1058,6 +1110,27 @@ int process_args (int argc, char *argv []) {
                                break;
                        }
                        break;
+               case 'R':
+                       if (v6routable_prefixcount >= MAX_ROUTABLE_PREFIXES) {
+                               ok = 0;
+                               fprintf (stderr, "%s: You cannot provide more than %d prefixes\n", program, MAX_ROUTABLE_PREFIXES);
+                               break;
+                       }
+                       char *prefix = strdup (optarg);
+                       char *prefixslash = strchr (prefix, '/');
+                       int prefixlen = atoi (prefixslash + 1);
+                       *prefixslash = '\0';
+                       if ((prefixlen < 16) || (prefixlen > 128)) {
+                               ok = 0;
+                               fprintf (stderr, "%s: IPv6 route prefix %s must be /16 up to /128\n", program, prefixslash + 1);
+                       } else if (inet_pton (AF_INET6, prefix, &v6routable_prefix [v6routable_prefixcount]) <= 0) {
+                               ok = 0;
+                               fprintf (stderr, "%s: Failed to parse IPv6 route %s", program, optarg);
+                       } else {
+                               v6routable_prefixlen [v6routable_prefixcount++] = prefixlen;
+                       }
+                       free (prefix);
+                       break;
                case 'd':
                        if (v6sox != -1) {
                                ok = 0;
@@ -1161,7 +1234,7 @@ int process_args (int argc, char *argv []) {
        }
        if (help) {
 #ifdef HAVE_SETUP_TUNNEL
-               fprintf (stderr, "Usage: %s [-d /dev/tunX] -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
+               fprintf (stderr, "Usage: %s [-d /dev/tunX] -l <v4server> [-x <port>] -L <v6prefix>/64 [-R <v6route>/n]...\n       %s -h\n", program, program);
                fprintf (stderr, "\tUse -s|-t|-u to masquerade a port (range) to last -m host or ::1\n");
 #else
                fprintf (stderr, "Usage: %s -d /dev/tunX -l <v4server> -L <v6prefix>/64\n       %s -h\n", program, program);
@@ -1169,15 +1242,25 @@ int process_args (int argc, char *argv []) {
 #endif
                return ok;
        }
-       if (!ok) {
+       if (v4name.sin_family == 0) {
+               fprintf (stderr, "%s: Use -l to specify an IPv4 address for the tunnel interface\n", program);
                return 0;
        }
+       if (v4name_xlate.sin_family == 0) {
+               memcpy (&v4name_xlate, &v4name, sizeof (v4name_xlate));
+       } else {
+               v4server = "127.0.0.1";
+       }
+       v4sox = socket (AF_INET, SOCK_DGRAM, 0);
        if (v4sox == -1) {
-               fprintf (stderr, "%s: Use -l to specify an IPv4 address for the tunnel interface\n", program);
+               fprintf (stderr, "%s: Failed to allocate UDPv4 socket: %s\n", program, strerror (errno));
                return 0;
        }
-       if (!v6server) {
-               fprintf (stderr, "%s: Use -L to specify a /64 prefix on the IPv6 side\n", program);
+       if (bind (v4sox, (struct sockaddr *) &v4name_xlate, sizeof (v4name_xlate)) != 0) {
+               fprintf (stderr, "%s: Failed to bind to UDPv4 %s:%d: %s\n", program, v4server, ntohs (v4name_xlate.sin_port), strerror (errno));
+               ok = 0;
+       }
+       if (!ok) {
                return 0;
        }
 #ifdef HAVE_SETUP_TUNNEL
@@ -1188,6 +1271,10 @@ int process_args (int argc, char *argv []) {
                }
                ok = setup_tunnel ();
        }
+       if (!v6server) {
+               fprintf (stderr, "%s: Use -L to specify a /64 prefix on the IPv6 side\n", program);
+               return 0;
+       }
 #else /* ! HAVE_SETUP_TUNNEL */
        if (v6sox == -1) {
                fprintf (stderr, "%s: You must specify a tunnel device with -d that is accessible to you\n", program);
@@ -1204,9 +1291,9 @@ int main (int argc, char *argv []) {
        //
        // Initialise
        program = argv [0];
-       memset (&v4name, 0, sizeof (v4name));
-       memset (&v6name, 0, sizeof (v6name));
-       v4name.sin_family  = AF_INET ;
+       memset (&v6name      , 0, sizeof (v6name      ));
+       memset (&v4name      , 0, sizeof (v4name      ));
+       memset (&v4name_xlate, 0, sizeof (v4name_xlate));
        v6name.sin6_family = AF_INET6;
        v4name.sin_port = htons (UDP_PORT_6BED4);   /* 6BED4 standard port */
        v4tunpi6.flags = 0;