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-2025 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, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "dhcp.h"
30#include "socket_util.h"
31#include "error.h"
32
33#include "memdbg.h"
34
35static int
36get_dhcp_message_type(const struct dhcp *dhcp, const int optlen)
37{
38 const uint8_t *p = (uint8_t *)(dhcp + 1);
39 int i;
40
41 for (i = 0; i < optlen; ++i)
42 {
43 const uint8_t type = p[i];
44 const int room = optlen - i;
45 if (type == DHCP_END) /* didn't find what we were looking for */
46 {
47 return -1;
48 }
49 else if (type == DHCP_PAD) /* no-operation */
50 {
51 }
52 else if (type == DHCP_MSG_TYPE) /* what we are looking for */
53 {
54 if (room >= 3)
55 {
56 if (p[i + 1] == 1) /* option length should be 1 */
57 {
58 return p[i + 2]; /* return message type */
59 }
60 }
61 return -1;
62 }
63 else /* some other option */
64 {
65 if (room >= 2)
66 {
67 const int len = p[i + 1]; /* get option length */
68 i += (len + 1); /* advance to next option */
69 }
70 }
71 }
72 return -1;
73}
74
75#if defined(__GNUC__) || defined(__clang__)
76#pragma GCC diagnostic push
77#pragma GCC diagnostic ignored "-Wconversion"
78#endif
79
80static in_addr_t
81do_extract(struct dhcp *dhcp, int optlen)
82{
83 uint8_t *p = (uint8_t *)(dhcp + 1);
84 int i;
85 in_addr_t ret = 0;
86
87 for (i = 0; i < optlen;)
88 {
89 const uint8_t type = p[i];
90 const int room = optlen - i;
91 if (type == DHCP_END)
92 {
93 break;
94 }
95 else if (type == DHCP_PAD)
96 {
97 ++i;
98 }
99 else if (type == DHCP_ROUTER)
100 {
101 if (room >= 2)
102 {
103 const int len = p[i + 1]; /* get option length */
104 if (len <= (room - 2))
105 {
106 /* get router IP address */
107 if (!ret && len >= 4 && (len & 3) == 0)
108 {
109 memcpy(&ret, p + i + 2, 4);
110 ret = ntohl(ret);
111 }
112 {
113 /* delete the router option */
114 uint8_t *dest = p + i;
115 const int owlen = len + 2; /* len of data to overwrite */
116 uint8_t *src = dest + owlen;
117 uint8_t *end = p + optlen;
118 const int movlen = end - src;
119 if (movlen > 0)
120 {
121 memmove(dest, src, movlen); /* overwrite router option */
122 }
123 memset(end - owlen, DHCP_PAD, owlen); /* pad tail */
124 }
125 }
126 else
127 {
128 break;
129 }
130 }
131 else
132 {
133 break;
134 }
135 }
136 else /* some other option */
137 {
138 if (room >= 2)
139 {
140 const int len = p[i + 1]; /* get option length */
141 i += (len + 2); /* advance to next option */
142 }
143 else
144 {
145 break;
146 }
147 }
148 }
149 return ret;
150}
151
152in_addr_t
154{
155 struct dhcp_full *df = (struct dhcp_full *)BPTR(ipbuf);
156 const int optlen =
157 BLEN(ipbuf)
158 - (sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr) + sizeof(struct dhcp));
159
160 if (optlen >= 0 && df->ip.protocol == OPENVPN_IPPROTO_UDP
161 && df->udp.source == htons(BOOTPS_PORT) && df->udp.dest == htons(BOOTPC_PORT)
162 && df->dhcp.op == BOOTREPLY)
163 {
164 const int message_type = get_dhcp_message_type(&df->dhcp, optlen);
165 if (message_type == DHCPACK || message_type == DHCPOFFER)
166 {
167 /* get the router IP address while padding out all DHCP router options */
168 const in_addr_t ret = do_extract(&df->dhcp, optlen);
169
170 /* recompute the UDP checksum */
171 df->udp.check = 0;
172 df->udp.check = htons(ip_checksum(
173 AF_INET, (uint8_t *)&df->udp,
174 sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
175 (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr, OPENVPN_IPPROTO_UDP));
176
177 /* only return the extracted Router address if DHCPACK */
178 if (message_type == DHCPACK)
179 {
180 if (ret)
181 {
182 struct gc_arena gc = gc_new();
183 msg(D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t(ret, 0, &gc));
184 gc_free(&gc);
185 }
186
187 return ret;
188 }
189 }
190 }
191 return 0;
192}
193
194#if defined(__GNUC__) || defined(__clang__)
195#pragma GCC diagnostic pop
196#endif
#define BPTR(buf)
Definition buffer.h:123
#define BLEN(buf)
Definition buffer.h:126
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
in_addr_t dhcp_extract_router_msg(struct buffer *ipbuf)
Definition dhcp.c:153
static in_addr_t do_extract(struct dhcp *dhcp, int optlen)
Definition dhcp.c:81
static int get_dhcp_message_type(const struct dhcp *dhcp, const int optlen)
Definition dhcp.c:36
#define DHCP_PAD
Definition dhcp.h:33
#define DHCP_ROUTER
Definition dhcp.h:34
#define DHCP_MSG_TYPE
Definition dhcp.h:35
#define DHCPACK
Definition dhcp.h:43
#define BOOTPS_PORT
Definition dhcp.h:49
#define BOOTPC_PORT
Definition dhcp.h:50
#define BOOTREPLY
Definition dhcp.h:55
#define DHCPOFFER
Definition dhcp.h:40
#define DHCP_END
Definition dhcp.h:36
#define D_ROUTE
Definition errlevel.h:79
#define msg(flags,...)
Definition error.h:152
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:120
#define OPENVPN_IPPROTO_UDP
Definition proto.h:106
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
struct openvpn_udphdr udp
Definition dhcp.h:77
struct dhcp dhcp
Definition dhcp.h:78
struct openvpn_iphdr ip
Definition dhcp.h:76
Definition dhcp.h:53
uint8_t op
Definition dhcp.h:56
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
uint32_t saddr
Definition proto.h:111
uint32_t daddr
Definition proto.h:112
uint8_t protocol
Definition proto.h:108
uint16_t check
Definition proto.h:159
uint16_t source
Definition proto.h:156
uint16_t dest
Definition proto.h:157
struct gc_arena gc
Definition test_ssl.c:131