circ_list.h \
common.h \
crypto.c crypto.h \
+ dhcp.c dhcp.h \
errlevel.h \
error.c error.h \
event.c event.h \
--- /dev/null
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2008 Telethra, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "dhcp.h"
+#include "socket.h"
+#include "error.h"
+
+#include "memdbg.h"
+
+static int
+get_dhcp_message_type (const struct dhcp *dhcp, const int optlen)
+{
+ const uint8_t *p = (uint8_t *) (dhcp + 1);
+ int i;
+
+ for (i = 0; i < optlen; ++i)
+ {
+ const uint8_t type = p[i];
+ const int room = optlen - i;
+ if (type == DHCP_END) /* didn't find what we were looking for */
+ return -1;
+ else if (type == DHCP_PAD) /* no-operation */
+ ;
+ else if (type == DHCP_MSG_TYPE) /* what we are looking for */
+ {
+ if (room >= 3)
+ {
+ if (p[i+1] == 1) /* option length should be 1 */
+ return p[i+2]; /* return message type */
+ }
+ return -1;
+ }
+ else /* some other option */
+ {
+ if (room >= 2)
+ {
+ const int len = p[i+1]; /* get option length */
+ i += (len + 1); /* advance to next option */
+ }
+ }
+ }
+ return -1;
+}
+
+static in_addr_t
+do_extract (struct dhcp *dhcp, const int optlen)
+{
+ uint8_t *p = (uint8_t *) (dhcp + 1);
+ int i;
+ in_addr_t ret = 0;
+
+ for (i = 0; i < optlen; ++i)
+ {
+ const uint8_t type = p[i];
+ const int room = optlen - i;
+ if (type == DHCP_END)
+ break;
+ else if (type == DHCP_PAD)
+ ;
+ else if (type == DHCP_ROUTER)
+ {
+ if (room >= 2)
+ {
+ const int len = p[i+1]; /* get option length */
+ if (len <= (room-2))
+ {
+ if (!ret && len >= 4 && (len & 3) == 0)
+ {
+ memcpy (&ret, p+i+2, 4); /* get router IP address */
+ ret = ntohl (ret);
+ }
+ memset (p+i, DHCP_PAD, len+2); /* delete the router option by padding it out */
+ }
+ i += (len + 1); /* advance to next option */
+ }
+ }
+ else /* some other option */
+ {
+ if (room >= 2)
+ {
+ const int len = p[i+1]; /* get option length */
+ i += (len + 1); /* advance to next option */
+ }
+ }
+ }
+ return ret;
+}
+
+static uint16_t
+udp_checksum (const uint8_t *buf,
+ const int len_udp,
+ const uint8_t *src_addr,
+ const uint8_t *dest_addr)
+{
+ uint16_t word16;
+ uint32_t sum = 0;
+ int i;
+
+ /* make 16 bit words out of every two adjacent 8 bit words and */
+ /* calculate the sum of all 16 bit words */
+ for (i = 0; i < len_udp; i += 2){
+ word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
+ sum += word16;
+ }
+
+ /* add the UDP pseudo header which contains the IP source and destination addresses */
+ for (i = 0; i < 4; i += 2){
+ word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
+ sum += word16;
+ }
+ for (i = 0; i < 4; i += 2){
+ word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
+ sum += word16;
+ }
+
+ /* the protocol number and the length of the UDP packet */
+ sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp;
+
+ /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ /* Take the one's complement of sum */
+ return ((uint16_t) ~sum);
+}
+
+in_addr_t
+dhcp_extract_router_msg (struct buffer *ipbuf)
+{
+ struct dhcp_full *df = (struct dhcp_full *) BPTR (ipbuf);
+ const int optlen = BLEN (ipbuf) - (sizeof (struct openvpn_iphdr) + sizeof (struct openvpn_udphdr) + sizeof (struct dhcp));
+
+ if (optlen >= 0
+ && df->ip.protocol == OPENVPN_IPPROTO_UDP
+ && df->udp.source == htons (BOOTPS_PORT)
+ && df->udp.dest == htons (BOOTPC_PORT)
+ && df->dhcp.op == BOOTREPLY
+ && get_dhcp_message_type (&df->dhcp, optlen) == DHCPACK)
+ {
+ /* get the router IP address while padding out all DHCP router options */
+ const in_addr_t ret = do_extract (&df->dhcp, optlen);
+
+ /* recompute the UDP checksum */
+ df->udp.check = htons (udp_checksum ((uint8_t *) &df->udp,
+ sizeof (struct openvpn_udphdr) + sizeof (struct dhcp) + optlen,
+ (uint8_t *)&df->ip.saddr,
+ (uint8_t *)&df->ip.daddr));
+
+ if (ret)
+ {
+ struct gc_arena gc = gc_new ();
+ msg (D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t (ret, 0, &gc));
+ gc_free (&gc);
+ }
+
+ return ret;
+ }
+ else
+ return 0;
+}
--- /dev/null
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2008 Telethra, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include "common.h"
+#include "buffer.h"
+#include "proto.h"
+
+#pragma pack(1)
+
+/* DHCP Option types */
+#define DHCP_PAD 0
+#define DHCP_ROUTER 3
+#define DHCP_MSG_TYPE 53 /* message type (u8) */
+#define DHCP_END 255
+
+/* DHCP Messages types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+/* DHCP UDP port numbers */
+#define BOOTPS_PORT 67
+#define BOOTPC_PORT 68
+
+struct dhcp {
+# define BOOTREQUEST 1
+# define BOOTREPLY 2
+ uint8_t op; /* message op */
+
+ uint8_t htype; /* hardware address type (e.g. '1' = 10Mb Ethernet) */
+ uint8_t hlen; /* hardware address length (e.g. '6' for 10Mb Ethernet) */
+ uint8_t hops; /* client sets to 0, may be used by relay agents */
+ uint32_t xid; /* transaction ID, chosen by client */
+ uint16_t secs; /* seconds since request process began, set by client */
+ uint16_t flags;
+ uint32_t ciaddr; /* client IP address, client sets if known */
+ uint32_t yiaddr; /* 'your' IP address -- server's response to client */
+ uint32_t siaddr; /* server IP address */
+ uint32_t giaddr; /* relay agent IP address */
+ uint8_t chaddr[16]; /* client hardware address */
+ uint8_t sname[64]; /* optional server host name */
+ uint8_t file[128]; /* boot file name */
+ uint32_t magic; /* must be 0x63825363 (network order) */
+};
+
+struct dhcp_full {
+ struct openvpn_iphdr ip;
+ struct openvpn_udphdr udp;
+ struct dhcp dhcp;
+# define DHCP_OPTIONS_BUFFER_SIZE 256
+ uint8_t options[DHCP_OPTIONS_BUFFER_SIZE];
+};
+
+#pragma pack()
+
+in_addr_t dhcp_extract_router_msg (struct buffer *ipbuf);
+
+#endif
#include "mss.h"
#include "event.h"
#include "ps.h"
+#include "dhcp.h"
#include "memdbg.h"
if (!c->options.passtos)
flags &= ~PIPV4_PASSTOS;
#endif
+ if (!c->options.route_gateway_via_dhcp || !route_list_default_gateway_needed (c->c1.route_list))
+ flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
if (buf->len > 0)
{
/* possibly alter the TCP MSS */
if (flags & PIPV4_MSSFIX)
mss_fixup (&ipbuf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame)));
+
+ /* possibly extract a DHCP router message */
+ if (flags & PIPV4_EXTRACT_DHCP_ROUTER)
+ {
+ const in_addr_t dhcp_router = dhcp_extract_router_msg (&ipbuf);
+ route_list_add_default_gateway (c->c1.route_list, c->c2.es, dhcp_router);
+ }
}
}
}
* The --mssfix option requires
* us to examine the IPv4 header.
*/
- process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_OUTGOING, &c->c2.to_tun);
+ process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_OUTGOING, &c->c2.to_tun);
if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN (&c->c2.frame))
{
#define PIPV4_PASSTOS (1<<0)
#define PIPV4_MSSFIX (1<<1)
#define PIPV4_OUTGOING (1<<2)
+#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
void process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf);
return BSTR (&out);
}
+static const char *
+print_opt_route_gateway_dhcp (struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (32, gc);
+ buf_printf (&out, "route-gateway dhcp");
+ return BSTR (&out);
+}
+
static const char *
print_opt_route (const in_addr_t network, const in_addr_t netmask, struct gc_arena *gc)
{
if (o->client)
msg (M_USAGE, "--server and --client cannot be used together");
- if (o->server_bridge_defined)
+ if (o->server_bridge_defined || o->server_bridge_proxy_dhcp)
msg (M_USAGE, "--server and --server-bridge cannot be used together");
if (o->shared_secret_file)
*
* ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0
* push "route-gateway 10.8.0.4"
+ *
+ * OR
+ *
+ * server-bridge
+ *
+ * EXPANDS TO:
+ *
+ * mode server
+ * tls-server
+ *
+ * push "route-gateway dhcp"
*/
- else if (o->server_bridge_defined)
+ else if (o->server_bridge_defined | o->server_bridge_proxy_dhcp)
{
if (o->client)
msg (M_USAGE, "--server-bridge and --client cannot be used together");
if (dev != DEV_TYPE_TAP)
msg (M_USAGE, "--server-bridge directive only makes sense with --dev tap");
- verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_start, o->server_bridge_netmask);
- verify_common_subnet ("--server-bridge", o->server_bridge_pool_start, o->server_bridge_pool_end, o->server_bridge_netmask);
- verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_end, o->server_bridge_netmask);
+ if (o->server_bridge_defined)
+ {
+ verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_start, o->server_bridge_netmask);
+ verify_common_subnet ("--server-bridge", o->server_bridge_pool_start, o->server_bridge_pool_end, o->server_bridge_netmask);
+ verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_end, o->server_bridge_netmask);
+ }
o->mode = MODE_SERVER;
o->tls_server = true;
- o->ifconfig_pool_defined = true;
- o->ifconfig_pool_start = o->server_bridge_pool_start;
- o->ifconfig_pool_end = o->server_bridge_pool_end;
- ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
- o->ifconfig_pool_netmask = o->server_bridge_netmask;
- push_option (o, print_opt_route_gateway (o->server_bridge_ip, &o->gc), M_USAGE);
+
+ if (o->server_bridge_defined)
+ {
+ o->ifconfig_pool_defined = true;
+ o->ifconfig_pool_start = o->server_bridge_pool_start;
+ o->ifconfig_pool_end = o->server_bridge_pool_end;
+ ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
+ o->ifconfig_pool_netmask = o->server_bridge_netmask;
+ push_option (o, print_opt_route_gateway (o->server_bridge_ip, &o->gc), M_USAGE);
+ }
+ else if (o->server_bridge_proxy_dhcp)
+ {
+ push_option (o, print_opt_route_gateway_dhcp (&o->gc), M_USAGE);
+ }
}
else
#endif /* P2MP_SERVER */
address if OpenVPN is being run in client mode, and is undefined in server mode.
.\"*********************************************************
.TP
-.B --route-gateway gw
+.B --route-gateway gw|'dhcp'
Specify a default gateway
.B gw
for use with
.B --route.
+
+If
+.B dhcp
+is specified as the parameter,
+the gateway address will be extracted from a DHCP
+negotiation with the OpenVPN server-side LAN.
+.\"*********************************************************
.TP
.B --route-metric m
Specify a default metric
instead.
.\"*********************************************************
.TP
-.B --server-bridge gateway netmask pool-start-IP pool-end-IP
+.B --server-bridge [ gateway netmask pool-start-IP pool-end-IP ]
A helper directive similar to
.B --server
which is designed to simplify the configuration
of OpenVPN's server mode in ethernet bridging configurations.
+If
+.B --server-bridge
+is used without any parameters, it will enable a DHCP-proxy
+mode, where connecting OpenVPN clients will receive an IP
+address for their TAP adapter from the DHCP server running
+on the OpenVPN server-side LAN.
+Note that only clients that support
+the binding of a DHCP client with the TAP adapter (such as
+Windows) can support this mode.
+
To configure ethernet bridging, you
must first use your OS's bridging capability
to bridge the TAP interface with the ethernet
.LP
.RE
.fi
+
+In another example,
+.B --server-bridge
+(without parameters) expands as follows:
+
+.RS
+.ft 3
+.nf
+.sp
+mode server
+tls-server
+
+push "route-gateway dhcp"
+.ft
+.LP
+.RE
+.fi
.\"*********************************************************
.TP
.B --push "option"
" 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-gateway gw : Specify a default gateway for use with --route.\n"
+ "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
"--route-metric m : Specify a default metric for use with --route.\n"
"--route-delay n [w] : Delay n seconds after connection initiation before\n"
" adding routes (may be 0). If not specified, routes will\n"
"\n"
"Multi-Client Server options (when --mode server is used):\n"
"--server network netmask : Helper option to easily configure server mode.\n"
- "--server-bridge IP netmask pool-start-IP pool-end-IP : Helper option to\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"
" execution. Peer must specify --pull in its config file.\n"
SHOW_INT (route_delay_window);
SHOW_BOOL (route_delay_defined);
SHOW_BOOL (route_nopull);
+ SHOW_BOOL (route_gateway_via_dhcp);
if (o->routes)
print_route_options (o->routes, D_SHOW_PARMS);
options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce)
{
#if P2MP_SERVER
- if (o->server_defined || o->server_bridge_defined)
+ if (o->server_defined || o->server_bridge_defined || o->server_bridge_proxy_dhcp)
{
if (ce->proto == PROTO_TCPv4)
ce->proto = PROTO_TCPv4_SERVER;
else if (streq (p[0], "route-gateway") && p[1])
{
VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
- if (ip_addr_dotted_quad_safe (p[1]) || is_special_addr (p[1]))
+ if (streq (p[1], "dhcp"))
{
- options->route_default_gateway = p[1];
+ options->route_gateway_via_dhcp = true;
}
else
{
- msg (msglevel, "route-gateway parm '%s' must be an IP address", p[1]);
- goto err;
+ if (ip_addr_dotted_quad_safe (p[1]) || is_special_addr (p[1]))
+ {
+ options->route_default_gateway = p[1];
+ }
+ else
+ {
+ msg (msglevel, "route-gateway parm '%s' must be an IP address", p[1]);
+ goto err;
+ }
}
}
else if (streq (p[0], "route-metric") && p[1])
options->server_bridge_pool_start = pool_start;
options->server_bridge_pool_end = pool_end;
}
+ else if (streq (p[0], "server-bridge") && !p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->server_bridge_proxy_dhcp = true;
+ }
else if (streq (p[0], "push") && p[1])
{
VERIFY_PERMISSION (OPT_P_PUSH);
bool route_delay_defined;
struct route_option_list *routes;
bool route_nopull;
+ bool route_gateway_via_dhcp;
#ifdef ENABLE_OCC
/* Enable options consistency check between peers */
# define SF_NOPOOL (1<<0)
unsigned int server_flags;
+ bool server_bridge_proxy_dhcp;
+
bool server_bridge_defined;
in_addr_t server_bridge_ip;
in_addr_t server_bridge_netmask;
CLEAR (*rl);
}
+void
+route_list_add_default_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr)
+{
+ rl->spec.remote_endpoint = addr;
+ rl->spec.remote_endpoint_defined = true;
+ setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
+}
+
bool
init_route_list (struct route_list *rl,
const struct route_option_list *opt,
in_addr_t remote_host,
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,
const struct tuntap *tt,
unsigned int flags,
return mask;
}
+static inline bool
+route_list_default_gateway_needed (const struct route_list *rl)
+{
+ if (!rl)
+ return false;
+ else
+ return !rl->spec.remote_endpoint_defined;
+}
+
#endif
# your server certificates with the nsCertType
# field set to "server". The build-key-server
# script in the easy-rsa folder will do this.
-;ns-cert-type server
+ns-cert-type server
# If a tls-auth key is used on the server
# then every client must also have the key.
# out unless you are ethernet bridging.
;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100
+# Configure server mode for ethernet bridging
+# using a DHCP-proxy, where clients talk
+# to the OpenVPN server-side DHCP server
+# to receive their IP address allocation
+# and DNS server addresses. You must first use
+# your OS's bridging capability to bridge the TAP
+# interface with the ethernet NIC interface.
+# Note: this mode only works on clients (such as
+# Windows), where the client-side TAP adapter is
+# bound to a DHCP client.
+;server-bridge
+
# Push routes to the client to allow it
# to reach other private subnets behind
# the server. Remember that these
# all IP traffic such as web browsing and
# and DNS lookups to go through the VPN
# (The OpenVPN server machine may need to NAT
-# the TUN/TAP interface to the internet in
-# order for this to work properly).
-# CAVEAT: May break client's network config if
-# client's local DHCP server packets get routed
-# through the tunnel. Solution: make sure
-# client's local DHCP server is reachable via
-# a more specific route than the default route
-# of 0.0.0.0/0.0.0.0.
-;push "redirect-gateway"
+# or bridge the TUN/TAP interface to the internet
+# in order for this to work properly).
+;push "redirect-gateway def1 bypass-dhcp"
# Certain Windows-specific network settings
# can be pushed to clients, such as DNS
# or WINS server addresses. CAVEAT:
# http://openvpn.net/faq.html#dhcpcaveats
-;push "dhcp-option DNS 10.8.0.1"
-;push "dhcp-option WINS 10.8.0.1"
+# The addresses below refer to the public
+# DNS servers provided by opendns.com.
+;push "dhcp-option DNS 208.67.222.222"
+;push "dhcp-option DNS 208.67.220.220"
# Uncomment this directive to allow different
# clients to be able to "see" each other.
dnl define the OpenVPN version
-define(PRODUCT_VERSION,[2.1_rc9])
+define(PRODUCT_VERSION,[2.1_rc9a])
dnl define the TAP version
define(PRODUCT_TAP_ID,[tap0901])
define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])