mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-09-20 03:52:28 +02:00
840799182c
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
207 lines
5.7 KiB
C
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;
|
|
}
|