mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-30 21:24:13 +08:00
367ed084db
Telethra to OpenVPN Technologies. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@3409 e7ae566f-a301-0410-adde-c780ea21d3b5
184 lines
5.2 KiB
C
184 lines
5.2 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-2008 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, 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;
|
|
}
|