Browse code

Enable IPv6 Payload in OpenVPN p2mp tun server mode. 20100104-1 release. (cherry picked from commit ec9dce6387afd198881493bfebf13bb121e8a56b)

Gert Doering authored on 2010/01/07 22:51:40
Showing 25 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,189 @@
0
+Do 31. Dez 15:32:40 CET 2009 Gert Doering
1
+
2
+  * Basic IPv6 p2mp functionality implemented
3
+
4
+  * new options:
5
+     - server-ipv6
6
+     - ifconfig-ipv6
7
+     - ifconfig-ipv6-pool
8
+     - route-ipv6
9
+     - iroute-ipv6
10
+
11
+  * modules touched:
12
+     - init.c: init & setup IPv6 route list & add/delete IPv6 routes
13
+     - tun.c: add "ifconfig" and "route" handling for IPv6
14
+     - multi.c: IPv6 ifconfig-pool assignments
15
+		put to route-hash table
16
+		push to client
17
+     - pool.c: extend pools to handle IPv4+IPv6, and also return IPv6 address
18
+	       IPv6 address saved to file if ifconfig-pool-persist is set
19
+	       (but ignored on read due to the way pools work)
20
+     - mroute.c: handle reading src/dst addresses from IPv6 packets
21
+		 (so multi.c can check against route-hash table)
22
+		 handle printing of IPv6 mroute_addr structure
23
+     - helper.c: implement "server-ipv6" macro (->ifconfig-ipv6, pool, ...)
24
+     - options.c: implement all the new options
25
+		  add helper functions for IPv6 address handling
26
+     - forward.c: tell do_route() about IPv6 routes
27
+     - route.c:   handle IPv6 route lists + route option lists
28
+		  extend add_routes() to do IPv4 + IPv6 route lists
29
+		  extend delete_routes() to do IPv4 + IPv6 route lists
30
+		  implement add_route_ipv6(), delete_route_ipv6() to call
31
+		  system-dependend external program to do the work
32
+     - push.c:    handle pushing of "ifconfig-ipv6" option
33
+     - socket.c:  helper function to check & print IPv6 address strings
34
+
35
+  * known issues:
36
+     - operating system support on all but Linux (ifconfig, route)
37
+     - route-ipv6 gateway handling
38
+     - iroute-ipv6 not implemented
39
+     - TAP support: ifconfig, routing (route needs gateway!)
40
+
41
+  * release as patch 20091231-1
42
+
43
+Thu Dec 31 17:02:08 CET 2009
44
+
45
+  * NetBSD port (NetBSD 3.1 on Sparc64)
46
+
47
+  * mroute.c, socket.c: make byte/word access to in6_addr more portable
48
+
49
+  * tun.c: fix IPv6 ifconfig arguments on NetBSD
50
+
51
+    still doesn't work on NetBSD 3.1, "ifconfig tun0 inet6..." errors with
52
+
53
+    ifconfig: SIOCAIFADDR: Address family not supported by protocol family
54
+
55
+    (sys/net/if_tun.c, needs to be revision 1.80 or later, NetBSD PR 32944,
56
+    included in NetBSD 4.0 and up)
57
+
58
+
59
+Fri Jan  1 14:07:15 CET 2010
60
+
61
+  * FreeBSD port (FreeBSD 6.3-p12 on i386)
62
+
63
+  * tun.c: implement IPv6 ifconfig setting for FreeBSD
64
+
65
+  * route.c: fix %s/%s argument to IPv6 route add/delete command for *BSD
66
+
67
+  * TEST SUCCESS: FreeBSD 6.3-p12, server-ipv6, route-ipv6, ccd/iroute-ipv6
68
+
69
+  * multi.c: implement setting and deleting of iroute-ipv6 
70
+             (multi_add_iroutes(), multi_del_iroutes())
71
+  * mroute.c: add mroute_helper_add_iroute6(), mroute_helper_del_iroute6()
72
+  * mroute.h: add prototypes, increase MR_HELPER_NET_LEN to 129 (/0.../128)
73
+  * multi.c: zeroize host part of IPv6 iroutes in multi_learn_in6_addr()
74
+  * mroute.c: implement mroute_addr_mask_host_bits() for IPv6
75
+
76
+  * TEST SUCCESS: Linux 2.6.30 (Gentoo)/iproute2, server-ipv6, ccd/iroute-ipv6
77
+
78
+  * TEST SUCCESS: Linux 2.6.30 (Gentoo)/ifconfig, client-ipv6
79
+
80
+  * TEST FAIL: NetBSD 5.0, IPv6 client
81
+     - "ifconfig tun0 .../64" does not create a "connected" route
82
+     - adding routes fails
83
+
84
+     --> more work to do here.
85
+
86
+  * release as patch 20100101-1
87
+
88
+  * TEST FAIL: 
89
+      FreeBSD 6.3-p12 server "--topology subnet"
90
+      Linux/ifconfig client
91
+    - BSD sends ICMP6 neighbor solicitations, which are ignored by Linux
92
+    - server tun interface is not in p2p mode, client tun interface *is*
93
+
94
+  * TEST SUCCESS: non-ipv6 enabled client -> "--server-ipv6" server
95
+    (warnings in the log file, but no malfunctions)
96
+
97
+
98
+Sat Jan  2 19:48:35 CET 2010
99
+
100
+  * tun.c: change "ipv6_support()", do not turn off tt->ipv6 unconditionally
101
+    if we don't know about OS IPv6 support - just log warning
102
+
103
+  * tun.c: implement "ifconfig inet6" setting for MacOS X / Darwin
104
+
105
+  * route.c: split *BSD system dependent part of add/delete_route_ipv6() 
106
+             into FreeBSD/Dragonfly and NetBSD/Darwin/OpenBSD variants 
107
+             ("2001:db8::/64" vs. "2001:db8:: --prefixlen 64").
108
+
109
+  * tun.c: on MacOS X, NetBSD and OpenBSD, explicitely set on-link route
110
+
111
+  * TEST SUCCESS: MacOS X, client-ipv6 with route-ipv6
112
+
113
+
114
+Sun Jan  3 10:55:31 CET 2010
115
+
116
+  * route.c: NetBSD fails with "-iface tun0", needs gateway address
117
+    (assume that the same syntax is needed for OpenBSD)
118
+
119
+  * route.h: introduce "remote_endpoint_ipv6" into "struct route_ipv6_list"
120
+
121
+  * init.c: pass "ifconfig_ipv6_remote" as gateway to init_route_ipv6_list()
122
+
123
+  * route.c: 
124
+    - init_route_ipv6(): use "remote_endpoint_ipv6" as IPv6 gateway address
125
+                         if no gateway was specified explicitely
126
+
127
+    - init_route_ipv6_list(): fill in "remote_endpoint_ipv6", if parseable
128
+
129
+    - get rid of "GATEWAY-LESS ROUTE6" warning
130
+
131
+  * route.c, add_route_ipv6()
132
+    - explicitely clear host bits of base address, to be able to more 
133
+      easily set up "connected" /64 routes on NetBSD+Darwin
134
+
135
+    - split system-dependent part between Darwin and NetBSD/OpenBSD
136
+      (Darwin can use "-iface tun0", NetBSD/OpenBSD get gateway address)
137
+
138
+    - change Solaris comments from "known-broken" to "unknown"
139
+
140
+  * tun.c: rework NetBSD tunnel initialization and tun_read() / tun_write()
141
+    to work the same way OpenBSD and NetBSD do - tunnel is put into 
142
+    "multi-af" mode, and all packet read/write activity is prepended by 
143
+    a 32 bit value specifying the address family.
144
+
145
+  * TEST SUCCESS: NetBSD 5.0/Sparc64: client-ipv6 with route-ipv6
146
+
147
+  * TEST SUCCESS: MacOS X 10.5: client-ipv6 with route-ipv6
148
+
149
+  * (RE-)TEST SUCCESS: Linux/iproute2: server-ipv6
150
+                       Linux/ifconfig: client-ipv6
151
+                       FreeBSD 6.3: server-ipv6
152
+
153
+  * release as patch 20100103-1
154
+
155
+  * options.c: document all new options in "--help"
156
+
157
+  * tun.c: fix typo in Solaris-specific section
158
+
159
+  * socket.h, socket.c: change u_int32_t to uint32_t 
160
+    (Solaris - and all the rest of the code uses "uintNN" anyway)
161
+
162
+Mon Jan  4 17:46:58 CET 2010
163
+
164
+  * socket.c: rework add_in6_addr() to use 32-bit access to struct in6_addr
165
+    (Solaris has no 16-bit values in union, but this is more elegant as well)
166
+
167
+  * tun.c: fix "ifconfig inet6" command for Solaris
168
+
169
+  * tun.c: make sure "tun0 inet6" is unplumbed first, cleanup leftovers
170
+
171
+  * route.c: add routes with "metric 0" on solaris, otherwise they just
172
+    don't work (someone who understands Solaris might want to fix this).
173
+
174
+  * Solaris "sort of" works now - ifconfig works, route add does not give
175
+    errors, "netstat -rn" looks right, but packets are discarded unless
176
+    the routes are installed with "metric 0".  So we just use "metric 0"...
177
+
178
+  * CAVEAT: Solaris "ifconfig ... preferred" interferes with source address
179
+    selection.  So if there are any active IPv6 interfaces configured with 
180
+    "preferred", packets leaving out the tunnel will use the wrong source
181
+    IPv6 address.  Not fixable from within OpenVPN.
182
+
183
+  * CAVEAT2: Solaris insists on doing DHCPv6 on tun0 interfaces by default,
184
+    so DHCPv6 solicitation packets will be seen.  Since the server end has
185
+    no idea what to do with them, they are a harmless nuisance.  Fixable
186
+    on the Solaris side via "ndpd.conf" (see ``man ifconfig'').
187
+
188
+  * release as patch 20100104-1
0 189
new file mode 100644
... ...
@@ -0,0 +1,180 @@
0
+TODO:
1
+
2
+ * tun.c -> init_tun()
3
+    [ifconfig-Parameter vorbereiten]
4
+
5
+    init.c -> do_open_tun() -> init.c::do_init_tun() -> tun.c::init_tun()
6
+			    -> do_ifconfig()
7
+
8
+ o tun.c -> do_ifconfig()
9
+    [ifconfig/ip aufrufen]
10
+
11
+    * Linux / ifconfig  
12
+    / Linux / iproute2  ** TESTEN **
13
+    o FreeBSD
14
+    / NetBSD ("needs patch", googlen) ** TESTEN **
15
+    / Solaris                         ** TESTEN **
16
+    o OpenBSD
17
+    o MacOS X
18
+
19
+ o tun.c (?) -> interface cleanup ("ip addr del dev tun0 ...")
20
+
21
+ o TAP mode und IPv6?  Fehlermeldung?
22
+    o einfach confen
23
+
24
+ o ifconfig_ipv6_remote -> kann eigentlich ersatzlos wegfallen
25
+   [tun.c, init.c, options.c, options.h]
26
+   o [kann nicht, braucht man als default-gateway auf Solaris :( ]
27
+
28
+ * push ifconfig-ipv6
29
+   push::send_push_reply() -> c->c2.push_ifconfig_local
30
+
31
+   ** wo wird das gesetzt? ** multi.c (und ggf. options.c / ifconfig-push)
32
+
33
+   o /netbits pushen (push.c) -> options.c "ifconfig-ipv6" muss auch
34
+     damit zurecht kommen, tut es derzeit aber nicht
35
+
36
+ * ifconfig_pool_write() -> IPv6 "wenn pool IPv6 hat"
37
+
38
+ * multi::multi_init() -> ifconfig_pool_init()
39
+   
40
+
41
+ * "route-ipv6"-Option und "push route-ipv6"
42
+   o "gateway" 
43
+   o "metric"
44
+   o "route-gateway-ipv6"-Option
45
+   o "ifconfig-ipv6-push"-Option
46
+      options.c -> options.push_ifconfig_...
47
+      multi.c 
48
+	mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
49
+
50
+
51
+ o "server-ipv6"-Option
52
+
53
+   o options.c, add_option() -> wird fuer "lokale" und "push"-Options
54
+     aufgerufen
55
+     no_more_than_n_args()
56
+     struct options [options.h]
57
+
58
+   * add_route_to_option_list() 
59
+     [route.c -> add_route_ipv6_to_option_list]
60
+     [options.h -> options->routes_ipv6]
61
+
62
+     o was passiert danach damit?
63
+
64
+   * socket.c: ip_or_dns_addr_safe()
65
+     --> ipv6_addr_safe()
66
+     --> ipv6_addr_safe_hexplusbits()
67
+
68
+   * Makro?  helper.c -> helper_client_server()      ******
69
+     * Fehler, wenn options->mode != MODE_SERVER
70
+     * "tun-ipv6" auto-enablen
71
+
72
+   * if (options->tun_ipv6)
73
+		 msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
74
+     [options.c, 1710]
75
+     [raus]
76
+
77
+   o struct tuntap->ipv6 = true, wenn "ipv6" und "system kann das"
78
+   o Fehler, wenn System kein IPv6 kann
79
+     ("NetBSD needs patch" -> googlen)
80
+
81
+ o Adress-Allokation an Clients (/128 aus ifconfig-ipv6-pool /64 erstmal nur)
82
+   o hash aus Client-Key als host part?
83
+     (nein, wir nehmen einfach "den gleichen Offset wie bei IPv4" und
84
+     add_in6_addr())
85
+
86
+ o "iroute-ipv6"-Option
87
+ o "ifconfig-ipv6"
88
+ o "ifconfig-ipv6-pool"
89
+ o "ifconfig-pool-persist-ipv6"-Option
90
+
91
+ o was tut #define LINUX_IPV6?
92
+ o was tut bestehender Code mit "ipv6"?
93
+
94
+
95
+ o Routing-/Forwarding-Funktion
96
+   read_tun() --> ??
97
+   ?? --> write_tun() 
98
+
99

                
100
+   server-seite anzupassen]
101
+
102
+ o ICMP
103
+
104
+ o Optionen dokumentieren (-> berniv6)
105
+   o server-ipv6
106
+   o ifconfig-ipv6
107
+   o ifconfig-ipv6-pool
108
+   o ifconfig-pool-persist (v4+v6, Formataenderung im File)
109
+   o iroute-ipv6
110
+   o route-ipv6
111
+   o tun-ipv6
112
+
113
+   * http://www.greenie.net/ipv6/openvpn.html  - DONE
114
+   o man pages, --help
115
+
116
+ * options.c
117
+    - get_ip_addr() --> socket.c getaddr()
118
+    - openvpn_inet_aton -> OIA_IP "ist IP"
119
+
120
+ * options.c, show_p2mp_parms()
121
+
122
+ * socket.c, print_in_addr_t()  --> print_in6_addr()
123
+
124
+ o forward_compatible?
125
+
126
+ o ifconfig_ipv6_pool_persist --> einfach ifconfig_pool_persist mitbenutzen?
127
+   Entscheidung: JA
128
+   o to be implemented: pool.c
129
+
130
+ o route.c:
131
+    clone_route_option_list(), copy_route_option_list(),
132
+    new_route_list(), add_route(), init_route_list(), ...
133
+    add_routes(), delete_routes(), setenv_routes(), 
134
+
135
+    -> wo werden die aufgerufen, wofuer verwendet, IPv6-Anpassung?
136
+
137
+    * add_route() ruft "/sbin/route add..." auf
138
+    o div. (redirect gateway related) -> route.c::add_route3() -> add_route()
139
+    o init.c::do_route() -> route.c::add_routes() -> add_route()
140
+    o init.c::do_open_tun() -> do_route()
141
+    o forward.c::check_add_routes_action() -> do_route()
142
+    o init.c::do_open_tun() -> init.c::do_init_route_list() ->
143
+      route.c::init_route_list()
144
+    * init.c::do_open_tun() -> do_alloc_route_list() -> new_route_ipv6_list()
145
+
146
+ o add_route_ipv6() - implementieren und testen
147
+    * Linux / ifconfig  
148
+    * Linux / iproute2
149
+    i FreeBSD
150
+    i NetBSD ("needs patch", googlen)
151
+    i Solaris *braucht Gateway*
152
+    i OpenBSD
153
+    i MacOS X
154
+
155
+ o delete_route_ipv6() - implementieren und testen
156
+    * Linux / ifconfig  
157
+    * Linux / iproute2
158
+    i FreeBSD
159
+    i NetBSD ("needs patch", googlen)
160
+    i Solaris
161
+    i OpenBSD
162
+    i MacOS X
163
+
164

                
165
+   aus ifconfig-ipv6 $remote")
166
+
167
+ o IPv6 TCPMSS oder "fragmentation required"?
168
+    o IPv6 MTU auf Interface setzen?
169
+    o sysdep!
170
+
171
+
172
+TESTEN
173
+ * ipv6_addr_safe() [--ifconfig-ipv6 null/zu lang/invalid]
174
+ o ipv6_addr_safe_hexplusbits() [--route-ipv6 ...]
175
+ * get_ipv6_addr() [--server-ipv6 ...]
176
+
177
+ o unmodifizierter 2.1-client -> 2.1+ipv6-Server?
178
+ o unmodifizierter 2.0-client -> 2.1+ipv6-Server?
179
+ o wie kann der Server das erkennen, und "kein v6" schicken?
0 180
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+This is an experimentally patched version of OpenVPN 2.1 with IPv6
1
+payload support.
2
+
3
+Go here for release notes and documentation:
4
+
5
+  http://www.greenie.net/ipv6/openvpn.html
6
+
7
+Gert Doering, 31.12.2009
0 8
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+known issues for IPv6 payload support in OpenVPN
1
+-----------------------------------------------
2
+
3
+1.) "--topology subnet" doesn't work together with IPv6 payload
4
+    (verified for FreeBSD server, Linux/ifconfig client, problems 
5
+    with ICMP6 neighbor solicitations from BSD not being answered by Linux)
6
+
7
+2.) NetBSD IPv6 support doesn't work
8
+    ("connected" route is not auto-created, "route-ipv6" adding fails)
9
+
10
+    * fixed, 3.1.10 *
11
+
12
+3.) route deletion for IPv6 routes is not yet done
13
+
14
+    * fixed for configured routes, 3.1.10 *
15
+    * missing for manual-ifconfig-connected (NetBSD, Darwin)
16
+
17
+4.) do "ifconfig tun0 inet6 unplumb"  or "ifconfig tun0 destroy" for
18
+    Solaris, *BSD, ... at program termination time, to clean up leftovers
19
+    (unless tunnel persistance is desired).
20
+
21
+    For Solaris, only the "ipv6 tun0" is affected, for the *BSDs all tun0
22
+    stay around.
23
+
24
+5.) add new option "ifconfig-ipv6-push"
25
+    (per-client static IPv6 assignment, -> radiusplugin, etc)
26
+
27
+6.) add new option "route-ipv6-gateway"
28
+
29
+7.) add "full" gateway handling for IPv6 in route.c 
30
+    (right now, the routes are just sent down the tun interface, if the
31
+    operating system in questions supports that, without care for the
32
+    gateway address - which does not work for gateways that are supposed
33
+    to point elsewhere.  Also, it doesn't work for TAP interfaces.
34
+
35
+8.) full IPv6 support for TAP interfaces 
36
+    (main issue should be routes+gateway - and testing :-) )
... ...
@@ -259,7 +259,8 @@ send_control_channel_string (struct context *c, const char *str, int msglevel)
259 259
 static void
