VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp@ 79563

Last change on this file since 79563 was 79563, checked in by vboxsync, 5 years ago

Dhcpd: Went over the Dhcpd and related code adding comments and doing some exception vetting. bugref:9288

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.7 KB
Line 
1/* $Id: DhcpMessage.cpp 79563 2019-07-06 01:22:56Z vboxsync $ */
2/** @file
3 * DHCP Message and its de/serialization.
4 */
5
6/*
7 * Copyright (C) 2017-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "DhcpdInternal.h"
19#include "DhcpMessage.h"
20#include "DhcpOptions.h"
21
22#include <iprt/string.h>
23#include <iprt/stream.h>
24
25
26
27DhcpMessage::DhcpMessage()
28 : m_xid(0), m_flags(0),
29 m_ciaddr(), m_yiaddr(), m_siaddr(), m_giaddr(),
30 m_sname(), m_file(),
31 m_optMessageType()
32{
33}
34
35
36/* static */
37DhcpClientMessage *DhcpClientMessage::parse(bool broadcasted, const void *buf, size_t buflen)
38{
39 if (buflen < RT_OFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
40 {
41 RTPrintf("%s: %zu bytes datagram is too short\n", __FUNCTION__, buflen);
42 return NULL;
43 }
44
45 PCRTNETBOOTP bp = (PCRTNETBOOTP)buf;
46
47 if (bp->bp_op != RTNETBOOTP_OP_REQUEST)
48 {
49 RTPrintf("%s: bad opcode: %d\n", __FUNCTION__, bp->bp_op);
50 return NULL;
51 }
52
53 if (bp->bp_htype != RTNET_ARP_ETHER)
54 {
55 RTPrintf("%s: unsupported htype %d\n", __FUNCTION__, bp->bp_htype);
56 return NULL;
57 }
58
59 if (bp->bp_hlen != sizeof(RTMAC))
60 {
61 RTPrintf("%s: unexpected hlen %d\n", __FUNCTION__, bp->bp_hlen);
62 return NULL;
63 }
64
65 if ( (bp->bp_chaddr.Mac.au8[0] & 0x01) != 0
66 && (bp->bp_flags & RTNET_DHCP_FLAG_BROADCAST) == 0)
67 {
68 RTPrintf("%s: multicast chaddr %RTmac without broadcast flag\n",
69 __FUNCTION__, &bp->bp_chaddr.Mac);
70 }
71
72 /* we don't want to deal with forwarding */
73 if (bp->bp_giaddr.u != 0)
74 {
75 RTPrintf("%s: giaddr %RTnaipv4\n", __FUNCTION__, bp->bp_giaddr.u);
76 return NULL;
77 }
78
79 if (bp->bp_hops != 0)
80 {
81 RTPrintf("%s: non-zero hops %d\n", __FUNCTION__, bp->bp_hops);
82 return NULL;
83 }
84
85 std::unique_ptr<DhcpClientMessage> msg(new DhcpClientMessage());
86
87 msg->m_broadcasted = broadcasted;
88
89 msg->m_xid = bp->bp_xid;
90 msg->m_flags = bp->bp_flags;
91
92 msg->m_mac = bp->bp_chaddr.Mac;
93
94 msg->m_ciaddr = bp->bp_ciaddr;
95 msg->m_yiaddr = bp->bp_yiaddr;
96 msg->m_siaddr = bp->bp_siaddr;
97 msg->m_giaddr = bp->bp_giaddr;
98
99 if (bp->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
100 {
101 RTPrintf("bad cookie\n");
102 return NULL;
103 }
104
105 int fOptOverload = msg->parseOptions(&bp->bp_vend.Dhcp.dhcp_opts, buflen - RT_OFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts));
106 if (fOptOverload < 0)
107 return NULL;
108
109 /* "The 'file' field MUST be interpreted next ..." */
110 if (fOptOverload & RTNET_DHCP_OPTION_OVERLOAD_FILE)
111 {
112 int status = msg->parseOptions(bp->bp_file, sizeof(bp->bp_file));
113 if (status != 0)
114 return NULL;
115 }
116 else if (bp->bp_file[0] != '\0')
117 {
118 /* must be zero terminated, ignore if not */
119 const char *pszFile = (const char *)bp->bp_file;
120 size_t len = RTStrNLen(pszFile, sizeof(bp->bp_file));
121 if (len < sizeof(bp->bp_file))
122 msg->m_file.assign(pszFile, len);
123 }
124
125 /* "... followed by the 'sname' field." */
126 if (fOptOverload & RTNET_DHCP_OPTION_OVERLOAD_SNAME)
127 {
128 int status = msg->parseOptions(bp->bp_sname, sizeof(bp->bp_sname));
129 if (status != 0) /* NB: this includes "nested" Option Overload */
130 return NULL;
131 }
132 else if (bp->bp_sname[0] != '\0')
133 {
134 /* must be zero terminated, ignore if not */
135 const char *pszSName = (const char *)bp->bp_sname;
136 size_t len = RTStrNLen(pszSName, sizeof(bp->bp_sname));
137 if (len < sizeof(bp->bp_sname))
138 msg->m_sname.assign(pszSName, len);
139 }
140
141 msg->m_optMessageType = OptMessageType(*msg);
142 if (!msg->m_optMessageType.present())
143 return NULL;
144
145 msg->m_id = ClientId(msg->m_mac, OptClientId(*msg));
146
147 return msg.release();
148}
149
150
151int DhcpClientMessage::parseOptions(const void *buf, size_t buflen)
152{
153 int fOptOverload = 0;
154 const uint8_t *data = static_cast<const uint8_t *>(buf);
155 while (buflen > 0)
156 {
157 uint8_t const opt = *data++;
158 --buflen;
159
160 if (opt == RTNET_DHCP_OPT_PAD)
161 continue;
162
163 if (opt == RTNET_DHCP_OPT_END)
164 break;
165
166 if (buflen == 0)
167 {
168 RTPrintf("option %d has no length field\n", opt);
169 return -1;
170 }
171
172 uint8_t optlen = *data++;
173 --buflen;
174
175 if (optlen > buflen)
176 {
177 RTPrintf("option %d truncated (length %d, but only %lu bytes left)\n", opt, optlen, (unsigned long)buflen);
178 return -1;
179 }
180
181#if 0
182 rawopts_t::const_iterator it(m_optmap.find(opt));
183 if (it != m_optmap.cend())
184 return -1;
185#endif
186 if (opt == RTNET_DHCP_OPT_OPTION_OVERLOAD)
187 {
188 if (optlen != 1)
189 {
190 RTPrintf("Overload Option (option %d) has invalid length %d\n", opt, optlen);
191 return -1;
192 }
193
194 fOptOverload = *data;
195
196 if ((fOptOverload & ~RTNET_DHCP_OPTION_OVERLOAD_MASK) != 0)
197 {
198 RTPrintf("Overload Option (option %d) has invalid value 0x%x\n", opt, fOptOverload);
199 return -1;
200 }
201 }
202 else
203 m_rawopts.insert(std::make_pair(opt, octets_t(data, data + optlen)));
204
205 data += optlen;
206 buflen -= optlen;
207 }
208
209 return fOptOverload;
210}
211
212
213void DhcpClientMessage::dump() const
214{
215 switch (m_optMessageType.value())
216 {
217 case RTNET_DHCP_MT_DISCOVER:
218 LogDHCP(("DISCOVER"));
219 break;
220
221 case RTNET_DHCP_MT_REQUEST:
222 LogDHCP(("REQUEST"));
223 break;
224
225 case RTNET_DHCP_MT_INFORM:
226 LogDHCP(("INFORM"));
227 break;
228
229 case RTNET_DHCP_MT_DECLINE:
230 LogDHCP(("DECLINE"));
231 break;
232
233 case RTNET_DHCP_MT_RELEASE:
234 LogDHCP(("RELEASE"));
235 break;
236
237 default:
238 LogDHCP(("<Unknown Mesage Type %d>", m_optMessageType.value()));
239 break;
240 }
241
242 if (OptRapidCommit(*this).present())
243 LogDHCP((" (rapid commit)"));
244
245
246 const OptServerId sid(*this);
247 if (sid.present())
248 LogDHCP((" for server %RTnaipv4", sid.value().u));
249
250 LogDHCP((" xid 0x%08x", m_xid));
251 LogDHCP((" chaddr %RTmac\n", &m_mac));
252
253 const OptClientId cid(*this);
254 if (cid.present()) {
255 if (cid.value().size() > 0)
256 LogDHCP((" client id: %.*Rhxs\n", cid.value().size(), &cid.value().front()));
257 else
258 LogDHCP((" client id: <empty>\n"));
259 }
260
261 LogDHCP((" ciaddr %RTnaipv4", m_ciaddr.u));
262 if (m_yiaddr.u != 0)
263 LogDHCP((" yiaddr %RTnaipv4", m_yiaddr.u));
264 if (m_siaddr.u != 0)
265 LogDHCP((" siaddr %RTnaipv4", m_siaddr.u));
266 if (m_giaddr.u != 0)
267 LogDHCP((" giaddr %RTnaipv4", m_giaddr.u));
268 LogDHCP(("%s\n", broadcast() ? "broadcast" : ""));
269
270
271 const OptRequestedAddress reqAddr(*this);
272 if (reqAddr.present())
273 LogDHCP((" requested address %RTnaipv4", reqAddr.value().u));
274 const OptLeaseTime reqLeaseTime(*this);
275 if (reqLeaseTime.present())
276 LogDHCP((" requested lease time %d", reqAddr.value()));
277 if (reqAddr.present() || reqLeaseTime.present())
278 LogDHCP(("\n"));
279
280 const OptParameterRequest params(*this);
281 if (params.present())
282 {
283 LogDHCP((" params {"));
284 typedef OptParameterRequest::value_t::const_iterator it_t;
285 for (it_t it = params.value().begin(); it != params.value().end(); ++it)
286 LogDHCP((" %d", *it));
287 LogDHCP((" }\n"));
288 }
289
290 bool fHeader = true;
291 for (rawopts_t::const_iterator it = m_rawopts.begin(); it != m_rawopts.end(); ++it)
292 {
293 const uint8_t optcode = (*it).first;
294 switch (optcode) {
295 case OptMessageType::optcode: /* FALLTHROUGH */
296 case OptClientId::optcode: /* FALLTHROUGH */
297 case OptRequestedAddress::optcode: /* FALLTHROUGH */
298 case OptLeaseTime::optcode: /* FALLTHROUGH */
299 case OptParameterRequest::optcode: /* FALLTHROUGH */
300 case OptRapidCommit::optcode:
301 break;
302
303 default:
304 if (fHeader)
305 {
306 LogDHCP((" other options:"));
307 fHeader = false;
308 }
309 LogDHCP((" %d", optcode));
310 break;
311 }
312 }
313 if (!fHeader)
314 LogDHCP(("\n"));
315}
316
317
318DhcpServerMessage::DhcpServerMessage(const DhcpClientMessage &req,
319 uint8_t messageTypeParam, RTNETADDRIPV4 serverAddr)
320 : DhcpMessage(),
321 m_optServerId(serverAddr)
322{
323 m_dst.u = 0xffffffff; /* broadcast */
324
325 m_optMessageType = OptMessageType(messageTypeParam);
326
327 /* copy values from the request (cf. RFC2131 Table 3) */
328 m_xid = req.xid();
329 m_flags = req.flags();
330 m_giaddr = req.giaddr();
331 m_mac = req.mac();
332
333 if (req.messageType() == RTNET_DHCP_MT_REQUEST)
334 m_ciaddr = req.ciaddr();
335}
336
337
338void DhcpServerMessage::maybeUnicast(const DhcpClientMessage &req)
339{
340 if (!req.broadcast() && req.ciaddr().u != 0)
341 setDst(req.ciaddr());
342}
343
344
345void DhcpServerMessage::addOption(DhcpOption *opt)
346{
347 m_optmap << opt;
348}
349
350
351void DhcpServerMessage::addOptions(const optmap_t &optmap)
352{
353 for (optmap_t::const_iterator it ( optmap.begin() );
354 it != optmap.end(); ++it)
355 {
356 m_optmap << it->second;
357 }
358}
359
360
361int DhcpServerMessage::encode(octets_t &data)
362{
363 /*
364 * Header, including DHCP cookie.
365 */
366 RTNETBOOTP bp;
367 RT_ZERO(bp);
368
369 bp.bp_op = RTNETBOOTP_OP_REPLY;
370 bp.bp_htype = RTNET_ARP_ETHER;
371 bp.bp_hlen = sizeof(RTMAC);
372
373 bp.bp_xid = m_xid;
374
375 bp.bp_ciaddr = m_ciaddr;
376 bp.bp_yiaddr = m_yiaddr;
377 bp.bp_siaddr = m_siaddr;
378 bp.bp_giaddr = m_giaddr;
379
380 bp.bp_chaddr.Mac = m_mac;
381
382 bp.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
383
384 data.insert(data.end(), (uint8_t *)&bp, (uint8_t *)&bp.bp_vend.Dhcp.dhcp_opts);
385
386 /*
387 * Options
388 */
389 data << m_optServerId
390 << m_optMessageType;
391
392 for (optmap_t::const_iterator it ( m_optmap.begin() );
393 it != m_optmap.end(); ++it)
394 {
395 RTPrintf("encoding option %d\n", it->first);
396 DhcpOption &opt = *it->second;
397 data << opt;
398 }
399
400 data << OptEnd();
401
402 if (data.size() < 548) /* XXX */
403 data.resize(548);
404
405 return VINF_SUCCESS;
406}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette