~ubuntu-branches/ubuntu/oneiric/openvpn/oneiric

1.2.1 by Alberto Gonzalez Iniesta
Import upstream version 2.1~rc11
1
/*
2
 *  OpenVPN -- An application to securely tunnel IP networks
3
 *             over a single TCP/UDP port, with support for SSL/TLS-based
4
 *             session authentication and key exchange,
5
 *             packet encryption, packet authentication, and
6
 *             packet compression.
7
 *
1.3.5 by Alberto Gonzalez Iniesta
Import upstream version 2.1.3
8
 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
1.2.1 by Alberto Gonzalez Iniesta
Import upstream version 2.1~rc11
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License version 2
12
 *  as published by the Free Software Foundation.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program (see the file COPYING included with this
21
 *  distribution); if not, write to the Free Software Foundation, Inc.,
22
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 */
24
25
#include "syshead.h"
26
27
#include "dhcp.h"
28
#include "socket.h"
29
#include "error.h"
30
31
#include "memdbg.h"
32
33
static int
34
get_dhcp_message_type (const struct dhcp *dhcp, const int optlen)
35
{
36
  const uint8_t *p = (uint8_t *) (dhcp + 1);
37
  int i;
38
39
  for (i = 0; i < optlen; ++i)
40
    {
41
      const uint8_t type = p[i];
42
      const int room = optlen - i;
43
      if (type == DHCP_END)           /* didn't find what we were looking for */
44
	return -1;
45
      else if (type == DHCP_PAD)      /* no-operation */
46
	;
47
      else if (type == DHCP_MSG_TYPE) /* what we are looking for */
48
	{
49
	  if (room >= 3)
50
	    {
51
	      if (p[i+1] == 1)        /* option length should be 1 */
52
		return p[i+2];        /* return message type */
53
	    }
54
	  return -1;
55
	}
56
      else                            /* some other option */
57
	{
58
	  if (room >= 2)
59
	    {
60
	      const int len = p[i+1]; /* get option length */
61
	      i += (len + 1);         /* advance to next option */
62
	    }
63
	}
64
    }
65
  return -1;
66
}
67
68
static in_addr_t
69
do_extract (struct dhcp *dhcp, const int optlen)
70
{
71
  uint8_t *p = (uint8_t *) (dhcp + 1);
72
  int i;
73
  in_addr_t ret = 0;
74
75
  for (i = 0; i < optlen; ++i)
76
    {
77
      const uint8_t type = p[i];
78
      const int room = optlen - i;
79
      if (type == DHCP_END)
80
	break;
81
      else if (type == DHCP_PAD)
82
	;
83
      else if (type == DHCP_ROUTER)
84
	{
85
	  if (room >= 2)
86
	    {
87
	      const int len = p[i+1]; /* get option length */
88
	      if (len <= (room-2))
89
		{
90
		  if (!ret && len >= 4 && (len & 3) == 0)
91
		    {
92
		      memcpy (&ret, p+i+2, 4);      /* get router IP address */
93
		      ret = ntohl (ret);
94
		    }
95
		  memset (p+i, DHCP_PAD, len+2);    /* delete the router option by padding it out */
96
		}
97
	      i += (len + 1);         /* advance to next option */
98
	    }
99
	}
100
      else                            /* some other option */
101
	{
102
	  if (room >= 2)
103
	    {
104
	      const int len = p[i+1]; /* get option length */
105
	      i += (len + 1);         /* advance to next option */
106
	    }
107
	}
108
    }
109
  return ret;
110
}
111
112
static uint16_t
113
udp_checksum (const uint8_t *buf,
114
	      const int len_udp,
115
	      const uint8_t *src_addr,
116
	      const uint8_t *dest_addr)
117
{
118
  uint16_t word16;
119
  uint32_t sum = 0;
120
  int i;
121
	
122
  /* make 16 bit words out of every two adjacent 8 bit words and  */
123
  /* calculate the sum of all 16 bit words */
124
  for (i = 0; i < len_udp; i += 2){
125
    word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
126
    sum += word16;
127
  }
128
129
  /* add the UDP pseudo header which contains the IP source and destination addresses */
130
  for (i = 0; i < 4; i += 2){
131
    word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
132
    sum += word16;
133
  }
134
  for (i = 0; i < 4; i += 2){
135
    word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
136
    sum += word16; 	
137
  }
138
139
  /* the protocol number and the length of the UDP packet */
140
  sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp;
141
142
  /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
143
  while (sum >> 16)
144
    sum = (sum & 0xFFFF) + (sum >> 16);
145
		
146
  /* Take the one's complement of sum */
147
  return ((uint16_t) ~sum);
148
}
149
150
in_addr_t
151
dhcp_extract_router_msg (struct buffer *ipbuf)
152
{
153
  struct dhcp_full *df = (struct dhcp_full *) BPTR (ipbuf);
154
  const int optlen = BLEN (ipbuf) - (sizeof (struct openvpn_iphdr) + sizeof (struct openvpn_udphdr) + sizeof (struct dhcp));
155
156
  if (optlen >= 0
157
      && df->ip.protocol == OPENVPN_IPPROTO_UDP
158
      && df->udp.source == htons (BOOTPS_PORT)
159
      && df->udp.dest == htons (BOOTPC_PORT)
160
      && df->dhcp.op == BOOTREPLY
161
      && get_dhcp_message_type (&df->dhcp, optlen) == DHCPACK)
162
    {
163
      /* get the router IP address while padding out all DHCP router options */
164
      const in_addr_t ret = do_extract (&df->dhcp, optlen);
165
166
      /* recompute the UDP checksum */
167
      df->udp.check = htons (udp_checksum ((uint8_t *) &df->udp, 
168
					   sizeof (struct openvpn_udphdr) + sizeof (struct dhcp) + optlen,
169
					   (uint8_t *)&df->ip.saddr,
170
					   (uint8_t *)&df->ip.daddr));
171
172
      if (ret)
173
	{
174
	  struct gc_arena gc = gc_new ();
175
	  msg (D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t (ret, 0, &gc));
176
	  gc_free (&gc);
177
	}
178
179
      return ret;
180
    }
181
  else
182
    return 0;
183
}