Enable IPv6 Payload in OpenVPN p2mp tun server mode. 20100104-1 release.

(cherry picked from commit ec9dce6387afd198881493bfebf13bb121e8a56b)
This commit is contained in:
Gert Doering 2010-01-07 14:51:40 +01:00
parent 285252d1a1
commit 512cda46b0
25 changed files with 1915 additions and 81 deletions

189
ChangeLog.IPv6 Normal file
View file

@ -0,0 +1,189 @@
Do 31. Dez 15:32:40 CET 2009 Gert Doering
* Basic IPv6 p2mp functionality implemented
* new options:
- server-ipv6
- ifconfig-ipv6
- ifconfig-ipv6-pool
- route-ipv6
- iroute-ipv6
* modules touched:
- init.c: init & setup IPv6 route list & add/delete IPv6 routes
- tun.c: add "ifconfig" and "route" handling for IPv6
- multi.c: IPv6 ifconfig-pool assignments
put to route-hash table
push to client
- pool.c: extend pools to handle IPv4+IPv6, and also return IPv6 address
IPv6 address saved to file if ifconfig-pool-persist is set
(but ignored on read due to the way pools work)
- mroute.c: handle reading src/dst addresses from IPv6 packets
(so multi.c can check against route-hash table)
handle printing of IPv6 mroute_addr structure
- helper.c: implement "server-ipv6" macro (->ifconfig-ipv6, pool, ...)
- options.c: implement all the new options
add helper functions for IPv6 address handling
- forward.c: tell do_route() about IPv6 routes
- route.c: handle IPv6 route lists + route option lists
extend add_routes() to do IPv4 + IPv6 route lists
extend delete_routes() to do IPv4 + IPv6 route lists
implement add_route_ipv6(), delete_route_ipv6() to call
system-dependend external program to do the work
- push.c: handle pushing of "ifconfig-ipv6" option
- socket.c: helper function to check & print IPv6 address strings
* known issues:
- operating system support on all but Linux (ifconfig, route)
- route-ipv6 gateway handling
- iroute-ipv6 not implemented
- TAP support: ifconfig, routing (route needs gateway!)
* release as patch 20091231-1
Thu Dec 31 17:02:08 CET 2009
* NetBSD port (NetBSD 3.1 on Sparc64)
* mroute.c, socket.c: make byte/word access to in6_addr more portable
* tun.c: fix IPv6 ifconfig arguments on NetBSD
still doesn't work on NetBSD 3.1, "ifconfig tun0 inet6..." errors with
ifconfig: SIOCAIFADDR: Address family not supported by protocol family
(sys/net/if_tun.c, needs to be revision 1.80 or later, NetBSD PR 32944,
included in NetBSD 4.0 and up)
Fri Jan 1 14:07:15 CET 2010
* FreeBSD port (FreeBSD 6.3-p12 on i386)
* tun.c: implement IPv6 ifconfig setting for FreeBSD
* route.c: fix %s/%s argument to IPv6 route add/delete command for *BSD
* TEST SUCCESS: FreeBSD 6.3-p12, server-ipv6, route-ipv6, ccd/iroute-ipv6
* multi.c: implement setting and deleting of iroute-ipv6
(multi_add_iroutes(), multi_del_iroutes())
* mroute.c: add mroute_helper_add_iroute6(), mroute_helper_del_iroute6()
* mroute.h: add prototypes, increase MR_HELPER_NET_LEN to 129 (/0.../128)
* multi.c: zeroize host part of IPv6 iroutes in multi_learn_in6_addr()
* mroute.c: implement mroute_addr_mask_host_bits() for IPv6
* TEST SUCCESS: Linux 2.6.30 (Gentoo)/iproute2, server-ipv6, ccd/iroute-ipv6
* TEST SUCCESS: Linux 2.6.30 (Gentoo)/ifconfig, client-ipv6
* TEST FAIL: NetBSD 5.0, IPv6 client
- "ifconfig tun0 .../64" does not create a "connected" route
- adding routes fails
--> more work to do here.
* release as patch 20100101-1
* TEST FAIL:
FreeBSD 6.3-p12 server "--topology subnet"
Linux/ifconfig client
- BSD sends ICMP6 neighbor solicitations, which are ignored by Linux
- server tun interface is not in p2p mode, client tun interface *is*
* TEST SUCCESS: non-ipv6 enabled client -> "--server-ipv6" server
(warnings in the log file, but no malfunctions)
Sat Jan 2 19:48:35 CET 2010
* tun.c: change "ipv6_support()", do not turn off tt->ipv6 unconditionally
if we don't know about OS IPv6 support - just log warning
* tun.c: implement "ifconfig inet6" setting for MacOS X / Darwin
* route.c: split *BSD system dependent part of add/delete_route_ipv6()
into FreeBSD/Dragonfly and NetBSD/Darwin/OpenBSD variants
("2001:db8::/64" vs. "2001:db8:: --prefixlen 64").
* tun.c: on MacOS X, NetBSD and OpenBSD, explicitely set on-link route
* TEST SUCCESS: MacOS X, client-ipv6 with route-ipv6
Sun Jan 3 10:55:31 CET 2010
* route.c: NetBSD fails with "-iface tun0", needs gateway address
(assume that the same syntax is needed for OpenBSD)
* route.h: introduce "remote_endpoint_ipv6" into "struct route_ipv6_list"
* init.c: pass "ifconfig_ipv6_remote" as gateway to init_route_ipv6_list()
* route.c:
- init_route_ipv6(): use "remote_endpoint_ipv6" as IPv6 gateway address
if no gateway was specified explicitely
- init_route_ipv6_list(): fill in "remote_endpoint_ipv6", if parseable
- get rid of "GATEWAY-LESS ROUTE6" warning
* route.c, add_route_ipv6()
- explicitely clear host bits of base address, to be able to more
easily set up "connected" /64 routes on NetBSD+Darwin
- split system-dependent part between Darwin and NetBSD/OpenBSD
(Darwin can use "-iface tun0", NetBSD/OpenBSD get gateway address)
- change Solaris comments from "known-broken" to "unknown"
* tun.c: rework NetBSD tunnel initialization and tun_read() / tun_write()
to work the same way OpenBSD and NetBSD do - tunnel is put into
"multi-af" mode, and all packet read/write activity is prepended by
a 32 bit value specifying the address family.
* TEST SUCCESS: NetBSD 5.0/Sparc64: client-ipv6 with route-ipv6
* TEST SUCCESS: MacOS X 10.5: client-ipv6 with route-ipv6
* (RE-)TEST SUCCESS: Linux/iproute2: server-ipv6
Linux/ifconfig: client-ipv6
FreeBSD 6.3: server-ipv6
* release as patch 20100103-1
* options.c: document all new options in "--help"
* tun.c: fix typo in Solaris-specific section
* socket.h, socket.c: change u_int32_t to uint32_t
(Solaris - and all the rest of the code uses "uintNN" anyway)
Mon Jan 4 17:46:58 CET 2010
* socket.c: rework add_in6_addr() to use 32-bit access to struct in6_addr
(Solaris has no 16-bit values in union, but this is more elegant as well)
* tun.c: fix "ifconfig inet6" command for Solaris
* tun.c: make sure "tun0 inet6" is unplumbed first, cleanup leftovers
* route.c: add routes with "metric 0" on solaris, otherwise they just
don't work (someone who understands Solaris might want to fix this).
* Solaris "sort of" works now - ifconfig works, route add does not give
errors, "netstat -rn" looks right, but packets are discarded unless
the routes are installed with "metric 0". So we just use "metric 0"...
* CAVEAT: Solaris "ifconfig ... preferred" interferes with source address
selection. So if there are any active IPv6 interfaces configured with
"preferred", packets leaving out the tunnel will use the wrong source
IPv6 address. Not fixable from within OpenVPN.
* CAVEAT2: Solaris insists on doing DHCPv6 on tun0 interfaces by default,
so DHCPv6 solicitation packets will be seen. Since the server end has
no idea what to do with them, they are a harmless nuisance. Fixable
on the Solaris side via "ndpd.conf" (see ``man ifconfig'').
* release as patch 20100104-1

180
NOTES Normal file
View file

