#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.
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;
* 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
}
+/* 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
*
// 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);
}
/* 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' },
{ "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 */
}
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;
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) {
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;
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;
}
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);
#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
}
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);
//
// 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;