Added "--server-bridge" (without parameters) to enable

DHCP proxy mode:  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.

Added "--route-gateway dhcp", to enable the extraction
of the gateway address from a DHCP negotiation with the
OpenVPN server-side LAN.

Modified client.conf and server.conf to reflect new option
modes.

Incremented version to 2.1_rc9a.


git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@3164 e7ae566f-a301-0410-adde-c780ea21d3b5
This commit is contained in:
james 2008-08-02 08:02:53 +00:00
parent 1698613d90
commit 03731db31b
14 changed files with 427 additions and 34 deletions

View File

@ -79,6 +79,7 @@ openvpn_SOURCES = \
circ_list.h \
common.h \
crypto.c crypto.h \
dhcp.c dhcp.h \
errlevel.h \
error.c error.h \
event.c event.h \

183
dhcp.c Normal file
View File

@ -0,0 +1,183 @@
/*
* 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;
}

87
dhcp.h Normal file
View File

@ -0,0 +1,87 @@
/*
* 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

View File

@ -31,6 +31,7 @@
#include "mss.h"
#include "event.h"
#include "ps.h"
#include "dhcp.h"
#include "memdbg.h"
@ -976,6 +977,8 @@ process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf)
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)
{
@ -1001,6 +1004,13 @@ process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf)
/* 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);
}
}
}
}
@ -1149,7 +1159,7 @@ process_outgoing_tun (struct context *c)
* 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))
{

View File

@ -75,6 +75,7 @@ bool send_control_channel_string (struct context *c, const char *str, int msglev
#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);

View File

@ -53,6 +53,14 @@ print_opt_route_gateway (const in_addr_t route_gateway, struct gc_arena *gc)
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)
{
@ -170,7 +178,7 @@ helper_client_server (struct options *o)
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)
@ -295,8 +303,19 @@ helper_client_server (struct options *o)
*
* 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");
@ -310,18 +329,29 @@ helper_client_server (struct options *o)
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 */

View File

@ -1214,11 +1214,18 @@ table (not supported on all OSes).
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
@ -2607,13 +2614,23 @@ if you are ethernet bridging. Use
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
@ -2662,6 +2679,23 @@ push "route-gateway 10.8.0.4"
.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"

View File

@ -169,7 +169,7 @@ 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-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"
@ -339,7 +339,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-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"
@ -1226,6 +1226,7 @@ show_settings (const struct options *o)
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);
@ -1888,7 +1889,7 @@ static void
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;
@ -4237,14 +4238,21 @@ add_option (struct options *options,
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])
@ -4395,6 +4403,11 @@ add_option (struct options *options,
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);

View File

@ -302,6 +302,7 @@ struct options
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 */
@ -340,6 +341,8 @@ struct options
# 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;

10
route.c
View File

@ -335,6 +335,16 @@ clear_route_list (struct route_list *rl)
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,

13
route.h
View File

@ -138,6 +138,10 @@ bool init_route_list (struct route_list *rl,
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,
@ -186,4 +190,13 @@ netbits_to_netmask (const int netbits)
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

View File

@ -100,7 +100,7 @@ key client.key
# 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.

View File

@ -114,6 +114,18 @@ ifconfig-pool-persist ipp.txt
# 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
@ -170,22 +182,18 @@ ifconfig-pool-persist ipp.txt
# 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.

View File

@ -1,5 +1,5 @@
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])