@ -0,0 +1,180 @@
TODO:
* tun.c -> init_tun()
[ifconfig-Parameter vorbereiten]
init.c -> do_open_tun() -> init.c::do_init_tun() -> tun.c::init_tun()
-> do_ifconfig()
o tun.c -> do_ifconfig()
[ifconfig/ip aufrufen]
* Linux / ifconfig
/ Linux / iproute2 ** TESTEN **
o FreeBSD
/ NetBSD ("needs patch", googlen) ** TESTEN **
/ Solaris ** TESTEN **
o OpenBSD
o MacOS X
o tun.c (?) -> interface cleanup ("ip addr del dev tun0 ...")
o TAP mode und IPv6? Fehlermeldung?
o einfach confen
o ifconfig_ipv6_remote -> kann eigentlich ersatzlos wegfallen
[tun.c, init.c, options.c, options.h]
o [kann nicht, braucht man als default-gateway auf Solaris :( ]
* push ifconfig-ipv6
push::send_push_reply() -> c->c2.push_ifconfig_local
** wo wird das gesetzt? ** multi.c (und ggf. options.c / ifconfig-push)
o /netbits pushen (push.c) -> options.c "ifconfig-ipv6" muss auch
damit zurecht kommen, tut es derzeit aber nicht
* ifconfig_pool_write() -> IPv6 "wenn pool IPv6 hat"
* multi::multi_init() -> ifconfig_pool_init()
* "route-ipv6"-Option und "push route-ipv6"
o "gateway"
o "metric"
o "route-gateway-ipv6"-Option
o "ifconfig-ipv6-push"-Option
options.c -> options.push_ifconfig_...
multi.c
mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
o "server-ipv6"-Option
o options.c, add_option() -> wird fuer "lokale" und "push"-Options
aufgerufen
no_more_than_n_args()
struct options [options.h]
* add_route_to_option_list()
[route.c -> add_route_ipv6_to_option_list]
[options.h -> options->routes_ipv6]
o was passiert danach damit?
* socket.c: ip_or_dns_addr_safe()
--> ipv6_addr_safe()
--> ipv6_addr_safe_hexplusbits()
* Makro? helper.c -> helper_client_server() ******
* Fehler, wenn options->mode != MODE_SERVER
* "tun-ipv6" auto-enablen
* if (options->tun_ipv6)
msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
[options.c, 1710]
[raus]
o struct tuntap->ipv6 = true, wenn "ipv6" und "system kann das"
o Fehler, wenn System kein IPv6 kann
("NetBSD needs patch" -> googlen)
o Adress-Allokation an Clients (/128 aus ifconfig-ipv6-pool /64 erstmal nur)
o hash aus Client-Key als host part?
(nein, wir nehmen einfach "den gleichen Offset wie bei IPv4" und
add_in6_addr())
o "iroute-ipv6"-Option
o "ifconfig-ipv6"
o "ifconfig-ipv6-pool"
o "ifconfig-pool-persist-ipv6"-Option
o was tut #define LINUX_IPV6?
o was tut bestehender Code mit "ipv6"?
o Routing-/Forwarding-Funktion
read_tun() --> ??
?? --> write_tun()
[muss für p2p schon funktionieren, d.h. vermutlich ist nur die
server-seite anzupassen]
o ICMP
o Optionen dokumentieren (-> berniv6)
o server-ipv6
o ifconfig-ipv6
o ifconfig-ipv6-pool
o ifconfig-pool-persist (v4+v6, Formataenderung im File)
o iroute-ipv6
o route-ipv6
o tun-ipv6
* http://www.greenie.net/ipv6/openvpn.html - DONE
o man pages, --help
* options.c
- get_ip_addr() --> socket.c getaddr()
- openvpn_inet_aton -> OIA_IP "ist IP"
* options.c, show_p2mp_parms()
* socket.c, print_in_addr_t() --> print_in6_addr()
o forward_compatible?
o ifconfig_ipv6_pool_persist --> einfach ifconfig_pool_persist mitbenutzen?
Entscheidung: JA
o to be implemented: pool.c
o route.c:
clone_route_option_list(), copy_route_option_list(),
new_route_list(), add_route(), init_route_list(), ...
add_routes(), delete_routes(), setenv_routes(),
-> wo werden die aufgerufen, wofuer verwendet, IPv6-Anpassung?
* add_route() ruft "/sbin/route add..." auf
o div. (redirect gateway related) -> route.c::add_route3() -> add_route()
o init.c::do_route() -> route.c::add_routes() -> add_route()
o init.c::do_open_tun() -> do_route()
o forward.c::check_add_routes_action() -> do_route()
o init.c::do_open_tun() -> init.c::do_init_route_list() ->
route.c::init_route_list()
* init.c::do_open_tun() -> do_alloc_route_list() -> new_route_ipv6_list()
o add_route_ipv6() - implementieren und testen
* Linux / ifconfig
* Linux / iproute2
i FreeBSD
i NetBSD ("needs patch", googlen)
i Solaris *braucht Gateway*
i OpenBSD
i MacOS X
o delete_route_ipv6() - implementieren und testen
* Linux / ifconfig
* Linux / iproute2
i FreeBSD
i NetBSD ("needs patch", googlen)
i Solaris
i OpenBSD
i MacOS X
o Gateway-Logik für IPv6-Routen mitschleifen ("explizit angeben oder
aus ifconfig-ipv6 $remote")
o IPv6 TCPMSS oder "fragmentation required"?
o IPv6 MTU auf Interface setzen?
o sysdep!
TESTEN
* ipv6_addr_safe() [--ifconfig-ipv6 null/zu lang/invalid]
o ipv6_addr_safe_hexplusbits() [--route-ipv6 ...]
* get_ipv6_addr() [--server-ipv6 ...]
o unmodifizierter 2.1-client -> 2.1+ipv6-Server?
o unmodifizierter 2.0-client -> 2.1+ipv6-Server?
o wie kann der Server das erkennen, und "kein v6" schicken?

8
README.IPv6 Normal file
View file

@ -0,0 +1,8 @@
This is an experimentally patched version of OpenVPN 2.1 with IPv6
payload support.
Go here for release notes and documentation:
http://www.greenie.net/ipv6/openvpn.html
Gert Doering, 31.12.2009

37
TODO.IPv6 Normal file
View file

@ -0,0 +1,37 @@
known issues for IPv6 payload support in OpenVPN
-----------------------------------------------
1.) "--topology subnet" doesn't work together with IPv6 payload
(verified for FreeBSD server, Linux/ifconfig client, problems
with ICMP6 neighbor solicitations from BSD not being answered by Linux)
2.) NetBSD IPv6 support doesn't work
("connected" route is not auto-created, "route-ipv6" adding fails)
* fixed, 3.1.10 *
3.) route deletion for IPv6 routes is not yet done
* fixed for configured routes, 3.1.10 *
* missing for manual-ifconfig-connected (NetBSD, Darwin)
4.) do "ifconfig tun0 inet6 unplumb" or "ifconfig tun0 destroy" for
Solaris, *BSD, ... at program termination time, to clean up leftovers
(unless tunnel persistance is desired).
For Solaris, only the "ipv6 tun0" is affected, for the *BSDs all tun0
stay around.
5.) add new option "ifconfig-ipv6-push"
(per-client static IPv6 assignment, -> radiusplugin, etc)
6.) add new option "route-ipv6-gateway"
7.) add "full" gateway handling for IPv6 in route.c
(right now, the routes are just sent down the tun interface, if the
operating system in questions supports that, without care for the
gateway address - which does not work for gateways that are supposed
to point elsewhere. Also, it doesn't work for TAP interfaces.
8.) full IPv6 support for TAP interfaces
(main issue should be routes+gateway - and testing :-) )

View file

@ -259,7 +259,8 @@ send_control_channel_string (struct context *c, const char *str, int msglevel)
static void
check_add_routes_action (struct context *c, const bool errors)
{
do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
c->c1.tuntap, c->plugins, c->c2.es);
update_time ();
event_timeout_clear (&c->c2.route_wakeup);
event_timeout_clear (&c->c2.route_wakeup_expire);

View file

