aboutsummaryrefslogtreecommitdiffstats
path: root/main/openvpn/openvpn-2.2.2-ipv6.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/openvpn/openvpn-2.2.2-ipv6.patch')
-rw-r--r--main/openvpn/openvpn-2.2.2-ipv6.patch3517
1 files changed, 0 insertions, 3517 deletions
diff --git a/main/openvpn/openvpn-2.2.2-ipv6.patch b/main/openvpn/openvpn-2.2.2-ipv6.patch
deleted file mode 100644
index f8b8015ea6..0000000000
--- a/main/openvpn/openvpn-2.2.2-ipv6.patch
+++ /dev/null
@@ -1,3517 +0,0 @@
-diff --git a/forward.c b/forward.c
-index 87d05cc..1f3d435 100644
---- a/forward.c
-+++ b/forward.c
-@@ -262,7 +262,8 @@ send_control_channel_string (struct context *c, const char *str, int msglevel)
- static void
- check_add_routes_action (struct context *c, const bool errors)
- {
-- do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
-+ do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
-+ c->c1.tuntap, c->plugins, c->c2.es);
- update_time ();
- event_timeout_clear (&c->c2.route_wakeup);
- event_timeout_clear (&c->c2.route_wakeup_expire);
-diff --git a/helper.c b/helper.c
-index a9d7fd9..266b246 100644
---- a/helper.c
-+++ b/helper.c
-@@ -142,6 +142,55 @@ helper_client_server (struct options *o)
-
- #if P2MP
- #if P2MP_SERVER
-+
-+ /*
-+ *
-+ * HELPER DIRECTIVE for IPv6
-+ *
-+ * server-ipv6 2001:db8::/64
-+ *
-+ * EXPANDS TO:
-+ *
-+ * tun-ipv6
-+ * push "tun-ipv6"
-+ * ifconfig-ipv6 2001:db8::1 2001:db8::2
-+ * if !nopool:
-+ * ifconfig-ipv6-pool 2001:db8::1:0/64
-+ *
-+ */
-+ if ( o->server_ipv6_defined )
-+ {
-+ if ( ! o->server_defined )
-+ {
-+ msg (M_USAGE, "--server-ipv6 must be used together with --server");
-+ }
-+ if ( o->server_flags & SF_NOPOOL )
-+ {
-+ msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" );
-+ }
-+ if ( o->ifconfig_ipv6_pool_defined )
-+ {
-+ msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly");
-+ }
-+
-+ /* local ifconfig is "base address + 1" and "+2" */
-+ o->ifconfig_ipv6_local =
-+ print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc );
-+ o->ifconfig_ipv6_remote =
-+ print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc );
-+
-+ /* pool starts at "base address + 0x10000" */
-+ ASSERT( o->server_netbits_ipv6 < 96 ); /* want 32 bits */
-+ o->ifconfig_ipv6_pool_defined = true;
-+ o->ifconfig_ipv6_pool_base =
-+ add_in6_addr( o->server_network_ipv6, 0x10000 );
-+ o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6;
-+
-+ o->tun_ipv6 = true;
-+
-+ push_option( o, "tun-ipv6", M_USAGE );
-+ }
-+
- /*
- *
- * HELPER DIRECTIVE:
-diff --git a/init.c b/init.c
-index d47a4ef..7fc8eb7 100644
---- a/init.c
-+++ b/init.c
-@@ -843,7 +843,7 @@ do_persist_tuntap (const struct options *options)
- msg (M_FATAL|M_OPTERR,
- "options --mktun or --rmtun should only be used together with --dev");
- tuncfg (options->dev, options->dev_type, options->dev_node,
-- options->tun_ipv6, options->persist_mode,
-+ options->persist_mode,
- options->username, options->groupname, &options->tuntap_options);
- if (options->persist_mode && options->lladdr)
- set_lladdr(options->dev, options->lladdr, NULL);
-@@ -1066,6 +1066,8 @@ do_alloc_route_list (struct context *c)
- {
- if (c->options.routes && !c->c1.route_list)
- c->c1.route_list = new_route_list (c->options.max_routes, &c->gc);
-+ if (c->options.routes_ipv6 && !c->c1.route_ipv6_list)
-+ c->c1.route_ipv6_list = new_route_ipv6_list (c->options.max_routes, &c->gc);
- }
-
-
-@@ -1108,6 +1110,45 @@ do_init_route_list (const struct options *options,
- }
- }
-
-+static void
-+do_init_route_ipv6_list (const struct options *options,
-+ struct route_ipv6_list *route_ipv6_list,
-+ bool fatal,
-+ struct env_set *es)
-+{
-+ const char *gw = NULL;
-+ int dev = dev_type_enum (options->dev, options->dev_type);
-+ int metric = 0;
-+
-+ if (dev != DEV_TYPE_TUN )
-+ msg( M_WARN, "IPv6 routes on TAP devices are going to fail on some platforms (need gateway spec)" ); /* TODO-GERT */
-+
-+ gw = options->ifconfig_ipv6_remote; /* default GW = remote end */
-+#if 0 /* not yet done for IPv6 - TODO!*/
-+ if ( options->route_ipv6_default_gateway ) /* override? */
-+ gw = options->route_ipv6_default_gateway;
-+#endif
-+
-+ if (options->route_default_metric)
-+ metric = options->route_default_metric;
-+
-+ if (!init_route_ipv6_list (route_ipv6_list,
-+ options->routes_ipv6,
-+ gw,
-+ metric,
-+ es))
-+ {
-+ if (fatal)
-+ openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
-+ }
-+ else
-+ {
-+ /* copy routes to environment */
-+ setenv_routes_ipv6 (es, route_ipv6_list);
-+ }
-+}
-+
-+
- /*
- * Called after all initialization has been completed.
- */
-@@ -1172,12 +1213,13 @@ initialization_sequence_completed (struct context *c, const unsigned int flags)
- void
- do_route (const struct options *options,
- struct route_list *route_list,
-+ struct route_ipv6_list *route_ipv6_list,
- const struct tuntap *tt,
- const struct plugin_list *plugins,
- struct env_set *es)
- {
-- if (!options->route_noexec && route_list)
-- add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
-+ if (!options->route_noexec && ( route_list || route_ipv6_list ) )
-+ add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
-
- if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
- {
-@@ -1234,11 +1276,16 @@ do_init_tun (struct context *c)
- c->options.topology,
- c->options.ifconfig_local,
- c->options.ifconfig_remote_netmask,
-+ c->options.ifconfig_ipv6_local,
-+ c->options.ifconfig_ipv6_remote,
- addr_host (&c->c1.link_socket_addr.local),
- addr_host (&c->c1.link_socket_addr.remote),
- !c->options.ifconfig_nowarn,
- c->c2.es);
-
-+ /* flag tunnel for IPv6 config if --tun-ipv6 is set */
-+ c->c1.tuntap->ipv6 = c->options.tun_ipv6;
-+
- init_tun_post (c->c1.tuntap,
- &c->c2.frame,
- &c->options.tuntap_options);
-@@ -1270,6 +1317,8 @@ do_open_tun (struct context *c)
- /* parse and resolve the route option list */
- if (c->options.routes && c->c1.route_list && c->c2.link_socket)
- do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
-+ if (c->options.routes_ipv6 && c->c1.route_ipv6_list )
-+ do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);
-
- /* do ifconfig */
- if (!c->options.ifconfig_noexec
-@@ -1286,7 +1335,7 @@ do_open_tun (struct context *c)
-
- /* open the tun device */
- open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
-- c->options.tun_ipv6, c->c1.tuntap);
-+ c->c1.tuntap);
-
- /* set the hardware address */
- if (c->options.lladdr)
-@@ -1315,7 +1364,8 @@ do_open_tun (struct context *c)
-
- /* possibly add routes */
- if (!c->options.route_delay_defined)
-- do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
-+ do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
-+ c->c1.tuntap, c->plugins, c->c2.es);
-
- /*
- * Did tun/tap driver give us an MTU?
-@@ -1389,8 +1439,9 @@ do_close_tun (struct context *c, bool force)
- #endif
-
- /* delete any routes we added */
-- if (c->c1.route_list)
-- delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
-+ if (c->c1.route_list || c->c1.route_ipv6_list )
-+ delete_routes (c->c1.route_list, c->c1.route_ipv6_list,
-+ c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
-
- /* actually close tun/tap device based on --down-pre flag */
- if (!c->options.down_pre)
-diff --git a/init.h b/init.h
-index cf5ca8a..5a1d1dc 100644
---- a/init.h
-+++ b/init.h
-@@ -63,6 +63,7 @@ void init_instance (struct context *c, const struct env_set *env, const unsigned
-
- void do_route (const struct options *options,
- struct route_list *route_list,
-+ struct route_ipv6_list *route_ipv6_list,
- const struct tuntap *tt,
- const struct plugin_list *plugins,
- struct env_set *es);
-diff --git a/misc.c b/misc.c
-index 4067d85..9d351f4 100644
---- a/misc.c
-+++ b/misc.c
-@@ -1001,7 +1001,9 @@ setenv_str_ex (struct env_set *es,
- {
- const char *str = construct_name_value (name_tmp, val_tmp, &gc);
- env_set_add (es, str);
-- /*msg (M_INFO, "SETENV_ES '%s'", str);*/
-+#if DEBUG_VERBOSE_SETENV
-+ msg (M_INFO, "SETENV_ES '%s'", str);
-+#endif
- }
- else
- env_set_del (es, name_tmp);
-diff --git a/mroute.c b/mroute.c
-index 1b3488f..6cfec27 100644
---- a/mroute.c
-+++ b/mroute.c
-@@ -88,12 +88,33 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int
- }
- }
-
-+static inline void
-+mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned int mask)
-+{
-+ if (ma)
-+ {
-+ ma->type = MR_ADDR_IPV6 | mask;
-+ ma->netbits = 0;
-+ ma->len = 16;
-+ *(struct in6_addr *)ma->addr = src;
-+ }
-+}
-+
- static inline bool
- mroute_is_mcast (const in_addr_t addr)
- {
- return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
- }
-
-+/* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies
-+ * the address as being a multicast address"
-+ */
-+static inline bool
-+mroute_is_mcast_ipv6 (const struct in6_addr addr)
-+{
-+ return (addr.s6_addr[0] == 0xff);
-+}
-+
- #ifdef ENABLE_PF
-
- static unsigned int
-@@ -157,13 +178,29 @@ mroute_extract_addr_ipv4 (struct mroute_addr *src,
- }
- break;
- case 6:
-- {
-- if( !ipv6warned ) {
-- msg (M_WARN, "IPv6 in tun mode is not supported in OpenVPN 2.2");
-- ipv6warned = true;
-- }
-- break;
-- }
-+ if (BLEN (buf) >= (int) sizeof (struct openvpn_ipv6hdr))
-+ {
-+ const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR (buf);
-+#if 0 /* very basic debug */
-+ struct gc_arena gc = gc_new ();
-+ msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
-+ print_in6_addr( ipv6->saddr, 0, &gc ),
-+ print_in6_addr( ipv6->daddr, 0, &gc ));
-+ gc_free (&gc);
-+#endif
-+
-+ mroute_get_in6_addr (src, ipv6->saddr, 0);
-+ mroute_get_in6_addr (dest, ipv6->daddr, 0);
-+
-+ if (mroute_is_mcast_ipv6 (ipv6->daddr))
-+ ret |= MROUTE_EXTRACT_MCAST;
-+
-+ ret |= MROUTE_EXTRACT_SUCCEEDED;
-+ }
-+ break;
-+ default:
-+ msg (M_WARN, "IP packet with unknown IP version=%d seen",
-+ OPENVPN_IPH_GET_VER (*BPTR(buf)));
- }
- }
- return ret;
-@@ -257,14 +294,36 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
- * Zero off the host bits in an address, leaving
- * only the network bits, using the netbits member of
- * struct mroute_addr as the controlling parameter.
-+ *
-+ * TODO: this is called for route-lookup for every yet-unhashed
-+ * destination address, so for lots of active net-iroutes, this
-+ * might benefit from some "zeroize 32 bit at a time" improvements
- */
- void
- mroute_addr_mask_host_bits (struct mroute_addr *ma)
- {
- in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
-- ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
-- addr &= netbits_to_netmask (ma->netbits);
-- *(in_addr_t*)ma->addr = htonl (addr);
-+ if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
-+ {
-+ addr &= netbits_to_netmask (ma->netbits);
-+ *(in_addr_t*)ma->addr = htonl (addr);
-+ }
-+ else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
-+ {
-+ int byte = ma->len-1; /* rightmost byte in address */
-+ int bits_to_clear = 128 - ma->netbits;
-+
-+ while( byte >= 0 && bits_to_clear > 0 )
-+ {
-+ if ( bits_to_clear >= 8 )
-+ { ma->addr[byte--] = 0; bits_to_clear -= 8; }
-+ else
-+ { ma->addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
-+ }
-+ ASSERT( bits_to_clear == 0 );
-+ }
-+ else
-+ ASSERT(0);
- }
-
- /*
-@@ -342,17 +401,24 @@ mroute_addr_print_ex (const struct mroute_addr *ma,
- }
- break;
- case MR_ADDR_IPV6:
-- buf_printf (&out, "IPV6");
-- break;
-- default:
-- buf_printf (&out, "UNKNOWN");
-- break;
-- }
-- return BSTR (&out);
-- }
-- else
-- return "[NULL]";
--}
-+ {
-+ buf_printf (&out, "%s",
-+ print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc));
-+ if (maddr.type & MR_WITH_NETBITS)
-+ {
-+ buf_printf (&out, "/%d", maddr.netbits);
-+ }
-+ }
-+ break;
-+ default:
-+ buf_printf (&out, "UNKNOWN");
-+ break;
-+ }
-+ return BSTR (&out);
-+ }
-+ else
-+ return "[NULL]";
-+ }
-
- /*
- * mroute_helper's main job is keeping track of
-@@ -423,6 +489,40 @@ mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir)
- }
- }
-
-+/* this is a bit inelegant, we really should have a helper to that
-+ * is only passed the netbits value, and not the whole struct iroute *
-+ * - thus one helper could do IPv4 and IPv6. For the sake of "not change
-+ * code unrelated to IPv4" this is left for later cleanup, for now.
-+ */
-+void
-+mroute_helper_add_iroute6 (struct mroute_helper *mh,
-+ const struct iroute_ipv6 *ir6)
-+{
-+ if (ir6->netbits >= 0)
-+ {
-+ ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
-+ ++mh->cache_generation;
-+ ++mh->net_len_refcount[ir6->netbits];
-+ if (mh->net_len_refcount[ir6->netbits] == 1)
-+ mroute_helper_regenerate (mh);
-+ }
-+}
-+
-+void
-+mroute_helper_del_iroute6 (struct mroute_helper *mh,
-+ const struct iroute_ipv6 *ir6)
-+{
-+ if (ir6->netbits >= 0)
-+ {
-+ ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
-+ ++mh->cache_generation;
-+ --mh->net_len_refcount[ir6->netbits];
-+ ASSERT (mh->net_len_refcount[ir6->netbits] >= 0);
-+ if (!mh->net_len_refcount[ir6->netbits])
-+ mroute_helper_regenerate (mh);
-+ }
-+}
-+
- void
- mroute_helper_free (struct mroute_helper *mh)
- {
-diff --git a/mroute.h b/mroute.h
-index 7265001..b72b5ff 100644
---- a/mroute.h
-+++ b/mroute.h
-@@ -85,7 +85,7 @@ struct mroute_addr {
- /*
- * Number of bits in an address. Should be raised for IPv6.
- */
--#define MR_HELPER_NET_LEN 32
-+#define MR_HELPER_NET_LEN 129
-
- /*
- * Used to help maintain CIDR routing table.
-@@ -127,6 +127,8 @@ struct mroute_helper *mroute_helper_init (int ageable_ttl_secs);
- void mroute_helper_free (struct mroute_helper *mh);
- void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
- void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
-+void mroute_helper_add_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
-+void mroute_helper_del_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
-
- /*
- * Given a raw packet in buf, return the src and dest
-diff --git a/multi.c b/multi.c
-index 22c0a3f..f703b8d 100644
---- a/multi.c
-+++ b/multi.c
-@@ -316,25 +316,18 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
- */
- if (t->options.ifconfig_pool_defined)
- {
-- if (dev == DEV_TYPE_TAP)
-- {
-- m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
-- t->options.ifconfig_pool_start,
-- t->options.ifconfig_pool_end,
-- t->options.duplicate_cn);
-- }
-- else if (dev == DEV_TYPE_TUN)
-- {
-- m->ifconfig_pool = ifconfig_pool_init (
-- (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
-- t->options.ifconfig_pool_start,
-- t->options.ifconfig_pool_end,
-- t->options.duplicate_cn);
-- }
-- else
-- {
-- ASSERT (0);
-- }
-+ int pool_type = IFCONFIG_POOL_INDIV;
-+
-+ if ( dev == DEV_TYPE_TUN && t->options.topology == TOP_NET30 )
-+ pool_type = IFCONFIG_POOL_30NET;
-+
-+ m->ifconfig_pool = ifconfig_pool_init (pool_type,
-+ t->options.ifconfig_pool_start,
-+ t->options.ifconfig_pool_end,
-+ t->options.duplicate_cn,
-+ t->options.ifconfig_ipv6_pool_defined,
-+ t->options.ifconfig_ipv6_pool_base,
-+ t->options.ifconfig_ipv6_pool_netbits );
-
- /* reload pool data from file */
- if (t->c1.ifconfig_pool_persist)
-@@ -429,10 +422,14 @@ multi_del_iroutes (struct multi_context *m,
- struct multi_instance *mi)
- {
- const struct iroute *ir;
-+ const struct iroute_ipv6 *ir6;
- if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
- {
- for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
- mroute_helper_del_iroute (m->route_helper, ir);
-+
-+ for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
-+ mroute_helper_del_iroute6 (m->route_helper, ir6);
- }
- }
-
-@@ -1078,6 +1075,37 @@ multi_learn_in_addr_t (struct multi_context *m,
- }
- }
-
-+static struct multi_instance *
-+multi_learn_in6_addr (struct multi_context *m,
-+ struct multi_instance *mi,
-+ struct in6_addr a6,
-+ int netbits, /* -1 if host route, otherwise # of network bits in address */
-+ bool primary)
-+{
-+ struct mroute_addr addr;
-+
-+ addr.len = 16;
-+ addr.type = MR_ADDR_IPV6;
-+ addr.netbits = 0;
-+ memcpy( &addr.addr, &a6, sizeof(a6) );
-+
-+ if (netbits >= 0)
-+ {
-+ addr.type |= MR_WITH_NETBITS;
-+ addr.netbits = (uint8_t) netbits;
-+ mroute_addr_mask_host_bits( &addr );
-+ }
-+
-+ {
-+ struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
-+#ifdef MANAGEMENT_DEF_AUTH
-+ if (management && owner)
-+ management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
-+#endif
-+ return owner;
-+ }
-+}
-+
- /*
- * A new client has connected, add routes (server -> client)
- * to internal routing table.
-@@ -1088,6 +1116,7 @@ multi_add_iroutes (struct multi_context *m,
- {
- struct gc_arena gc = gc_new ();
- const struct iroute *ir;
-+ const struct iroute_ipv6 *ir6;
- if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
- {
- mi->did_iroutes = true;
-@@ -1107,6 +1136,22 @@ multi_add_iroutes (struct multi_context *m,
-
- multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
- }
-+ for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
-+ {
-+ if (ir6->netbits >= 0)
-+ msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
-+ print_in6_addr (ir6->network, 0, &gc),
-+ ir6->netbits,
-+ multi_instance_string (mi, false, &gc));
-+ else
-+ msg (D_MULTI_LOW, "MULTI: internal route %s -> %s",
-+ print_in6_addr (ir6->network, 0, &gc),
-+ multi_instance_string (mi, false, &gc));
-+
-+ mroute_helper_add_iroute6 (m->route_helper, ir6);
-+
-+ multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false);
-+ }
- }
- gc_free (&gc);
- }
-@@ -1192,21 +1237,37 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
- mi->context.c2.push_ifconfig_defined = true;
- mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
- mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
-+
-+ /* the current implementation does not allow "static IPv4, pool IPv6",
-+ * (see below) so issue a warning if that happens - don't break the
-+ * session, though, as we don't even know if this client WANTS IPv6
-+ */
-+ if ( mi->context.c1.tuntap->ipv6 &&
-+ mi->context.options.ifconfig_ipv6_pool_defined &&
-+ ! mi->context.options.push_ifconfig_ipv6_defined )
-+ {
-+ msg( M_INFO, "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, automatic IPv6 assignment from --ifconfig-ipv6-pool does not work. Use --ifconfig-ipv6-push for IPv6 then." );
-+ }
- }
- else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
- {
- in_addr_t local=0, remote=0;
-+ struct in6_addr remote_ipv6;
- const char *cn = NULL;
-
- if (!mi->context.options.duplicate_cn)
- cn = tls_common_name (mi->context.c2.tls_multi, true);
-
-- mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
-+ mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, &remote_ipv6, cn);
- if (mi->vaddr_handle >= 0)
- {
- const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
- const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
-
-+ msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s",
-+ print_in_addr_t( remote, 0, &gc ),
-+ print_in6_addr( remote_ipv6, 0, &gc ) );
-+
- /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
- mi->context.c2.push_ifconfig_local = remote;
- if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
-@@ -1228,12 +1289,46 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
- else
- msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
- multi_instance_string (mi, false, &gc));
-+
-+ if ( mi->context.options.ifconfig_ipv6_pool_defined )
-+ {
-+ mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
-+ mi->context.c2.push_ifconfig_ipv6_remote =
-+ mi->context.c1.tuntap->local_ipv6;
-+ mi->context.c2.push_ifconfig_ipv6_netbits =
-+ mi->context.options.ifconfig_ipv6_pool_netbits;
-+ mi->context.c2.push_ifconfig_ipv6_defined = true;
-+ }
- }
- else
- {
- msg (D_MULTI_ERRORS, "MULTI: no free --ifconfig-pool addresses are available");
- }
- }
-+
-+ /* IPv6 push_ifconfig is a bit problematic - since IPv6 shares the
-+ * pool handling with IPv4, the combination "static IPv4, dynamic IPv6"
-+ * will fail (because no pool will be allocated in this case).
-+ * OTOH, this doesn't make too much sense in reality - and the other
-+ * way round ("dynamic IPv4, static IPv6") or "both static" makes sense
-+ * -> and so it's implemented right now
-+ */
-+ if ( mi->context.c1.tuntap->ipv6 &&
-+ mi->context.options.push_ifconfig_ipv6_defined )
-+ {
-+ mi->context.c2.push_ifconfig_ipv6_local =
-+ mi->context.options.push_ifconfig_ipv6_local;
-+ mi->context.c2.push_ifconfig_ipv6_remote =
-+ mi->context.options.push_ifconfig_ipv6_remote;
-+ mi->context.c2.push_ifconfig_ipv6_netbits =
-+ mi->context.options.push_ifconfig_ipv6_netbits;
-+ mi->context.c2.push_ifconfig_ipv6_defined = true;
-+
-+ msg( M_INFO, "MULTI_sva: push_ifconfig_ipv6 %s/%d",
-+ print_in6_addr( mi->context.c2.push_ifconfig_ipv6_local, 0, &gc ),
-+ mi->context.c2.push_ifconfig_ipv6_netbits );
-+ }
-+
- gc_free (&gc);
- }
-
-@@ -1272,6 +1367,11 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi)
- SA_SET_IF_NONZERO);
- }
- }
-+
-+ /* TODO: I'm not exactly sure what these environment variables are
-+ * used for, but if we have them for IPv4, we should also have
-+ * them for IPv6, no?
-+ */
- }
-
- /*
-@@ -1661,6 +1761,15 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
- print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
- }
-
-+ if (mi->context.c2.push_ifconfig_ipv6_defined)
-+ {
-+ multi_learn_in6_addr (m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
-+ /* TODO: find out where addresses are "unlearned"!! */
-+ msg (D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
-+ multi_instance_string (mi, false, &gc),
-+ print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
-+ }
-+
- /* add routes locally, pointing to new client, if
- --iroute options have been specified */
- multi_add_iroutes (m, mi);
-diff --git a/openvpn.8 b/openvpn.8
-index 67a9779..5322618 100644
---- a/openvpn.8
-+++ b/openvpn.8
-@@ -794,6 +794,8 @@ or
- .B \-\-dev tunX.
- A warning will be displayed
- if no specific IPv6 TUN support for your OS has been compiled into OpenVPN.
-+
-+See below for further IPv6-related configuration options.
- .\"*********************************************************
- .TP
- .B \-\-dev-node node
-@@ -4949,6 +4951,57 @@ if certificates are stored as private objects.
- .B \-\-verb
- option can be used BEFORE this option to produce debugging information.
- .\"*********************************************************
-+.SS IPv6 Related Options
-+.\"*********************************************************
-+The following options exist to support IPv6 tunneling in peer-to-peer
-+and client-server mode. As of now, this is just very basic
-+documentation of the IPv6-related options. More documentation can be
-+found on http://www.greenie.net/ipv6/openvpn.html.
-+.TP
-+.B --ifconfig-ipv6 ipv6addr/bits ipv6remote
-+configure IPv6 address
-+.B ipv6addr/bits
-+on the ``tun'' device. The second parameter is used as route target for
-+.B --route-ipv6
-+if no gateway is specified.
-+.TP
-+.B --route-ipv6 ipv6addr/bits [gateway] [metric]
-+setup IPv6 routing in the system to send the specified IPv6 network
-+into OpenVPN's ``tun'' device
-+.TP
-+.B --server-ipv6 ipv6addr/bits
-+convenience-function to enable a number of IPv6 related options at
-+once, namely
-+.B --ifconfig-ipv6, --ifconfig-ipv6-pool, --tun-ipv6
-+and
-+.B --push tun-ipv6
-+Is only accepted if ``--mode server'' or ``--server'' is set.
-+.TP
-+.B --ifconfig-ipv6-pool ipv6addr/bits
-+Specify an IPv6 address pool for dynamic assignment to clients. The
-+pool starts at
-+.B ipv6addr
-+and increments by +1 for every new client (linear mode). The
-+.B /bits
-+setting controls the size of the pool.
-+.TP
-+.B --ifconfig-ipv6-push ipv6addr/bits ipv6remote
-+for ccd/ per-client static IPv6 interface configuration, see
-+.B --client-config-dir
-+and
-+.B --ifconfig-push
-+for more details.
-+.TP
-+.B --iroute-ipv6 ipv6addr/bits
-+for ccd/ per-client static IPv6 route configuration, see
-+.B --iroute
-+for more details how to setup and use this, and how
-+.B --iroute
-+and
-+.B --route
-+interact.
-+
-+.\"*********************************************************
- .SH SCRIPTING AND ENVIRONMENTAL VARIABLES
- OpenVPN exports a series
- of environmental variables for use by user-defined scripts.
-diff --git a/openvpn.h b/openvpn.h
-index 641bf93..e5e6e58 100644
---- a/openvpn.h
-+++ b/openvpn.h
-@@ -165,6 +165,9 @@ struct context_1
- /* list of --route directives */
- struct route_list *route_list;
-
-+ /* list of --route-ipv6 directives */
-+ struct route_ipv6_list *route_ipv6_list;
-+
- /* --status file */
- struct status_output *status_output;
- bool status_output_owned;
-@@ -417,6 +420,11 @@ struct context_2
- in_addr_t push_ifconfig_local;
- in_addr_t push_ifconfig_remote_netmask;
-
-+ bool push_ifconfig_ipv6_defined;
-+ struct in6_addr push_ifconfig_ipv6_local;
-+ int push_ifconfig_ipv6_netbits;
-+ struct in6_addr push_ifconfig_ipv6_remote;
-+
- /* client authentication state, CAS_SUCCEEDED must be 0 */
- # define CAS_SUCCEEDED 0
- # define CAS_PENDING 1
-diff --git a/options.c b/options.c
-index 7a5e35d..8fdd8a5 100644
---- a/options.c
-+++ b/options.c
-@@ -79,6 +79,7 @@ const char title_string[] =
- #ifdef ENABLE_EUREPHIA
- " [eurephia]"
- #endif
-+ " [IPv6 payload 20110522-1 (2.2.0)]"
- " built on " __DATE__
- ;
-
-@@ -172,6 +173,8 @@ static const char usage_message[] =
- " addresses outside of the subnets used by either peer.\n"
- " TAP: configure device to use IP address l as a local\n"
- " endpoint and rn as a subnet mask.\n"
-+ "--ifconfig-ipv6 l r : configure device to use IPv6 address l as local\n"
-+ " endpoint (as a /64) and r as remote endpoint\n"
- "--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
- " pass --ifconfig parms by environment to scripts.\n"
- "--ifconfig-nowarn : Don't warn if the --ifconfig option on this side of the\n"
-@@ -182,6 +185,10 @@ static const char usage_message[] =
- " netmask default: 255.255.255.255\n"
- " gateway default: taken from --route-gateway or --ifconfig\n"
- " Specify default by leaving blank or setting to \"nil\".\n"
-+ "--route-ipv6 network/bits [gateway] [metric] :\n"
-+ " Add IPv6 route to routing table after connection\n"
-+ " is established. Multiple routes can be specified.\n"
-+ " gateway default: taken from --route-ipv6-gateway or --ifconfig\n"
- "--max-routes n : Specify the maximum number of routes that may be defined\n"
- " or pulled from a server.\n"
- "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
-@@ -370,6 +377,7 @@ static const char usage_message[] =
- "\n"
- "Multi-Client Server options (when --mode server is used):\n"
- "--server network netmask : Helper option to easily configure server mode.\n"
-+ "--server-ipv6 network/bits : Configure IPv6 server mode.\n"
- "--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
- " easily configure ethernet bridging server mode.\n"
- "--push \"option\" : Push a config file option back to the peer for remote\n"
-@@ -383,10 +391,16 @@ static const char usage_message[] =
- "--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n"
- " data to file, at seconds intervals (default=600).\n"
- " If seconds=0, file will be treated as read-only.\n"
-+ "--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
-+ " to be dynamically allocated to connecting clients.\n"
- "--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
- " overrides --ifconfig-pool dynamic allocation.\n"
- " Only valid in a client-specific config file.\n"
-+ "--ifconfig-ipv6-push local/bits remote : Push an ifconfig-ipv6 option to\n"
-+ " remote, overrides --ifconfig-ipv6-pool allocation.\n"
-+ " Only valid in a client-specific config file.\n"
- "--iroute network [netmask] : Route subnet to client.\n"
-+ "--iroute-ipv6 network/bits : Route IPv6 subnet to client.\n"
- " Sets up internal routes only.\n"
- " Only valid in a client-specific config file.\n"
- "--disable : Client is disabled.\n"
-@@ -871,6 +885,78 @@ get_ip_addr (const char *ip_string, int msglevel, bool *error)
- return ret;
- }
-
-+/* helper: parse a text string containing an IPv6 address + netbits
-+ * in "standard format" (2001:dba::/32)
-+ * "/nn" is optional, default to /64 if missing
-+ *
-+ * return true if parsing succeeded, modify *network and *netbits
-+ * return address part without "/nn" in *printable_ipv6 (if != NULL)
-+ */
-+bool
-+get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
-+ unsigned int * netbits, char ** printable_ipv6, int msglevel )
-+{
-+ int rc;
-+ char * sep, * endp;
-+ int bits;
-+ struct in6_addr t_network;
-+
-+ sep = strchr( prefix_str, '/' );
-+ if ( sep == NULL )
-+ {
-+ bits = 64;
-+ }
-+ else
-+ {
-+ bits = strtol( sep+1, &endp, 10 );
-+ if ( *endp != '\0' || bits < 0 || bits > 128 )
-+ {
-+ msg (msglevel, "IPv6 prefix '%s': invalid '/bits' spec", prefix_str);
-+ return false;
-+ }
-+ }
-+
-+ /* temporary replace '/' in caller-provided string with '\0', otherwise
-+ * inet_pton() will refuse prefix string
-+ * (alternative would be to strncpy() the prefix to temporary buffer)
-+ */
-+
-+ if ( sep != NULL ) *sep = '\0';
-+
-+ rc = inet_pton( AF_INET6, prefix_str, &t_network );
-+
-+ if ( rc == 1 && printable_ipv6 != NULL )
-+ {
-+ *printable_ipv6 = string_alloc( prefix_str, NULL );
-+ }
-+
-+ if ( sep != NULL ) *sep = '/';
-+
-+ if ( rc != 1 )
-+ {
-+ msg (msglevel, "IPv6 prefix '%s': invalid IPv6 address", prefix_str);
-+ return false;
-+ }
-+
-+ if ( netbits != NULL )
-+ {
-+ *netbits = bits;
-+ }
-+ if ( network != NULL )
-+ {
-+ *network = t_network;
-+ }
-+ return true; /* parsing OK, values set */
-+}
-+
-+static bool ipv6_addr_safe_hexplusbits( const char * ipv6_prefix_spec )
-+{
-+ struct in6_addr t_addr;
-+ unsigned int t_bits;
-+
-+ return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, NULL, M_WARN );
-+}
-+
- static char *
- string_substitute (const char *src, int from, int to, struct gc_arena *gc)
- {
-@@ -989,6 +1075,8 @@ show_p2mp_parms (const struct options *o)
- #if P2MP_SERVER
- msg (D_SHOW_PARMS, " server_network = %s", print_in_addr_t (o->server_network, 0, &gc));
- msg (D_SHOW_PARMS, " server_netmask = %s", print_in_addr_t (o->server_netmask, 0, &gc));
-+ msg (D_SHOW_PARMS, " server_network_ipv6 = %s", print_in6_addr (o->server_network_ipv6, 0, &gc) );
-+ SHOW_INT (server_netbits_ipv6);
- msg (D_SHOW_PARMS, " server_bridge_ip = %s", print_in_addr_t (o->server_bridge_ip, 0, &gc));
- msg (D_SHOW_PARMS, " server_bridge_netmask = %s", print_in_addr_t (o->server_bridge_netmask, 0, &gc));
- msg (D_SHOW_PARMS, " server_bridge_pool_start = %s", print_in_addr_t (o->server_bridge_pool_start, 0, &gc));
-@@ -1009,6 +1097,9 @@ show_p2mp_parms (const struct options *o)
- msg (D_SHOW_PARMS, " ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc));
- SHOW_STR (ifconfig_pool_persist_filename);
- SHOW_INT (ifconfig_pool_persist_refresh_freq);
-+ SHOW_BOOL (ifconfig_ipv6_pool_defined);
-+ msg (D_SHOW_PARMS, " ifconfig_ipv6_pool_base = %s", print_in6_addr (o->ifconfig_ipv6_pool_base, 0, &gc));
-+ SHOW_INT (ifconfig_ipv6_pool_netbits);
- SHOW_INT (n_bcast_buf);
- SHOW_INT (tcp_queue_limit);
- SHOW_INT (real_hash_size);
-@@ -1022,6 +1113,9 @@ show_p2mp_parms (const struct options *o)
- SHOW_BOOL (push_ifconfig_defined);
- msg (D_SHOW_PARMS, " push_ifconfig_local = %s", print_in_addr_t (o->push_ifconfig_local, 0, &gc));
- msg (D_SHOW_PARMS, " push_ifconfig_remote_netmask = %s", print_in_addr_t (o->push_ifconfig_remote_netmask, 0, &gc));
-+ SHOW_BOOL (push_ifconfig_ipv6_defined);
-+ msg (D_SHOW_PARMS, " push_ifconfig_ipv6_local = %s/%d", print_in6_addr (o->push_ifconfig_ipv6_local, 0, &gc), o->push_ifconfig_ipv6_netbits );
-+ msg (D_SHOW_PARMS, " push_ifconfig_ipv6_remote = %s", print_in6_addr (o->push_ifconfig_ipv6_remote, 0, &gc));
- SHOW_BOOL (enable_c2c);
- SHOW_BOOL (duplicate_cn);
- SHOW_INT (cf_max);
-@@ -1076,6 +1170,25 @@ option_iroute (struct options *o,
- o->iroutes = ir;
- }
-
-+static void
-+option_iroute_ipv6 (struct options *o,
-+ const char *prefix_str,
-+ int msglevel)
-+{
-+ struct iroute_ipv6 *ir;
-+
-+ ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc);
-+
-+ if ( get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, NULL, msglevel ) < 0 )
-+ {
-+ msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification",
-+ prefix_str);
-+ return;
-+ }
-+
-+ ir->next = o->iroutes_ipv6;
-+ o->iroutes_ipv6 = ir;
-+}
- #endif /* P2MP_SERVER */
- #endif /* P2MP */
-
-@@ -1113,6 +1226,13 @@ rol_check_alloc (struct options *options)
- options->routes = new_route_option_list (options->max_routes, &options->gc);
- }
-
-+void
-+rol6_check_alloc (struct options *options)
-+{
-+ if (!options->routes_ipv6)
-+ options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
-+}
-+
- #ifdef ENABLE_DEBUG
- static void
- show_connection_entry (const struct connection_entry *o)
-@@ -1203,6 +1323,9 @@ show_settings (const struct options *o)
- SHOW_STR (ifconfig_remote_netmask);
- SHOW_BOOL (ifconfig_noexec);
- SHOW_BOOL (ifconfig_nowarn);
-+ SHOW_STR (ifconfig_ipv6_local);
-+ SHOW_INT (ifconfig_ipv6_netbits);
-+ SHOW_STR (ifconfig_ipv6_remote);
-
- #ifdef HAVE_GETTIMEOFDAY
- SHOW_INT (shaper);
-@@ -1863,8 +1986,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
- if (options->connection_list)
- msg (M_USAGE, "<connection> cannot be used with --mode server");
- #endif
-+#if 0
- if (options->tun_ipv6)
- msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
-+#endif
- if (options->shaper)
- msg (M_USAGE, "--shaper cannot be used with --mode server");
- if (options->inetd)
-@@ -1889,6 +2014,11 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
- msg (M_USAGE, "--up-delay cannot be used with --mode server");
- if (!options->ifconfig_pool_defined && options->ifconfig_pool_persist_filename)
- msg (M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool");
-+ if (options->ifconfig_ipv6_pool_defined && !options->ifconfig_ipv6_local )
-+ msg (M_USAGE, "--ifconfig-ipv6-pool needs --ifconfig-ipv6");
-+ if (options->ifconfig_ipv6_local && !options->tun_ipv6 )
-+ msg (M_INFO, "Warning: --ifconfig-ipv6 without --tun-ipv6 will not do IPv6");
-+
- if (options->auth_user_pass_file)
- msg (M_USAGE, "--auth-user-pass cannot be used with --mode server (it should be used on the client side only)");
- if (options->ccd_exclusive && !options->client_config_dir)
-@@ -1920,6 +2050,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
- */
- if (options->ifconfig_pool_defined || options->ifconfig_pool_persist_filename)
- msg (M_USAGE, "--ifconfig-pool/--ifconfig-pool-persist requires --mode server");
-+ if (options->ifconfig_ipv6_pool_defined)
-+ msg (M_USAGE, "--ifconfig-ipv6-pool requires --mode server");
- if (options->real_hash_size != defaults.real_hash_size
- || options->virtual_hash_size != defaults.virtual_hash_size)
- msg (M_USAGE, "--hash-size requires --mode server");
-@@ -2461,6 +2593,8 @@ options_string (const struct options *o,
- o->topology,
- o->ifconfig_local,
- o->ifconfig_remote_netmask,
-+ o->ifconfig_ipv6_local,
-+ o->ifconfig_ipv6_remote,
- (in_addr_t)0,
- (in_addr_t)0,
- false,
-@@ -3786,6 +3920,30 @@ add_option (struct options *options,
- goto err;
- }
- }
-+ else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] )
-+ {
-+ unsigned int netbits;
-+ char * ipv6_local;
-+
-+ VERIFY_PERMISSION (OPT_P_UP);
-+ if ( get_ipv6_addr( p[1], NULL, &netbits, &ipv6_local, msglevel ) &&
-+ ipv6_addr_safe( p[2] ) )
-+ {
-+ if ( netbits < 64 || netbits > 124 )
-+ {
-+ msg( msglevel, "ifconfig-ipv6: /netbits must be between 64 and 124, not '/%d'", netbits );
-+ goto err;
-+ }
-+ options->ifconfig_ipv6_local = ipv6_local;
-+ options->ifconfig_ipv6_netbits = netbits;
-+ options->ifconfig_ipv6_remote = p[2];
-+ }
-+ else
-+ {
-+ msg (msglevel, "ifconfig-ipv6 parms '%s' and '%s' must be valid addresses", p[1], p[2]);
-+ goto err;
-+ }
-+ }
- else if (streq (p[0], "ifconfig-noexec"))
- {
- VERIFY_PERMISSION (OPT_P_UP);
-@@ -4586,6 +4744,26 @@ add_option (struct options *options,
- }
- add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]);
- }
-+ else if (streq (p[0], "route-ipv6") && p[1])
-+ {
-+ VERIFY_PERMISSION (OPT_P_ROUTE);
-+ rol6_check_alloc (options);
-+ if (pull_mode)
-+ {
-+ if (!ipv6_addr_safe_hexplusbits (p[1]))
-+ {
-+ msg (msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
-+ goto err;
-+ }
-+ if (p[2] && !ipv6_addr_safe (p[2]))
-+ {
-+ msg (msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
-+ goto err;
-+ }
-+ /* p[3] is metric, if present */
-+ }
-+ add_route_ipv6_to_option_list (options->routes_ipv6, p[1], p[2], p[3]);
-+ }
- else if (streq (p[0], "max-routes") && p[1])
- {
- int max_routes;
-@@ -4797,6 +4975,33 @@ add_option (struct options *options,
- }
- }
- }
-+ else if (streq (p[0], "server-ipv6") && p[1] )
-+ {
-+ const int lev = M_WARN;
-+ struct in6_addr network;
-+ unsigned int netbits = 0;
-+
-+ VERIFY_PERMISSION (OPT_P_GENERAL);
-+ if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev) )
-+ {
-+ msg (msglevel, "error parsing --server-ipv6 parameter");
-+ goto err;
-+ }
-+ if ( netbits != 64 )
-+ {
-+ msg( msglevel, "--server-ipv6 settings: only /64 supported right now (not /%d)", netbits );
-+ goto err;
-+ }
-+ options->server_ipv6_defined = true;
-+ options->server_network_ipv6 = network;
-+ options->server_netbits_ipv6 = netbits;
-+
-+ if (p[2]) /* no "nopool" options or similar for IPv6 */
-+ {
-+ msg (msglevel, "error parsing --server-ipv6: %s is not a recognized flag", p[3]);
-+ goto err;
-+ }
-+ }
- else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4])
- {
- const int lev = M_WARN;
-@@ -4881,6 +5086,28 @@ add_option (struct options *options,
- VERIFY_PERMISSION (OPT_P_GENERAL);
- options->topology = TOP_P2P;
- }
-+ else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] )
-+ {
-+ const int lev = M_WARN;
-+ struct in6_addr network;
-+ unsigned int netbits = 0;
-+
-+ VERIFY_PERMISSION (OPT_P_GENERAL);
-+ if ( ! get_ipv6_addr (p[1], &network, &netbits, NULL, lev ) )
-+ {
-+ msg (msglevel, "error parsing --ifconfig-ipv6-pool parameters");
-+ goto err;
-+ }
-+ if ( netbits != 64 )
-+ {
-+ msg( msglevel, "--ifconfig-ipv6-pool settings: only /64 supported right now (not /%d)", netbits );
-+ goto err;
-+ }
-+
-+ options->ifconfig_ipv6_pool_defined = true;
-+ options->ifconfig_ipv6_pool_base = network;
-+ options->ifconfig_ipv6_pool_netbits = netbits;
-+ }
- else if (streq (p[0], "hash-size") && p[1] && p[2])
- {
- int real, virtual;
-@@ -5076,6 +5303,11 @@ add_option (struct options *options,
- }
- option_iroute (options, p[1], netmask, msglevel);
- }
-+ else if (streq (p[0], "iroute-ipv6") && p[1])
-+ {
-+ VERIFY_PERMISSION (OPT_P_INSTANCE);
-+ option_iroute_ipv6 (options, p[1], msglevel);
-+ }
- else if (streq (p[0], "ifconfig-push") && p[1] && p[2])
- {
- in_addr_t local, remote_netmask;
-@@ -5114,6 +5346,43 @@ add_option (struct options *options,
- goto err;
- }
- }
-+ else if (streq (p[0], "ifconfig-ipv6-push") && p[1] )
-+ {
-+ struct in6_addr local, remote;
-+ unsigned int netbits;
-+
-+ VERIFY_PERMISSION (OPT_P_INSTANCE);
-+
-+ if ( ! get_ipv6_addr( p[1], &local, &netbits, NULL, msglevel ) )
-+ {
-+ msg (msglevel, "cannot parse --ifconfig-ipv6-push addresses");
-+ goto err;
-+ }
-+
-+ if ( p[2] )
-+ {
-+ if ( !get_ipv6_addr( p[2], &remote, NULL, NULL, msglevel ) )
-+ {
-+ msg( msglevel, "cannot parse --ifconfig-ipv6-push addresses");
-+ goto err;
-+ }
-+ }
-+ else
-+ {
-+ if ( ! options->ifconfig_ipv6_local ||
-+ ! get_ipv6_addr( options->ifconfig_ipv6_local, &remote,
-+ NULL, NULL, msglevel ) )
-+ {
-+ msg( msglevel, "second argument to --ifconfig-ipv6-push missing and no global --ifconfig-ipv6 address set");
-+ goto err;
-+ }
-+ }
-+
-+ options->push_ifconfig_ipv6_defined = true;
-+ options->push_ifconfig_ipv6_local = local;
-+ options->push_ifconfig_ipv6_netbits = netbits;
-+ options->push_ifconfig_ipv6_remote = remote;
-+ }
- else if (streq (p[0], "disable"))
- {
- VERIFY_PERMISSION (OPT_P_INSTANCE);
-diff --git a/options.h b/options.h
-index dd49355..3b01597 100644
---- a/options.h
-+++ b/options.h
-@@ -205,6 +205,9 @@ struct options
- int topology; /* one of the TOP_x values from proto.h */
- const char *ifconfig_local;
- const char *ifconfig_remote_netmask;
-+ const char *ifconfig_ipv6_local;
-+ int ifconfig_ipv6_netbits;
-+ const char *ifconfig_ipv6_remote;
- bool ifconfig_noexec;
- bool ifconfig_nowarn;
- #ifdef HAVE_GETTIMEOFDAY
-@@ -326,6 +329,7 @@ struct options
- bool route_delay_defined;
- int max_routes;
- struct route_option_list *routes;
-+ struct route_ipv6_option_list *routes_ipv6; /* IPv6 */
- bool route_nopull;
- bool route_gateway_via_dhcp;
- bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
-@@ -363,6 +367,9 @@ struct options
- bool server_defined;
- in_addr_t server_network;
- in_addr_t server_netmask;
-+ bool server_ipv6_defined; /* IPv6 */
-+ struct in6_addr server_network_ipv6; /* IPv6 */
-+ unsigned int server_netbits_ipv6; /* IPv6 */
-
- # define SF_NOPOOL (1<<0)
- # define SF_TCP_NODELAY_HELPER (1<<1)
-@@ -384,6 +391,11 @@ struct options
- in_addr_t ifconfig_pool_netmask;
- const char *ifconfig_pool_persist_filename;
- int ifconfig_pool_persist_refresh_freq;
-+
-+ bool ifconfig_ipv6_pool_defined; /* IPv6 */
-+ struct in6_addr ifconfig_ipv6_pool_base; /* IPv6 */
-+ int ifconfig_ipv6_pool_netbits; /* IPv6 */
-+
- int real_hash_size;
- int virtual_hash_size;
- const char *client_connect_script;
-@@ -395,12 +407,17 @@ struct options
- int n_bcast_buf;
- int tcp_queue_limit;
- struct iroute *iroutes;
-+ struct iroute_ipv6 *iroutes_ipv6; /* IPv6 */
- bool push_ifconfig_defined;
- in_addr_t push_ifconfig_local;
- in_addr_t push_ifconfig_remote_netmask;
- bool push_ifconfig_constraint_defined;
- in_addr_t push_ifconfig_constraint_network;
- in_addr_t push_ifconfig_constraint_netmask;
-+ bool push_ifconfig_ipv6_defined; /* IPv6 */
-+ struct in6_addr push_ifconfig_ipv6_local; /* IPv6 */
-+ int push_ifconfig_ipv6_netbits; /* IPv6 */
-+ struct in6_addr push_ifconfig_ipv6_remote; /* IPv6 */
- bool enable_c2c;
- bool duplicate_cn;
- int cf_max;
-@@ -723,6 +740,10 @@ void options_string_import (struct options *options,
- unsigned int *option_types_found,
- struct env_set *es);
-
-+bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
-+ unsigned int * netbits, char ** printable_ipv6,
-+ int msglevel );
-+
- /*
- * inline functions
- */
-diff --git a/pool.c b/pool.c
-index 84333df..60dc520 100644
---- a/pool.c
-+++ b/pool.c
-@@ -132,7 +132,10 @@ ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_
- }
-
- struct ifconfig_pool *
--ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
-+ifconfig_pool_init (int type, in_addr_t start, in_addr_t end,
-+ const bool duplicate_cn,
-+ const bool ipv6_pool, const struct in6_addr ipv6_base,
-+ const int ipv6_netbits )
- {
- struct gc_arena gc = gc_new ();
- struct ifconfig_pool *pool = NULL;
-@@ -157,11 +160,31 @@ ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplica
- ASSERT (0);
- }
-
-+ /* IPv6 pools are always "INDIV" type */
-+ pool->ipv6 = ipv6_pool;
-+
-+ if ( pool->ipv6 )
-+ {
-+ pool->base_ipv6 = ipv6_base;
-+ pool->size_ipv6 = ipv6_netbits>96? ( 1<<(128-ipv6_netbits) )
-+ : IFCONFIG_POOL_MAX;
-+
-+ msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
-+ pool->size, pool->size_ipv6, ipv6_netbits,
-+ print_in6_addr( pool->base_ipv6, 0, &gc ));
-+
-+ /* the current code is very simple and assumes that the IPv6
-+ * pool is at least as big as the IPv4 pool, and we don't need
-+ * to do separate math etc. for IPv6
-+ */
-+ ASSERT( pool->size < pool->size_ipv6 );
-+ }
-+
- ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
-
-- msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
-+ msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
- print_in_addr_t (pool->base, 0, &gc),
-- pool->size);
-+ pool->size, pool->ipv6 );
-
- gc_free (&gc);
- return pool;
-@@ -181,7 +204,7 @@ ifconfig_pool_free (struct ifconfig_pool *pool)
- }
-
- ifconfig_pool_handle
--ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
-+ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name)
- {
- int i;
-
-@@ -214,6 +237,12 @@ ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *
- default:
- ASSERT (0);
- }
-+
-+ /* IPv6 pools are always INDIV (--linear) */
-+ if ( pool->ipv6 && remote_ipv6 )
-+ {
-+ *remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
-+ }
- }
- return i;
- }
-@@ -288,6 +317,19 @@ ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool
- return ret;
- }
-
-+static struct in6_addr
-+ifconfig_pool_handle_to_ipv6_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
-+{
-+ struct in6_addr ret = in6addr_any;
-+
-+ /* IPv6 pools are always INDIV (--linear) */
-+ if (hand >= 0 && hand < pool->size_ipv6 )
-+ {
-+ ret = add_in6_addr( pool->base_ipv6, hand );
-+ }
-+ return ret;
-+}
-+
- static void
- ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
- {
-@@ -317,9 +359,20 @@ ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out)
- if (e->common_name)
- {
- const in_addr_t ip = ifconfig_pool_handle_to_ip_base (pool, i);
-- status_printf (out, "%s,%s",
-- e->common_name,
-- print_in_addr_t (ip, 0, &gc));
-+ if ( pool->ipv6 )
-+ {
-+ struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base (pool, i);
-+ status_printf (out, "%s,%s,%s",
-+ e->common_name,
-+ print_in_addr_t (ip, 0, &gc),
-+ print_in6_addr (ip6, 0, &gc));
-+ }
-+ else
-+ {
-+ status_printf (out, "%s,%s",
-+ e->common_name,
-+ print_in_addr_t (ip, 0, &gc));
-+ }
- }
- }
- gc_free (&gc);
-@@ -409,6 +462,9 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool
- int c = *BSTR(&in);
- if (c == '#' || c == ';')
- continue;
-+ msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
-+ BSTR(&in) );
-+
- if (buf_parse (&in, ',', cn_buf, buf_size)
- && buf_parse (&in, ',', ip_buf, buf_size))
- {
-@@ -416,6 +472,7 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool
- const in_addr_t addr = getaddr (GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
- if (succeeded)
- {
-+ msg( M_INFO, "succeeded -> ifconfig_pool_set()");
- ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
- }
- }
-@@ -471,7 +528,7 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end)
- #else
- cn = buf;
- #endif
-- h = ifconfig_pool_acquire (p, &local, &remote, cn);
-+ h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
- if (h < 0)
- break;
- msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s",
-@@ -506,7 +563,7 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end)
- #else
- cn = buf;
- #endif
-- h = ifconfig_pool_acquire (p, &local, &remote, cn);
-+ h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
- if (h < 0)
- break;
- msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",
-diff --git a/pool.h b/pool.h
-index 81264a9..fc9d6ab 100644
---- a/pool.h
-+++ b/pool.h
-@@ -52,6 +52,9 @@ struct ifconfig_pool
- int size;
- int type;
- bool duplicate_cn;
-+ bool ipv6;
-+ struct in6_addr base_ipv6;
-+ unsigned int size_ipv6;
- struct ifconfig_pool_entry *list;
- };
-
-@@ -63,13 +66,13 @@ struct ifconfig_pool_persist
-
- typedef int ifconfig_pool_handle;
-
--struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn);
-+struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn, const bool ipv6_pool, const struct in6_addr ipv6_base, const int ipv6_netbits );
-
- void ifconfig_pool_free (struct ifconfig_pool *pool);
-
- bool ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end);
-
--ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name);
-+ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name);
-
- bool ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard);
-
-diff --git a/proto.h b/proto.h
-index 55f0832..b8e8997 100644
---- a/proto.h
-+++ b/proto.h
-@@ -108,6 +108,21 @@ struct openvpn_iphdr {
- };
-
- /*
-+ * IPv6 header
-+ */
-+struct openvpn_ipv6hdr {
-+ uint8_t version_prio;
-+ uint8_t flow_lbl[3];
-+ uint16_t payload_len;
-+ uint8_t nexthdr;
-+ uint8_t hop_limit;
-+
-+ struct in6_addr saddr;
-+ struct in6_addr daddr;
-+};
-+
-+
-+/*
- * UDP header
- */
- struct openvpn_udphdr {
-diff --git a/push.c b/push.c
-index 08c7f99..1fd8bea 100644
---- a/push.c
-+++ b/push.c
-@@ -189,8 +189,26 @@ send_push_reply (struct context *c)
- const int safe_cap = BCAP (&buf) - extra;
- bool push_sent = false;
-
-+ msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
-+
- buf_printf (&buf, "%s", cmd);
-
-+ if ( c->c2.push_ifconfig_ipv6_defined )
-+ {
-+ /* IPv6 is put into buffer first, could be lengthy */
-+ /* TODO: push "/netbits" as well, to allow non-/64 subnet sizes
-+ * (needs changes in options.c, options.h, and other places)
-+ */
-+ buf_printf( &buf, ",ifconfig-ipv6 %s %s",
-+ print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
-+ print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
-+ if (BLEN (&buf) >= safe_cap)
-+ {
-+ msg (M_WARN, "--push ifconfig-ipv6 option is too long");
-+ goto fail;
-+ }
-+ }
-+
- while (e)
- {
- if (e->enable)
-diff --git a/route.c b/route.c
-index b5092fe..7c81f75 100644
---- a/route.c
-+++ b/route.c
-@@ -35,6 +35,7 @@
- #include "socket.h"
- #include "manage.h"
- #include "win32.h"
-+#include "options.h"
-
- #include "memdbg.h"
-
-@@ -68,6 +69,15 @@ new_route_option_list (const int max_routes, struct gc_arena *a)
- return ret;
- }
-
-+struct route_ipv6_option_list *
-+new_route_ipv6_option_list (const int max_routes, struct gc_arena *a)
-+{
-+ struct route_ipv6_option_list *ret;
-+ ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a);
-+ ret->capacity = max_routes;
-+ return ret;
-+}
-+
- struct route_option_list *
- clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
- {
-@@ -95,6 +105,15 @@ new_route_list (const int max_routes, struct gc_arena *a)
- return ret;
- }
-
-+struct route_ipv6_list *
-+new_route_ipv6_list (const int max_routes, struct gc_arena *a)
-+{
-+ struct route_ipv6_list *ret;
-+ ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a);
-+ ret->capacity = max_routes;
-+ return ret;
-+}
-+
- static const char *
- route_string (const struct route *r, struct gc_arena *gc)
- {
-@@ -311,6 +330,68 @@ init_route (struct route *r,
- return false;
- }
-
-+static bool
-+init_route_ipv6 (struct route_ipv6 *r6,
-+ const struct route_ipv6_option *r6o,
-+ const struct route_ipv6_list *rl6 )
-+{
-+ r6->option = r6o;
-+ r6->defined = false;
-+
-+ if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, NULL, M_WARN ))
-+ goto fail;
-+
-+ /* gateway */
-+ if (is_route_parm_defined (r6o->gateway))
-+ {
-+ if ( inet_pton( AF_INET6, r6o->gateway, &r6->gateway ) != 1 )
-+ {
-+ msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway );
-+ }
-+ }
-+ else if (rl6->remote_endpoint_defined)
-+ {
-+ r6->gateway = rl6->remote_endpoint_ipv6;
-+ }
-+ else
-+ {
-+ msg (M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options");
-+ goto fail;
-+ }
-+
-+ /* metric */
-+
-+ r6->metric_defined = false;
-+ r6->metric = 0;
-+ if (is_route_parm_defined (r6o->metric))
-+ {
-+ r6->metric = atoi (r6o->metric);
-+ if (r6->metric < 0)
-+ {
-+ msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
-+ r6o->prefix,
-+ r6o->metric);
-+ goto fail;
-+ }
-+ r6->metric_defined = true;
-+ }
-+ else if (rl6->default_metric_defined)
-+ {
-+ r6->metric = rl6->default_metric;
-+ r6->metric_defined = true;
-+ }
-+
-+ r6->defined = true;
-+
-+ return true;
-+
-+ fail:
-+ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
-+ r6o->prefix);
-+ r6->defined = false;
-+ return false;
-+}
-+
- void
- add_route_to_option_list (struct route_option_list *l,
- const char *network,
-@@ -331,6 +412,23 @@ add_route_to_option_list (struct route_option_list *l,
- }
-
- void
-+add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
-+ const char *prefix,
-+ const char *gateway,
-+ const char *metric)
-+{
-+ struct route_ipv6_option *ro;
-+ if (l->n >= l->capacity)
-+ msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file",
-+ l->capacity);
-+ ro = &l->routes_ipv6[l->n];
-+ ro->prefix = prefix;
-+ ro->gateway = gateway;
-+ ro->metric = metric;
-+ ++l->n;
-+}
-+
-+void
- clear_route_list (struct route_list *rl)
- {
- const int capacity = rl->capacity;
-@@ -340,6 +438,15 @@ clear_route_list (struct route_list *rl)
- }
-
- void
-+clear_route_ipv6_list (struct route_ipv6_list *rl6)
-+{
-+ const int capacity = rl6->capacity;
-+ const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
-+ memset(rl6, 0, rl6_size);
-+ rl6->capacity = capacity;
-+}
-+
-+void
- route_list_add_default_gateway (struct route_list *rl,
- struct env_set *es,
- const in_addr_t addr)
-@@ -469,6 +576,72 @@ init_route_list (struct route_list *rl,
- return ret;
- }
-
-+bool
-+init_route_ipv6_list (struct route_ipv6_list *rl6,
-+ const struct route_ipv6_option_list *opt6,
-+ const char *remote_endpoint,
-+ int default_metric,
-+ struct env_set *es)
-+{
-+ struct gc_arena gc = gc_new ();
-+ bool ret = true;
-+
-+ clear_route_ipv6_list (rl6);
-+
-+ rl6->flags = opt6->flags;
-+
-+ if (default_metric)
-+ {
-+ rl6->default_metric = default_metric;
-+ rl6->default_metric_defined = true;
-+ }
-+
-+ /* "default_gateway" is stuff for "redirect-gateway", which we don't
-+ * do for IPv6 yet -> TODO
-+ */
-+ {
-+ dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
-+ }
-+
-+ if ( is_route_parm_defined( remote_endpoint ))
-+ {
-+ if ( inet_pton( AF_INET6, remote_endpoint,
-+ &rl6->remote_endpoint_ipv6) == 1 )
-+ {
-+ rl6->remote_endpoint_defined = true;
-+ }
-+ else
-+ {
-+ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
-+ ret = false;
-+ }
-+ }
-+ else
-+ rl6->remote_endpoint_defined = false;
-+
-+
-+ if (!(opt6->n >= 0 && opt6->n <= rl6->capacity))
-+ msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity);
-+
-+ /* parse the routes from opt to rl6 */
-+ {
-+ int i, j = 0;
-+ for (i = 0; i < opt6->n; ++i)
-+ {
-+ if (!init_route_ipv6 (&rl6->routes_ipv6[j],
-+ &opt6->routes_ipv6[i],
-+ rl6 ))
-+ ret = false;
-+ else
-+ ++j;
-+ }
-+ rl6->n = j;
-+ }
-+
-+ gc_free (&gc);
-+ return ret;
-+}
-+
- static void
- add_route3 (in_addr_t network,
- in_addr_t netmask,
-@@ -704,10 +877,13 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
- }
-
- void
--add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
-+add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
-+ const struct tuntap *tt, unsigned int flags, const struct env_set *es)
- {
-- redirect_default_route_to_vpn (rl, tt, flags, es);
-- if (!rl->routes_added)
-+ if (rl)
-+ redirect_default_route_to_vpn (rl, tt, flags, es);
-+
-+ if (rl && !rl->routes_added)
- {
- int i;
-
-@@ -732,12 +908,27 @@ add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags,
- }
- rl->routes_added = true;
- }
-+
-+ if (rl6 && !rl6->routes_added)
-+ {
-+ int i;
-+
-+ for (i = 0; i < rl6->n; ++i)
-+ {
-+ struct route_ipv6 *r = &rl6->routes_ipv6[i];
-+ if (flags & ROUTE_DELETE_FIRST)
-+ delete_route_ipv6 (r, tt, flags, es);
-+ add_route_ipv6 (r, tt, flags, es);
-+ }
-+ rl6->routes_added = true;
-+ }
- }
-
- void
--delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
-+delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
-+ const struct tuntap *tt, unsigned int flags, const struct env_set *es)
- {
-- if (rl->routes_added)
-+ if (rl && rl->routes_added)
- {
- int i;
- for (i = rl->n - 1; i >= 0; --i)
-@@ -747,9 +938,28 @@ delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flag
- }
- rl->routes_added = false;
- }
-- undo_redirect_default_route_to_vpn (rl, tt, flags, es);
-
-- clear_route_list (rl);
-+ if ( rl )
-+ {
-+ undo_redirect_default_route_to_vpn (rl, tt, flags, es);
-+ clear_route_list (rl);
-+ }
-+
-+ if ( rl6 && rl6->routes_added )
-+ {
-+ int i;
-+ for (i = rl6->n - 1; i >= 0; --i)
-+ {
-+ const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
-+ delete_route_ipv6 (r6, tt, flags, es);
-+ }
-+ rl6->routes_added = false;
-+ }
-+
-+ if ( rl6 )
-+ {
-+ clear_route_ipv6_list (rl6);
-+ }
- }
-
- #ifdef ENABLE_DEBUG
-@@ -832,6 +1042,34 @@ setenv_routes (struct env_set *es, const struct route_list *rl)
- setenv_route (es, &rl->routes[i], i + 1);
- }
-
-+static void
-+setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
-+{
-+ struct gc_arena gc = gc_new ();
-+ if (r6->defined)
-+ {
-+ struct buffer name1 = alloc_buf_gc( 256, &gc );
-+ struct buffer val = alloc_buf_gc( 256, &gc );
-+ struct buffer name2 = alloc_buf_gc( 256, &gc );
-+
-+ buf_printf( &name1, "route_ipv6_network_%d", i );
-+ buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
-+ r6->netbits );
-+ setenv_str( es, BSTR(&name1), BSTR(&val) );
-+
-+ buf_printf( &name2, "route_ipv6_gateway_%d", i );
-+ setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
-+ }
-+ gc_free (&gc);
-+}
-+void
-+setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
-+{
-+ int i;
-+ for (i = 0; i < rl6->n; ++i)
-+ setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
-+}
-+
- void
- add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
- {
-@@ -1025,6 +1263,187 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
- gc_free (&gc);
- }
-
-+
-+static const char *
-+print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits,
-+ struct gc_arena * gc)
-+{
-+ /* clear host bit parts of route
-+ * (needed if routes are specified improperly, or if we need to
-+ * explicitely setup/clear the "connected" network routes on some OSes)
-+ */
-+ int byte = 15;
-+ int bits_to_clear = 128 - netbits;
-+
-+ while( byte >= 0 && bits_to_clear > 0 )
-+ {
-+ if ( bits_to_clear >= 8 )
-+ { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
-+ else
-+ { network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
-+ }
-+
-+ return print_in6_addr( network_copy, 0, gc);
-+}
-+
-+void
-+add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
-+{
-+ struct gc_arena gc;
-+ struct argv argv;
-+
-+ const char *network;
-+ const char *gateway;
-+ bool status = false;
-+ const char *device = tt->actual_name;
-+
-+ if (!r6->defined)
-+ return;
-+
-+ gc_init (&gc);
-+ argv_init (&argv);
-+
-+ network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
-+ gateway = print_in6_addr( r6->gateway, 0, &gc);
-+
-+ if ( !tt->ipv6 )
-+ {
-+ msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s",
-+ network, r6->netbits, device );
-+ return;
-+ }
-+
-+ msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s",
-+ network, r6->netbits, gateway, r6->metric, device );
-+
-+ /*
-+ * Filter out routes which are essentially no-ops
-+ * (not currently done for IPv6)
-+ */
-+
-+#if defined(TARGET_LINUX)
-+#ifdef CONFIG_FEATURE_IPROUTE
-+ argv_printf (&argv, "%s -6 route add %s/%d dev %s",
-+ iproute_path,
-+ network,
-+ r6->netbits,
-+ device);
-+ if (r6->metric_defined)
-+ argv_printf_cat (&argv, " metric %d", r6->metric);
-+
-+#else
-+ argv_printf (&argv, "%s -A inet6 add %s/%d dev %s",
-+ ROUTE_PATH,
-+ network,
-+ r6->netbits,
-+ device);
-+ if (r6->metric_defined)
-+ argv_printf_cat (&argv, " metric %d", r6->metric);
-+#endif /*CONFIG_FEATURE_IPROUTE*/
-+ argv_msg (D_ROUTE, &argv);
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
-+
-+#elif defined (WIN32)
-+
-+ /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
-+ argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s",
-+ get_win_sys_path(),
-+ NETSH_PATH_SUFFIX,
-+ network,
-+ r6->netbits,
-+ device);
-+
-+ /* next-hop depends on TUN or TAP mode:
-+ * - in TAP mode, we use the "real" next-hop
-+ * - in TUN mode we use a special-case link-local address that the tapdrvr
-+ * knows about and will answer ND (neighbor discovery) packets for
-+ */
-+ if ( tt->type == DEV_TYPE_TUN )
-+ argv_printf_cat( &argv, " %s", "fe80::8" );
-+ else
-+ argv_printf_cat( &argv, " %s", gateway );
-+
-+#if 0
-+ if (r->metric_defined)
-+ argv_printf_cat (&argv, " METRIC %d", r->metric);
-+#endif
-+
-+ /* in some versions of Windows, routes are persistent across reboots by
-+ * default, unless "store=active" is set (pointed out by Tony Lim, thanks)
-+ */
-+ argv_printf_cat( &argv, " store=active" );
-+
-+ argv_msg (D_ROUTE, &argv);
-+
-+ netcmd_semaphore_lock ();
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
-+ netcmd_semaphore_release ();
-+
-+#elif defined (TARGET_SOLARIS)
-+
-+ /* example: route add -inet6 2001:db8::/32 somegateway 0 */
-+
-+ /* for some weird reason, this does not work for me unless I set
-+ * "metric 0" - otherwise, the routes will be nicely installed, but
-+ * packets will just disappear somewhere. So we use "0" now...
-+ */
-+
-+ argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
-+ ROUTE_PATH,
-+ network,
-+ r6->netbits,
-+ gateway );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
-+
-+#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
-+
-+ argv_printf (&argv, "%s add -inet6 %s/%d -iface %s",
-+ ROUTE_PATH,
-+ network,
-+ r6->netbits,
-+ device );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed");
-+
-+#elif defined(TARGET_DARWIN)
-+
-+ argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s",
-+ ROUTE_PATH,
-+ network, r6->netbits, device );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed");
-+
-+#elif defined(TARGET_OPENBSD)
-+
-+ argv_printf (&argv, "%s add -inet6 %s -prefixlen %d %s",
-+ ROUTE_PATH,
-+ network, r6->netbits, gateway );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route add -inet6 command failed");
-+
-+#elif defined(TARGET_NETBSD)
-+
-+ argv_printf (&argv, "%s add -inet6 %s/%d %s",
-+ ROUTE_PATH,
-+ network, r6->netbits, gateway );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed");
-+
-+#else
-+ msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script");
-+#endif
-+
-+ r6->defined = status;
-+ argv_reset (&argv);
-+ gc_free (&gc);
-+}
-+
- static void
- delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
- {
-@@ -1164,6 +1583,142 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
- gc_free (&gc);
- }
-
-+void
-+delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
-+{
-+ struct gc_arena gc;
-+ struct argv argv;
-+ const char *network;
-+ const char *gateway;
-+ const char *device = tt->actual_name;
-+
-+ if (!r6->defined)
-+ return;
-+
-+ gc_init (&gc);
-+ argv_init (&argv);
-+
-+ network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
-+ gateway = print_in6_addr( r6->gateway, 0, &gc);
-+
-+ if ( !tt->ipv6 )
-+ {
-+ msg( M_INFO, "delete_route_ipv6(): not deleting %s/%d, no IPv6 on if %s",
-+ network, r6->netbits, device );
-+ return;
-+ }
-+
-+ msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits );
-+
-+#if defined(TARGET_LINUX)
-+#ifdef CONFIG_FEATURE_IPROUTE
-+ argv_printf (&argv, "%s -6 route del %s/%d dev %s",
-+ iproute_path,
-+ network,
-+ r6->netbits,
-+ device);
-+#else
-+ argv_printf (&argv, "%s -A inet6 del %s/%d dev %s",
-+ ROUTE_PATH,
-+ network,
-+ r6->netbits,
-+ device);
-+#endif /*CONFIG_FEATURE_IPROUTE*/
-+ argv_msg (D_ROUTE, &argv);
-+ openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed");
-+
-+#elif defined (WIN32)
-+
-+ /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */
-+ argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s",
-+ get_win_sys_path(),
-+ NETSH_PATH_SUFFIX,
-+ network,
-+ r6->netbits,
-+ device);
-+
-+ /* next-hop depends on TUN or TAP mode:
-+ * - in TAP mode, we use the "real" next-hop
-+ * - in TUN mode we use a special-case link-local address that the tapdrvr
-+ * knows about and will answer ND (neighbor discovery) packets for
-+ * (and "route deletion without specifying next-hop" does not work...)
-+ */
-+ if ( tt->type == DEV_TYPE_TUN )
-+ argv_printf_cat( &argv, " %s", "fe80::8" );
-+ else
-+ argv_printf_cat( &argv, " %s", gateway );
-+
-+#if 0
-+ if (r->metric_defined)
-+ argv_printf_cat (&argv, "METRIC %d", r->metric);
-+#endif
-+
-+ argv_msg (D_ROUTE, &argv);
-+
-+ netcmd_semaphore_lock ();
-+ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
-+ netcmd_semaphore_release ();
-+
-+#elif defined (TARGET_SOLARIS)
-+
-+ /* example: route delete -inet6 2001:db8::/32 somegateway */
-+ /* GERT-TODO: this is untested, but should work */
-+
-+ argv_printf (&argv, "%s delete -inet6 %s/%d %s",
-+ ROUTE_PATH,
-+ network,
-+ r6->netbits,
-+ gateway );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete -inet6 command failed");
-+
-+#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
-+
-+ argv_printf (&argv, "%s delete -inet6 %s/%d -iface %s",
-+ ROUTE_PATH,
-+ network,
-+ r6->netbits,
-+ device );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
-+
-+#elif defined(TARGET_DARWIN)
-+
-+ argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d -iface %s",
-+ ROUTE_PATH,
-+ network, r6->netbits, device );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
-+
-+#elif defined(TARGET_OPENBSD)
-+
-+ argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d %s",
-+ ROUTE_PATH,
-+ network, r6->netbits, gateway );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route delete -inet6 command failed");
-+
-+#elif defined(TARGET_NETBSD)
-+
-+ argv_printf (&argv, "%s delete -inet6 %s/%d %s",
-+ ROUTE_PATH,
-+ network, r6->netbits, gateway );
-+
-+ argv_msg (D_ROUTE, &argv);
-+ openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route delete -inet6 command failed");
-+
-+#else
-+ msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script");
-+#endif
-+
-+ argv_reset (&argv);
-+ gc_free (&gc);
-+}
-+
- /*
- * The --redirect-gateway option requires OS-specific code below
- * to get the current default gateway.
-diff --git a/route.h b/route.h
-index c5cbb7c..6a7704f 100644
---- a/route.h
-+++ b/route.h
-@@ -92,6 +92,19 @@ struct route_option_list {
- struct route_option routes[EMPTY_ARRAY_SIZE];
- };
-
-+struct route_ipv6_option {
-+ const char *prefix; /* e.g. "2001:db8:1::/64" */
-+ const char *gateway; /* e.g. "2001:db8:0::2" */
-+ const char *metric; /* e.g. "5" */
-+};
-+
-+struct route_ipv6_option_list {
-+ unsigned int flags;
-+ int capacity;
-+ int n;
-+ struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE];
-+};
-+
- struct route {
- bool defined;
- const struct route_option *option;
-@@ -113,6 +126,31 @@ struct route_list {
- struct route routes[EMPTY_ARRAY_SIZE];
- };
-
-+struct route_ipv6 {
-+ bool defined;
-+ const struct route_ipv6_option *option;
-+ struct in6_addr network;
-+ unsigned int netbits;
-+ struct in6_addr gateway;
-+ bool metric_defined;
-+ int metric;
-+};
-+
-+struct route_ipv6_list {
-+ bool routes_added;
-+ unsigned int flags;
-+ int default_metric;
-+ bool default_metric_defined;
-+ struct in6_addr remote_endpoint_ipv6;
-+ bool remote_endpoint_defined;
-+ bool did_redirect_default_gateway; /* TODO (?) */
-+ bool did_local; /* TODO (?) */
-+ int capacity;
-+ int n;
-+ struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE];
-+};
-+
-+
- #if P2MP
- /* internal OpenVPN route */
- struct iroute {
-@@ -120,15 +158,25 @@ struct iroute {
- int netbits;
- struct iroute *next;
- };
-+
-+struct iroute_ipv6 {
-+ struct in6_addr network;
-+ unsigned int netbits;
-+ struct iroute_ipv6 *next;
-+};
- #endif
-
- struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
-+struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
- struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
- void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
-
- struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
-+struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);
-
- void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
-+void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
-+void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
-
- void add_route_to_option_list (struct route_option_list *l,
- const char *network,
-@@ -136,6 +184,11 @@ void add_route_to_option_list (struct route_option_list *l,
- const char *gateway,
- const char *metric);
-
-+void add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
-+ const char *prefix,
-+ const char *gateway,
-+ const char *metric);
-+
- bool init_route_list (struct route_list *rl,
- const struct route_option_list *opt,
- const char *remote_endpoint,
-@@ -143,21 +196,30 @@ bool init_route_list (struct route_list *rl,
- in_addr_t remote_host,
- struct env_set *es);
-
-+bool init_route_ipv6_list (struct route_ipv6_list *rl6,
-+ const struct route_ipv6_option_list *opt6,
-+ const char *remote_endpoint,
-+ int default_metric,
-+ struct env_set *es);
-+
- void route_list_add_default_gateway (struct route_list *rl,
- struct env_set *es,
- const in_addr_t addr);
-
- void add_routes (struct route_list *rl,
-+ struct route_ipv6_list *rl6,
- const struct tuntap *tt,
- unsigned int flags,
- const struct env_set *es);
-
- void delete_routes (struct route_list *rl,
-+ struct route_ipv6_list *rl6,
- const struct tuntap *tt,
- unsigned int flags,
- const struct env_set *es);
-
- void setenv_routes (struct env_set *es, const struct route_list *rl);
-+void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6);
-
- bool is_special_addr (const char *addr_str);
-
-diff --git a/socket.c b/socket.c
-index 4720398..c04edc9 100644
---- a/socket.c
-+++ b/socket.c
-@@ -342,6 +342,24 @@ ip_addr_dotted_quad_safe (const char *dotted_quad)
- }
- }
-
-+bool
-+ipv6_addr_safe (const char *ipv6_text_addr)
-+{
-+ /* verify non-NULL */
-+ if (!ipv6_text_addr)
-+ return false;
-+
-+ /* verify length is within limits */
-+ if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN )
-+ return false;
-+
-+ /* verify that string will convert to IPv6 address */
-+ {
-+ struct in6_addr a6;
-+ return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
-+ }
-+}
-+
- static bool
- dns_addr_safe (const char *addr)
- {
-@@ -2032,6 +2050,55 @@ print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc)
- return BSTR (&out);
- }
-
-+/*
-+ * Convert an in6_addr in host byte order
-+ * to an ascii representation of an IPv6 address
-+ */
-+const char *
-+print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
-+{
-+ struct buffer out = alloc_buf_gc (64, gc);
-+ char tmp_out_buf[64]; /* inet_ntop wants pointer to buffer */
-+
-+ if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 ||
-+ !(flags & IA_EMPTY_IF_UNDEF))
-+ {
-+ inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
-+ buf_printf (&out, "%s", tmp_out_buf );
-+ }
-+ return BSTR (&out);
-+}
-+
-+/* add some offset to an ipv6 address
-+ * (add in steps of 32 bits, taking overflow into next round)
-+ */
-+#ifndef s6_addr32
-+# ifdef TARGET_SOLARIS
-+# define s6_addr32 _S6_un._S6_u32
-+# else
-+# define s6_addr32 __u6_addr.__u6_addr32
-+# endif
-+#endif
-+#ifndef UINT32_MAX
-+# define UINT32_MAX (4294967295U)
-+#endif
-+struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add )
-+{
-+ int i;
-+ uint32_t h;
-+
-+ for( i=3; i>=0 && add > 0 ; i-- )
-+ {
-+ h = ntohl( base.s6_addr32[i] );
-+ base.s6_addr32[i] = htonl( (h+add) & UINT32_MAX );
-+ /* 32-bit overrun?
-+ * caveat: can't do "h+add > UINT32_MAX" with 32bit math!
-+ */
-+ add = ( h > UINT32_MAX - add )? 1: 0;
-+ }
-+ return base;
-+}
-+
- /* set environmental variables for ip/port in *addr */
- void
- setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
-@@ -2337,6 +2404,58 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
-
- #ifdef WIN32
-
-+/*
-+ * inet_ntop() and inet_pton() wrap-implementations using
-+ * WSAAddressToString() and WSAStringToAddress() functions
-+ */
-+const char *
-+inet_ntop(int af, const void *src, char *dst, socklen_t size)
-+{
-+ struct sockaddr_storage ss;
-+ unsigned long s = size;
-+
-+ CLEAR(ss);
-+ ss.ss_family = af;
-+
-+ switch(af) {
-+ case AF_INET:
-+ ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
-+ break;
-+ case AF_INET6:
-+ ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
-+ break;
-+ default:
-+ ASSERT (0);
-+ }
-+ // cannot direclty use &size because of strict aliasing rules
-+ return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)?
-+ dst : NULL;
-+}
-+
-+int
-+inet_pton(int af, const char *src, void *dst)
-+{
-+ struct sockaddr_storage ss;
-+ int size = sizeof(ss);
-+ char src_copy[INET6_ADDRSTRLEN+1];
-+
-+ CLEAR(ss);
-+ // stupid non-const API
-+ strncpynt(src_copy, src, INET6_ADDRSTRLEN+1);
-+
-+ if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) {
-+ switch(af) {
-+ case AF_INET:
-+ *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
-+ return 1;
-+ case AF_INET6:
-+ *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
-+ return 1;
-+ }
-+ }
-+ return 0;
-+}
-+
- int
- socket_recv_queue (struct link_socket *sock, int maxsize)
- {
-diff --git a/socket.h b/socket.h
-index eef98d1..17943e7 100644
---- a/socket.h
-+++ b/socket.h
-@@ -351,6 +351,8 @@ const char *print_link_socket_actual (const struct link_socket_actual *act,
- #define IA_EMPTY_IF_UNDEF (1<<0)
- #define IA_NET_ORDER (1<<1)
- const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
-+const char *print_in6_addr (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc);
-+struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add );
-
- #define SA_IP_PORT (1<<0)
- #define SA_SET_IF_NONZERO (1<<1)
-@@ -404,6 +406,7 @@ int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr);
- bool ip_addr_dotted_quad_safe (const char *dotted_quad);
- bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn);
- bool mac_addr_safe (const char *mac_addr);
-+bool ipv6_addr_safe (const char *ipv6_text_addr);
-
- socket_descriptor_t create_socket_tcp (void);
-
-diff --git a/syshead.h b/syshead.h
-index b81ce59..fe4af3f 100644
---- a/syshead.h
-+++ b/syshead.h
-@@ -28,6 +28,10 @@
- /*
- * Only include if not during configure
- */
-+#ifdef WIN32
-+/* USE_PF_INET6: win32 ipv6 exists only after 0x0501 (XP) */
-+#define WINVER 0x0501
-+#endif
- #ifndef PACKAGE_NAME
- #include "config.h"
- #endif
-@@ -339,6 +343,9 @@
- #ifdef WIN32
- #include <iphlpapi.h>
- #include <wininet.h>
-+/* The following two headers are needed of USE_PF_INET6 */
-+#include <winsock2.h>
-+#include <ws2tcpip.h>
- #endif
-
- #ifdef HAVE_SYS_MMAN_H
-diff --git a/tun.c b/tun.c
-index d03e8c7..4be71de 100644
---- a/tun.c
-+++ b/tun.c
-@@ -56,13 +56,14 @@ static void netsh_ifconfig (const struct tuntap_options *to,
- const in_addr_t ip,
- const in_addr_t netmask,
- const unsigned int flags);
-+static void netsh_command (const struct argv *a, int n);
-
- static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
-
- #endif
-
- #ifdef TARGET_SOLARIS
--static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
-+static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6);
- #include <stropts.h>
- #endif
-
-@@ -129,30 +130,6 @@ guess_tuntap_dev (const char *dev,
- return dev;
- }
-
--/*
-- * Called by the open_tun function of OSes to check if we
-- * explicitly support IPv6.
-- *
-- * In this context, explicit means that the OS expects us to
-- * do something special to the tun socket in order to support
-- * IPv6, i.e. it is not transparent.
-- *
-- * ipv6_explicitly_supported should be set to false if we don't
-- * have any explicit IPv6 code in the tun device handler.
-- *
-- * If ipv6_explicitly_supported is true, then we have explicit
-- * OS-specific tun dev code for handling IPv6. If so, tt->ipv6
-- * is set according to the --tun-ipv6 command line option.
-- */
--static void
--ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
--{
-- tt->ipv6 = false;
-- if (ipv6_explicitly_supported)
-- tt->ipv6 = ipv6;
-- else if (ipv6)
-- msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
--}
-
- /* --ifconfig-nowarn disables some options sanity checking */
- static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";
-@@ -423,6 +400,8 @@ init_tun (const char *dev, /* --dev option */
- int topology, /* one of the TOP_x values */
- const char *ifconfig_local_parm, /* --ifconfig parm 1 */
- const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
-+ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 IPv6 */
-+ const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 IPv6 */
- in_addr_t local_public,
- in_addr_t remote_public,
- const bool strict_warn,
-@@ -537,6 +516,40 @@ init_tun (const char *dev, /* --dev option */
-
- tt->did_ifconfig_setup = true;
- }
-+
-+ if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm)
-+ {
-+ const char *ifconfig_ipv6_local = NULL;
-+ const char *ifconfig_ipv6_remote = NULL;
-+
-+ /*
-+ * Convert arguments to binary IPv6 addresses.
-+ */
-+
-+ if ( inet_pton( AF_INET6, ifconfig_ipv6_local_parm, &tt->local_ipv6 ) != 1 ||
-+ inet_pton( AF_INET6, ifconfig_ipv6_remote_parm, &tt->remote_ipv6 ) != 1 )
-+ {
-+ msg( M_FATAL, "init_tun: problem converting IPv6 ifconfig addresses %s and %s to binary", ifconfig_ipv6_local_parm, ifconfig_ipv6_remote_parm );
-+ }
-+ tt->netbits_ipv6 = 64;
-+
-+ /*
-+ * Set ifconfig parameters
-+ */
-+ ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
-+ ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
-+
-+ /*
-+ * Set environmental variables with ifconfig parameters.
-+ */
-+ if (es)
-+ {
-+ setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local);
-+ setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote);
-+ }
-+ tt->did_ifconfig_ipv6_setup = true;
-+ }
-+
- gc_free (&gc);
- return tt;
- }
-@@ -559,6 +572,40 @@ init_tun_post (struct tuntap *tt,
- #endif
- }
-
-+#if defined(TARGET_WIN32) || \
-+ defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD)
-+
-+/* some of the platforms will auto-add a "network route" pointing
-+ * to the interface on "ifconfig tunX 2001:db8::1/64", others need
-+ * an extra call to "route add..."
-+ * -> helper function to simplify code below
-+ */
-+void add_route_connected_v6_net(struct tuntap * tt,
-+ const struct env_set *es)
-+{
-+ struct route_ipv6 r6;
-+
-+ r6.defined = true;
-+ r6.network = tt->local_ipv6;
-+ r6.netbits = tt->netbits_ipv6;
-+ r6.gateway = tt->local_ipv6;
-+ add_route_ipv6 (&r6, tt, 0, es);
-+}
-+
-+void delete_route_connected_v6_net(struct tuntap * tt,
-+ const struct env_set *es)
-+{
-+ struct route_ipv6 r6;
-+
-+ r6.defined = true;
-+ r6.network = tt->local_ipv6;
-+ r6.netbits = tt->netbits_ipv6;
-+ r6.gateway = tt->local_ipv6;
-+ delete_route_ipv6 (&r6, tt, 0, es);
-+}
-+#endif
-+
-+
- /* execute the ifconfig command through the shell */
- void
- do_ifconfig (struct tuntap *tt,
-@@ -574,10 +621,16 @@ do_ifconfig (struct tuntap *tt,
- const char *ifconfig_local = NULL;
- const char *ifconfig_remote_netmask = NULL;
- const char *ifconfig_broadcast = NULL;
-+ const char *ifconfig_ipv6_local = NULL;
-+ const char *ifconfig_ipv6_remote = NULL;
-+ bool do_ipv6 = false;
- struct argv argv;
-
- argv_init (&argv);
-
-+ msg( M_INFO, "do_ifconfig, tt->ipv6=%d, tt->did_ifconfig_ipv6_setup=%d",
-+ tt->ipv6, tt->did_ifconfig_ipv6_setup );
-+
- /*
- * We only handle TUN/TAP devices here, not --dev null devices.
- */
-@@ -589,6 +642,13 @@ do_ifconfig (struct tuntap *tt,
- ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
- ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
-
-+ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
-+ {
-+ ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
-+ ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
-+ do_ipv6 = true;
-+ }
-+
- /*
- * If TAP-style device, generate broadcast address.
- */
-@@ -647,7 +707,19 @@ do_ifconfig (struct tuntap *tt,
- argv_msg (M_INFO, &argv);
- openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
- }
-- tt->did_ifconfig = true;
-+ if ( do_ipv6 )
-+ {
-+ argv_printf( &argv,
-+ "%s -6 addr add %s/%d dev %s",
-+ iproute_path,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6,
-+ actual
-+ );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip -6 addr add failed");
-+ }
-+ tt->did_ifconfig = true;
- #else
- if (tun)
- argv_printf (&argv,
-@@ -670,6 +742,18 @@ do_ifconfig (struct tuntap *tt,
- );
- argv_msg (M_INFO, &argv);
- openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
-+ if ( do_ipv6 )
-+ {
-+ argv_printf (&argv,
-+ "%s %s inet6 add %s/%d",
-+ IFCONFIG_PATH,
-+ actual,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6
-+ );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig inet6 failed");
-+ }
- tt->did_ifconfig = true;
-
- #endif /*CONFIG_FEATURE_IPROUTE*/
-@@ -693,7 +777,7 @@ do_ifconfig (struct tuntap *tt,
-
- argv_msg (M_INFO, &argv);
- if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
-- solaris_error_close (tt, es, actual);
-+ solaris_error_close (tt, es, actual, false);
-
- argv_printf (&argv,
- "%s %s netmask 255.255.255.255",
-@@ -725,7 +809,53 @@ do_ifconfig (struct tuntap *tt,
-
- argv_msg (M_INFO, &argv);
- if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
-- solaris_error_close (tt, es, actual);
-+ solaris_error_close (tt, es, actual, false);
-+
-+ if ( do_ipv6 )
-+ {
-+ argv_printf (&argv, "%s %s inet6 unplumb",
-+ IFCONFIG_PATH, actual );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, 0, NULL);
-+
-+ if ( tt->type == DEV_TYPE_TUN )
-+ {
-+ argv_printf (&argv,
-+ "%s %s inet6 plumb %s/%d %s up",
-+ IFCONFIG_PATH,
-+ actual,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6,
-+ ifconfig_ipv6_remote
-+ );
-+ }
-+ else /* tap mode */
-+ {
-+ /* base IPv6 tap interface needs to be brought up first
-+ */
-+ argv_printf (&argv, "%s %s inet6 plumb up",
-+ IFCONFIG_PATH, actual );
-+ argv_msg (M_INFO, &argv);
-+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 (prepare) failed"))
-+ solaris_error_close (tt, es, actual, true);
-+
-+ /* we might need to do "ifconfig %s inet6 auto-dhcp drop"
-+ * after the system has noticed the interface and fired up
-+ * the DHCPv6 client - but this takes quite a while, and the
-+ * server will ignore the DHCPv6 packets anyway. So we don't.
-+ */
-+
-+ /* static IPv6 addresses need to go to a subinterface (tap0:1)
-+ */
-+ argv_printf (&argv,
-+ "%s %s inet6 addif %s/%d up",
-+ IFCONFIG_PATH, actual,
-+ ifconfig_ipv6_local, tt->netbits_ipv6 );
-+ }
-+ argv_msg (M_INFO, &argv);
-+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 failed"))
-+ solaris_error_close (tt, es, actual, true);
-+ }
-
- if (!tun && tt->topology == TOP_SUBNET)
- {
-@@ -787,10 +917,42 @@ do_ifconfig (struct tuntap *tt,
- );
- argv_msg (M_INFO, &argv);
- openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
-+ if ( do_ipv6 )
-+ {
-+ argv_printf (&argv,
-+ "%s %s inet6 %s/%d",
-+ IFCONFIG_PATH,
-+ actual,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6
-+ );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig inet6 failed");
-+
-+ /* and, hooray, we explicitely need to add a route... */
-+ add_route_connected_v6_net(tt, es);
-+ }
- tt->did_ifconfig = true;
-
- #elif defined(TARGET_NETBSD)
-
-+/* whether or not NetBSD can do IPv6 can be seen by the availability of
-+ * the TUNSIFHEAD ioctl() - see next TARGET_NETBSD block for more details
-+ */
-+#ifdef TUNSIFHEAD
-+# define NETBSD_MULTI_AF
-+#endif
-+
-+ /* as on OpenBSD and Darwin, destroy and re-create tun<x> interface
-+ */
-+ argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, 0, "NetBSD ifconfig destroy failed");
-+
-+ argv_printf (&argv, "%s %s create", IFCONFIG_PATH, actual );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig create failed");
-+
- if (tun)
- argv_printf (&argv,
- "%s %s %s %s mtu %d netmask 255.255.255.255 up",
-@@ -817,6 +979,27 @@ do_ifconfig (struct tuntap *tt,
- );
- argv_msg (M_INFO, &argv);
- openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
-+
-+ if ( do_ipv6 )
-+ {
-+#ifdef NETBSD_MULTI_AF
-+ argv_printf (&argv,
-+ "%s %s inet6 %s/%d",
-+ IFCONFIG_PATH,
-+ actual,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6
-+ );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig inet6 failed");
-+
-+ /* and, hooray, we explicitely need to add a route... */
-+ add_route_connected_v6_net(tt, es);
-+#else
-+ msg( M_INFO, "no IPv6 support for tun interfaces on NetBSD before 4.0 (if your system is newer, recompile openvpn)" );
-+ tt->ipv6 = false;
-+#endif
-+ }
- tt->did_ifconfig = true;
-
- #elif defined(TARGET_DARWIN)
-@@ -882,6 +1065,22 @@ do_ifconfig (struct tuntap *tt,
- add_route (&r, tt, 0, es);
- }
-
-+ if ( do_ipv6 )
-+ {
-+ argv_printf (&argv,
-+ "%s %s inet6 %s/%d",
-+ IFCONFIG_PATH,
-+ actual,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6
-+ );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed");
-+
-+ /* and, hooray, we explicitely need to add a route... */
-+ add_route_connected_v6_net(tt, es);
-+ }
-+
- #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
-
- /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
-@@ -920,6 +1119,19 @@ do_ifconfig (struct tuntap *tt,
- add_route (&r, tt, 0, es);
- }
-
-+ if ( do_ipv6 )
-+ {
-+ argv_printf (&argv,
-+ "%s %s inet6 %s/%d",
-+ IFCONFIG_PATH,
-+ actual,
-+ ifconfig_ipv6_local,
-+ tt->netbits_ipv6
-+ );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed");
-+ }
-+
- #elif defined (WIN32)
- {
- /*
-@@ -959,6 +1171,34 @@ do_ifconfig (struct tuntap *tt,
- tt->did_ifconfig = true;
- }
-
-+ /* IPv6 always uses "netsh" interface */
-+ if ( do_ipv6 )
-+ {
-+ char * saved_actual;
-+
-+ if (!strcmp (actual, "NULL"))
-+ msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Win32 adapter, you must also specify --dev-node");
-+
-+ /* example: netsh interface ipv6 set address MyTap 2001:608:8003::d store=active */
-+ argv_printf (&argv,
-+ "%s%sc interface ipv6 set address %s %s store=active",
-+ get_win_sys_path(),
-+ NETSH_PATH_SUFFIX,
-+ actual,
-+ ifconfig_ipv6_local );
-+
-+ netsh_command (&argv, 4);
-+
-+ /* explicit route needed */
-+ /* on windows, OpenVPN does ifconfig first, open_tun later, so
-+ * tt->actual_name might not yet be initialized, but routing code
-+ * needs to know interface name - point to "actual", restore later
-+ */
-+ saved_actual = tt->actual_name;
-+ tt->actual_name = (char*) actual;
-+ add_route_connected_v6_net(tt, es);
-+ tt->actual_name = saved_actual;
-+ }
- #else
- msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
- #endif
-@@ -991,14 +1231,16 @@ open_null (struct tuntap *tt)
- #ifndef WIN32
- static void
- open_tun_generic (const char *dev, const char *dev_type, const char *dev_node,
-- bool ipv6, bool ipv6_explicitly_supported, bool dynamic,
-+ bool ipv6_explicitly_supported, bool dynamic,
- struct tuntap *tt)
- {
- char tunname[256];
- char dynamic_name[256];
- bool dynamic_opened = false;
-
-- ipv6_support (ipv6, ipv6_explicitly_supported, tt);
-+
-+ if ( tt->ipv6 && ! ipv6_explicitly_supported )
-+ msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
-
- if (tt->type == DEV_TYPE_NULL)
- {
-@@ -1094,16 +1336,16 @@ close_tun_generic (struct tuntap *tt)
- #if !PEDANTIC
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
- struct ifreq ifr;
-
-- /*
-- * Set tt->ipv6 to true if
-- * (a) we have the capability of supporting --tun-ipv6, and
-- * (b) --tun-ipv6 was specified.
-+ /* warn if a very old linux version is used & --tun-ipv6 set
- */
-- ipv6_support (ipv6, LINUX_IPV6, tt);
-+#if LINUX_IPV6 == 0
-+ if ( tt->ipv6 )
-+ msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
-+#endif
-
- /*
- * We handle --dev null specially, we do not open /dev/null for this.
-@@ -1222,9 +1464,9 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
- #else
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
-- open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
-+ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
- }
-
- #endif /* HAVE_LINUX_IF_TUN_H */
-@@ -1244,7 +1486,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
- #endif
-
- void
--tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
-+tuncfg (const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
- {
- struct tuntap *tt;
-
-@@ -1252,7 +1494,7 @@ tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6,
- clear_tuntap (tt);
- tt->type = dev_type_enum (dev, dev_type);
- tt->options = *options;
-- open_tun (dev, dev_type, dev_node, ipv6, tt);
-+ open_tun (dev, dev_type, dev_node, tt);
- if (ioctl (tt->fd, TUNSETPERSIST, persist_mode) < 0)
- msg (M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
- if (username != NULL)
-@@ -1395,7 +1637,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
- #endif
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
- int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
- struct lifreq ifr;
-@@ -1406,8 +1648,11 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
- bool is_tun;
- struct strioctl strioc_if, strioc_ppa;
-
-- ipv6_support (ipv6, true, tt);
-- memset(&ifr, 0x0, sizeof(ifr));
-+ /* improved generic TUN/TAP driver from
-+ * http://www.whiteboard.ne.jp/~admin2/tuntap/
-+ * has IPv6 support
-+ */
-+ CLEAR(ifr);
-
- if (tt->type == DEV_TYPE_NULL)
- {
-@@ -1561,6 +1806,18 @@ solaris_close_tun (struct tuntap *tt)
- {
- if (tt)
- {
-+ /* IPv6 interfaces need to be 'manually' de-configured */
-+ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
-+ {
-+ struct argv argv;
-+ argv_init (&argv);
-+ argv_printf( &argv, "%s %s inet6 unplumb",
-+ IFCONFIG_PATH, tt->actual_name );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed");
-+ argv_reset (&argv);
-+ }
-+
- if (tt->ip_fd >= 0)
- {
- struct lifreq ifr;
-@@ -1613,11 +1870,20 @@ close_tun (struct tuntap *tt)
- }
-
- static void
--solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
-+solaris_error_close (struct tuntap *tt, const struct env_set *es,
-+ const char *actual, bool unplumb_inet6 )
- {
- struct argv argv;
- argv_init (&argv);
-
-+ if (unplumb_inet6)
-+ {
-+ argv_printf( &argv, "%s %s inet6 unplumb",
-+ IFCONFIG_PATH, actual );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, es, 0, "Solaris ifconfig inet6 unplumb failed");
-+ }
-+
- argv_printf (&argv,
- "%s %s unplumb",
- IFCONFIG_PATH,
-@@ -1674,9 +1940,9 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
- */
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
-- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
-+ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
-
- /* Enable multicast on the interface */
- if (tt->fd >= 0)
-@@ -1697,12 +1963,31 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
- }
- }
-
-+/* the current way OpenVPN handles tun devices on OpenBSD leads to
-+ * lingering tunX interfaces after close -> for a full cleanup, they
-+ * need to be explicitely destroyed
-+ */
-+
- void
- close_tun (struct tuntap* tt)
- {
- if (tt)
- {
-+ struct gc_arena gc = gc_new ();
-+ struct argv argv;
-+
-+ /* setup command, close tun dev (clears tt->actual_name!), run command
-+ */
-+
-+ argv_init (&argv);
-+ argv_printf (&argv, "%s %s destroy",
-+ IFCONFIG_PATH, tt->actual_name);
-+
- close_tun_generic (tt);
-+
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)");
-+
- free (tt);
- }
- }
-@@ -1765,33 +2050,51 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
- #elif defined(TARGET_NETBSD)
-
- /*
-- * NetBSD does not support IPv6 on tun out of the box,
-- * but there exists a patch. When this patch is applied,
-- * only two things are left to openvpn:
-- * 1. Activate multicasting (this has already been done
-- * before by the kernel, but we make sure that nobody
-- * has deactivated multicasting inbetween.
-- * 2. Deactivate "link layer mode" (otherwise NetBSD
-- * prepends the address family to the packet, and we
-- * would run into the same trouble as with OpenBSD.
-+ * NetBSD before 4.0 does not support IPv6 on tun out of the box,
-+ * but there exists a patch (sys/net/if_tun.c, 1.79->1.80, see PR 32944).
-+ *
-+ * NetBSD 4.0 and up do, but we need to put the tun interface into
-+ * "multi_af" mode, which will prepend the address family to all packets
-+ * (same as OpenBSD and FreeBSD). If this is not enabled, the kernel
-+ * silently drops all IPv6 packets on output and gets confused on input.
-+ *
-+ * On earlier versions, multi_af is not available at all, so we have
-+ * two different NetBSD code variants here :-(
-+ *
- */
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
-- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
-+#ifdef NETBSD_MULTI_AF
-+ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
-+#else
-+ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
-+#endif
-+
- if (tt->fd >= 0)
- {
- int i = IFF_POINTOPOINT|IFF_MULTICAST;
- ioctl (tt->fd, TUNSIFMODE, &i); /* multicast on */
- i = 0;
- ioctl (tt->fd, TUNSLMODE, &i); /* link layer mode off */
-+
-+#ifdef NETBSD_MULTI_AF
-+ i = 1;
-+ if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0) /* multi-af mode on */
-+ {
-+ msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
-+ }
-+#endif
- }
- }
-
- void
- close_tun (struct tuntap *tt)
- {
-+ /* TODO: we really should cleanup non-persistant tunX with
-+ * "ifconfig tunX destroy" here...
-+ */
- if (tt)
- {
- close_tun_generic (tt);
-@@ -1799,6 +2102,65 @@ close_tun (struct tuntap *tt)
- }
- }
-
-+#ifdef NETBSD_MULTI_AF
-+
-+static inline int
-+netbsd_modify_read_write_return (int len)
-+{
-+ if (len > 0)
-+ return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
-+ else
-+ return len;
-+}
-+
-+int
-+write_tun (struct tuntap* tt, uint8_t *buf, int len)
-+{
-+ if (tt->type == DEV_TYPE_TUN)
-+ {
-+ u_int32_t type;
-+ struct iovec iv[2];
-+ struct openvpn_iphdr *iph;
-+
-+ iph = (struct openvpn_iphdr *) buf;
-+
-+ if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6)
-+ type = htonl (AF_INET6);
-+ else
-+ type = htonl (AF_INET);
-+
-+ iv[0].iov_base = (char *)&type;
-+ iv[0].iov_len = sizeof (type);
-+ iv[1].iov_base = buf;
-+ iv[1].iov_len = len;
-+
-+ return netbsd_modify_read_write_return (writev (tt->fd, iv, 2));
-+ }
-+ else
-+ return write (tt->fd, buf, len);
-+}
-+
-+int
-+read_tun (struct tuntap* tt, uint8_t *buf, int len)
-+{
-+ if (tt->type == DEV_TYPE_TUN)
-+ {
-+ u_int32_t type;
-+ struct iovec iv[2];
-+
-+ iv[0].iov_base = (char *)&type;
-+ iv[0].iov_len = sizeof (type);
-+ iv[1].iov_base = buf;
-+ iv[1].iov_len = len;
-+
-+ return netbsd_modify_read_write_return (readv (tt->fd, iv, 2));
-+ }
-+ else
-+ return read (tt->fd, buf, len);
-+}
-+
-+#else /* not NETBSD_MULTI_AF -> older code, IPv4 only */
-+
- int
- write_tun (struct tuntap* tt, uint8_t *buf, int len)
- {
-@@ -1810,6 +2172,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
- {
- return read (tt->fd, buf, len);
- }
-+#endif /* NETBSD_MULTI_AF */
-
- #elif defined(TARGET_FREEBSD)
-
-@@ -1823,9 +2186,9 @@ freebsd_modify_read_write_return (int len)
- }
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
-- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
-+ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
-
- if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
- {
-@@ -1911,9 +2274,9 @@ dragonfly_modify_read_write_return (int len)
- }
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
-- open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
-+ open_tun_generic (dev, dev_type, dev_node, true, true, tt);
-
- if (tt->fd >= 0)
- {
-@@ -1982,6 +2345,61 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
- return read (tt->fd, buf, len);
- }
-
-+#elif defined(TARGET_DARWIN)
-+
-+/* Darwin (MacOS X) is mostly "just use the generic stuff", but there
-+ * is always one caveat...:
-+ *
-+ * If IPv6 is configured, and the tun device is closed, the IPv6 address
-+ * configured to the tun interface changes to a lingering /128 route
-+ * pointing to lo0. Need to unconfigure... (observed on 10.5)
-+ */
-+
-+void
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
-+{
-+ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
-+}
-+
-+void
-+close_tun (struct tuntap* tt)
-+{
-+ if (tt)
-+ {
-+ struct gc_arena gc = gc_new ();
-+ struct argv argv;
-+ argv_init (&argv);
-+
-+ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
-+ {
-+ const char * ifconfig_ipv6_local =
-+ print_in6_addr (tt->local_ipv6, 0, &gc);
-+
-+ argv_printf (&argv, "%s delete -inet6 %s",
-+ ROUTE_PATH, ifconfig_ipv6_local );
-+ argv_msg (M_INFO, &argv);
-+ openvpn_execve_check (&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)");
-+ }
-+
-+ close_tun_generic (tt);
-+ free (tt);
-+ argv_reset (&argv);
-+ gc_free (&gc);
-+ }
-+}
-+
-+int
-+write_tun (struct tuntap* tt, uint8_t *buf, int len)
-+{
-+ return write (tt->fd, buf, len);
-+}
-+
-+int
-+read_tun (struct tuntap* tt, uint8_t *buf, int len)
-+{
-+ return read (tt->fd, buf, len);
-+}
-+
- #elif defined(WIN32)
-
- int
-@@ -3967,7 +4385,7 @@ fork_register_dns_action (struct tuntap *tt)
- }
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
- struct gc_arena gc = gc_new ();
- char device_path[256];
-@@ -3978,7 +4396,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
-
- /*netcmd_semaphore_lock ();*/
-
-- ipv6_support (ipv6, false, tt);
-+ msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 );
-
- if (tt->type == DEV_TYPE_NULL)
- {
-@@ -4432,6 +4850,26 @@ close_tun (struct tuntap *tt)
-
- if (tt)
- {
-+ if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
-+ {
-+ struct argv argv;
-+ argv_init (&argv);
-+
-+ /* remove route pointing to interface */
-+ delete_route_connected_v6_net(tt, NULL);
-+
-+ /* netsh interface ipv6 delete address \"%s\" %s */
-+ const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
-+ argv_printf (&argv,
-+ "%s%sc interface ipv6 delete address %s %s",
-+ get_win_sys_path(),
-+ NETSH_PATH_SUFFIX,
-+ tt->actual_name,
-+ ifconfig_ipv6_local );
-+
-+ netsh_command (&argv, 1);
-+ argv_reset (&argv);
-+ }
- #if 1
- if (tt->ipapi_context_defined)
- {
-@@ -4535,9 +4973,9 @@ ipset2ascii_all (struct gc_arena *gc)
- #else /* generic */
-
- void
--open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
-+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
- {
-- open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
-+ open_tun_generic (dev, dev_type, dev_node, false, true, tt);
- }
-
- void
-diff --git a/tun.h b/tun.h
-index 011ab54..f28b8d8 100644
---- a/tun.h
-+++ b/tun.h
-@@ -130,6 +130,7 @@ struct tuntap
- int topology; /* one of the TOP_x values */
-
- bool did_ifconfig_setup;
-+ bool did_ifconfig_ipv6_setup;
- bool did_ifconfig;
-
- bool ipv6;
-@@ -146,6 +147,10 @@ struct tuntap
- in_addr_t remote_netmask;
- in_addr_t broadcast;
-
-+ struct in6_addr local_ipv6;
-+ struct in6_addr remote_ipv6;
-+ int netbits_ipv6;
-+
- #ifdef WIN32
- HANDLE hand;
- struct overlapped_io reads;
-@@ -197,7 +202,7 @@ tuntap_defined (const struct tuntap *tt)
- void clear_tuntap (struct tuntap *tuntap);
-
- void open_tun (const char *dev, const char *dev_type, const char *dev_node,
-- bool ipv6, struct tuntap *tt);
-+ struct tuntap *tt);
-
- void close_tun (struct tuntap *tt);
-
-@@ -206,7 +211,7 @@ int write_tun (struct tuntap* tt, uint8_t *buf, int len);
- int read_tun (struct tuntap* tt, uint8_t *buf, int len);
-
- void tuncfg (const char *dev, const char *dev_type, const char *dev_node,
-- bool ipv6, int persist_mode, const char *username,
-+ int persist_mode, const char *username,
- const char *groupname, const struct tuntap_options *options);
-
- const char *guess_tuntap_dev (const char *dev,
-@@ -219,6 +224,8 @@ struct tuntap *init_tun (const char *dev, /* --dev option */
- int topology, /* one of the TOP_x values */
- const char *ifconfig_local_parm, /* --ifconfig parm 1 */
- const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
-+ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 / IPv6 */
-+ const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 / IPv6 */
- in_addr_t local_public,
- in_addr_t remote_public,
- const bool strict_warn,
-diff --git a/win32.c b/win32.c
-index 2b7bf7b..cf6cc2d 100644
---- a/win32.c
-+++ b/win32.c
-@@ -874,16 +874,21 @@ win_safe_filename (const char *fn)
- static char *
- env_block (const struct env_set *es)
- {
-+ char * force_path = "PATH=C:\\Windows\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem";
-+
- if (es)
- {
- struct env_item *e;
- char *ret;
- char *p;
- size_t nchars = 1;
-+ bool path_seen = false;
-
- for (e = es->list; e != NULL; e = e->next)
- nchars += strlen (e->string) + 1;
-
-+ nchars += strlen(force_path)+1;
-+
- ret = (char *) malloc (nchars);
- check_malloc_return (ret);
-
-@@ -895,7 +900,18 @@ env_block (const struct env_set *es)
- strcpy (p, e->string);
- p += strlen (e->string) + 1;
- }
-+ if ( strncmp(e->string, "PATH=", 5 ) == 0 )
-+ path_seen = true;
-+ }
-+
-+ /* make sure PATH is set */
-+ if ( !path_seen )
-+ {
-+ msg( M_INFO, "env_block: add %s", force_path );
-+ strcpy( p, force_path );
-+ p += strlen(force_path) + 1;
- }
-+
- *p = '\0';
- return ret;
- }
-diff --git a/win32.h b/win32.h
-index b6a162e..829933f 100644
---- a/win32.h
-+++ b/win32.h
-@@ -269,6 +269,8 @@ char *get_win_sys_path (void);
-
- /* call self in a subprocess */
- void fork_to_self (const char *cmdline);
-+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
-+int inet_pton(int af, const char *src, void *st);
-
- /* Find temporary directory */
- const char *win_get_tempdir();