0
0
mirror of https://github.com/OpenVPN/openvpn.git synced 2024-09-20 03:52:28 +02:00
openvpn/dhcp.c
James Yonan 840799182c Fixed client issues with DHCP Router option extraction/deletion when
using layer 2 with DHCP proxy:

* Extract/delete Router option from both DHCPOFFER and DHCPACK
  messages.  Prevously we only considered DHCPACK messages.
  With DHCPACK messages, we extract the Router IP for
  use as the vpn_gateway, as well as delete the Router option from
  the DHCP message.  For DHCPOFFER, we only delete the Router
  message.

* Monitor all DHCPOFFER and DHCPACK messages for possible Router
  options needing to be extracted/deleted.  Previously, we turned
  off monitoring after the first successful extraction/deletion
  from a DHCPACK message.

* Previously, we deleted Router options by padding them with DHCP
  PAD options.  This has proven not to work with some DHCP clients,
  so we now delete the message entirely, and add PADs to the end of
  the message so as not to change its length.

* In some cases, UDP checksum was not being correctly updated for
  modified DHCP packets.

To properly use this feature on Linux, after tunnel comes up,
run these commands:

  ifconfig tap0 up
  dhclient tap0

Version 2.1.17

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@7682 e7ae566f-a301-0410-adde-c780ea21d3b5
2011-12-14 17:04:05 +01:00

207 lines
5.7 KiB
C

/*
* 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-2010 OpenVPN Technologies, 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, int optlen)
{
uint8_t *p = (uint8_t *) (dhcp + 1);
int i;
in_addr_t ret = 0;
for (i = 0; i < optlen; )
{
const uint8_t type = p[i];
const int room = optlen - i;
if (type == DHCP_END)
break;
else if (type == DHCP_PAD)
++i;
else if (type == DHCP_ROUTER)
{
if (room >= 2)
{
const int len = p[i+1]; /* get option length */
if (len <= (room-2))
{
/* get router IP address */
if (!ret && len >= 4 && (len & 3) == 0)
{
memcpy (&ret, p+i+2, 4);
ret = ntohl (ret);
}
{
/* delete the router option */
uint8_t *dest = p + i;
const int owlen = len + 2; /* len of data to overwrite */
uint8_t *src = dest + owlen;
uint8_t *end = p + optlen;
const int movlen = end - src;
if (movlen > 0)
memmove(dest, src, movlen); /* overwrite router option */
memset(end - owlen, DHCP_PAD, owlen); /* pad tail */
}
}
else
break;
}
else
break;
}
else /* some other option */
{
if (room >= 2)
{
const int len = p[i+1]; /* get option length */
i += (len + 2); /* advance to next option */
}
else
break;
}
}
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)
{
const int message_type = get_dhcp_message_type (&df->dhcp, optlen);
if (message_type == DHCPACK || message_type == DHCPOFFER)
{
/* 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 = 0;
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));
/* only return the extracted Router address if DHCPACK */
if (message_type == DHCPACK)
{
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;
}
}
}
return 0;
}