@ -142,6 +142,55 @@ helper_client_server (struct options *o)
#if P2MP
#if P2MP_SERVER
/*
*
* HELPER DIRECTIVE for IPv6
*
* server-ipv6 2001:db8::/64
*
* EXPANDS TO:
*
* tun-ipv6
* push "tun-ipv6"
* ifconfig-ipv6 2001:db8::1 2001:db8::2
* if !nopool:
* ifconfig-ipv6-pool 2001:db8::1:0/64
*
*/
if ( o->server_ipv6_defined )
{
if ( ! o->server_defined )
{
msg (M_USAGE, "--server-ipv6 must be used together with --server");
}
if ( o->server_flags & SF_NOPOOL )
{
msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" );
}
if ( o->ifconfig_ipv6_pool_defined )
{
msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly");
}
/* local ifconfig is "base address + 1" and "+2" */
o->ifconfig_ipv6_local =
print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc );
o->ifconfig_ipv6_remote =
print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc );
/* pool starts at "base address + 0x10000" */
ASSERT( o->server_netbits_ipv6 < 96 ); /* want 32 bits */
o->ifconfig_ipv6_pool_defined = true;
o->ifconfig_ipv6_pool_base =
add_in6_addr( o->server_network_ipv6, 0x10000 );
o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6;
o->tun_ipv6 = true;
push_option( o, "tun-ipv6", M_USAGE );
}
/*
*
* HELPER DIRECTIVE:

58
init.c
View file

@ -1066,6 +1066,8 @@ do_alloc_route_list (struct context *c)
{
if (c->options.routes && !c->c1.route_list)
c->c1.route_list = new_route_list (c->options.max_routes, &c->gc);
if (c->options.routes_ipv6 && !c->c1.route_ipv6_list)
c->c1.route_ipv6_list = new_route_ipv6_list (c->options.max_routes, &c->gc);
}
@ -1108,6 +1110,45 @@ do_init_route_list (const struct options *options,
}
}
static void
do_init_route_ipv6_list (const struct options *options,
struct route_ipv6_list *route_ipv6_list,
bool fatal,
struct env_set *es)
{
const char *gw = NULL;
int dev = dev_type_enum (options->dev, options->dev_type);
int metric = 0;
if (dev != DEV_TYPE_TUN )
msg( M_WARN, "IPv6 routes on TAP devices are going to fail on some platforms (need gateway spec)" ); /* TODO-GERT */
gw = options->ifconfig_ipv6_remote; /* default GW = remote end */
#if 0 /* not yet done for IPv6 - TODO!*/
if ( options->route_ipv6_default_gateway ) /* override? */
gw = options->route_ipv6_default_gateway;
#endif
if (options->route_default_metric)
metric = options->route_default_metric;
if (!init_route_ipv6_list (route_ipv6_list,
options->routes_ipv6,
gw,
metric,
es))
{
if (fatal)
openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
}
else
{
/* copy routes to environment */
setenv_routes_ipv6 (es, route_ipv6_list);
}
}
/*
* Called after all initialization has been completed.
*/
@ -1171,12 +1212,13 @@ initialization_sequence_completed (struct context *c, const unsigned int flags)
void
do_route (const struct options *options,
struct route_list *route_list,
struct route_ipv6_list *route_ipv6_list,
const struct tuntap *tt,
const struct plugin_list *plugins,
struct env_set *es)
{
if (!options->route_noexec && route_list)
add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
if (!options->route_noexec && ( route_list || route_ipv6_list ) )
add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
{
@ -1233,6 +1275,8 @@ do_init_tun (struct context *c)
c->options.topology,
c->options.ifconfig_local,
c->options.ifconfig_remote_netmask,
c->options.ifconfig_ipv6_local,
c->options.ifconfig_ipv6_remote,
addr_host (&c->c1.link_socket_addr.local),
addr_host (&c->c1.link_socket_addr.remote),
!c->options.ifconfig_nowarn,
@ -1269,6 +1313,8 @@ do_open_tun (struct context *c)
/* parse and resolve the route option list */
if (c->options.routes && c->c1.route_list && c->c2.link_socket)
do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
if (c->options.routes_ipv6 && c->c1.route_ipv6_list )
do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);
/* do ifconfig */
if (!c->options.ifconfig_noexec
@ -1315,7 +1361,8 @@ do_open_tun (struct context *c)
/* possibly add routes */
if (!c->options.route_delay_defined)
do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
c->c1.tuntap, c->plugins, c->c2.es);
/*
* Did tun/tap driver give us an MTU?
@ -1390,8 +1437,9 @@ do_close_tun (struct context *c, bool force)
#endif
/* delete any routes we added */
if (c->c1.route_list)
delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
if (c->c1.route_list || c->c1.route_ipv6_list )
delete_routes (c->c1.route_list, c->c1.route_ipv6_list,
c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
/* actually close tun/tap device based on --down-pre flag */
if (!c->options.down_pre)

1
init.h
View file

@ -63,6 +63,7 @@ void init_instance (struct context *c, const struct env_set *env, const unsigned
void do_route (const struct options *options,
struct route_list *route_list,
struct route_ipv6_list *route_ipv6_list,
const struct tuntap *tt,
const struct plugin_list *plugins,
struct env_set *es);

2
misc.c
View file

@ -1004,7 +1004,7 @@ setenv_str_ex (struct env_set *es,
{
const char *str = construct_name_value (name_tmp, val_tmp, &gc);
env_set_add (es, str);
/*msg (M_INFO, "SETENV_ES '%s'", str);*/
msg (M_INFO, "SETENV_ES '%s'", str);/**/
}
else
env_set_del (es, name_tmp);

143
mroute.c
View file

@ -88,12 +88,33 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int
}
}
static inline void
mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned int mask)
{
if (ma)
{
ma->type = MR_ADDR_IPV6 | mask;
ma->netbits = 0;
ma->len = 16;
*(struct in6_addr *)ma->addr = src;
}
}
static inline bool
mroute_is_mcast (const in_addr_t addr)
{
return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
}
/* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies
* the address as being a multicast address"
*/
static inline bool
mroute_is_mcast_ipv6 (const struct in6_addr addr)
{
return (addr.s6_addr[0] == 0xff);
}
#ifdef ENABLE_PF
static unsigned int
@ -155,10 +176,29 @@ mroute_extract_addr_ipv4 (struct mroute_addr *src,
}
break;
case 6:
{
msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet");
break;
}
if (BLEN (buf) >= (int) sizeof (struct openvpn_ipv6hdr))
{
const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR (buf);
#if 0 /* very basic debug */
struct gc_arena gc = gc_new ();
msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
print_in6_addr( ipv6->saddr, 0, &gc ),
print_in6_addr( ipv6->daddr, 0, &gc ));
gc_free (&gc);
#endif
mroute_get_in6_addr (src, ipv6->saddr, 0);
mroute_get_in6_addr (dest, ipv6->daddr, 0);
if (mroute_is_mcast_ipv6 (ipv6->daddr))
ret |= MROUTE_EXTRACT_MCAST;
ret |= MROUTE_EXTRACT_SUCCEEDED;
}
break;
default:
msg (M_WARN, "IP packet with unknown IP version=%d seen",
OPENVPN_IPH_GET_VER (*BPTR(buf)));
}
}
return ret;
@ -252,14 +292,36 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
* Zero off the host bits in an address, leaving
* only the network bits, using the netbits member of
* struct mroute_addr as the controlling parameter.
*
* TODO: this is called for route-lookup for every yet-unhashed
* destination address, so for lots of active net-iroutes, this
* might benefit from some "zeroize 32 bit at a time" improvements
*/
void
mroute_addr_mask_host_bits (struct mroute_addr *ma)
{
in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
addr &= netbits_to_netmask (ma->netbits);
*(in_addr_t*)ma->addr = htonl (addr);
if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
{
addr &= netbits_to_netmask (ma->netbits);
*(in_addr_t*)ma->addr = htonl (addr);
}
else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
{
int byte = ma->len-1; /* rightmost byte in address */
int bits_to_clear = 128 - ma->netbits;
while( byte >= 0 && bits_to_clear > 0 )
{
if ( bits_to_clear >= 8 )
{ ma->addr[byte--] = 0; bits_to_clear -= 8; }
else
{ ma->addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
}
ASSERT( bits_to_clear == 0 );
}
else
ASSERT(0);
}
/*
@ -337,17 +399,24 @@ mroute_addr_print_ex (const struct mroute_addr *ma,
}
break;
case MR_ADDR_IPV6:
buf_printf (&out, "IPV6");
break;
default:
buf_printf (&out, "UNKNOWN");
break;
}
return BSTR (&out);
}
else
return "[NULL]";
}
{
buf_printf (&out, "%s",
print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc));
if (maddr.type & MR_WITH_NETBITS)
{
buf_printf (&out, "/%d", maddr.netbits);
}
}
break;
default:
buf_printf (&out, "UNKNOWN");
break;
}
return BSTR (&out);
}
else
return "[NULL]";
}
/*
* mroute_helper's main job is keeping track of
@ -418,6 +487,44 @@ mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir)
}
}
/* this is a bit inelegant, we really should have a helper to that
* is only passed the netbits value, and not the whole struct iroute *
* - thus one helper could do IPv4 and IPv6. For the sake of "not change
* code unrelated to IPv4" this is left for later cleanup, for now.
*/
void
mroute_helper_add_iroute6 (struct mroute_helper *mh,
const struct iroute_ipv6 *ir6)
{
if (ir6->netbits >= 0)
{
ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
mroute_helper_lock (mh);
++mh->cache_generation;
++mh->net_len_refcount[ir6->netbits];
if (mh->net_len_refcount[ir6->netbits] == 1)
mroute_helper_regenerate (mh);
mroute_helper_unlock (mh);
}
}
void
mroute_helper_del_iroute6 (struct mroute_helper *mh,
const struct iroute_ipv6 *ir6)
{
if (ir6->netbits >= 0)
{
ASSERT (ir6->netbits < MR_HELPER_NET_LEN);
mroute_helper_lock (mh);
++mh->cache_generation;
--mh->net_len_refcount[ir6->netbits];
ASSERT (mh->net_len_refcount[ir6->netbits] >= 0);
if (!mh->net_len_refcount[ir6->netbits])
mroute_helper_regenerate (mh);
mroute_helper_unlock (mh);
}
}
void
mroute_helper_free (struct mroute_helper *mh)
{

View file

@ -85,7 +85,7 @@ struct mroute_addr {
/*
* Number of bits in an address. Should be raised for IPv6.
*/
#define MR_HELPER_NET_LEN 32
#define MR_HELPER_NET_LEN 129
/*
* Used to help maintain CIDR routing table.
@ -127,6 +127,8 @@ struct mroute_helper *mroute_helper_init (int ageable_ttl_secs);
void mroute_helper_free (struct mroute_helper *mh);
void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
void mroute_helper_add_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
void mroute_helper_del_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6);
/*
* Given a raw packet in buf, return the src and dest

114
multi.c
View file

@ -316,25 +316,18 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
*/
if (t->options.ifconfig_pool_defined)
{
if (dev == DEV_TYPE_TAP)
{
m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
t->options.ifconfig_pool_start,
t->options.ifconfig_pool_end,
t->options.duplicate_cn);
}
else if (dev == DEV_TYPE_TUN)
{
m->ifconfig_pool = ifconfig_pool_init (
(t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
t->options.ifconfig_pool_start,
t->options.ifconfig_pool_end,
t->options.duplicate_cn);
}
else
{
ASSERT (0);
}
int pool_type = IFCONFIG_POOL_INDIV;
if ( dev == DEV_TYPE_TUN && t->options.topology == TOP_NET30 )
pool_type = IFCONFIG_POOL_30NET;
m->ifconfig_pool = ifconfig_pool_init (pool_type,
t->options.ifconfig_pool_start,
t->options.ifconfig_pool_end,
t->options.duplicate_cn,
t->options.ifconfig_ipv6_pool_defined,
t->options.ifconfig_ipv6_pool_base,
t->options.ifconfig_ipv6_pool_netbits );
/* reload pool data from file */
if (t->c1.ifconfig_pool_persist)
@ -429,10 +422,14 @@ multi_del_iroutes (struct multi_context *m,
struct multi_instance *mi)
{
const struct iroute *ir;
const struct iroute_ipv6 *ir6;
if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
{
for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
mroute_helper_del_iroute (m->route_helper, ir);
for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
mroute_helper_del_iroute6 (m->route_helper, ir6);
}
}
@ -1078,6 +1075,37 @@ multi_learn_in_addr_t (struct multi_context *m,
}
}
static struct multi_instance *
multi_learn_in6_addr (struct multi_context *m,
struct multi_instance *mi,
struct in6_addr a6,
int netbits, /* -1 if host route, otherwise # of network bits in address */
bool primary)
{
struct mroute_addr addr;
addr.len = 16;
addr.type = MR_ADDR_IPV6;
addr.netbits = 0;
memcpy( &addr.addr, &a6, sizeof(a6) );
if (netbits >= 0)
{
addr.type |= MR_WITH_NETBITS;
addr.netbits = (uint8_t) netbits;
mroute_addr_mask_host_bits( &addr );
}
{
struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
#ifdef MANAGEMENT_DEF_AUTH
if (management && owner)
management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
#endif
return owner;
}
}
/*
* A new client has connected, add routes (server -> client)
* to internal routing table.
@ -1088,6 +1116,7 @@ multi_add_iroutes (struct multi_context *m,
{
struct gc_arena gc = gc_new ();
const struct iroute *ir;
const struct iroute_ipv6 *ir6;
if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
{
mi->did_iroutes = true;
@ -1107,6 +1136,22 @@ multi_add_iroutes (struct multi_context *m,
multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
}
for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next )
{
if (ir6->netbits >= 0)
msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
print_in6_addr (ir6->network, 0, &gc),
ir6->netbits,
multi_instance_string (mi, false, &gc));
else
msg (D_MULTI_LOW, "MULTI: internal route %s -> %s",
print_in6_addr (ir6->network, 0, &gc),
multi_instance_string (mi, false, &gc));
mroute_helper_add_iroute6 (m->route_helper, ir6);
multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false);
}
}
gc_free (&gc);
}
@ -1196,17 +1241,22 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
{
in_addr_t local=0, remote=0;
struct in6_addr remote_ipv6;
const char *cn = NULL;
if (!mi->context.options.duplicate_cn)
cn = tls_common_name (mi->context.c2.tls_multi, true);
mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, &remote_ipv6, cn);
if (mi->vaddr_handle >= 0)
{
const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s",
print_in_addr_t( remote, 0, &gc ),
print_in6_addr( remote_ipv6, 0, &gc ) );
/* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
mi->context.c2.push_ifconfig_local = remote;
if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
@ -1228,6 +1278,16 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
else
msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
multi_instance_string (mi, false, &gc));
if ( mi->context.options.ifconfig_ipv6_pool_defined )
{
mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
mi->context.c2.push_ifconfig_ipv6_remote =
mi->context.c1.tuntap->local_ipv6;
mi->context.c2.push_ifconfig_ipv6_netbits =
mi->context.options.ifconfig_ipv6_pool_netbits;
mi->context.c2.push_ifconfig_ipv6_defined = true;
}
}
else
{
@ -1272,6 +1332,11 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi)
SA_SET_IF_NONZERO);
}
}
/* TODO: I'm not exactly sure what these environment variables are
* used for, but if we have them for IPv4, we should also have
* them for IPv6, no?
*/
}
/*
@ -1661,6 +1726,15 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
}
if (mi->context.c2.push_ifconfig_ipv6_defined)
{
multi_learn_in6_addr (m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
/* TODO: find out where addresses are "unlearned"!! */
msg (D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
multi_instance_string (mi, false, &gc),
print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
}
/* add routes locally, pointing to new client, if
--iroute options have been specified */
multi_add_iroutes (m, mi);

View file

@ -165,6 +165,9 @@ struct context_1
/* list of --route directives */
struct route_list *route_list;
/* list of --route-ipv6 directives */
struct route_ipv6_list *route_ipv6_list;
/* --status file */
struct status_output *status_output;
bool status_output_owned;
@ -417,6 +420,11 @@ struct context_2
in_addr_t push_ifconfig_local;
in_addr_t push_ifconfig_remote_netmask;
bool push_ifconfig_ipv6_defined;
struct in6_addr push_ifconfig_ipv6_local;
int push_ifconfig_ipv6_netbits;
struct in6_addr push_ifconfig_ipv6_remote;
/* client authentication state, CAS_SUCCEEDED must be 0 */
# define CAS_SUCCEEDED 0
# define CAS_PENDING 1

187
options.c
View file

@ -172,6 +172,8 @@ static const char usage_message[] =
" addresses outside of the subnets used by either peer.\n"
" TAP: configure device to use IP address l as a local\n"
" endpoint and rn as a subnet mask.\n"
"--ifconfig-ipv6 l r : configure device to use IPv6 address l as local\n"
" endpoint (as a /64) and r as remote endpoint\n"
"--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
" pass --ifconfig parms by environment to scripts.\n"
"--ifconfig-nowarn : Don't warn if the --ifconfig option on this side of the\n"
@ -182,6 +184,10 @@ static const char usage_message[] =
" netmask default: 255.255.255.255\n"
" gateway default: taken from --route-gateway or --ifconfig\n"
" Specify default by leaving blank or setting to \"nil\".\n"
"--route-ipv6 network/bits [gateway] [metric] :\n"
" Add IPv6 route to routing table after connection\n"
" is established. Multiple routes can be specified.\n"
" gateway default: taken from --route-ipv6-gateway or --ifconfig\n"
"--max-routes n : Specify the maximum number of routes that may be defined\n"
" or pulled from a server.\n"
"--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
@ -370,6 +376,7 @@ static const char usage_message[] =
"\n"
"Multi-Client Server options (when --mode server is used):\n"
"--server network netmask : Helper option to easily configure server mode.\n"
"--server-ipv6 network/bits : Configure IPv6 server mode.\n"
"--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
" easily configure ethernet bridging server mode.\n"
"--push \"option\" : Push a config file option back to the peer for remote\n"
@ -383,10 +390,13 @@ static const char usage_message[] =
"--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n"
" data to file, at seconds intervals (default=600).\n"
" If seconds=0, file will be treated as read-only.\n"
"--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
" to be dynamically allocated to connecting clients.\n"
"--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
" overrides --ifconfig-pool dynamic allocation.\n"
" Only valid in a client-specific config file.\n"
"--iroute network [netmask] : Route subnet to client.\n"
"--iroute-ipv6 network/bits : Route IPv6 subnet to client.\n"
" Sets up internal routes only.\n"
" Only valid in a client-specific config file.\n"
"--disable : Client is disabled.\n"
@ -871,6 +881,58 @@ get_ip_addr (const char *ip_string, int msglevel, bool *error)
return ret;
}
/* parse a text string containing an IPv6 address + netbits
* in "standard format" (2001:dba::/32)
* return true if parsing succeeded, modify *network and *netbits
*/
bool
get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
unsigned int * netbits, int msglevel )
{
int rc;
char * sep, * endp;
int bits;
sep = strchr( prefix_str, '/' );
if ( sep == NULL )
{
msg (msglevel, "IPv6 prefix '%s': missing '/'", prefix_str);
return false;
}
bits = strtol( sep+1, &endp, 10 );
if ( *endp != '\0' || bits < 0 || bits > 128 )
{
msg (msglevel, "IPv6 prefix '%s': invalid '/bits' spec", prefix_str);
return false;
}
/* temporary replace '/' in caller-provided string with '\0', otherwise
* inet_pton() will refuse prefix string
* (alternative would be to strncpy() the prefix to temporary buffer)
*/
*sep = '\0';
rc = inet_pton( AF_INET6, prefix_str, network );
*sep = '/';
if ( rc != 1 )
{
msg (msglevel, "IPv6 prefix '%s': invalid network part", prefix_str);
return false;
}
*netbits = bits;
return true; /* parsing OK, values set */
}
static bool ipv6_addr_safe_hexplusbits( const char * ipv6_prefix_spec )
{
struct in6_addr t_addr;
unsigned int t_bits;
return get_ipv6_addr( ipv6_prefix_spec, &t_addr, &t_bits, M_WARN );
}
static char *
string_substitute (const char *src, int from, int to, struct gc_arena *gc)
{
@ -989,6 +1051,8 @@ show_p2mp_parms (const struct options *o)
#if P2MP_SERVER
msg (D_SHOW_PARMS, " server_network = %s", print_in_addr_t (o->server_network, 0, &gc));
msg (D_SHOW_PARMS, " server_netmask = %s", print_in_addr_t (o->server_netmask, 0, &gc));
msg (D_SHOW_PARMS, " server_network_ipv6 = %s", print_in6_addr (o->server_network_ipv6, 0, &gc) );
SHOW_INT (server_netbits_ipv6);
msg (D_SHOW_PARMS, " server_bridge_ip = %s", print_in_addr_t (o->server_bridge_ip, 0, &gc));
msg (D_SHOW_PARMS, " server_bridge_netmask = %s", print_in_addr_t (o->server_bridge_netmask, 0, &gc));
msg (D_SHOW_PARMS, " server_bridge_pool_start = %s", print_in_addr_t (o->server_bridge_pool_start, 0, &gc));
@ -1009,6 +1073,8 @@ show_p2mp_parms (const struct options *o)
msg (D_SHOW_PARMS, " ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc));
SHOW_STR (ifconfig_pool_persist_filename);
SHOW_INT (ifconfig_pool_persist_refresh_freq);
msg (D_SHOW_PARMS, " ifconfig_ipv6_pool_base = %s", print_in6_addr (o->ifconfig_ipv6_pool_base, 0, &gc));
SHOW_INT (ifconfig_ipv6_pool_netbits);
SHOW_INT (n_bcast_buf);
SHOW_INT (tcp_queue_limit);
SHOW_INT (real_hash_size);
@ -1076,6 +1142,25 @@ option_iroute (struct options *o,
o->iroutes = ir;
}
static void
option_iroute_ipv6 (struct options *o,
const char *prefix_str,
int msglevel)
{
struct iroute_ipv6 *ir;
ALLOC_OBJ_GC (ir, struct iroute_ipv6, &o->gc);
if ( get_ipv6_addr (prefix_str, &ir->network, &ir->netbits, msglevel ) < 0 )
{
msg (msglevel, "in --iroute-ipv6 %s: Bad IPv6 prefix specification",
prefix_str);
return;
}
ir->next = o->iroutes_ipv6;
o->iroutes_ipv6 = ir;
}
#endif /* P2MP_SERVER */
#endif /* P2MP */
@ -1113,6 +1198,13 @@ rol_check_alloc (struct options *options)
options->routes = new_route_option_list (options->max_routes, &options->gc);
}
void
rol6_check_alloc (struct options *options)
{
if (!options->routes_ipv6)
options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
}
#ifdef ENABLE_DEBUG
static void
show_connection_entry (const struct connection_entry *o)
@ -1203,6 +1295,8 @@ show_settings (const struct options *o)
SHOW_STR (ifconfig_remote_netmask);
SHOW_BOOL (ifconfig_noexec);
SHOW_BOOL (ifconfig_nowarn);
SHOW_STR (ifconfig_ipv6_local);
SHOW_STR (ifconfig_ipv6_remote);
#ifdef HAVE_GETTIMEOFDAY
SHOW_INT (shaper);
@ -1863,8 +1957,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
if (options->connection_list)
msg (M_USAGE, "<connection> cannot be used with --mode server");
#endif
#if 0
if (options->tun_ipv6)
msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
#endif
if (options->shaper)
msg (M_USAGE, "--shaper cannot be used with --mode server");
if (options->inetd)
@ -2461,6 +2557,8 @@ options_string (const struct options *o,
o->topology,
o->ifconfig_local,
o->ifconfig_remote_netmask,
o->ifconfig_ipv6_local,
o->ifconfig_ipv6_remote,
(in_addr_t)0,
(in_addr_t)0,
false,
@ -3794,6 +3892,21 @@ add_option (struct options *options,
goto err;
}
}
else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] )
{
VERIFY_PERMISSION (OPT_P_UP);
/* TODO: should we accept address + netbits (2001:db8::1/64) here? */
if ( ipv6_addr_safe( p[1] ) && ipv6_addr_safe( p[2] ) )
{
options->ifconfig_ipv6_local = p[1];
options->ifconfig_ipv6_remote = p[2];
}
else
{
msg (msglevel, "ifconfig-ipv6 parms '%s' and '%s' must be valid addresses", p[1], p[2]);
goto err;
}
}
else if (streq (p[0], "ifconfig-noexec"))
{
VERIFY_PERMISSION (OPT_P_UP);
@ -4594,6 +4707,26 @@ add_option (struct options *options,
}
add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]);
}
else if (streq (p[0], "route-ipv6") && p[1])
{
VERIFY_PERMISSION (OPT_P_ROUTE);
rol6_check_alloc (options);
if (pull_mode)
{
if (!ipv6_addr_safe_hexplusbits (p[1]))
{
msg (msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
goto err;
}
if (p[2] && !ipv6_addr_safe (p[2]))
{
msg (msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
goto err;
}
/* p[3] is metric, if present */
}
add_route_ipv6_to_option_list (options->routes_ipv6, p[1], p[2], p[3]);
}
else if (streq (p[0], "max-routes") && p[1])
{
int max_routes;
@ -4805,6 +4938,33 @@ add_option (struct options *options,
}
}
}
else if (streq (p[0], "server-ipv6") && p[1] )
{
const int lev = M_WARN;
struct in6_addr network;
unsigned int netbits = 0;
VERIFY_PERMISSION (OPT_P_GENERAL);
if ( ! get_ipv6_addr (p[1], &network, &netbits, lev) )
{
msg (msglevel, "error parsing --server-ipv6 parameter");
goto err;
}
if ( netbits != 64 )
{
msg( msglevel, "--server-ipv6 settings: only /64 supported right now (not /%d)", netbits );
goto err;
}
options->server_ipv6_defined = true;
options->server_network_ipv6 = network;
options->server_netbits_ipv6 = netbits;
if (p[2]) /* no "nopool" options or similar for IPv6 */
{
msg (msglevel, "error parsing --server: %s is not a recognized flag", p[3]);
goto err;
}
}
else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4])
{
const int lev = M_WARN;
@ -4889,6 +5049,28 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
options->topology = TOP_P2P;
}
else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] )
{
const int lev = M_WARN;
struct in6_addr network;
unsigned int netbits = 0;
VERIFY_PERMISSION (OPT_P_GENERAL);
if ( ! get_ipv6_addr (p[1], &network, &netbits, lev ) )
{
msg (msglevel, "error parsing --ifconfig-ipv6-pool parameters");
goto err;
}
if ( netbits != 64 )
{
msg( msglevel, "--ifconfig-ipv6-pool settings: only /64 supported right now (not /%d)", netbits );
goto err;
}
options->ifconfig_ipv6_pool_defined = true;
options->ifconfig_ipv6_pool_base = network;
options->ifconfig_ipv6_pool_netbits = netbits;
}
else if (streq (p[0], "hash-size") && p[1] && p[2])
{
int real, virtual;
@ -5084,6 +5266,11 @@ add_option (struct options *options,
}
option_iroute (options, p[1], netmask, msglevel);
}
else if (streq (p[0], "iroute-ipv6") && p[1])
{
VERIFY_PERMISSION (OPT_P_INSTANCE);
option_iroute_ipv6 (options, p[1], msglevel);
}
else if (streq (p[0], "ifconfig-push") && p[1] && p[2])
{
in_addr_t local, remote_netmask;

View file

@ -205,6 +205,8 @@ struct options
int topology; /* one of the TOP_x values from proto.h */
const char *ifconfig_local;
const char *ifconfig_remote_netmask;
const char *ifconfig_ipv6_local;
const char *ifconfig_ipv6_remote;
bool ifconfig_noexec;
bool ifconfig_nowarn;
#ifdef HAVE_GETTIMEOFDAY
@ -326,6 +328,7 @@ struct options
bool route_delay_defined;
int max_routes;
struct route_option_list *routes;
struct route_ipv6_option_list *routes_ipv6; /* IPv6 */
bool route_nopull;
bool route_gateway_via_dhcp;
bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
@ -361,6 +364,9 @@ struct options
bool server_defined;
in_addr_t server_network;
in_addr_t server_netmask;
bool server_ipv6_defined; /* IPv6 */
struct in6_addr server_network_ipv6; /* IPv6 */
unsigned int server_netbits_ipv6; /* IPv6 */
# define SF_NOPOOL (1<<0)
# define SF_TCP_NODELAY_HELPER (1<<1)
@ -382,6 +388,11 @@ struct options
in_addr_t ifconfig_pool_netmask;
const char *ifconfig_pool_persist_filename;
int ifconfig_pool_persist_refresh_freq;
bool ifconfig_ipv6_pool_defined; /* IPv6 */
struct in6_addr ifconfig_ipv6_pool_base; /* IPv6 */
int ifconfig_ipv6_pool_netbits; /* IPv6 */
int real_hash_size;
int virtual_hash_size;
const char *client_connect_script;
@ -394,6 +405,7 @@ struct options
int n_bcast_buf;
int tcp_queue_limit;
struct iroute *iroutes;
struct iroute_ipv6 *iroutes_ipv6; /* IPv6 */
bool push_ifconfig_defined;
in_addr_t push_ifconfig_local;
in_addr_t push_ifconfig_remote_netmask;
@ -722,6 +734,9 @@ void options_string_import (struct options *options,
unsigned int *option_types_found,
struct env_set *es);
bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network,
unsigned int * netbits, int msglevel );
/*
* inline functions
*/