260 260
 check_add_routes_action (struct context *c, const bool errors)
261 261
 {
262
-  do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
262
+  do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
263
+	    c->c1.tuntap, c->plugins, c->c2.es);
263 264
   update_time ();
264 265
   event_timeout_clear (&c->c2.route_wakeup);
265 266
   event_timeout_clear (&c->c2.route_wakeup_expire);
... ...
@@ -142,6 +142,55 @@ helper_client_server (struct options *o)
142 142
 
143 143
 #if P2MP
144 144
 #if P2MP_SERVER
145
+
146
+  /* 
147
+   *
148
+   * HELPER DIRECTIVE for IPv6
149
+   *
150
+   * server-ipv6 2001:db8::/64
151
+   *
152
+   * EXPANDS TO:
153
+   *
154
+   * tun-ipv6
155
+   * push "tun-ipv6"
156
+   * ifconfig-ipv6 2001:db8::1 2001:db8::2
157
+   * if !nopool: 
158
+   *   ifconfig-ipv6-pool 2001:db8::1:0/64
159
+   * 
160
+   */
161
+   if ( o->server_ipv6_defined )
162
+     {
163
+	if ( ! o->server_defined )
164
+	  {
165
+	    msg (M_USAGE, "--server-ipv6 must be used together with --server");
166
+	  }
167
+	if ( o->server_flags & SF_NOPOOL )
168
+	  {
169
+	    msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" );
170
+	  }
171
+	if ( o->ifconfig_ipv6_pool_defined )
172
+	  {
173
+	    msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly");
174
+	  }
175
+
176
+        /* local ifconfig is "base address + 1" and "+2" */
177
+	o->ifconfig_ipv6_local = 
178
+		print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc );
179
+	o->ifconfig_ipv6_remote = 
180
+		print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc );
181
+
182
+	/* pool starts at "base address + 0x10000" */
183
+	ASSERT( o->server_netbits_ipv6 < 96 );		/* want 32 bits */
184
+	o->ifconfig_ipv6_pool_defined = true;
185
+	o->ifconfig_ipv6_pool_base = 
186
+		add_in6_addr( o->server_network_ipv6, 0x10000 );
187
+	o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6;
188
+
189
+	o->tun_ipv6 = true;
190
+
191
+	push_option( o, "tun-ipv6", M_USAGE );
192
+     }
193
+
145 194
   /*
146 195
    *
147 196
    * HELPER DIRECTIVE:
... ...
@@ -1066,6 +1066,8 @@ do_alloc_route_list (struct context *c)
1066 1066
 {
1067 1067
   if (c->options.routes && !c->c1.route_list)
1068 1068
     c->c1.route_list = new_route_list (c->options.max_routes, &c->gc);
1069
+  if (c->options.routes_ipv6 && !c->c1.route_ipv6_list)
1070
+    c->c1.route_ipv6_list = new_route_ipv6_list (c->options.max_routes, &c->gc);
1069 1071
 }
1070 1072
 
1071 1073
 
... ...
@@ -1108,6 +1110,45 @@ do_init_route_list (const struct options *options,
1108 1108
     }
1109 1109
 }
1110 1110
 
1111
+static void
1112
+do_init_route_ipv6_list (const struct options *options,
1113
+		    struct route_ipv6_list *route_ipv6_list,
1114
+		    bool fatal,
1115
+		    struct env_set *es)
1116
+{
1117
+  const char *gw = NULL;
1118
+  int dev = dev_type_enum (options->dev, options->dev_type);
1119
+  int metric = 0;
1120
+
1121
+  if (dev != DEV_TYPE_TUN )
1122
+    msg( M_WARN, "IPv6 routes on TAP devices are going to fail on some platforms (need gateway spec)" );	/* TODO-GERT */
1123
+
1124
+  gw = options->ifconfig_ipv6_remote;		/* default GW = remote end */
1125
+#if 0					/* not yet done for IPv6 - TODO!*/
1126
+  if ( options->route_ipv6_default_gateway )		/* override? */
1127
+    gw = options->route_ipv6_default_gateway;
1128
+#endif
1129
+
1130
+  if (options->route_default_metric)
1131
+    metric = options->route_default_metric;
1132
+
1133
+  if (!init_route_ipv6_list (route_ipv6_list,
1134
+			options->routes_ipv6,
1135
+			gw,
1136
+			metric,
1137
+			es))
1138
+    {
1139
+      if (fatal)
1140
+	openvpn_exit (OPENVPN_EXIT_STATUS_ERROR);	/* exit point */
1141
+    }
1142
+  else
1143
+    {
1144
+      /* copy routes to environment */
1145
+      setenv_routes_ipv6 (es, route_ipv6_list);
1146
+    }
1147
+}
1148
+
1149
+
1111 1150
 /*
1112 1151
  * Called after all initialization has been completed.
1113 1152
  */
... ...
@@ -1171,12 +1212,13 @@ initialization_sequence_completed (struct context *c, const unsigned int flags)
1171 1171
 void
1172 1172
 do_route (const struct options *options,
1173 1173
 	  struct route_list *route_list,
1174
+	  struct route_ipv6_list *route_ipv6_list,
1174 1175
 	  const struct tuntap *tt,
1175 1176
 	  const struct plugin_list *plugins,
1176 1177
 	  struct env_set *es)
1177 1178
 {
1178
-  if (!options->route_noexec && route_list)
1179
-    add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
1179
+  if (!options->route_noexec && ( route_list || route_ipv6_list ) )
1180
+    add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
1180 1181
 
1181 1182
   if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
1182 1183
     {
... ...
@@ -1233,6 +1275,8 @@ do_init_tun (struct context *c)
1233 1233
 			   c->options.topology,
1234 1234
 			   c->options.ifconfig_local,
1235 1235
 			   c->options.ifconfig_remote_netmask,
1236
+			   c->options.ifconfig_ipv6_local,
1237
+			   c->options.ifconfig_ipv6_remote,
1236 1238
 			   addr_host (&c->c1.link_socket_addr.local),
1237 1239
 			   addr_host (&c->c1.link_socket_addr.remote),
1238 1240
 			   !c->options.ifconfig_nowarn,
... ...
@@ -1269,6 +1313,8 @@ do_open_tun (struct context *c)
1269 1269
       /* parse and resolve the route option list */
1270 1270
       if (c->options.routes && c->c1.route_list && c->c2.link_socket)
1271 1271
 	do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
1272
+      if (c->options.routes_ipv6 && c->c1.route_ipv6_list )
1273
+	do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);
1272 1274
 
1273 1275
       /* do ifconfig */
1274 1276
       if (!c->options.ifconfig_noexec
... ...
@@ -1315,7 +1361,8 @@ do_open_tun (struct context *c)
1315 1315
 
1316 1316
       /* possibly add routes */
1317 1317
       if (!c->options.route_delay_defined)
1318
-	do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
1318
+	do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
1319
+		  c->c1.tuntap, c->plugins, c->c2.es);
1319 1320
 
1320 1321
       /*
1321 1322
        * Did tun/tap driver give us an MTU?
... ...
@@ -1390,8 +1437,9 @@ do_close_tun (struct context *c, bool force)
1390 1390
 #endif
1391 1391
 
1392 1392
 	  /* delete any routes we added */
1393
-	  if (c->c1.route_list)
1394
-	    delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
1393
+	  if (c->c1.route_list || c->c1.route_ipv6_list )
1394
+	    delete_routes (c->c1.route_list, c->c1.route_ipv6_list,
1395
+			   c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
1395 1396
 
1396 1397
 	  /* actually close tun/tap device based on --down-pre flag */
1397 1398
 	  if (!c->options.down_pre)
... ...
@@ -63,6 +63,7 @@ void init_instance (struct context *c, const struct env_set *env, const unsigned
63 63
 
64 64
 void do_route (const struct options *options,
65 65
 	       struct route_list *route_list,
66
+	       struct route_ipv6_list *route_ipv6_list,
66 67
 	       const struct tuntap *tt,
67 68
 	       const struct plugin_list *plugins,
68 69
 	       struct env_set *es);
... ...
@@ -1004,7 +1004,7 @@ setenv_str_ex (struct env_set *es,
1004 1004
 	{
1005 1005
 	  const char *str = construct_name_value (name_tmp, val_tmp, &gc);
1006 1006
 	  env_set_add (es, str);
1007
-	  /*msg (M_INFO, "SETENV_ES '%s'", str);*/
1007
+	  msg (M_INFO, "SETENV_ES '%s'", str);/**/
1008 1008
 	}
1009 1009
       else
1010 1010
 	env_set_del (es, name_tmp);
... ...
@@ -88,12 +88,33 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int
88 88
     }
89 89
 }
90 90
 
91
+static inline void
92
+mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned int mask)
93
+{
94
+  if (ma)
95
+    {
96
+      ma->type = MR_ADDR_IPV6 | mask;
97
+      ma->netbits = 0;
98
+      ma->len = 16;
99
+      *(struct in6_addr *)ma->addr = src;
100
+    }
101
+}
102
+
91 103
 static inline bool
92 104
 mroute_is_mcast (const in_addr_t addr)
93 105
 {
94 106
   return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
95 107
 }
96 108
 
109
+/* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies 
110
+ *                 the address as being a multicast address"
111
+ */
112
+static inline bool
113
+mroute_is_mcast_ipv6 (const struct in6_addr addr)
114
+{
115
+  return (addr.s6_addr[0] == 0xff);
116
+}
117
+
97 118
 #ifdef ENABLE_PF
98 119
 
99 120
 static unsigned int
... ...
@@ -155,10 +176,29 @@ mroute_extract_addr_ipv4 (struct mroute_addr *src,
155 155
 	    }
156 156
 	  break;
157 157
 	case 6:
158
-	  {
159
-	    msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet"); 
160
-	    break;
161
-	  }
158
+	  if (BLEN (buf) >= (int) sizeof (struct openvpn_ipv6hdr))
159
+	    {
160
+	      const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR (buf);
161
+#if 0				/* very basic debug */
162
+	      struct gc_arena gc = gc_new ();
163
+	      msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
164
+			print_in6_addr( ipv6->saddr, 0, &gc ),
165
+			print_in6_addr( ipv6->daddr, 0, &gc ));
166
+	      gc_free (&gc);
167
+#endif
168
+
169
+	      mroute_get_in6_addr (src, ipv6->saddr, 0);
170
+	      mroute_get_in6_addr (dest, ipv6->daddr, 0);
171
+
172
+	      if (mroute_is_mcast_ipv6 (ipv6->daddr))
173
+		ret |= MROUTE_EXTRACT_MCAST;
174
+
175
+	      ret |= MROUTE_EXTRACT_SUCCEEDED;
176
+	    }
177
+	  break;
178
+	default:
179
+	    msg (M_WARN, "IP packet with unknown IP version=%d seen",
180
+	                 OPENVPN_IPH_GET_VER (*BPTR(buf)));
162 181
 	}
