OpenVPN
dhcp.c
Go to the documentation of this file.
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  *
8  * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
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 along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "syshead.h"
29 
30 #include "dhcp.h"
31 #include "socket.h"
32 #include "error.h"
33 
34 #include "memdbg.h"
35 
36 static int
37 get_dhcp_message_type(const struct dhcp *dhcp, const int optlen)
38 {
39  const uint8_t *p = (uint8_t *) (dhcp + 1);
40  int i;
41 
42  for (i = 0; i < optlen; ++i)
43  {
44  const uint8_t type = p[i];
45  const int room = optlen - i;
46  if (type == DHCP_END) /* didn't find what we were looking for */
47  {
48  return -1;
49  }
50  else if (type == DHCP_PAD) /* no-operation */
51  {
52  }
53  else if (type == DHCP_MSG_TYPE) /* what we are looking for */
54  {
55  if (room >= 3)
56  {
57  if (p[i+1] == 1) /* option length should be 1 */
58  {
59  return p[i+2]; /* return message type */
60  }
61  }
62  return -1;
63  }
64  else /* some other option */
65  {
66  if (room >= 2)
67  {
68  const int len = p[i+1]; /* get option length */
69  i += (len + 1); /* advance to next option */
70  }
71  }
72  }
73  return -1;
74 }
75 
76 static in_addr_t
77 do_extract(struct dhcp *dhcp, int optlen)
78 {
79  uint8_t *p = (uint8_t *) (dhcp + 1);
80  int i;
81  in_addr_t ret = 0;
82 
83  for (i = 0; i < optlen; )
84  {
85  const uint8_t type = p[i];
86  const int room = optlen - i;
87  if (type == DHCP_END)
88  {
89  break;
90  }
91  else if (type == DHCP_PAD)
92  {
93  ++i;
94  }
95  else if (type == DHCP_ROUTER)
96  {
97  if (room >= 2)
98  {
99  const int len = p[i+1]; /* get option length */
100  if (len <= (room-2))
101  {
102  /* get router IP address */
103  if (!ret && len >= 4 && (len & 3) == 0)
104  {
105  memcpy(&ret, p+i+2, 4);
106  ret = ntohl(ret);
107  }
108  {
109  /* delete the router option */
110  uint8_t *dest = p + i;
111  const int owlen = len + 2; /* len of data to overwrite */
112  uint8_t *src = dest + owlen;
113  uint8_t *end = p + optlen;
114  const int movlen = end - src;
115  if (movlen > 0)
116  {
117  memmove(dest, src, movlen); /* overwrite router option */
118  }
119  memset(end - owlen, DHCP_PAD, owlen); /* pad tail */
120  }
121  }
122  else
123  {
124  break;
125  }
126  }
127  else
128  {
129  break;
130  }
131  }
132  else /* some other option */
133  {
134  if (room >= 2)
135  {
136  const int len = p[i+1]; /* get option length */
137  i += (len + 2); /* advance to next option */
138  }
139  else
140  {
141  break;
142  }
143  }
144  }
145  return ret;
146 }
147 
148 in_addr_t
150 {
151  struct dhcp_full *df = (struct dhcp_full *) BPTR(ipbuf);
152  const int optlen = BLEN(ipbuf) - (sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr) + sizeof(struct dhcp));
153 
154  if (optlen >= 0
156  && df->udp.source == htons(BOOTPS_PORT)
157  && df->udp.dest == htons(BOOTPC_PORT)
158  && df->dhcp.op == BOOTREPLY)
159  {
160  const int message_type = get_dhcp_message_type(&df->dhcp, optlen);
161  if (message_type == DHCPACK || message_type == DHCPOFFER)
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 = 0;
168  df->udp.check = htons(ip_checksum(AF_INET, (uint8_t *)&df->udp,
169  sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
170  (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr,
172 
173  /* only return the extracted Router address if DHCPACK */
174  if (message_type == DHCPACK)
175  {
176  if (ret)
177  {
178  struct gc_arena gc = gc_new();
179  msg(D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t(ret, 0, &gc));
180  gc_free(&gc);
181  }
182 
183  return ret;
184  }
185  }
186  }
187  return 0;
188 }
dhcp_full::ip
struct openvpn_iphdr ip
Definition: dhcp.h:75
openvpn_udphdr::source
uint16_t source
Definition: proto.h:169
error.h
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1030
D_ROUTE
#define D_ROUTE
Definition: errlevel.h:80
OPENVPN_IPPROTO_UDP
#define OPENVPN_IPPROTO_UDP
Definition: proto.h:122
BOOTREPLY
#define BOOTREPLY
Definition: dhcp.h:55
dhcp_full::udp
struct openvpn_udphdr udp
Definition: dhcp.h:76
get_dhcp_message_type
static int get_dhcp_message_type(const struct dhcp *dhcp, const int optlen)
Definition: dhcp.c:37
DHCP_MSG_TYPE
#define DHCP_MSG_TYPE
Definition: dhcp.h:36
do_extract
static in_addr_t do_extract(struct dhcp *dhcp, int optlen)
Definition: dhcp.c:77
DHCP_PAD
#define DHCP_PAD
Definition: dhcp.h:34
openvpn_udphdr
Definition: proto.h:168
openvpn_iphdr::daddr
uint32_t daddr
Definition: proto.h:128
openvpn_iphdr::protocol
uint8_t protocol
Definition: proto.h:124
openvpn_iphdr::saddr
uint32_t saddr
Definition: proto.h:127
BLEN
#define BLEN(buf)
Definition: buffer.h:127
DHCP_ROUTER
#define DHCP_ROUTER
Definition: dhcp.h:35
dhcp_extract_router_msg
in_addr_t dhcp_extract_router_msg(struct buffer *ipbuf)
Definition: dhcp.c:149
openvpn_udphdr::check
uint16_t check
Definition: proto.h:172
dhcp::op
uint8_t op
Definition: dhcp.h:56
buffer
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
DHCPOFFER
#define DHCPOFFER
Definition: dhcp.h:41
BOOTPC_PORT
#define BOOTPC_PORT
Definition: dhcp.h:51
print_in_addr_t
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
Definition: socket.c:2904
syshead.h
BPTR
#define BPTR(buf)
Definition: buffer.h:124
dhcp.h
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
DHCP_END
#define DHCP_END
Definition: dhcp.h:37
DHCPACK
#define DHCPACK
Definition: dhcp.h:44
BOOTPS_PORT
#define BOOTPS_PORT
Definition: dhcp.h:50
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1038
socket.h
config.h
dhcp_full::dhcp
struct dhcp dhcp
Definition: dhcp.h:77
openvpn_udphdr::dest
uint16_t dest
Definition: proto.h:170
openvpn_iphdr
Definition: proto.h:106
memdbg.h
msg
#define msg(flags,...)
Definition: error.h:144
dhcp_full
Definition: dhcp.h:74
dhcp
Definition: dhcp.h:53
ip_checksum
uint16_t ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, const uint8_t *src_addr, const uint8_t *dest_addr, const int proto)
Calculates an IP or IPv6 checksum with a pseudo header as required by TCP, UDP and ICMPv6.
Definition: proto.c:123