75
pool.c
View file

@ -132,7 +132,10 @@ ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_
}
struct ifconfig_pool *
ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
ifconfig_pool_init (int type, in_addr_t start, in_addr_t end,
const bool duplicate_cn,
const bool ipv6_pool, const struct in6_addr ipv6_base,
const int ipv6_netbits )
{
struct gc_arena gc = gc_new ();
struct ifconfig_pool *pool = NULL;
@ -157,11 +160,31 @@ ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplica
ASSERT (0);
}
/* IPv6 pools are always "INDIV" type */
pool->ipv6 = ipv6_pool;
if ( pool->ipv6 )
{
pool->base_ipv6 = ipv6_base;
pool->size_ipv6 = ipv6_netbits>96? ( 1<<(128-ipv6_netbits) )
: IFCONFIG_POOL_MAX;
msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
pool->size, pool->size_ipv6, ipv6_netbits,
print_in6_addr( pool->base_ipv6, 0, &gc ));
/* the current code is very simple and assumes that the IPv6
* pool is at least as big as the IPv4 pool, and we don't need
* to do separate math etc. for IPv6
*/
ASSERT( pool->size < pool->size_ipv6 );
}
ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
print_in_addr_t (pool->base, 0, &gc),
pool->size);
pool->size, pool->ipv6 );
gc_free (&gc);
return pool;
@ -181,7 +204,7 @@ ifconfig_pool_free (struct ifconfig_pool *pool)
}
ifconfig_pool_handle
ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name)
{
int i;
@ -214,6 +237,12 @@ ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *
default:
ASSERT (0);
}
/* IPv6 pools are always INDIV (--linear) */
if ( pool->ipv6 && remote_ipv6 )
{
*remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
}
}
return i;
}
@ -288,6 +317,19 @@ ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool
return ret;
}
static struct in6_addr
ifconfig_pool_handle_to_ipv6_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
{
struct in6_addr ret = in6addr_any;
/* IPv6 pools are always INDIV (--linear) */
if (hand >= 0 && hand < pool->size_ipv6 )
{
ret = add_in6_addr( pool->base_ipv6, hand );
}
return ret;
}
static void
ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
{
@ -317,9 +359,20 @@ ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out)
if (e->common_name)
{
const in_addr_t ip = ifconfig_pool_handle_to_ip_base (pool, i);
status_printf (out, "%s,%s",
e->common_name,
print_in_addr_t (ip, 0, &gc));
if ( pool->ipv6 )
{
struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base (pool, i);
status_printf (out, "%s,%s,%s",
e->common_name,
print_in_addr_t (ip, 0, &gc),
print_in6_addr (ip6, 0, &gc));
}
else
{
status_printf (out, "%s,%s",
e->common_name,
print_in_addr_t (ip, 0, &gc));
}
}
}
gc_free (&gc);
@ -409,6 +462,9 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool
int c = *BSTR(&in);
if (c == '#' || c == ';')
continue;
msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
BSTR(&in) );
if (buf_parse (&in, ',', cn_buf, buf_size)
&& buf_parse (&in, ',', ip_buf, buf_size))
{
@ -416,6 +472,7 @@ ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool
const in_addr_t addr = getaddr (GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
if (succeeded)
{
msg( M_INFO, "succeeded -> ifconfig_pool_set()");
ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
}
}
@ -471,7 +528,7 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end)
#else
cn = buf;
#endif
h = ifconfig_pool_acquire (p, &local, &remote, cn);
h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
if (h < 0)
break;
msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s",
@ -506,7 +563,7 @@ ifconfig_pool_test (in_addr_t start, in_addr_t end)
#else
cn = buf;
#endif
h = ifconfig_pool_acquire (p, &local, &remote, cn);
h = ifconfig_pool_acquire (p, &local, &remote, NULL, cn);
if (h < 0)
break;
msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",