163 182
     }
164 183
   return ret;
... ...
@@ -252,14 +292,36 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
252 252
  * Zero off the host bits in an address, leaving
253 253
  * only the network bits, using the netbits member of
254 254
  * struct mroute_addr as the controlling parameter.
255
+ *
256
+ * TODO: this is called for route-lookup for every yet-unhashed
257
+ * destination address, so for lots of active net-iroutes, this
258
+ * might benefit from some "zeroize 32 bit at a time" improvements
255 259
  */
256 260
 void
257 261
 mroute_addr_mask_host_bits (struct mroute_addr *ma)
258 262
 {
259 263
   in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
260
-  ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
261
-  addr &= netbits_to_netmask (ma->netbits);
262
-  *(in_addr_t*)ma->addr = htonl (addr);
264
+  if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
265
+    {
266
+      addr &= netbits_to_netmask (ma->netbits);
267
+      *(in_addr_t*)ma->addr = htonl (addr);
268
+    }
269
+  else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
270
+    {
271
+      int byte = ma->len-1;		/* rightmost byte in address */
272
+      int bits_to_clear = 128 - ma->netbits;
273
+
274
+      while( byte >= 0 && bits_to_clear > 0 )
275
+        {
276
+	  if ( bits_to_clear >= 8 )
277
+	    { ma->addr[byte--] = 0; bits_to_clear -= 8; }
278
+	  else
279
+	    { ma->addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
280
+        }
281
+      ASSERT( bits_to_clear == 0 );
282
+    }
283
+  else
284
+      ASSERT(0);
263 285
 }
264 286
 
265 287
 /*
... ...
@@ -337,17 +399,24 @@ mroute_addr_print_ex (const struct mroute_addr *ma,
337 337
 	  }
338 338
 	  break;
339 339
 	case MR_ADDR_IPV6:
340
-	  buf_printf (&out, "IPV6"); 
341
-	  break;
342
-	default:
343
-	  buf_printf (&out, "UNKNOWN"); 
344
-	  break;
345
-	}
346
-      return BSTR (&out);
347
-    }
348
-  else
349
-    return "[NULL]";
350
-}
340
+	  {
341
+	    buf_printf (&out, "%s",
342
+		  print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc)); 
343
+	    if (maddr.type & MR_WITH_NETBITS)
344
+	      {
345
+		buf_printf (&out, "/%d", maddr.netbits);
346
+	      }
347
+	    }
348
+	    break;
349
+	  default:
350
+	    buf_printf (&out, "UNKNOWN"); 
351
+	    break;
352
+	  }
353
+	return BSTR (&out);
354
+      }
355
+    else
356
+      return "[NULL]";
357
+  }
351 358
 
352 359
 /*
353 360
  * mroute_helper's main job is keeping track of
... ...
@@ -418,6 +487,44 @@ mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir)
418 418
     }
419 419
 }
420 420
 
421
+/* this is a bit inelegant, we really should have a helper to that 
422
+ * is only passed the netbits value, and not the whole struct iroute *
423
+ * - thus one helper could do IPv4 and IPv6.  For the sake of "not change
424
+ * code unrelated to IPv4" this is left for later cleanup, for now.
425
+ */
426
+void
427
+mroute_helper_add_iroute6 (struct mroute_helper *mh, 
428
+                           const struct iroute_ipv6 *ir6)
429
+{
430
+  if (ir6->netbits >= 0)
431
+    {
432
+      ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
433
+      mroute_helper_lock (mh);
434
+      ++mh->cache_generation;
435
+      ++mh->net_len_refcount[ir6->netbits];
436
+      if (mh->net_len_refcount[ir6->netbits] == 1)
437
+	mroute_helper_regenerate (mh);
438
+      mroute_helper_unlock (mh);
439
+    }
440
+}
441
+
442
+void
443
+mroute_helper_del_iroute6 (struct mroute_helper *mh, 
444
+			   const struct iroute_ipv6 *ir6)
445
+{
446
+  if (ir6->netbits >= 0)
447
+    {
448
+      ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
449
+      mroute_helper_lock (mh);
450
+      ++mh->cache_generation;
451
+      --mh->net_len_refcount[ir6->netbits];
452
+      ASSERT (mh->net_len_refcount[ir6->netbits] >= 0);
453
+      if (!mh->net_len_refcount[ir6->netbits])
454
+	mroute_helper_regenerate (mh);
455
+      mroute_helper_unlock (mh);
456
+    }
457
+}
458
+
421 459
 void
422 460
 mroute_helper_free (struct mroute_helper *mh)
423 461
 {
... ...
@@ -85,7 +85,7 @@ struct mroute_addr {
85 85
 /*
86 86
  * Number of bits in an address.  Should be raised for IPv6.
87 87
  */
88
-#define MR_HELPER_NET_LEN 32
88
+#define MR_HELPER_NET_LEN 129
89 89
 
90 90
 /*
91 91
  * Used to help maintain CIDR routing table.
... ...
@@ -127,6 +127,8 @@ struct mroute_helper *mroute_helper_init (int ageable_ttl_secs);
127 127
 void mroute_helper_free (struct mroute_helper *mh);
128 128
 void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
129 129
 void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
130
+void mroute_helper_add_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
131
+void mroute_helper_del_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
130 132
 
131 133
 /*
132 134
  * Given a raw packet in buf, return the src and dest
... ...
@@ -316,25 +316,18 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
316 316
    */
317 317
   if (t->options.ifconfig_pool_defined)
318 318
     {
319
-      if (dev == DEV_TYPE_TAP)
320
-	{
321
-	  m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
322
-						 t->options.ifconfig_pool_start,
323
-						 t->options.ifconfig_pool_end,
324
-						 t->options.duplicate_cn);
325
-	}
326
-      else if (dev == DEV_TYPE_TUN)
327
-	{
328
-	  m->ifconfig_pool = ifconfig_pool_init (
329
-	    (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
330
-	    t->options.ifconfig_pool_start,
331
-	    t->options.ifconfig_pool_end,
332
-	    t->options.duplicate_cn);
333
-	}
334
-      else
335
-	{
336
-	  ASSERT (0);
337
-	}
319
+      int pool_type = IFCONFIG_POOL_INDIV;
320
+
321
+      if ( dev == DEV_TYPE_TUN && t->options.topology == TOP_NET30 )
322
+	pool_type = IFCONFIG_POOL_30NET;
323
+
324
+      m->ifconfig_pool = ifconfig_pool_init (pool_type,
325
+				 t->options.ifconfig_pool_start,
326
+				 t->options.ifconfig_pool_end,
327
+				 t->options.duplicate_cn,
328
+				 t->options.ifconfig_ipv6_pool_defined,
329
+				 t->options.ifconfig_ipv6_pool_base,
330
+				 t->options.ifconfig_ipv6_pool_netbits );
338 331
 
339 332
       /* reload pool data from file */
340 333
       if (t->c1.ifconfig_pool_persist)
... ...
@@ -429,10 +422,14 @@ multi_del_iroutes (struct multi_context *m,
429 429
 		   struct multi_instance *mi)
430 430
 {
431 431
   const struct iroute *ir;
432
+  const struct iroute_ipv6 *ir6;
432 433
   if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
433 434
     {
434 435
       for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
435 436
 	mroute_helper_del_iroute (m->route_helper, ir);
437
+
438
+      for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
439
+	mroute_helper_del_iroute6 (m->route_helper, ir6);
436 440
     }
437 441
 }
438 442
 
... ...
@@ -1078,6 +1075,37 @@ multi_learn_in_addr_t (struct multi_context *m,
1078 1078
   }
1079 1079
 }
1080 1080
 
1081
+static struct multi_instance *
1082
+multi_learn_in6_addr  (struct multi_context *m,
1083
+		       struct multi_instance *mi,
1084
+		       struct in6_addr a6,
1085
+		       int netbits, /* -1 if host route, otherwise # of network bits in address */
1086
+		       bool primary)
1087
+{
1088
+  struct mroute_addr addr;
1089
+
1090
+  addr.len = 16;
1091
+  addr.type = MR_ADDR_IPV6;
1092
+  addr.netbits = 0;
1093
+  memcpy( &addr.addr, &a6, sizeof(a6) );
1094
+
1095
+  if (netbits >= 0)
1096
+    {
1097
+      addr.type |= MR_WITH_NETBITS;
1098
+      addr.netbits = (uint8_t) netbits;
1099
+      mroute_addr_mask_host_bits( &addr );
1100
+    }
1101
+
1102
+  {
1103
+    struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
1104
+#ifdef MANAGEMENT_DEF_AUTH
1105
+    if (management && owner)
1106
+      management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
1107
+#endif
1108
+    return owner;
1109
+  }
1110
+}
1111
+
1081 1112
 /*
1082 1113
  * A new client has connected, add routes (server -> client)
1083 1114
  * to internal routing table.
... ...
@@ -1088,6 +1116,7 @@ multi_add_iroutes (struct multi_context *m,
1088 1088
 {
1089 1089
   struct gc_arena gc = gc_new ();
1090 1090
   const struct iroute *ir;
1091
+  const struct iroute_ipv6 *ir6;
1091 1092
   if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
1092 1093
     {
1093 1094
       mi->did_iroutes = true;
... ...
@@ -1107,6 +1136,22 @@ multi_add_iroutes (struct multi_context *m,
1107 1107
       
1108 1108
 	  multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
1109 1109
 	}
1110
+      for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
1111
+	{
1112
+	  if (ir6->netbits >= 0)
1113
+	    msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
1114
+		 print_in6_addr (ir6->network, 0, &gc),
1115
+		 ir6->netbits,
1116
+		 multi_instance_string (mi, false, &gc));
1117
+	  else
1118
+	    msg (D_MULTI_LOW, "MULTI: internal route %s -> %s",
1119
+		 print_in6_addr (ir6->network, 0, &gc),
1120
+		 multi_instance_string (mi, false, &gc));
1121
+
1122
+	  mroute_helper_add_iroute6 (m->route_helper, ir6);
1123
+      
1124
+	  multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false);
1125
+	}
1110 1126
     }
1111 1127
   gc_free (&gc);
1112 1128
 }
... ...
@@ -1196,17 +1241,22 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
1196 1196
   else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
1197 1197
     {
1198 1198
       in_addr_t local=0, remote=0;
1199
+      struct in6_addr remote_ipv6;
1199 1200
       const char *cn = NULL;
1200 1201
 
1201 1202
       if (!mi->context.options.duplicate_cn)
1202 1203
 	cn = tls_common_name (mi->context.c2.tls_multi, true);
1203 1204
 
1204
-      mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
1205
+      mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, &remote_ipv6, cn);
1205 1206
       if (mi->vaddr_handle >= 0)
1206 1207
 	{
1207 1208
 	  const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
1208 1209
 	  const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
1209 1210
 
1211
+	  msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s", 
1212
+		    print_in_addr_t( remote, 0, &gc ),
1213
+		    print_in6_addr( remote_ipv6, 0, &gc ) );
1214
+
1210 1215
 	  /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
1211 1216
 	  mi->context.c2.push_ifconfig_local = remote;
1212 1217
 	  if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
... ...
@@ -1228,6 +1278,16 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
1228 1228
 	  else
1229 1229
 	    msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
1230 1230
 		 multi_instance_string (mi, false, &gc));
1231
+
1232
+	  if ( mi->context.options.ifconfig_ipv6_pool_defined )
1233
+	    {
1234
+	      mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
1235
+	      mi->context.c2.push_ifconfig_ipv6_remote = 
1236
+		    mi->context.c1.tuntap->local_ipv6;
1237
+	      mi->context.c2.push_ifconfig_ipv6_netbits = 
1238
+		    mi->context.options.ifconfig_ipv6_pool_netbits;
1239
+	      mi->context.c2.push_ifconfig_ipv6_defined = true;
1240
+	    }
1231 1241
 	}
1232 1242
       else
1233 1243
 	{
... ...
@@ -1272,6 +1332,11 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi)
1272 1272
 			    SA_SET_IF_NONZERO);
1273 1273
 	}
1274 1274
     }
1275
+
1276
+    /* TODO: I'm not exactly sure what these environment variables are
1277
+     *       used for, but if we have them for IPv4, we should also have
1278
+     *       them for IPv6, no?
1279
+     */
1275 1280
 }
1276 1281
 
1277 1282
 /*
... ...
@@ -1661,6 +1726,15 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
1661 1661
 		       print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
1662 1662
 		}
1663 1663
 
1664
+	      if (mi->context.c2.push_ifconfig_ipv6_defined)
1665
+		{
1666
+		  multi_learn_in6_addr (m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
1667
+		  /* TODO: find out where addresses are "unlearned"!! */
1668
+		  msg (D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
1669
+		       multi_instance_string (mi, false, &gc),
1670
+		       print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
1671
+		}
1672
+
1664 1673
 	      /* add routes locally, pointing to new client, if
1665 1674
 		 --iroute options have been specified */
1666 1675
 	      multi_add_iroutes (m, mi);
... ...
@@ -165,6 +165,9 @@ struct context_1
165 165
   /* list of --route directives */
166 166
   struct route_list *route_list;
167 167
 
168
+  /* list of --route-ipv6 directives */
169
+  struct route_ipv6_list *route_ipv6_list;
170
+
168 171
   /* --status file */
169 172
   struct status_output *status_output;
170 173
   bool status_output_owned;
... ...
@@ -417,6 +420,11 @@ struct context_2
417 417
   in_addr_t push_ifconfig_local;
418 418
   in_addr_t push_ifconfig_remote_netmask;
419 419
 
420
+  bool            push_ifconfig_ipv6_defined;
421
+  struct in6_addr push_ifconfig_ipv6_local;
422
+  int             push_ifconfig_ipv6_netbits;
423
+  struct in6_addr push_ifconfig_ipv6_remote;
424
+
420 425
   /* client authentication state, CAS_SUCCEEDED must be 0 */
421 426
 # define CAS_SUCCEEDED 0
422 427
 # define CAS_PENDING   1
... ...
@@ -172,6 +172,8 @@ static const char usage_message[] =
172 172
   "                  addresses outside of the subnets used by either peer.\n"
173 173
   "                  TAP: configure device to use IP address l as a local\n"
174 174
   "                  endpoint and rn as a subnet mask.\n"
175
+  "--ifconfig-ipv6 l r : configure device to use IPv6 address l as local\n"
176
+  "                      endpoint (as a /64) and r as remote endpoint\n"
175 177
   "--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
176 178
   "                    pass --ifconfig parms by environment to scripts.\n"
177 179
   "--ifconfig-nowarn : Don't warn if the --ifconfig option on this side of the\n"
... ...
@@ -182,6 +184,10 @@ static const char usage_message[] =
182 182
   "                  netmask default: 255.255.255.255\n"
183 183
   "                  gateway default: taken from --route-gateway or --ifconfig\n"
184 184
   "                  Specify default by leaving blank or setting to \"nil\".\n"
185
+  "--route-ipv6 network/bits [gateway] [metric] :\n"
186
+  "                  Add IPv6 route to routing table after connection\n"
187
+  "                  is established.  Multiple routes can be specified.\n"
188
+  "                  gateway default: taken from --route-ipv6-gateway or --ifconfig\n"
185 189
   "--max-routes n :  Specify the maximum number of routes that may be defined\n"
186 190
   "                  or pulled from a server.\n"
187 191
   "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
... ...
@@ -370,6 +376,7 @@ static const char usage_message[] =
370 370
   "\n"
371 371
   "Multi-Client Server options (when --mode server is used):\n"
372 372
   "--server network netmask : Helper option to easily configure server mode.\n"
373
+  "--server-ipv6 network/bits : Configure IPv6 server mode.\n"
373 374
   "--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
374 375
   "                    easily configure ethernet bridging server mode.\n"
375 376
   "--push \"option\" : Push a config file option back to the peer for remote\n"
... ...
@@ -383,10 +390,13 @@ static const char usage_message[] =
383 383
   "--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n"
384 384
   "                  data to file, at seconds intervals (default=600).\n"
385 385
   "                  If seconds=0, file will be treated as read-only.\n"
386
+  "--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
387
+  "                  to be dynamically allocated to connecting clients.\n"
386 388
   "--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
387 389
   "                  overrides --ifconfig-pool dynamic allocation.\n"
388 390
   "                  Only valid in a client-specific config file.\n"
389 391
   "--iroute network [netmask] : Route subnet to client.\n"
392
+  "--iroute-ipv6 network/bits : Route IPv6 subnet to client.\n"
390 393
   "                  Sets up internal routes only.\n"
391 394
   "                  Only valid in a client-specific config file.\n"
392 395
   "--disable       : Client is disabled.\n"
... ...
@@ -871,6 +881,58 @@ get_ip_addr (const char *ip_string, int msglevel, bool *error)
871 871
   return ret;
872 872
 }
873 873
 
874
+/* parse a text string containing an IPv6 address + netbits
875
+ * in "standard format" (2001:dba::/32)
876
+ * return true if parsing succeeded, modify *network and *netbits
877
+ */
878
+bool
879
+get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
880
+	       unsigned int * netbits, int msglevel )
881
+{
882
+    int rc;
883
+    char * sep, * endp;
884
+    int bits;
885
+
886
+    sep = strchr( prefix_str, '/' );
887
+    if ( sep == NULL )
888
+    {
889
+      msg (msglevel, "IPv6 prefix '%s': missing '/'", prefix_str);
890
+      return false;
891
+    }
892
+
893
+    bits = strtol( sep+1, &endp, 10 );
894
+    if ( *endp != '\0' || bits < 0 || bits > 128 )
895
+    {
896
+      msg (msglevel, "IPv6 prefix '%s': invalid '/bits' spec", prefix_str);
897
+      return false;
898
+    }
899
+
900
+    /* temporary replace '/' in caller-provided string with '\0', otherwise
901
+     * inet_pton() will refuse prefix string
902
+     * (alternative would be to strncpy() the prefix to temporary buffer)
903
+     */
904
+
905
+    *sep = '\0';
906
+    rc = inet_pton( AF_INET6, prefix_str, network );
907
+    *sep = '/';
908
+
909
+    if ( rc != 1 )
910
+    {
911
+      msg (msglevel, "IPv6 prefix '%s': invalid network part", prefix_str);
912
+      return false;
913
+    }
914
+    *netbits = bits;
915
+    return true;		/* parsing OK, values set */
916
+}
917
+
918
+static bool ipv6_addr_safe_hexplusbits( const char * ipv6_prefix_spec )
919
+{
920
+    struct in6_addr t_addr;
921
+    unsigned int t_bits;
922
+
923
+    return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, M_WARN );
924
+}
925
+
874 926
 static char *