7
pool.h
View file

@ -52,6 +52,9 @@ struct ifconfig_pool
int size;
int type;
bool duplicate_cn;
bool ipv6;
struct in6_addr base_ipv6;
unsigned int size_ipv6;
struct ifconfig_pool_entry *list;
};
@ -63,13 +66,13 @@ struct ifconfig_pool_persist
typedef int ifconfig_pool_handle;
struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn);
struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn, const bool ipv6_pool, const struct in6_addr ipv6_base, const int ipv6_netbits );
void ifconfig_pool_free (struct ifconfig_pool *pool);
bool ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end);
ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name);
ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name);
bool ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard);

15
proto.h
View file

@ -107,6 +107,21 @@ struct openvpn_iphdr {
/*The options start here. */
};
/*
* IPv6 header
*/
struct openvpn_ipv6hdr {
uint8_t version_prio;
uint8_t flow_lbl[3];
uint16_t payload_len;
uint8_t nexthdr;
uint8_t hop_limit;
struct in6_addr saddr;
struct in6_addr daddr;
};
/*
* UDP header
*/

16
push.c
View file

@ -191,6 +191,22 @@ send_push_reply (struct context *c)
buf_printf (&buf, "%s", cmd);
if ( c->c2.push_ifconfig_ipv6_defined )
{
/* IPv6 is put into buffer first, could be lengthy */
/* TODO: push "/netbits" as well, to allow non-/64 subnet sizes
* (needs changes in options.c, options.h, and other places)
*/
buf_printf( &buf, ",ifconfig-ipv6 %s %s",
print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
if (BLEN (&buf) >= safe_cap)
{
msg (M_WARN, "--push ifconfig-ipv6 option is too long");
goto fail;
}
}
while (e)
{
if (e->enable)

477
route.c
View file

@ -39,6 +39,7 @@
#include "memdbg.h"
static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
static void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
#ifdef ENABLE_DEBUG
@ -68,6 +69,15 @@ new_route_option_list (const int max_routes, struct gc_arena *a)
return ret;
}
struct route_ipv6_option_list *
new_route_ipv6_option_list (const int max_routes, struct gc_arena *a)
{
struct route_ipv6_option_list *ret;
ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a);
ret->capacity = max_routes;
return ret;
}
struct route_option_list *
clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
{
@ -95,6 +105,15 @@ new_route_list (const int max_routes, struct gc_arena *a)
return ret;
}
struct route_ipv6_list *
new_route_ipv6_list (const int max_routes, struct gc_arena *a)
{
struct route_ipv6_list *ret;
ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a);
ret->capacity = max_routes;
return ret;
}
static const char *
route_string (const struct route *r, struct gc_arena *gc)
{
@ -311,6 +330,68 @@ init_route (struct route *r,
return false;
}
static bool
init_route_ipv6 (struct route_ipv6 *r6,
const struct route_ipv6_option *r6o,
const struct route_ipv6_list *rl6 )
{
r6->option = r6o;
r6->defined = false;
if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, M_WARN ))
goto fail;
/* gateway */
if (is_route_parm_defined (r6o->gateway))
{
if ( inet_pton( AF_INET6, r6o->gateway, &r6->gateway ) != 1 )
{
msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway );
}
}
else if (rl6->remote_endpoint_defined)
{
r6->gateway = rl6->remote_endpoint_ipv6;
}
else
{
msg (M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options");
goto fail;
}
/* metric */
r6->metric_defined = false;
r6->metric = 0;
if (is_route_parm_defined (r6o->metric))
{
r6->metric = atoi (r6o->metric);
if (r6->metric < 0)
{
msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
r6o->prefix,
r6o->metric);
goto fail;
}
r6->metric_defined = true;
}
else if (rl6->default_metric_defined)
{
r6->metric = rl6->default_metric;
r6->metric_defined = true;
}
r6->defined = true;
return true;
fail:
msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
r6o->prefix);
r6->defined = false;
return false;
}
void
add_route_to_option_list (struct route_option_list *l,
const char *network,
@ -330,6 +411,23 @@ add_route_to_option_list (struct route_option_list *l,
++l->n;
}
void
add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
const char *prefix,
const char *gateway,
const char *metric)
{
struct route_ipv6_option *ro;
if (l->n >= l->capacity)
msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file",
l->capacity);
ro = &l->routes_ipv6[l->n];
ro->prefix = prefix;
ro->gateway = gateway;
ro->metric = metric;
++l->n;
}
void
clear_route_list (struct route_list *rl)
{
@ -339,6 +437,15 @@ clear_route_list (struct route_list *rl)
rl->capacity = capacity;
}
void
clear_route_ipv6_list (struct route_ipv6_list *rl6)
{
const int capacity = rl6->capacity;
const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
memset(rl6, 0, rl6_size);
rl6->capacity = capacity;
}
void
route_list_add_default_gateway (struct route_list *rl,
struct env_set *es,
@ -469,6 +576,72 @@ init_route_list (struct route_list *rl,
return ret;
}
bool
init_route_ipv6_list (struct route_ipv6_list *rl6,
const struct route_ipv6_option_list *opt6,
const char *remote_endpoint,
int default_metric,
struct env_set *es)
{
struct gc_arena gc = gc_new ();
bool ret = true;
clear_route_ipv6_list (rl6);
rl6->flags = opt6->flags;
if (default_metric)
{
rl6->default_metric = default_metric;
rl6->default_metric_defined = true;
}
/* "default_gateway" is stuff for "redirect-gateway", which we don't
* do for IPv6 yet -> TODO
*/
{
dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF");
}
if ( is_route_parm_defined( remote_endpoint ))
{
if ( inet_pton( AF_INET6, remote_endpoint,
&rl6->remote_endpoint_ipv6) == 1 )
{
rl6->remote_endpoint_defined = true;
}
else
{
msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint);
ret = false;
}
}
else
rl6->remote_endpoint_defined = false;
if (!(opt6->n >= 0 && opt6->n <= rl6->capacity))
msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity);
/* parse the routes from opt to rl6 */
{
int i, j = 0;
for (i = 0; i < opt6->n; ++i)
{
if (!init_route_ipv6 (&rl6->routes_ipv6[j],
&opt6->routes_ipv6[i],
rl6 ))
ret = false;
else
++j;
}
rl6->n = j;
}
gc_free (&gc);
return ret;
}
static void
add_route3 (in_addr_t network,
in_addr_t netmask,
@ -704,10 +877,13 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
}
void
add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
redirect_default_route_to_vpn (rl, tt, flags, es);
if (!rl->routes_added)
if (rl)
redirect_default_route_to_vpn (rl, tt, flags, es);
if (rl && !rl->routes_added)
{
int i;
@ -732,12 +908,27 @@ add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags,
}
rl->routes_added = true;
}
if (rl6 && !rl6->routes_added)
{
int i;
for (i = 0; i < rl6->n; ++i)
{
struct route_ipv6 *r = &rl6->routes_ipv6[i];
if (flags & ROUTE_DELETE_FIRST)
delete_route_ipv6 (r, tt, flags, es);
add_route_ipv6 (r, tt, flags, es);
}
rl6->routes_added = true;
}
}
void
delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
if (rl->routes_added)
if (rl && rl->routes_added)
{
int i;
for (i = rl->n - 1; i >= 0; --i)
@ -747,9 +938,28 @@ delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flag
}
rl->routes_added = false;
}
undo_redirect_default_route_to_vpn (rl, tt, flags, es);
clear_route_list (rl);
if ( rl )
{
undo_redirect_default_route_to_vpn (rl, tt, flags, es);
clear_route_list (rl);
}
if ( rl6 && rl6->routes_added )
{
int i;
for (i = rl6->n - 1; i >= 0; --i)
{
const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
delete_route_ipv6 (r6, tt, flags, es);
}
rl6->routes_added = false;
}
if ( rl6 )
{
clear_route_ipv6_list (rl6);
}
}
#ifdef ENABLE_DEBUG
@ -832,6 +1042,34 @@ setenv_routes (struct env_set *es, const struct route_list *rl)
setenv_route (es, &rl->routes[i], i + 1);
}
static void
setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
{
struct gc_arena gc = gc_new ();
if (r6->defined)
{
struct buffer name1 = alloc_buf_gc( 256, &gc );
struct buffer val = alloc_buf_gc( 256, &gc );
struct buffer name2 = alloc_buf_gc( 256, &gc );
buf_printf( &name1, "route_ipv6_network_%d", i );
buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
r6->netbits );
setenv_str( es, BSTR(&name1), BSTR(&val) );
buf_printf( &name2, "route_ipv6_gateway_%d", i );
setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
}
gc_free (&gc);
}
void
setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
{
int i;
for (i = 0; i < rl6->n; ++i)
setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
}
void
add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
@ -1025,6 +1263,136 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gc_free (&gc);
}
void
add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
const char *network;
const char *gateway;
bool status = false;
const char *device = tt->actual_name;
int byte, bits_to_clear;
struct in6_addr network_copy = r6->network;
if (!r6->defined)
return;
gc_init (&gc);
argv_init (&argv);
/* clear host bit parts of route
* (needed if routes are specified improperly, or if we need to
* explicitely setup the "connected" network routes on some OSes)
*/
byte = 15;
bits_to_clear = 128 - r6->netbits;
while( byte >= 0 && bits_to_clear > 0 )
{
if ( bits_to_clear >= 8 )
{ network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
else
{ network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
}
network = print_in6_addr( network_copy, 0, &gc);
gateway = print_in6_addr( r6->gateway, 0, &gc);
msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s",
network, r6->netbits, gateway, r6->metric, device );
/*
* Filter out routes which are essentially no-ops
* (not currently done for IPv6)
*/
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
argv_printf (&argv, "%s -6 route add %s/%d dev %s",
iproute_path,
network,
r6->netbits,
device);
if (r6->metric_defined)
argv_printf_cat (&argv, " metric %d", r6->metric);
#else
argv_printf (&argv, "%s -A inet6 add %s/%d dev %s",
ROUTE_PATH,
network,
r6->netbits,
device);
if (r6->metric_defined)
argv_printf_cat (&argv, " metric %d", r6->metric);
#endif /*CONFIG_FEATURE_IPROUTE*/
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
#elif defined (WIN32)
msg( M_FATAL, "no idea how to set IPv6 routes on windows (unimplemented)" );
#elif defined (TARGET_SOLARIS)
/* example: route add -inet6 2001:db8::/32 somegateway 0 */
/* for some weird reason, this does not work for me unless I set
* "metric 0" - otherwise, the routes will be nicely installed, but
* packets will just disappear somewhere. So we use "0" now...
*/
argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
ROUTE_PATH,
network,
r6->netbits,
gateway );
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
argv_printf (&argv, "%s add -inet6 %s/%d -iface %s",
ROUTE_PATH,
network,
r6->netbits,
device );
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed");
#elif defined(TARGET_DARWIN)
argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s",
ROUTE_PATH,
network, r6->netbits, device );
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed");
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
/* GERT-TODO: this needs real-world testing on OpenBSD, but it should work
*/
argv_printf (&argv, "%s add -inet6 %s/%d %s",
ROUTE_PATH,
network, r6->netbits, gateway );
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD/OpenBSD route add -inet6 command failed");
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script");
#endif
r6->defined = status;
argv_reset (&argv);
gc_free (&gc);
}
static void
delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
@ -1164,6 +1532,101 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
gc_free (&gc);
}
static void
delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
const char *network;
const char *gateway;
const char *device = tt->actual_name;
if (!r6->defined)
return;
gc_init (&gc);
argv_init (&argv);
network = print_in6_addr( r6->network, 0, &gc);
gateway = print_in6_addr( r6->gateway, 0, &gc);
msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits );
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
argv_printf (&argv, "%s -6 route del %s/%d dev %s",
iproute_path,
network,
r6->netbits,
device);
#else
argv_printf (&argv, "%s -A inet6 del %s/%d dev %s",
ROUTE_PATH,
network,
r6->netbits,
device);
#endif /*CONFIG_FEATURE_IPROUTE*/
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed");
#elif defined (WIN32)
msg( M_FATAL, "no idea how to delete IPv6 routes on windows (unimplemented)" );
#elif defined (TARGET_SOLARIS)
/* example: route delete -inet6 2001:db8::/32 somegateway */
/* GERT-TODO: this is untested, but should work */
argv_printf (&argv, "%s delete -inet6 %s/%d %s",
ROUTE_PATH,
network,
r6->netbits,
gateway );
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete -inet6 command failed");
#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
argv_printf (&argv, "%s delete -inet6 %s/%d -iface %s",
ROUTE_PATH,
network,
r6->netbits,
device );
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
#elif defined(TARGET_DARWIN)
argv_printf (&argv, "%s delete -inet6 %s -prefixlen %d -iface %s",
ROUTE_PATH,
network, r6->netbits, device );
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route delete -inet6 command failed");
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
/* GERT-TODO: this needs real-world testing on OpenBSD, but it should work
*/
argv_printf (&argv, "%s delete -inet6 %s/%d %s",
ROUTE_PATH,
network, r6->netbits, gateway );
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD/OpenBSD route delete -inet6 command failed");
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script");
#endif
argv_reset (&argv);
gc_free (&gc);
}
/*
* The --redirect-gateway option requires OS-specific code below
* to get the current default gateway.

61
route.h
View file

@ -92,6 +92,19 @@ struct route_option_list {
struct route_option routes[EMPTY_ARRAY_SIZE];
};
struct route_ipv6_option {
const char *prefix; /* e.g. "2001:db8:1::/64" */
const char *gateway; /* e.g. "2001:db8:0::2" */
const char *metric; /* e.g. "5" */
};
struct route_ipv6_option_list {
unsigned int flags;
int capacity;
int n;
struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE];
};
struct route {
bool defined;
const struct route_option *option;
@ -113,6 +126,31 @@ struct route_list {
struct route routes[EMPTY_ARRAY_SIZE];
};
struct route_ipv6 {
bool defined;
const struct route_ipv6_option *option;
struct in6_addr network;
int netbits;
struct in6_addr gateway;
bool metric_defined;
int metric;
};
struct route_ipv6_list {
bool routes_added;
unsigned int flags;
int default_metric;
bool default_metric_defined;
struct in6_addr remote_endpoint_ipv6;
bool remote_endpoint_defined;
bool did_redirect_default_gateway; /* TODO (?) */
bool did_local; /* TODO (?) */
int capacity;
int n;
struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE];
};
#if P2MP
/* internal OpenVPN route */
struct iroute {
@ -120,15 +158,24 @@ struct iroute {
int netbits;
struct iroute *next;
};
struct iroute_ipv6 {
struct in6_addr network;
unsigned int netbits;
struct iroute_ipv6 *next;
};
#endif
struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);
void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
void add_route_to_option_list (struct route_option_list *l,
const char *network,
@ -136,6 +183,11 @@ void add_route_to_option_list (struct route_option_list *l,
const char *gateway,
const char *metric);
void add_route_ipv6_to_option_list (struct route_ipv6_option_list *l,
const char *prefix,
const char *gateway,
const char *metric);
bool init_route_list (struct route_list *rl,
const struct route_option_list *opt,
const char *remote_endpoint,
@ -143,21 +195,30 @@ bool init_route_list (struct route_list *rl,
in_addr_t remote_host,
struct env_set *es);
bool init_route_ipv6_list (struct route_ipv6_list *rl6,
const struct route_ipv6_option_list *opt6,
const char *remote_endpoint,
int default_metric,
struct env_set *es);
void route_list_add_default_gateway (struct route_list *rl,
struct env_set *es,
const in_addr_t addr);
void add_routes (struct route_list *rl,
struct route_ipv6_list *rl6,
const struct tuntap *tt,
unsigned int flags,
const struct env_set *es);
void delete_routes (struct route_list *rl,
struct route_ipv6_list *rl6,
const struct tuntap *tt,
unsigned int flags,
const struct env_set *es);
void setenv_routes (struct env_set *es, const struct route_list *rl);
void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6);
bool is_special_addr (const char *addr_str);

View file

@ -342,6 +342,24 @@ ip_addr_dotted_quad_safe (const char *dotted_quad)
}
}
bool
ipv6_addr_safe (const char *ipv6_text_addr)
{
/* verify non-NULL */
if (!ipv6_text_addr)
return false;
/* verify length is within limits */
if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN )
return false;
/* verify that string will convert to IPv6 address */
{
struct in6_addr a6;
return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
}
}
static bool
dns_addr_safe (const char *addr)
{
@ -2032,6 +2050,58 @@ print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc)
return BSTR (&out);
}
/*
* Convert an in6_addr in host byte order
* to an ascii representation of an IPv6 address
* (we reuse the L_INET_NTOA mutex, no contention here)
*/
const char *
print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
{
struct buffer out = alloc_buf_gc (64, gc);
char tmp_out_buf[64]; /* inet_ntop wants pointer to buffer */
if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 ||
!(flags & IA_EMPTY_IF_UNDEF))
{
mutex_lock_static (L_INET_NTOA);
inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
buf_printf (&out, "%s", tmp_out_buf );
mutex_unlock_static (L_INET_NTOA);
}
return BSTR (&out);
}
/* add some offset to an ipv6 address
* (add in steps of 32 bits, taking overflow into next round)
*/
#ifndef s6_addr32
# ifdef TARGET_SOLARIS
# define s6_addr32 _S6_un._S6_u32
# else
# define s6_addr32 __u6_addr.__u6_addr32
# endif
#endif
#ifndef UINT32_MAX
# define UINT32_MAX (4294967295U)
#endif
struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add )
{
int i;
uint32_t h;
for( i=3; i>=0 && add > 0 ; i-- )
{
h = ntohl( base.s6_addr32[i] );
base.s6_addr32[i] = htonl( (h+add) & UINT32_MAX );
/* 32-bit overrun?
* caveat: can't do "h+add > UINT32_MAX" with 32bit math!
*/
add = ( h > UINT32_MAX - add )? 1: 0;
}
return base;
}
/* set environmental variables for ip/port in *addr */
void
setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)