875 927
 string_substitute (const char *src, int from, int to, struct gc_arena *gc)
876 928
 {
... ...
@@ -989,6 +1051,8 @@ show_p2mp_parms (const struct options *o)
989 989
 #if P2MP_SERVER
990 990
   msg (D_SHOW_PARMS, "  server_network = %s", print_in_addr_t (o->server_network, 0, &gc));
991 991
   msg (D_SHOW_PARMS, "  server_netmask = %s", print_in_addr_t (o->server_netmask, 0, &gc));
992
+  msg (D_SHOW_PARMS, "  server_network_ipv6 = %s", print_in6_addr (o->server_network_ipv6, 0, &gc) );
993
+  SHOW_INT (server_netbits_ipv6);
992 994
   msg (D_SHOW_PARMS, "  server_bridge_ip = %s", print_in_addr_t (o->server_bridge_ip, 0, &gc));
993 995
   msg (D_SHOW_PARMS, "  server_bridge_netmask = %s", print_in_addr_t (o->server_bridge_netmask, 0, &gc));
994 996
   msg (D_SHOW_PARMS, "  server_bridge_pool_start = %s", print_in_addr_t (o->server_bridge_pool_start, 0, &gc));
... ...
@@ -1009,6 +1073,8 @@ show_p2mp_parms (const struct options *o)
1009 1009
   msg (D_SHOW_PARMS, "  ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc));
1010 1010
   SHOW_STR (ifconfig_pool_persist_filename);
1011 1011
   SHOW_INT (ifconfig_pool_persist_refresh_freq);
1012
+  msg (D_SHOW_PARMS, "  ifconfig_ipv6_pool_base = %s", print_in6_addr (o->ifconfig_ipv6_pool_base, 0, &gc));
1013
+  SHOW_INT (ifconfig_ipv6_pool_netbits);
1012 1014
   SHOW_INT (n_bcast_buf);
1013 1015
   SHOW_INT (tcp_queue_limit);
1014 1016
   SHOW_INT (real_hash_size);
... ...
@@ -1076,6 +1142,25 @@ option_iroute (struct options *o,
1076 1076
   o->iroutes = ir;
1077 1077
 }
1078 1078
 
1079
+static void
1080
+option_iroute_ipv6 (struct options *o,
1081
+	       const char *prefix_str,
1082
+	       int msglevel)
1083
+{
1084
+  struct iroute_ipv6 *ir;
1085
+
1086
+  ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc);
1087
+
1088
+  if ( get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, msglevel ) < 0 )
1089
+    {
1090
+      msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification",
1091
+	   prefix_str);
1092
+      return;
1093
+    }
1094
+
1095
+  ir->next = o->iroutes_ipv6;
1096
+  o->iroutes_ipv6 = ir;
1097
+}
1079 1098
 #endif /* P2MP_SERVER */
1080 1099
 #endif /* P2MP */
1081 1100
 
... ...
@@ -1113,6 +1198,13 @@ rol_check_alloc (struct options *options)
1113 1113
     options->routes = new_route_option_list (options->max_routes, &options->gc);
1114 1114
 }
1115 1115
 
1116
+void
1117
+rol6_check_alloc (struct options *options)
1118
+{
1119
+  if (!options->routes_ipv6)
1120
+    options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
1121
+}
1122
+
1116 1123
 #ifdef ENABLE_DEBUG
1117 1124
 static void
1118 1125
 show_connection_entry (const struct connection_entry *o)
... ...
@@ -1203,6 +1295,8 @@ show_settings (const struct options *o)
1203 1203
   SHOW_STR (ifconfig_remote_netmask);
1204 1204
   SHOW_BOOL (ifconfig_noexec);
1205 1205
   SHOW_BOOL (ifconfig_nowarn);
1206
+  SHOW_STR (ifconfig_ipv6_local);
1207
+  SHOW_STR (ifconfig_ipv6_remote);
1206 1208
 
1207 1209
 #ifdef HAVE_GETTIMEOFDAY
1208 1210
   SHOW_INT (shaper);
... ...
@@ -1863,8 +1957,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
1863 1863
       if (options->connection_list)
1864 1864
 	msg (M_USAGE, "<connection> cannot be used with --mode server");
1865 1865
 #endif
1866
+#if 0
1866 1867
       if (options->tun_ipv6)
1867 1868
 	msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
1869
+#endif
1868 1870
       if (options->shaper)
1869 1871
 	msg (M_USAGE, "--shaper cannot be used with --mode server");
1870 1872
       if (options->inetd)
... ...
@@ -2461,6 +2557,8 @@ options_string (const struct options *o,
2461 2461
 		     o->topology,
2462 2462
 		     o->ifconfig_local,
2463 2463
 		     o->ifconfig_remote_netmask,
2464
+		     o->ifconfig_ipv6_local,
2465
+		     o->ifconfig_ipv6_remote,
2464 2466
 		     (in_addr_t)0,
2465 2467
 		     (in_addr_t)0,
2466 2468
 		     false,
... ...
@@ -3794,6 +3892,21 @@ add_option (struct options *options,
3794 3794
 	  goto err;
3795 3795
 	}
3796 3796
     }
3797
+  else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] )
3798
+    {
3799
+      VERIFY_PERMISSION (OPT_P_UP);
3800
+      /* TODO: should we accept address + netbits (2001:db8::1/64) here? */
3801
+      if ( ipv6_addr_safe( p[1] ) && ipv6_addr_safe( p[2] ) )
3802
+        {
3803
+	  options->ifconfig_ipv6_local = p[1];
3804
+	  options->ifconfig_ipv6_remote = p[2];
3805
+        }
3806
+      else
3807
+	{
3808
+	  msg (msglevel, "ifconfig-ipv6 parms '%s' and '%s' must be valid addresses", p[1], p[2]);
3809
+	  goto err;
3810
+	}
3811
+    }
3797 3812
   else if (streq (p[0], "ifconfig-noexec"))
3798 3813
     {
3799 3814
       VERIFY_PERMISSION (OPT_P_UP);
... ...
@@ -4594,6 +4707,26 @@ add_option (struct options *options,
4594 4594
 	}
4595 4595
       add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]);
4596 4596
     }
4597
+  else if (streq (p[0], "route-ipv6") && p[1])
4598
+    {
4599
+      VERIFY_PERMISSION (OPT_P_ROUTE);
4600
+      rol6_check_alloc (options);
4601
+      if (pull_mode)
4602
+	{
4603
+	  if (!ipv6_addr_safe_hexplusbits (p[1]))
4604
+	    {
4605
+	      msg (msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
4606
+	      goto err;
4607
+	    }
4608
+	  if (p[2] && !ipv6_addr_safe (p[2]))
4609
+	    {
4610
+	      msg (msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
4611
+	      goto err;
4612
+	    }
4613
+	  /* p[3] is metric, if present */
4614
+	}
4615
+      add_route_ipv6_to_option_list (options->routes_ipv6, p[1], p[2], p[3]);
4616
+    }
4597 4617
   else if (streq (p[0], "max-routes") && p[1])
4598 4618
     {
4599 4619
       int max_routes;
... ...
@@ -4805,6 +4938,33 @@ add_option (struct options *options,
4805 4805
 	    }
4806 4806
 	}
4807 4807
     }
4808
+  else if (streq (p[0], "server-ipv6") && p[1] )
4809
+    {
4810
+      const int lev = M_WARN;
4811
+      struct in6_addr network;
4812
+      unsigned int netbits = 0;
4813
+
4814
+      VERIFY_PERMISSION (OPT_P_GENERAL);
4815
+      if ( ! get_ipv6_addr (p[1], &network, &netbits, lev) )
4816
+	{
4817
+	  msg (msglevel, "error parsing --server-ipv6 parameter");
4818
+	  goto err;
4819
+	}
4820
+      if ( netbits != 64 )
4821
+	{
4822
+	  msg( msglevel, "--server-ipv6 settings: only /64 supported right now (not /%d)", netbits );
4823
+	  goto err;
4824
+	}
4825
+      options->server_ipv6_defined = true;
4826
+      options->server_network_ipv6 = network;
4827
+      options->server_netbits_ipv6 = netbits;
4828
+
4829
+      if (p[2])		/* no "nopool" options or similar for IPv6 */
4830
+	{
4831
+	  msg (msglevel, "error parsing --server: %s is not a recognized flag", p[3]);
4832
+	  goto err;
4833
+	}
4834
+    }
4808 4835
   else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4])
4809 4836
     {
4810 4837
       const int lev = M_WARN;
... ...
@@ -4889,6 +5049,28 @@ add_option (struct options *options,
4889 4889
       VERIFY_PERMISSION (OPT_P_GENERAL);
4890 4890
       options->topology = TOP_P2P;
4891 4891
     }
4892
+  else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] )
4893
+    {
4894
+      const int lev = M_WARN;
4895
+      struct in6_addr network;
4896
+      unsigned int netbits = 0;
4897
+
4898
+      VERIFY_PERMISSION (OPT_P_GENERAL);
4899
+      if ( ! get_ipv6_addr (p[1], &network, &netbits, lev ) )
4900
+	{
4901
+	  msg (msglevel, "error parsing --ifconfig-ipv6-pool parameters");
4902
+	  goto err;
4903
+	}
4904
+      if ( netbits != 64 )
4905
+	{
4906
+	  msg( msglevel, "--ifconfig-ipv6-pool settings: only /64 supported right now (not /%d)", netbits );
4907
+	  goto err;
4908
+	}
4909
+
4910
+      options->ifconfig_ipv6_pool_defined = true;
4911
+      options->ifconfig_ipv6_pool_base = network;
4912
+      options->ifconfig_ipv6_pool_netbits = netbits;
4913
+    }
4892 4914
   else if (streq (p[0], "hash-size") && p[1] && p[2])
4893 4915
     {
4894 4916
       int real, virtual;
... ...
@@ -5084,6 +5266,11 @@ add_option (struct options *options,
5084 5084
 	}
5085 5085
       option_iroute (options, p[1], netmask, msglevel);
5086 5086
     }
5087
+  else if (streq (p[0], "iroute-ipv6") && p[1])
5088
+    {
5089
+      VERIFY_PERMISSION (OPT_P_INSTANCE);
5090
+      option_iroute_ipv6 (options, p[1], msglevel);
5091
+    }
5087 5092
   else if (streq (p[0], "ifconfig-push") && p[1] && p[2])
5088 5093
     {
5089 5094
       in_addr_t local, remote_netmask;
... ...
@@ -205,6 +205,8 @@ struct options
205 205
   int topology; /* one of the TOP_x values from proto.h */
206 206
   const char *ifconfig_local;
207 207
   const char *ifconfig_remote_netmask;
208
+  const char *ifconfig_ipv6_local;
209
+  const char *ifconfig_ipv6_remote;
208 210
   bool ifconfig_noexec;
209 211
   bool ifconfig_nowarn;
210 212
 #ifdef HAVE_GETTIMEOFDAY
... ...
@@ -326,6 +328,7 @@ struct options
326 326
   bool route_delay_defined;
327 327
   int max_routes;
328 328
   struct route_option_list *routes;
329
+  struct route_ipv6_option_list *routes_ipv6;			/* IPv6 */
329 330
   bool route_nopull;
330 331
   bool route_gateway_via_dhcp;
331 332
   bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
... ...
@@ -361,6 +364,9 @@ struct options
361 361
   bool server_defined;
362 362
   in_addr_t server_network;
363 363
   in_addr_t server_netmask;
364
+  bool server_ipv6_defined;				/* IPv6 */
365
+  struct in6_addr server_network_ipv6;			/* IPv6 */
366
+  unsigned int    server_netbits_ipv6;			/* IPv6 */
364 367
 
365 368
 # define SF_NOPOOL (1<<0)
366 369
 # define SF_TCP_NODELAY_HELPER (1<<1)
... ...
@@ -382,6 +388,11 @@ struct options
382 382
   in_addr_t ifconfig_pool_netmask;
383 383
   const char *ifconfig_pool_persist_filename;
384 384
   int ifconfig_pool_persist_refresh_freq;
385
+
386
+  bool   ifconfig_ipv6_pool_defined;			/* IPv6 */
387
+  struct in6_addr ifconfig_ipv6_pool_base;		/* IPv6 */
388
+  int    ifconfig_ipv6_pool_netbits;			/* IPv6 */
389
+
385 390
   int real_hash_size;
386 391
   int virtual_hash_size;
387 392
   const char *client_connect_script;
... ...
@@ -394,6 +405,7 @@ struct options
394 394
   int n_bcast_buf;
395 395
   int tcp_queue_limit;
396 396
   struct iroute *iroutes;
397
+  struct iroute_ipv6 *iroutes_ipv6;			/* IPv6 */
397 398
   bool push_ifconfig_defined;
398 399
   in_addr_t push_ifconfig_local;
399 400
   in_addr_t push_ifconfig_remote_netmask;
... ...
@@ -722,6 +734,9 @@ void options_string_import (struct options *options,
722 722
 			    unsigned int *option_types_found,
723 723
 			    struct env_set *es);
724 724
 
725
+bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
726
+		    unsigned int * netbits, int msglevel );
727
+
725 728
 /*
726 729
  * inline functions
727 730
  */
... ...
@@ -132,7 +132,10 @@ ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_
132 132
 }
133 133
 
134 134
 struct ifconfig_pool *
135
-ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
135
+ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, 
136
+		    const bool duplicate_cn,
137
+		    const bool ipv6_pool, const struct in6_addr ipv6_base, 
138
+		    const int ipv6_netbits )
136 139
 {
137 140
   struct gc_arena gc = gc_new ();
138 141
   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
157 157
       ASSERT (0);
158 158
     }
159 159
 
160
+  /* IPv6 pools are always "INDIV" type */
161
+  pool->ipv6 = ipv6_pool;
162
+
163
+  if ( pool->ipv6 )
164
+    {
165
+      pool->base_ipv6 = ipv6_base;
166
+      pool->size_ipv6 = ipv6_netbits>96? ( 1<<(128-ipv6_netbits) ) 
167
+				       : IFCONFIG_POOL_MAX;
168
+
169
+      msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
170
+			    pool->size, pool->size_ipv6, ipv6_netbits,
171
+			    print_in6_addr( pool->base_ipv6, 0, &gc ));
172
+
173
+      /* the current code is very simple and assumes that the IPv6
174
+       * pool is at least as big as the IPv4 pool, and we don't need
175
+       * to do separate math etc. for IPv6
176
+       */
177
+      ASSERT( pool->size < pool->size_ipv6 );
178
+    }
179
+
160 180
   ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
161 181
 
162
-  msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
182
+  msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
163 183
        print_in_addr_t (pool->base, 0, &gc),
164
-       pool->size);
184
+       pool->size, pool->ipv6 );
165 185
 
166 186
   gc_free (&gc);
167 187
   return pool;
... ...
@@ -181,7 +204,7 @@ ifconfig_pool_free (struct ifconfig_pool *pool)
181 181
 }
182 182
 
183 183
 ifconfig_pool_handle
184
-ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
184
+ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name)
185 185
 {
186 186
   int i;
187 187
 
... ...
@@ -214,6 +237,12 @@ ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *
214 214
 	default:
215 215
 	  ASSERT (0);
216 216
 	}
217
+
218
+      /* IPv6 pools are always INDIV (--linear) */
219
+      if ( pool->ipv6 && remote_ipv6 )
220
+	{
221
+	  *remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
222
+	}
217 223
     }
218 224
   return i;
219 225
 }
... ...
@@ -288,6 +317,19 @@ ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool
288 288
   return ret;
289 289
 }
290 290
 
291
+static struct in6_addr
292
+ifconfig_pool_handle_to_ipv6_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
293
+{
294
+  struct in6_addr ret = in6addr_any;
295
+
296
+  /* IPv6 pools are always INDIV (--linear) */
297
+  if (hand >= 0 && hand < pool->size_ipv6 )
298
+    {
299
+      ret = add_in6_addr( pool->base_ipv6, hand );
300
+    }
301
+  return ret;
302
+}
303
+
291 304
 static void
292 305
 ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
293 306
 {
... ...
@@ -317,9 +359,20 @@ ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out)
317 317
 	  if (e->common_name)
318 318
 	    {
319 319
 	      const in_addr_t ip = ifconfig_pool_handle_to_ip_base (pool, i);
320
-	      status_printf (out, "%s,%s",
321
-			     e->common_name,
322
-			     print_in_addr_t (ip, 0, &gc));
320
+	      if ( pool->ipv6 )
321
+		{
322
+		  struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base (pool, i);
323
+		  status_printf (out, "%s,%s,%s",
324
+				 e->common_name,
325
+				 print_in_addr_t (ip, 0, &gc),
326
+				 print_in6_addr (ip6, 0, &gc));
327
+		}
328
+	      else
329
+		{
330
+		  status_printf (out, "%s,%s",
331
+				 e->common_name,
332
+				 print_in_addr_t (ip, 0, &gc));
333
+		}
323 334
 	    }
324 335
 	}
325 336
       gc_free (&gc);
... ...
@@ -409,6 +462,9 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool
409 409
 	      int c = *BSTR(&in);
410 410
 	      if (c == '#' || c == ';')
411 411
 		continue;
412
+	      msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
413
+				BSTR(&in) );
414
+
412 415
 	      if (buf_parse (&in, ',', cn_buf, buf_size)
413 416
 		  && buf_parse (&in, ',', ip_buf, buf_size))
414 417
 		{
... ...
@@ -416,6 +472,7 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool
416 416
 		  const in_addr_t addr = getaddr (GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
417 417
 		  if (succeeded)
418 418
 		    {
419
+		      msg( M_INFO, "succeeded -> ifconfig_pool_set()");
419 420
 		      ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
420 421
 		    }
421 422
 		}
... ...
@@ -471,7 +528,7 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end)
471 471
 #else
472 472
       cn = buf;
473 473
 #endif
474
-      h = ifconfig_pool_acquire (p, &local, &remote, cn);
474
+      h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
475 475
       if (h < 0)
476 476
 	break;
477 477
       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)
506 506
 #else
507 507
       cn = buf;
508 508
 #endif
509
-      h = ifconfig_pool_acquire (p, &local, &remote, cn);
509
+      h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
510 510
       if (h < 0)
511 511
 	break;
512 512
       msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",
... ...
@@ -52,6 +52,9 @@ struct ifconfig_pool
52 52
   int size;
53 53
   int type;
54 54
   bool duplicate_cn;
55
+  bool ipv6;
56
+  struct in6_addr base_ipv6;
57
+  unsigned int size_ipv6;
55 58
   struct ifconfig_pool_entry *list;
56 59
 };
57 60
 
... ...
@@ -63,13 +66,13 @@ struct ifconfig_pool_persist
63 63
 
64 64
 typedef int ifconfig_pool_handle;
65 65
 
66
-struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn);
66
+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 );
67 67
 
68 68
 void ifconfig_pool_free (struct ifconfig_pool *pool);
69 69
 
70 70
 bool ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end);
71 71
 
72
-ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name);
72
+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);
73 73
 
74 74
 bool ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard);
75 75
 
... ...
@@ -108,6 +108,21 @@ struct openvpn_iphdr {
108 108
 };
109 109
 
110 110
 /*
111
+ * IPv6 header
112
+ */
113
+struct openvpn_ipv6hdr {
114
+        uint8_t		version_prio;
115
+        uint8_t		flow_lbl[3];
116
+        uint16_t	payload_len;
117
+        uint8_t		nexthdr;
118
+        uint8_t		hop_limit;
119
+
120
+        struct  in6_addr        saddr;
121
+        struct  in6_addr        daddr;
122
+};
123
+
124
+
125
+/*
111 126
  * UDP header
112 127
  */
113 128
 struct openvpn_udphdr {
... ...
@@ -191,6 +191,22 @@ send_push_reply (struct context *c)
191 191
 
192 192
   buf_printf (&buf, "%s", cmd);
193 193
 
194
+  if ( c->c2.push_ifconfig_ipv6_defined )
195
+    {
196
+      /* IPv6 is put into buffer first, could be lengthy */
197
+      /* TODO: push "/netbits" as well, to allow non-/64 subnet sizes
198
+       *       (needs changes in options.c, options.h, and other places)
199
+       */
200
+      buf_printf( &buf, ",ifconfig-ipv6 %s %s",
201
+		    print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
202
+		    print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
203
+      if (BLEN (&buf) >= safe_cap)
204
+	{
205
+	  msg (M_WARN, "--push ifconfig-ipv6 option is too long");
206
+	  goto fail;
207
+	}
208
+    }
209
+
194 210
   while (e)
195 211
     {
196 212
       if (e->enable)
... ...
@@ -39,6 +39,7 @@
39 39
 #include "memdbg.h"
40 40
 
41 41
 static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
42
+static void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
42 43
 static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
43 44
 
44 45
 #ifdef ENABLE_DEBUG
... ...
@@ -68,6 +69,15 @@ new_route_option_list (const int max_routes, struct gc_arena *a)
68 68
   return ret;
69 69
 }
70 70
 
71
+struct route_ipv6_option_list *
72
+new_route_ipv6_option_list (const int max_routes, struct gc_arena *a)
73
+{
74
+  struct route_ipv6_option_list *ret;
75
+  ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a);
76
+  ret->capacity = max_routes;
77
+  return ret;
78
+}
79
+
71 80
 struct route_option_list *
72 81
 clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
73 82
 {
... ...
@@ -95,6 +105,15 @@ new_route_list (const int max_routes, struct gc_arena *a)
95 95
   return ret;
96 96
 }
97 97
 
98
+struct route_ipv6_list *
99
+new_route_ipv6_list (const int max_routes, struct gc_arena *a)
100
+{
101
+  struct route_ipv6_list *ret;
102
+  ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a);
103
+  ret->capacity = max_routes;
104
+  return ret;
105
+}
106
+
98 107
 static const char *
99 108
 route_string (const struct route *r, struct gc_arena *gc)
100 109
 {
... ...
@@ -311,6 +330,68 @@ init_route (struct route *r,
311 311
   return false;
312 312
 }
313 313
 
314
+static bool
315
+init_route_ipv6 (struct route_ipv6 *r6,
316
+	         const struct route_ipv6_option *r6o,
317
+	         const struct route_ipv6_list *rl6 )
318
+{
319
+  r6->option = r6o;
320
+  r6->defined = false;
321
+
322
+  if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, M_WARN ))
323
+    goto fail;
324
+
325
+  /* gateway */
326
+  if (is_route_parm_defined (r6o->gateway))
327
+    {
328
+      if ( inet_pton( AF_INET6, r6o->gateway, &r6->gateway ) != 1 )
329
+        {
330
+	  msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway );
331
+        }
332
+    }
333
+  else if (rl6->remote_endpoint_defined)
334
+    {
335
+      r6->gateway = rl6->remote_endpoint_ipv6;
336
+    }
337
+  else
338
+    {
339
+      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");
340
+      goto fail;
341
+    }
342
+
343
+  /* metric */
344
+
345
+  r6->metric_defined = false;
346
+  r6->metric = 0;
347
+  if (is_route_parm_defined (r6o->metric))
348
+    {
349
+      r6->metric = atoi (r6o->metric);
350
+      if (r6->metric < 0)
351
+	{
352
+	  msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
353
+	       r6o->prefix,
354
+	       r6o->metric);
355
+	  goto fail;
356
+	}
357
+      r6->metric_defined = true;
358
+    }
359
+  else if (rl6->default_metric_defined)
360
+    {
361
+      r6->metric = rl6->default_metric;
362
+      r6->metric_defined = true;
363
+    }
364
+
365
+  r6->defined = true;
366
+
367
+  return true;
368
+
369
+ fail:
370
+  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
371
+       r6o->prefix);
372
+  r6->defined = false;
373
+  return false;
374
+}
375
+
314 376
 void