View file

@ -351,6 +351,8 @@ const char *print_link_socket_actual (const struct link_socket_actual *act,
#define IA_EMPTY_IF_UNDEF (1<<0)
#define IA_NET_ORDER (1<<1)
const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
const char *print_in6_addr (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc);
struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add );
#define SA_IP_PORT (1<<0)
#define SA_SET_IF_NONZERO (1<<1)
@ -404,6 +406,7 @@ int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr);
bool ip_addr_dotted_quad_safe (const char *dotted_quad);
bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn);
bool mac_addr_safe (const char *mac_addr);
bool ipv6_addr_safe (const char *ipv6_text_addr);
socket_descriptor_t create_socket_tcp (void);

267
tun.c
View file

@ -62,7 +62,7 @@ static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
#endif
#ifdef TARGET_SOLARIS
static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6);
#include <stropts.h>
#endif
@ -143,14 +143,15 @@ guess_tuntap_dev (const char *dev,
* If ipv6_explicitly_supported is true, then we have explicit
* OS-specific tun dev code for handling IPv6. If so, tt->ipv6
* is set according to the --tun-ipv6 command line option.
*
* (enabling IPv6 on tun devices might work anyway, but since
* we don't know, we log a warning)
*/
static void
ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
{
tt->ipv6 = false;
if (ipv6_explicitly_supported)
tt->ipv6 = ipv6;
else if (ipv6)
tt->ipv6 = ipv6;
if (ipv6 && !ipv6_explicitly_supported)
msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
}
@ -423,6 +424,8 @@ init_tun (const char *dev, /* --dev option */
int topology, /* one of the TOP_x values */
const char *ifconfig_local_parm, /* --ifconfig parm 1 */
const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 IPv6 */
const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 IPv6 */
in_addr_t local_public,
in_addr_t remote_public,
const bool strict_warn,
@ -430,6 +433,7 @@ init_tun (const char *dev, /* --dev option */
{
struct gc_arena gc = gc_new ();
struct tuntap *tt;
bool tun;
ALLOC_OBJ (tt, struct tuntap);
clear_tuntap (tt);
@ -437,18 +441,17 @@ init_tun (const char *dev, /* --dev option */
tt->type = dev_type_enum (dev, dev_type);
tt->topology = topology;
/*
* We only handle TUN/TAP devices here, not --dev null devices.
*/
tun = is_tun_p2p (tt);
if (ifconfig_local_parm && ifconfig_remote_netmask_parm)
{
bool tun = false;
const char *ifconfig_local = NULL;
const char *ifconfig_remote_netmask = NULL;
const char *ifconfig_broadcast = NULL;
/*
* We only handle TUN/TAP devices here, not --dev null devices.
*/
tun = is_tun_p2p (tt);
/*
* Convert arguments to binary IPv4 addresses.
*/
@ -537,6 +540,40 @@ init_tun (const char *dev, /* --dev option */
tt->did_ifconfig_setup = true;
}
if (ifconfig_ipv6_local_parm && ifconfig_ipv6_remote_parm)
{
const char *ifconfig_ipv6_local = NULL;
const char *ifconfig_ipv6_remote = NULL;
/*
* Convert arguments to binary IPv6 addresses.
*/
if ( inet_pton( AF_INET6, ifconfig_ipv6_local_parm, &tt->local_ipv6 ) != 1 ||
inet_pton( AF_INET6, ifconfig_ipv6_remote_parm, &tt->remote_ipv6 ) != 1 )
{
msg( M_FATAL, "init_tun: problem converting IPv6 ifconfig addresses %s and %s to binary", ifconfig_ipv6_local_parm, ifconfig_ipv6_remote_parm );
}
tt->netbits_ipv6 = 64;
/*
* Set ifconfig parameters
*/
ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
/*
* Set environmental variables with ifconfig parameters.
*/
if (es)
{
setenv_str (es, "ifconfig_ipv6_local", ifconfig_ipv6_local);
setenv_str (es, "ifconfig_ipv6_remote", ifconfig_ipv6_remote);
}
tt->did_ifconfig_ipv6_setup = true;
}
gc_free (&gc);
return tt;
}
@ -574,10 +611,15 @@ do_ifconfig (struct tuntap *tt,
const char *ifconfig_local = NULL;
const char *ifconfig_remote_netmask = NULL;
const char *ifconfig_broadcast = NULL;
const char *ifconfig_ipv6_local = NULL;
const char *ifconfig_ipv6_remote = NULL;
bool do_ipv6 = false;
struct argv argv;
argv_init (&argv);
msg( M_INFO, "do_ifconfig, ipv6=%d", tt->ipv6 );
/*
* We only handle TUN/TAP devices here, not --dev null devices.
*/
@ -589,6 +631,13 @@ do_ifconfig (struct tuntap *tt,
ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
{
ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc);
do_ipv6 = true;
}
/*
* If TAP-style device, generate broadcast address.
*/
@ -635,6 +684,18 @@ do_ifconfig (struct tuntap *tt,
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
if ( do_ipv6 ) /* GERT-TODO: yet UNTESTED! */
{
argv_printf( &argv,
"%s -6 addr add %s/%d dev %s",
iproute_path,
ifconfig_ipv6_local,
tt->netbits_ipv6,
actual
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ip -6 addr add failed");
}
} else {
argv_printf (&argv,
"%s addr add dev %s %s/%d broadcast %s",
@ -670,6 +731,18 @@ do_ifconfig (struct tuntap *tt,
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
if ( do_ipv6 )
{
argv_printf (&argv,
"%s %s inet6 add %s/%d",
IFCONFIG_PATH,
actual,
ifconfig_ipv6_local,
tt->netbits_ipv6
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig inet6 failed");
}
tt->did_ifconfig = true;
#endif /*CONFIG_FEATURE_IPROUTE*/
@ -693,7 +766,7 @@ do_ifconfig (struct tuntap *tt,
argv_msg (M_INFO, &argv);
if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
solaris_error_close (tt, es, actual);
solaris_error_close (tt, es, actual, false);
argv_printf (&argv,
"%s %s netmask 255.255.255.255",
@ -725,7 +798,27 @@ do_ifconfig (struct tuntap *tt,
argv_msg (M_INFO, &argv);
if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
solaris_error_close (tt, es, actual);
solaris_error_close (tt, es, actual, false);
if ( do_ipv6 ) /* GERT-TODO: UNTESTED */
{
argv_printf (&argv, "%s %s inet6 unplumb",
IFCONFIG_PATH, actual );
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, 0, NULL);
argv_printf (&argv,
"%s %s inet6 plumb %s/%d %s up",
IFCONFIG_PATH,
actual,
ifconfig_ipv6_local,
tt->netbits_ipv6,
ifconfig_ipv6_remote
);
argv_msg (M_INFO, &argv);
if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig IPv6 failed"))
solaris_error_close (tt, es, actual, true);
}
if (!tun && tt->topology == TOP_SUBNET)
{
@ -787,10 +880,19 @@ do_ifconfig (struct tuntap *tt,
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
if ( do_ipv6 )
{
msg( M_FATAL, "can't configure IPv6 on OpenBSD yet - unimplemented" );
}
tt->did_ifconfig = true;
#elif defined(TARGET_NETBSD)
/* as on OpenBSD and Darwin, destroy and re-create tun0 interface
*/
argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual );
argv_msg (M_INFO, &argv);
if (tun)
argv_printf (&argv,
"%s %s %s %s mtu %d netmask 255.255.255.255 up",
@ -817,6 +919,27 @@ do_ifconfig (struct tuntap *tt,
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
if ( do_ipv6 )
{
struct route_ipv6 r6;
argv_printf (&argv,
"%s %s inet6 %s/%d",
IFCONFIG_PATH,
actual,
ifconfig_ipv6_local,
tt->netbits_ipv6
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
/* and, hooray, we explicitely need to add a route... */
r6.defined = true;
r6.network = tt->local_ipv6;
r6.netbits = tt->netbits_ipv6;
r6.gateway = tt->local_ipv6;
add_route_ipv6 (&r6, tt, 0, es);
}
tt->did_ifconfig = true;
#elif defined(TARGET_DARWIN)
@ -882,6 +1005,27 @@ do_ifconfig (struct tuntap *tt,
add_route (&r, tt, 0, es);
}
if ( do_ipv6 )
{
struct route_ipv6 r6;
argv_printf (&argv,
"%s %s inet6 %s/%d",
IFCONFIG_PATH,
actual,
ifconfig_ipv6_local,
tt->netbits_ipv6
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed");
/* and, hooray, we explicitely need to add a route... */
r6.defined = true;
r6.network = tt->local_ipv6;
r6.netbits = tt->netbits_ipv6;
r6.gateway = tt->local_ipv6;
add_route_ipv6 (&r6, tt, 0, es);
}
#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
@ -920,6 +1064,19 @@ do_ifconfig (struct tuntap *tt,
add_route (&r, tt, 0, es);
}
if ( do_ipv6 )
{
argv_printf (&argv,
"%s %s inet6 %s/%d",
IFCONFIG_PATH,
actual,
ifconfig_ipv6_local,
tt->netbits_ipv6
);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed");
}
#elif defined (WIN32)
{
/*
@ -959,6 +1116,10 @@ do_ifconfig (struct tuntap *tt,
tt->did_ifconfig = true;
}
if ( do_ipv6 )
{
msg( M_FATAL, "can't configure IPv6 on Win32 yet - unimplemented" );
}
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
#endif
@ -1415,6 +1576,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
bool is_tun;
struct strioctl strioc_if, strioc_ppa;
/* improved generic TUN/TAP driver from
* http://www.whiteboard.ne.jp/~admin2/tuntap/
* has IPv6 support
*/
ipv6_support (ipv6, true, tt);
memset(&ifr, 0x0, sizeof(ifr));
@ -1622,11 +1787,20 @@ close_tun (struct tuntap *tt)
}
static void
solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
solaris_error_close (struct tuntap *tt, const struct env_set *es,
const char *actual, bool unplumb_inet6 )
{
struct argv argv;
argv_init (&argv);
if (unplumb_inet6)
{
argv_printf( &argv, "%s %s inet6 unplumb",
IFCONFIG_PATH, actual );
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, 0, "Solaris ifconfig inet6 unplumb failed");
}
argv_printf (&argv,
"%s %s unplumb",
IFCONFIG_PATH,
@ -1774,17 +1948,34 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
#elif defined(TARGET_NETBSD)
/*
* NetBSD does not support IPv6 on tun out of the box,
* but there exists a patch. When this patch is applied,
* only two things are left to openvpn:
* NetBSD before 4.0 does not support IPv6 on tun out of the box,
* but there exists a patch (sys/net/if_tun.c, 1.79->1.80, see PR 32944).
*
* When this patch is applied, only two things are left to openvpn:
* 1. Activate multicasting (this has already been done
* before by the kernel, but we make sure that nobody
* has deactivated multicasting inbetween.
* 2. Deactivate "link layer mode" (otherwise NetBSD
* prepends the address family to the packet, and we
* would run into the same trouble as with OpenBSD.
*
* ... unfortunately, it doesn't work that way. If TUN_IFHEAD is disabled
* ("no prepending of the AF"), then the kernel code just drops IPv6 packets
* on output, and gets confused on input.
*
* So we have to do it the same way as FreeBSD and OpenBSD do it
* (and we really should merge FreeBSD, NetBSD and OpenBSD together)
*/
static inline int
netbsd_modify_read_write_return (int len)
{
if (len > 0)
return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
else
return len;
}
void
open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
{
@ -1795,12 +1986,20 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
ioctl (tt->fd, TUNSIFMODE, &i); /* multicast on */
i = 0;
ioctl (tt->fd, TUNSLMODE, &i); /* link layer mode off */
i = 1;
if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0) /* multi-af mode on */
{
msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
}
}
}
void
close_tun (struct tuntap *tt)
{
/* TODO: we really should cleanup non-persistant tunX with
* "ifconfig tunX destroy" here...
*/
if (tt)
{
close_tun_generic (tt);
@ -1811,12 +2010,46 @@ close_tun (struct tuntap *tt)
int
write_tun (struct tuntap* tt, uint8_t *buf, int len)
{
if (tt->type == DEV_TYPE_TUN)
{
u_int32_t type;
struct iovec iv[2];
struct openvpn_iphdr *iph;
iph = (struct openvpn_iphdr *) buf;
if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6)
type = htonl (AF_INET6);
else
type = htonl (AF_INET);
iv[0].iov_base = (char *)&type;
iv[0].iov_len = sizeof (type);
iv[1].iov_base = buf;
iv[1].iov_len = len;
return netbsd_modify_read_write_return (writev (tt->fd, iv, 2));
}
else
return write (tt->fd, buf, len);
}
int
read_tun (struct tuntap* tt, uint8_t *buf, int len)
{
if (tt->type == DEV_TYPE_TUN)
{
u_int32_t type;
struct iovec iv[2];
iv[0].iov_base = (char *)&type;
iv[0].iov_len = sizeof (type);
iv[1].iov_base = buf;
iv[1].iov_len = len;
return netbsd_modify_read_write_return (readv (tt->fd, iv, 2));
}
else
return read (tt->fd, buf, len);
}

7
tun.h
View file

@ -130,6 +130,7 @@ struct tuntap
int topology; /* one of the TOP_x values */
bool did_ifconfig_setup;
bool did_ifconfig_ipv6_setup;
bool did_ifconfig;
bool ipv6;
@ -146,6 +147,10 @@ struct tuntap
in_addr_t remote_netmask;
in_addr_t broadcast;
struct in6_addr local_ipv6;
struct in6_addr remote_ipv6;
int netbits_ipv6;
#ifdef WIN32
HANDLE hand;
struct overlapped_io reads;
@ -219,6 +224,8 @@ struct tuntap *init_tun (const char *dev, /* --dev option */
int topology, /* one of the TOP_x values */
const char *ifconfig_local_parm, /* --ifconfig parm 1 */
const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 / IPv6 */
const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 / IPv6 */
in_addr_t local_public,
in_addr_t remote_public,
const bool strict_warn,