315 377
 add_route_to_option_list (struct route_option_list *l,
316 378
 			  const char *network,
... ...
@@ -331,6 +412,23 @@ add_route_to_option_list (struct route_option_list *l,
331 331
 }
332 332
 
333 333
 void
334
+add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
335
+			  const char *prefix,
336
+			  const char *gateway,
337
+			  const char *metric)
338
+{
339
+  struct route_ipv6_option *ro;
340
+  if (l->n >= l->capacity)
341
+    msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file",
342
+	 l->capacity);
343
+  ro = &l->routes_ipv6[l->n];
344
+  ro->prefix = prefix;
345
+  ro->gateway = gateway;
346
+  ro->metric = metric;
347
+  ++l->n;
348
+}
349
+
350
+void
334 351
 clear_route_list (struct route_list *rl)
335 352
 {
336 353
   const int capacity = rl->capacity;
... ...
@@ -340,6 +438,15 @@ clear_route_list (struct route_list *rl)
340 340
 }
341 341
 
342 342
 void
343
+clear_route_ipv6_list (struct route_ipv6_list *rl6)
344
+{
345
+  const int capacity = rl6->capacity;
346
+  const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
347
+  memset(rl6, 0, rl6_size);
348
+  rl6->capacity = capacity;
349
+}
350
+
351
+void
343 352
 route_list_add_default_gateway (struct route_list *rl,
344 353
 				struct env_set *es,
345 354
 				const in_addr_t addr)
... ...
@@ -469,6 +576,72 @@ init_route_list (struct route_list *rl,
469 469
   return ret;
470 470
 }
471 471
 
472
+bool
473
+init_route_ipv6_list (struct route_ipv6_list *rl6,
474
+		 const struct route_ipv6_option_list *opt6,
475
+		 const char *remote_endpoint,
476
+		 int default_metric,
477
+		 struct env_set *es)
478
+{
479
+  struct gc_arena gc = gc_new ();
480
+  bool ret = true;
481
+
482
+  clear_route_ipv6_list (rl6);
483
+
484
+  rl6->flags = opt6->flags;
485
+
486
+  if (default_metric)
487
+    {
488
+      rl6->default_metric = default_metric;
489
+      rl6->default_metric_defined = true;
490
+    }
491
+
492
+  /* "default_gateway" is stuff for "redirect-gateway", which we don't
493
+   * do for IPv6 yet -> TODO
494
+   */
495
+    {
496
+      dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
497
+    }
498
+
499
+  if ( is_route_parm_defined( remote_endpoint ))
500
+    {
501
+      if ( inet_pton( AF_INET6, remote_endpoint, 
502
+			&rl6->remote_endpoint_ipv6) == 1 )
503
+        {
504
+	  rl6->remote_endpoint_defined = true;
505
+        }
506
+      else
507
+	{
508
+	  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
509
+          ret = false;
510
+	}
511
+    }
512
+  else
513
+    rl6->remote_endpoint_defined = false;
514
+
515
+
516
+  if (!(opt6->n >= 0 && opt6->n <= rl6->capacity))
517
+    msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity);
518
+
519
+  /* parse the routes from opt to rl6 */
520
+  {
521
+    int i, j = 0;
522
+    for (i = 0; i < opt6->n; ++i)
523
+      {
524
+	if (!init_route_ipv6 (&rl6->routes_ipv6[j],
525
+			      &opt6->routes_ipv6[i],
526
+			      rl6 ))
527
+	  ret = false;
528
+	else
529
+	  ++j;
530
+      }
531
+    rl6->n = j;
532
+  }
533
+
534
+  gc_free (&gc);
535
+  return ret;
536
+}
537
+
472 538
 static void
473 539
 add_route3 (in_addr_t network,
474 540
 	    in_addr_t netmask,
... ...
@@ -704,10 +877,13 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
704 704
 }
705 705
 
706 706
 void
707
-add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
707
+add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
708
+	    const struct tuntap *tt, unsigned int flags, const struct env_set *es)
708 709
 {
709
-  redirect_default_route_to_vpn (rl, tt, flags, es);
710
-  if (!rl->routes_added)
710
+  if (rl) 
711
+      redirect_default_route_to_vpn (rl, tt, flags, es);
712
+
713
+  if (rl && !rl->routes_added)
711 714
     {
712 715
       int i;
713 716
 
... ...
@@ -732,12 +908,27 @@ add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags,
732 732
 	}
733 733
       rl->routes_added = true;
734 734
     }
735
+
736
+  if (rl6 && !rl6->routes_added)
737
+    {
738
+      int i;
739
+
740
+      for (i = 0; i < rl6->n; ++i)
741
+	{
742
+	  struct route_ipv6 *r = &rl6->routes_ipv6[i];
743
+	  if (flags & ROUTE_DELETE_FIRST)
744
+	    delete_route_ipv6 (r, tt, flags, es);
745
+	  add_route_ipv6 (r, tt, flags, es);
746
+	}
747
+      rl6->routes_added = true;
748
+    }
735 749
 }
736 750
 
737 751
 void
738
-delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
752
+delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
753
+	       const struct tuntap *tt, unsigned int flags, const struct env_set *es)
739 754
 {
740
-  if (rl->routes_added)
755
+  if (rl && rl->routes_added)
741 756
     {
742 757
       int i;
743 758
       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
747 747
 	}
748 748
       rl->routes_added = false;
749 749
     }
750
-  undo_redirect_default_route_to_vpn (rl, tt, flags, es);
751 750
 
752
-  clear_route_list (rl);
751
+  if ( rl )
752
+    {
753
+      undo_redirect_default_route_to_vpn (rl, tt, flags, es);
754
+      clear_route_list (rl);
755
+    }
756
+
757
+  if ( rl6 && rl6->routes_added )
758
+    {
759
+      int i;
760
+      for (i = rl6->n - 1; i >= 0; --i)
761
+	{
762
+	  const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
763
+	  delete_route_ipv6 (r6, tt, flags, es);
764
+	}
765
+      rl6->routes_added = false;
766
+    }
767
+
768
+  if ( rl6 )
769
+    {
770
+      clear_route_ipv6_list (rl6);
771
+    }
753 772
 }
754 773
 
755 774
 #ifdef ENABLE_DEBUG
... ...
@@ -832,6 +1042,34 @@ setenv_routes (struct env_set *es, const struct route_list *rl)
832 832
     setenv_route (es, &rl->routes[i], i + 1);
833 833
 }
834 834
 
835
+static void
836
+setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
837
+{
838
+  struct gc_arena gc = gc_new ();
839
+  if (r6->defined)
840
+    {
841
+      struct buffer name1 = alloc_buf_gc( 256, &gc );
842
+      struct buffer val = alloc_buf_gc( 256, &gc );
843
+      struct buffer name2 = alloc_buf_gc( 256, &gc );
844
+
845
+      buf_printf( &name1, "route_ipv6_network_%d", i );
846
+      buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
847
+				 r6->netbits );
848
+      setenv_str( es, BSTR(&name1), BSTR(&val) );
849
+
850
+      buf_printf( &name2, "route_ipv6_gateway_%d", i );
851
+      setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
852
+    }
853
+  gc_free (&gc);
854
+}
855
+void
856
+setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
857
+{
858
+  int i;
859
+  for (i = 0; i < rl6->n; ++i)
860
+    setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
861
+}
862
+
835 863
 void
836 864
 add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
837 865
 {
... ...
@@ -1025,6 +1263,136 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
1025 1025
   gc_free (&gc);
1026 1026
 }
1027 1027
 
1028
+void
1029
+add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
1030
+{
1031
+  struct gc_arena gc;
1032
+  struct argv argv;
1033
+
1034
+  const char *network;
1035
+  const char *gateway;
1036
+  bool status = false;
1037
+  const char *device = tt->actual_name;
1038
+  int byte, bits_to_clear;
1039
+  struct in6_addr network_copy = r6->network;
1040
+
1041
+  if (!r6->defined)
1042
+    return;
1043
+
1044
+  gc_init (&gc);
1045
+  argv_init (&argv);
1046
+
1047
+  /* clear host bit parts of route 
1048
+   * (needed if routes are specified improperly, or if we need to 
1049
+   * explicitely setup the "connected" network routes on some OSes)
1050
+   */
1051
+  byte = 15;
1052
+  bits_to_clear = 128 - r6->netbits;
1053
+
1054
+  while( byte >= 0 && bits_to_clear > 0 )
1055
+    {
1056
+      if ( bits_to_clear >= 8 )
1057
+	{ network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
1058
+      else
1059
+	{ network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
1060
+    }
1061
+
1062
+  network = print_in6_addr( network_copy, 0, &gc);
1063
+  gateway = print_in6_addr( r6->gateway, 0, &gc);
1064
+
1065
+  msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s",
1066
+		network, r6->netbits, gateway, r6->metric, device );
1067
+
1068
+  /*
1069
+   * Filter out routes which are essentially no-ops
1070
+   * (not currently done for IPv6)
1071
+   */
1072
+
1073
+#if defined(TARGET_LINUX)
1074
+#ifdef CONFIG_FEATURE_IPROUTE
1075
+  argv_printf (&argv, "%s -6 route add %s/%d dev %s",
1076
+  	      iproute_path,
1077
+	      network,
1078
+	      r6->netbits,
1079
+	      device);
1080
+  if (r6->metric_defined)
1081
+    argv_printf_cat (&argv, " metric %d", r6->metric);
1082
+
1083
+#else
1084
+  argv_printf (&argv, "%s -A inet6 add %s/%d dev %s",
1085
+		ROUTE_PATH,
1086
+	      network,
1087
+	      r6->netbits,
1088
+	      device);
1089
+  if (r6->metric_defined)
1090
+    argv_printf_cat (&argv, " metric %d", r6->metric);
1091
+#endif  /*CONFIG_FEATURE_IPROUTE*/
1092
+  argv_msg (D_ROUTE, &argv);
1093
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
1094
+
1095
+#elif defined (WIN32)
1096
+
1097
+  msg( M_FATAL, "no idea how to set IPv6 routes on windows (unimplemented)" );
1098
+
1099
+#elif defined (TARGET_SOLARIS)
1100
+
1101
+  /* example: route add -inet6 2001:db8::/32 somegateway 0 */
1102
+
1103
+  /* for some weird reason, this does not work for me unless I set
1104
+   * "metric 0" - otherwise, the routes will be nicely installed, but
1105
+   * packets will just disappear somewhere.  So we use "0" now...
1106
+   */
1107
+
1108
+  argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
1109
+		ROUTE_PATH,
1110
+		network,
1111
+		r6->netbits,
1112
+		gateway );
1113
+
1114
+  argv_msg (D_ROUTE, &argv);
1115
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
1116
+
1117
+#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
1118
+
1119
+  argv_printf (&argv, "%s add -inet6 %s/%d -iface %s",
1120
+		ROUTE_PATH,
1121
+	        network,
1122
+	        r6->netbits,
1123
+	        device );
1124
+
1125
+  argv_msg (D_ROUTE, &argv);
1126
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed");
1127
+
1128
+#elif defined(TARGET_DARWIN) 
1129
+
1130
+  argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s",
1131
+		ROUTE_PATH,
1132
+	        network, r6->netbits, device );
1133
+
1134
+  argv_msg (D_ROUTE, &argv);
1135
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed");
1136
+
1137
+#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
1138
+
1139
+  /* GERT-TODO: this needs real-world testing on OpenBSD, but it should work
1140
+   */
1141
+
1142
+  argv_printf (&argv, "%s add -inet6 %s/%d %s",
1143
+		ROUTE_PATH,
1144
+	        network, r6->netbits, gateway );
1145
+
1146
+  argv_msg (D_ROUTE, &argv);
1147
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD/OpenBSD route add -inet6 command failed");
1148
+
1149
+#else
1150
+  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");
1151
+#endif
1152
+
1153
+  r6->defined = status;
1154
+  argv_reset (&argv);
1155
+  gc_free (&gc);
1156
+}
1157
+
1028 1158
 static void
1029 1159
 delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
1030 1160
 {
... ...
@@ -1164,6 +1532,101 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
1164 1164
   gc_free (&gc);
1165 1165
 }
1166 1166
 
1167
+static void
1168
+delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
1169
+{
1170
+  struct gc_arena gc;
1171
+  struct argv argv;
1172
+  const char *network;
1173
+  const char *gateway;
1174
+  const char *device = tt->actual_name;
1175
+
1176
+  if (!r6->defined)
1177
+    return;
1178
+
1179
+  gc_init (&gc);
1180
+  argv_init (&argv);
1181
+
1182
+  network = print_in6_addr( r6->network, 0, &gc);
1183
+  gateway = print_in6_addr( r6->gateway, 0, &gc);
1184
+
1185
+  msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits );
1186
+
1187
+#if defined(TARGET_LINUX)
1188
+#ifdef CONFIG_FEATURE_IPROUTE
1189
+  argv_printf (&argv, "%s -6 route del %s/%d dev %s",
1190
+  	      iproute_path,
1191
+	      network,
1192
+	      r6->netbits,
1193
+	      device);
1194
+#else
1195
+  argv_printf (&argv, "%s -A inet6 del %s/%d dev %s",
1196
+		ROUTE_PATH,
1197
+	      network,
1198
+	      r6->netbits,
1199
+	      device);
1200
+#endif  /*CONFIG_FEATURE_IPROUTE*/
1201
+  argv_msg (D_ROUTE, &argv);
1202
+  openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed");
1203
+
1204
+#elif defined (WIN32)
1205
+
1206
+  msg( M_FATAL, "no idea how to delete IPv6 routes on windows (unimplemented)" );
1207
+
1208
+#elif defined (TARGET_SOLARIS)
1209
+
1210
+  /* example: route delete -inet6 2001:db8::/32 somegateway */
1211
+  /* GERT-TODO: this is untested, but should work */
1212
+
1213
+  argv_printf (&argv, "%s delete -inet6 %s/%d %s",
1214
+		ROUTE_PATH,
1215
+		network,
1216
+		r6->netbits,
1217
+		gateway );
1218
+
1219
+  argv_msg (D_ROUTE, &argv);
1220
+  openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete -inet6 command failed");
1221
+
1222
+#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
1223
+
1224
+  argv_printf (&argv, "%s delete -inet6 %s/%d -iface %s",
1225
+		ROUTE_PATH,
1226
+	        network,
1227
+	        r6->netbits,
1228
+	        device );
1229
+
1230
+  argv_msg (D_ROUTE, &argv);
1231
+  openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
1232
+
1233
+#elif defined(TARGET_DARWIN) 
1234
+
1235
+  argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d -iface %s",
1236
+		ROUTE_PATH, 
1237
+		network, r6->netbits, device );
1238
+
1239
+  argv_msg (D_ROUTE, &argv);
1240
+  openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
1241
+
1242
+#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
1243
+
1244
+  /* GERT-TODO: this needs real-world testing on OpenBSD, but it should work
1245
+   */
1246
+
1247
+  argv_printf (&argv, "%s delete -inet6 %s/%d %s",
1248
+		ROUTE_PATH,
1249
+	        network, r6->netbits, gateway );
1250
+
1251
+  argv_msg (D_ROUTE, &argv);
1252
+  openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD/OpenBSD route delete -inet6 command failed");
1253
+
1254
+#else
1255
+  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");
1256
+#endif
1257
+
1258
+  argv_reset (&argv);
1259
+  gc_free (&gc);
1260
+}
1261
+
1167 1262
 /*
1168 1263
  * The --redirect-gateway option requires OS-specific code below
1169 1264
  * to get the current default gateway.
... ...
@@ -92,6 +92,19 @@ struct route_option_list {
92 92
   struct route_option routes[EMPTY_ARRAY_SIZE];
93 93
 };
94 94
 
95
+struct route_ipv6_option {
96
+  const char *prefix;		/* e.g. "2001:db8:1::/64" */
97
+  const char *gateway;		/* e.g. "2001:db8:0::2" */
98
+  const char *metric;		/* e.g. "5" */
99
+};
100
+
101
+struct route_ipv6_option_list {
102
+  unsigned int flags;
103
+  int capacity;
104
+  int n;
105
+  struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE];
106
+};
107
+
95 108
 struct route {
96 109
   bool defined;
97 110
   const struct route_option *option;
... ...
@@ -113,6 +126,31 @@ struct route_list {
113 113
   struct route routes[EMPTY_ARRAY_SIZE];
114 114
 };
115 115
 
116
+struct route_ipv6 {
117
+  bool defined;
118
+  const struct route_ipv6_option *option;
119
+  struct in6_addr network;
120
+  int netbits;
121
+  struct in6_addr gateway;
122
+  bool metric_defined;
123
+  int metric;
124
+};
125
+
126
+struct route_ipv6_list {
127
+  bool routes_added;
128
+  unsigned int flags;
129
+  int default_metric;
130
+  bool default_metric_defined;
131
+  struct in6_addr remote_endpoint_ipv6;
132
+  bool remote_endpoint_defined;
133
+  bool did_redirect_default_gateway;			/* TODO (?) */
134
+  bool did_local;					/* TODO (?) */
135
+  int capacity;
136
+  int n;
137
+  struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE];
138
+};
139
+
140
+
116 141
 #if P2MP
117 142
 /* internal OpenVPN route */
118 143
 struct iroute {
... ...
@@ -120,15 +158,24 @@ struct iroute {
120 120
   int netbits;
121 121
   struct iroute *next;
122 122
 };
123
+
124
+struct iroute_ipv6 {
125
+  struct in6_addr network;
126
+  unsigned int netbits;
127
+  struct iroute_ipv6 *next;
128
+};
123 129
 #endif
124 130
 
125 131
 struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
132
+struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
126 133
 struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
127 134
 void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
128 135
 
129 136
 struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
137
+struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);
130 138
 
131 139
 void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
140
+void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
132 141
 
133 142
 void add_route_to_option_list (struct route_option_list *l,
134 143
 			       const char *network,
... ...
@@ -136,6 +183,11 @@ void add_route_to_option_list (struct route_option_list *l,
136 136
 			       const char *gateway,
137 137
 			       const char *metric);
138 138
 
139
+void add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
140
+			       const char *prefix,
141
+			       const char *gateway,
142
+			       const char *metric);
143
+
139 144
 bool init_route_list (struct route_list *rl,
140 145
 		      const struct route_option_list *opt,
141 146
 		      const char *remote_endpoint,
... ...
@@ -143,21 +195,30 @@ bool init_route_list (struct route_list *rl,
143 143
 		      in_addr_t remote_host,
144 144
 		      struct env_set *es);
145 145
 
146
+bool init_route_ipv6_list (struct route_ipv6_list *rl6,
147
+		      const struct route_ipv6_option_list *opt6,
148
+		      const char *remote_endpoint,
149
+		      int default_metric,
150
+		      struct env_set *es);
151
+
146 152
 void route_list_add_default_gateway (struct route_list *rl,
147 153
 				     struct env_set *es,
148 154
 				     const in_addr_t addr);
149 155
 
150 156
 void add_routes (struct route_list *rl,
157
+		 struct route_ipv6_list *rl6,
151 158
 		 const struct tuntap *tt,
152 159
 		 unsigned int flags,
153 160
 		 const struct env_set *es);
154 161
 
155 162
 void delete_routes (struct route_list *rl,
163
+		    struct route_ipv6_list *rl6,
156 164
 		    const struct tuntap *tt,
157 165
 		    unsigned int flags,
158 166
 		    const struct env_set *es);
159 167
 
160 168
 void setenv_routes (struct env_set *es, const struct route_list *rl);
169
+void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6);
161 170
 
162 171
 bool is_special_addr (const char *addr_str);
163 172
 
... ...
@@ -342,6 +342,24 @@ ip_addr_dotted_quad_safe (const char *dotted_quad)
342 342
   }
343 343
 }
344 344
 
345
+bool
346
+ipv6_addr_safe (const char *ipv6_text_addr)
347
+{
348
+  /* verify non-NULL */
349
+  if (!ipv6_text_addr)
350
+    return false;
351
+
352
+  /* verify length is within limits */
353
+  if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN )
354
+    return false;
355
+
356
+  /* verify that string will convert to IPv6 address */
357
+  {
358
+    struct in6_addr a6;
359
+    return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
360
+  }
361
+}
362
+
345 363
 static bool
346 364
 dns_addr_safe (const char *addr)
347 365
 {
... ...
@@ -2032,6 +2050,58 @@ print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc)
2032 2032
   return BSTR (&out);
2033 2033
 }
2034 2034
 
2035
+/*
2036
+ * Convert an in6_addr in host byte order
2037
+ * to an ascii representation of an IPv6 address
2038
+ * (we reuse the L_INET_NTOA mutex, no contention here)
2039
+ */
2040
+const char *
2041
+print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
2042
+{
2043
+  struct buffer out = alloc_buf_gc (64, gc);
2044
+  char tmp_out_buf[64];		/* inet_ntop wants pointer to buffer */
2045
+
2046
+  if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 || 
2047
+       !(flags & IA_EMPTY_IF_UNDEF))
2048
+    {
2049
+      mutex_lock_static (L_INET_NTOA);
2050
+      inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
2051
+      buf_printf (&out, "%s", tmp_out_buf );
2052
+      mutex_unlock_static (L_INET_NTOA);
2053
+    }
2054
+  return BSTR (&out);
2055
+}
2056
+
2057
+/* add some offset to an ipv6 address
2058
+ * (add in steps of 32 bits, taking overflow into next round)
2059
+ */
2060
+#ifndef s6_addr32
2061
+# ifdef TARGET_SOLARIS
2062
+#  define s6_addr32 _S6_un._S6_u32
2063
+# else
2064
+#  define s6_addr32 __u6_addr.__u6_addr32
2065
+# endif
2066
+#endif
2067
+#ifndef UINT32_MAX
2068
+# define UINT32_MAX (4294967295U)
2069
+#endif
2070
+struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add )
2071
+{
2072
+    int i;
2073
+    uint32_t h;
2074
+
2075
+    for( i=3; i>=0 && add > 0 ; i-- )
2076
+    {
2077
+	h = ntohl( base.s6_addr32[i] );
2078
+	base.s6_addr32[i] = htonl( (h+add) & UINT32_MAX );
2079
+	/* 32-bit overrun? 
2080
+	 * caveat: can't do "h+add > UINT32_MAX" with 32bit math!
2081
+         */
2082
+	add = ( h > UINT32_MAX - add )?  1: 0;
2083
+    }
2084
+    return base;
2085
+}
2086
+
2035 2087
 /* set environmental variables for ip/port in *addr */
2036 2088
 void
2037 2089
 setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
... ...
@@ -351,6 +351,8 @@ const char *print_link_socket_actual (const struct link_socket_actual *act,
351 351
 #define IA_EMPTY_IF_UNDEF (1<<0)
352 352
 #define IA_NET_ORDER      (1<<1)
353 353
 const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
354
+const char *print_in6_addr  (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc);
355
+struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add );
354 356
 
355 357
 #define SA_IP_PORT        (1<<0)
356 358
 #define SA_SET_IF_NONZERO (1<<1)
... ...
@@ -404,6 +406,7 @@ int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr);
404 404
 bool ip_addr_dotted_quad_safe (const char *dotted_quad);
405 405
 bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn);
406 406
 bool mac_addr_safe (const char *mac_addr);
407
+bool ipv6_addr_safe (const char *ipv6_text_addr);
407 408
 
408 409
 socket_descriptor_t create_socket_tcp (void);
409 410
 
... ...
@@ -62,7 +62,7 @@ static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
62 62
 #endif
63 63
 
64 64
 #ifdef TARGET_SOLARIS
65
-static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
65
+static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6);
66 66
 #include <stropts.h>
67 67
 #endif
68 68
 
... ...
@@ -143,14 +143,15 @@ guess_tuntap_dev (const char *dev,
143 143
  * If ipv6_explicitly_supported is true, then we have explicit
144 144
  * OS-specific tun dev code for handling IPv6.  If so, tt->ipv6
145 145
  * is set according to the --tun-ipv6 command line option.
146
+ *
147
+ * (enabling IPv6 on tun devices might work anyway, but since 
148
+ * we don't know, we log a warning)
146 149
  */
147 150
 static void
148 151
 ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
149 152
 {
150
-  tt->ipv6 = false;
151
-  if (ipv6_explicitly_supported)
152
-    tt->ipv6 = ipv6;
153
-  else if (ipv6)
153
+  tt->ipv6 = ipv6;
154
+  if (ipv6 && !ipv6_explicitly_supported)
154 155
     msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
155 156
 }
156 157
 
... ...
@@ -423,6 +424,8 @@ init_tun (const char *dev,       /* --dev option */
423 423
 	  int topology,          /* one of the TOP_x values */
424 424
 	  const char *ifconfig_local_parm,          /* --ifconfig parm 1 */
425 425
 	  const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
426
+	  const char *ifconfig_ipv6_local_parm,     /* --ifconfig parm 1 IPv6 */
427
+	  const char *ifconfig_ipv6_remote_parm,    /* --ifconfig parm 2 IPv6 */
426 428
 	  in_addr_t local_public,
427 429
 	  in_addr_t remote_public,
428 430
 	  const bool strict_warn,
... ...
@@ -430,6 +433,7 @@ init_tun (const char *dev,       /* --dev option */
430 430
 {
431 431
   struct gc_arena gc = gc_new ();
432 432
   struct tuntap *tt;
433
+  bool tun;
433 434
 
434 435
   ALLOC_OBJ (tt, struct tuntap);
435 436
   clear_tuntap (tt);
... ...
@@ -437,19 +441,18 @@ init_tun (const char *dev,       /* --dev option */
437 437
   tt->type = dev_type_enum (dev, dev_type);
438 438
   tt->topology = topology;
439 439
 
440
+  /*
441
+   * We only handle TUN/TAP devices here, not --dev null devices.
442
+   */
443
+  tun = is_tun_p2p (tt);
444
+
440 445
   if (ifconfig_local_parm && ifconfig_remote_netmask_parm)
441 446
     {
442
-      bool tun = false;
443 447
       const char *ifconfig_local = NULL;
444 448
       const char *ifconfig_remote_netmask = NULL;
445 449
       const char *ifconfig_broadcast = NULL;
446 450
 
447 451
       /*
448
-       * We only handle TUN/TAP devices here, not --dev null devices.
449
-       */
450
-      tun = is_tun_p2p (tt);
451
-
452
-      /*
453 452
        * Convert arguments to binary IPv4 addresses.
454 453
        */
455 454
 
... ...
@@ -537,6 +540,40 @@ init_tun (const char *dev,       /* --dev option */
537 537
 
538 538
       tt->did_ifconfig_setup = true;
539 539
     }
540
+
541
+  if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm)
542
+    {
543
+      const char *ifconfig_ipv6_local = NULL;
544
+      const char *ifconfig_ipv6_remote = NULL;
545
+
546
+      /*
547
+       * Convert arguments to binary IPv6 addresses.
548
+       */
549
+
550
+      if ( inet_pton( AF_INET6, ifconfig_ipv6_local_parm, &tt->local_ipv6 ) != 1 ||
551
+           inet_pton( AF_INET6, ifconfig_ipv6_remote_parm, &tt->remote_ipv6 ) != 1 ) 
552
+	{
553
+	  msg( M_FATAL, "init_tun: problem converting IPv6 ifconfig addresses %s and %s to binary", ifconfig_ipv6_local_parm, ifconfig_ipv6_remote_parm );
554
+	}
555
+      tt->netbits_ipv6 = 64;
556
+
557
+      /*
558
+       * Set ifconfig parameters
559
+       */
560
+      ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
561
+      ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
562
+
563
+      /*
564
+       * Set environmental variables with ifconfig parameters.
565
+       */
566
+      if (es)
567
+	{
568
+	  setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local);
569
+	  setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote);
570
+	}
571
+      tt->did_ifconfig_ipv6_setup = true;
572
+    }
573
+
540 574
   gc_free (&gc);
541 575
   return tt;
542 576
 }
... ...
@@ -574,10 +611,15 @@ do_ifconfig (struct tuntap *tt,
574 574
       const char *ifconfig_local = NULL;
575 575
       const char *ifconfig_remote_netmask = NULL;
576 576
       const char *ifconfig_broadcast = NULL;
577
+      const char *ifconfig_ipv6_local = NULL;
578
+      const char *ifconfig_ipv6_remote = NULL;
579
+      bool do_ipv6 = false;
577 580
       struct argv argv;
578 581
 
579 582
       argv_init (&argv);
580 583
 
584
+      msg( M_INFO, "do_ifconfig, ipv6=%d", tt->ipv6 );
585
+
581 586
       /*
582 587
        * We only handle TUN/TAP devices here, not --dev null devices.
583 588
        */
... ...
@@ -589,6 +631,13 @@ do_ifconfig (struct tuntap *tt,
589 589
       ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
590 590
       ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
591 591
 
592
+      if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
593
+        {
594
+	  ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
595
+	  ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
596
+	  do_ipv6 = true;
597
+	}
598
+
592 599
       /*
593 600
        * If TAP-style device, generate broadcast address.
594 601
        */
... ...
@@ -635,6 +684,18 @@ do_ifconfig (struct tuntap *tt,
635 635
 				  );
636 636
 		  argv_msg (M_INFO, &argv);
637 637
 		  openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
638
+		  if ( do_ipv6 )		/* GERT-TODO: yet UNTESTED! */
639
+		    {
640
+		      argv_printf( &argv,
641
+				  "%s -6 addr add %s/%d dev %s",
642
+				  iproute_path,
643
+				  ifconfig_ipv6_local,
644
+				  tt->netbits_ipv6,
645
+				  actual
646
+				  );
647
+		      argv_msg (M_INFO, &argv);
648
+		      openvpn_execve_check (&argv, es, S_FATAL, "Linux ip -6 addr add failed");
649
+		    }
638 650
 	} else {
639 651
 		argv_printf (&argv,
640 652
 				  "%s addr add dev %s %s/%d broadcast %s",
... ...
@@ -670,6 +731,18 @@ do_ifconfig (struct tuntap *tt,
670 670
 			  );
671 671
       argv_msg (M_INFO, &argv);
672 672
       openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
673
+      if ( do_ipv6 )
674
+	{
675
+	  argv_printf (&argv,
676
+			  "%s %s inet6 add %s/%d",
677
+			  IFCONFIG_PATH,
678
+			  actual,
679
+			  ifconfig_ipv6_local,
680
+			  tt->netbits_ipv6
681
+			  );
682
+	  argv_msg (M_INFO, &argv);
683
+	  openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig inet6 failed");
684
+	}
673 685
       tt->did_ifconfig = true;
674 686
 
675 687
 #endif /*CONFIG_FEATURE_IPROUTE*/
... ...
@@ -693,7 +766,7 @@ do_ifconfig (struct tuntap *tt,
693 693
 
694 694
 	  argv_msg (M_INFO, &argv);
695 695
 	  if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
696
-	    solaris_error_close (tt, es, actual);
696
+	    solaris_error_close (tt, es, actual, false);
697 697
 
698 698
 	  argv_printf (&argv,
699 699
 			    "%s %s netmask 255.255.255.255",
... ...
@@ -725,7 +798,27 @@ do_ifconfig (struct tuntap *tt,
725 725
 
726 726
       argv_msg (M_INFO, &argv);
727 727
       if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
728
-	solaris_error_close (tt, es, actual);
728
+	solaris_error_close (tt, es, actual, false);
729
+
730
+      if ( do_ipv6 )			/* GERT-TODO: UNTESTED */
731
+        {
732
+ 	  argv_printf (&argv, "%s %s inet6 unplumb",
733
+			    IFCONFIG_PATH, actual );
734
+	  argv_msg (M_INFO, &argv);
735
+	  openvpn_execve_check (&argv, es, 0, NULL);
736
+
737
+	  argv_printf (&argv,
738
+			    "%s %s inet6 plumb %s/%d %s up",
739
+			    IFCONFIG_PATH,
740
+			    actual,
741
+			    ifconfig_ipv6_local,
742
+			    tt->netbits_ipv6,
743
+			    ifconfig_ipv6_remote
744
+			    );
745
+	  argv_msg (M_INFO, &argv);
746
+	  if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 failed"))
747
+	    solaris_error_close (tt, es, actual, true);
748
+        }
729 749
 
730 750
       if (!tun && tt->topology == TOP_SUBNET)
731 751
 	{
... ...
@@ -787,10 +880,19 @@ do_ifconfig (struct tuntap *tt,
787 787
 			  );
788 788
       argv_msg (M_INFO, &argv);
789 789
       openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
790
+      if ( do_ipv6 )
791
+	{
792
+	  msg( M_FATAL, "can't configure IPv6 on OpenBSD yet - unimplemented" );
793
+	}
790 794
       tt->did_ifconfig = true;
791 795
 
792 796
 #elif defined(TARGET_NETBSD)
793 797
 
798
+      /* as on OpenBSD and Darwin, destroy and re-create tun0 interface
799
+       */
800
+      argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual );
801
+      argv_msg (M_INFO, &argv);
802
+
794 803
       if (tun)
795 804
 	argv_printf (&argv,
796 805
 			  "%s %s %s %s mtu %d netmask 255.255.255.255 up",
... ...
@@ -817,6 +919,27 @@ do_ifconfig (struct tuntap *tt,
817 817
 			  );
818 818
       argv_msg (M_INFO, &argv);
819 819
       openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
820
+
821
+      if ( do_ipv6 )
822
+	{
823
+	  struct route_ipv6 r6;
824
+	  argv_printf (&argv,
825
+			  "%s %s inet6 %s/%d",
826
+			  IFCONFIG_PATH,
827
+			  actual,
828
+			  ifconfig_ipv6_local,
829
+			  tt->netbits_ipv6
830
+			  );
831
+	  argv_msg (M_INFO, &argv);
832
+	  openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
833
+
834
+	  /* and, hooray, we explicitely need to add a route... */
835
+	  r6.defined = true;
836
+	  r6.network = tt->local_ipv6;
837
+	  r6.netbits = tt->netbits_ipv6;
838
+	  r6.gateway = tt->local_ipv6;
839
+	  add_route_ipv6 (&r6, tt, 0, es);
840
+	}
820 841
       tt->did_ifconfig = true;
821 842
 
822 843
 #elif defined(TARGET_DARWIN)
... ...
@@ -882,6 +1005,27 @@ do_ifconfig (struct tuntap *tt,
882 882
 	  add_route (&r, tt, 0, es);
883 883
 	}
884 884
 
885
+      if ( do_ipv6 )
886
+	{
887
+	  struct route_ipv6 r6;
888
+          argv_printf (&argv,
889
+                              "%s %s inet6 %s/%d",
890
+                              IFCONFIG_PATH,
891
+                              actual,
892
+                              ifconfig_ipv6_local,
893
+                              tt->netbits_ipv6
894
+                              );
895
+	  argv_msg (M_INFO, &argv);
896
+	  openvpn_execve_check (&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed");
897
+
898
+	  /* and, hooray, we explicitely need to add a route... */
899
+	  r6.defined = true;
900
+	  r6.network = tt->local_ipv6;
901
+	  r6.netbits = tt->netbits_ipv6;
902
+	  r6.gateway = tt->local_ipv6;
903
+	  add_route_ipv6 (&r6, tt, 0, es);
904
+	}
905
+
885 906
 #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
886 907
 
887 908
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
... ...
@@ -920,6 +1064,19 @@ do_ifconfig (struct tuntap *tt,
920 920
           add_route (&r, tt, 0, es);
921 921
         }
922 922
 
923
+      if ( do_ipv6 )
924
+	{
925
+          argv_printf (&argv,
926
+                              "%s %s inet6 %s/%d",
927
+                              IFCONFIG_PATH,
928
+                              actual,
929
+                              ifconfig_ipv6_local,
930
+                              tt->netbits_ipv6
931
+                              );
932
+	  argv_msg (M_INFO, &argv);
933
+	  openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed");
934
+	}
935
+
923 936
 #elif defined (WIN32)
924 937
       {
925 938
 	/*
... ...
@@ -959,6 +1116,10 @@ do_ifconfig (struct tuntap *tt,
959 959
 	tt->did_ifconfig = true;
960 960
       }
961 961
 
962
+      if ( do_ipv6 )
963
+	{
964
+	  msg( M_FATAL, "can't configure IPv6 on Win32 yet - unimplemented" );
965
+	}
962 966
 #else
963 967
       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.");
964 968
 #endif
... ...
@@ -1415,6 +1576,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
1415 1415
   bool is_tun;
1416 1416
   struct strioctl  strioc_if, strioc_ppa;
1417 1417
 
1418
+  /* improved generic TUN/TAP driver from
1419
+   * http://www.whiteboard.ne.jp/~admin2/tuntap/
1420
+   * has IPv6 support
1421
+   */
1418 1422
   ipv6_support (ipv6, true, tt);
1419 1423
   memset(&ifr, 0x0, sizeof(ifr));
1420 1424
 
... ...
@@ -1622,11 +1787,20 @@ close_tun (struct tuntap *tt)
1622 1622
 }
1623 1623
 
1624 1624
 static void
1625
-solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
1625
+solaris_error_close (struct tuntap *tt, const struct env_set *es, 
1626
+                     const char *actual, bool unplumb_inet6 )
1626 1627
 {
1627 1628
   struct argv argv;
1628 1629
   argv_init (&argv);
1629 1630
 
1631
+  if (unplumb_inet6)
1632
+    {
1633
+      argv_printf( &argv, "%s %s inet6 unplumb",
1634
+		   IFCONFIG_PATH, actual );
1635
+      argv_msg (M_INFO, &argv);
1636
+      openvpn_execve_check (&argv, es, 0, "Solaris ifconfig inet6 unplumb failed");
1637
+    }
1638
+
1630 1639
   argv_printf (&argv,
1631 1640
 		    "%s %s unplumb",
1632 1641
 		    IFCONFIG_PATH,
... ...
@@ -1774,17 +1948,34 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
1774 1774
 #elif defined(TARGET_NETBSD)
1775 1775
 
1776 1776
 /*
1777
- * NetBSD does not support IPv6 on tun out of the box,
1778
- * but there exists a patch. When this patch is applied,
1779
- * only two things are left to openvpn:
1777
+ * NetBSD before 4.0 does not support IPv6 on tun out of the box,
1778
+ * but there exists a patch (sys/net/if_tun.c, 1.79->1.80, see PR 32944).
1779
+ *
1780
+ * When this patch is applied, only two things are left to openvpn:
1780 1781
  * 1. Activate multicasting (this has already been done
1781 1782
  *    before by the kernel, but we make sure that nobody
1782 1783
  *    has deactivated multicasting inbetween.
1783 1784
  * 2. Deactivate "link layer mode" (otherwise NetBSD 
1784 1785
  *    prepends the address family to the packet, and we
1785 1786
  *    would run into the same trouble as with OpenBSD.
1787
+ *
1788
+ * ... unfortunately, it doesn't work that way.  If TUN_IFHEAD is disabled
1789
+ * ("no prepending of the AF"), then the kernel code just drops IPv6 packets
1790
+ * on output, and gets confused on input.
1791
+ *
1792
+ * So we have to do it the same way as FreeBSD and OpenBSD do it 
1793
+ * (and we really should merge FreeBSD, NetBSD and OpenBSD together)
1786 1794
  */
1787 1795
 
1796
+static inline int
1797
+netbsd_modify_read_write_return (int len)
1798
+{
1799
+  if (len > 0)
1800
+    return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
1801
+  else
1802
+    return len;
1803
+}
1804
+
1788 1805
 void
1789 1806
 open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
1790 1807
 {
... ...
@@ -1795,12 +1986,20 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
1795 1795
         ioctl (tt->fd, TUNSIFMODE, &i);  /* multicast on */
1796 1796
         i = 0;
1797 1797
         ioctl (tt->fd, TUNSLMODE, &i);   /* link layer mode off */
1798
+        i = 1;
1799
+        if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0) 	/* multi-af mode on */
1800
+	  {
1801
+	    msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
1802
+	  }
1798 1803
       }
1799 1804
 }
1800 1805
 
1801 1806
 void
1802 1807
 close_tun (struct tuntap *tt)
1803 1808
 {
1809
+  /* TODO: we really should cleanup non-persistant tunX with 
1810
+   * "ifconfig tunX destroy" here...
1811
+   */
1804 1812
   if (tt)
1805 1813
     {
1806 1814
       close_tun_generic (tt);
... ...
@@ -1811,12 +2010,46 @@ close_tun (struct tuntap *tt)
1811 1811
 int
1812 1812
 write_tun (struct tuntap* tt, uint8_t *buf, int len)
1813 1813
 {
1814
+  if (tt->type == DEV_TYPE_TUN)
1815
+    {
1816
+      u_int32_t type;
1817
+      struct iovec iv[2];
1818
+      struct openvpn_iphdr *iph;
1819
+
1820
+      iph = (struct openvpn_iphdr *) buf;
1821
+
1822
+      if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6)
1823
+        type = htonl (AF_INET6);
1824
+      else 
1825
+        type = htonl (AF_INET);
1826
+
1827
+      iv[0].iov_base = (char *)&type;
1828
+      iv[0].iov_len = sizeof (type);
1829
+      iv[1].iov_base = buf;
1830
+      iv[1].iov_len = len;
1831
+
1832
+      return netbsd_modify_read_write_return (writev (tt->fd, iv, 2));
1833
+    }
1834
+  else
1814 1835
     return write (tt->fd, buf, len);
1815 1836
 }
1816 1837
 
1817 1838
 int
1818 1839
 read_tun (struct tuntap* tt, uint8_t *buf, int len)
1819 1840
 {
1841
+  if (tt->type == DEV_TYPE_TUN)
1842
+    {
1843
+      u_int32_t type;
1844
+      struct iovec iv[2];
1845
+
1846
+      iv[0].iov_base = (char *)&type;
1847
+      iv[0].iov_len = sizeof (type);
1848
+      iv[1].iov_base = buf;
1849
+      iv[1].iov_len = len;
1850
+
1851
+      return netbsd_modify_read_write_return (readv (tt->fd, iv, 2));
1852
+    }
1853
+  else
1820 1854
     return read (tt->fd, buf, len);
1821 1855
 }
1822 1856
 
... ...
@@ -130,6 +130,7 @@ struct tuntap
130 130
   int topology; /* one of the TOP_x values */
131 131
 
132 132
   bool did_ifconfig_setup;
133
+  bool did_ifconfig_ipv6_setup;
133 134
   bool did_ifconfig;
134 135
 
135 136
   bool ipv6;
... ...
@@ -146,6 +147,10 @@ struct tuntap
146 146
   in_addr_t remote_netmask;
147 147
   in_addr_t broadcast;
148 148
 
149
+  struct in6_addr local_ipv6;
150
+  struct in6_addr remote_ipv6;
151
+  int netbits_ipv6;
152
+
149 153
 #ifdef WIN32
150 154
   HANDLE hand;
151 155
   struct overlapped_io reads;
... ...
@@ -219,6 +224,8 @@ struct tuntap *init_tun (const char *dev,       /* --dev option */
219 219
 			 int topology,          /* one of the TOP_x values */
220 220
 			 const char *ifconfig_local_parm,          /* --ifconfig parm 1 */
221 221
 			 const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
222
+			 const char *ifconfig_ipv6_local_parm,     /* --ifconfig parm 1 / IPv6 */
223
+			 const char *ifconfig_ipv6_remote_parm,    /* --ifconfig parm 2 / IPv6 */
222 224
 			 in_addr_t local_public,
223 225
 			 in_addr_t remote_public,
224 226
 			 const bool strict_warn,