VirtualBox

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

Last change on this file since 94068 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: DhcpMessage.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * DHCP Message and its de/serialization.
4 */
5
6/*
7 * Copyright (C) 2017-2022 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "DhcpdInternal.h"
23#include "DhcpMessage.h"
24#include "DhcpOptions.h"
25
26#include <iprt/ctype.h>
27#include <iprt/string.h>
28
29
30
31DhcpMessage::DhcpMessage()
32 : m_xid(0)
33 , m_flags(0)
34 , m_ciaddr()
35 , m_yiaddr()
36 , m_siaddr()
37 , m_giaddr()
38#if 0 /* not currently unused */
39 , m_sname()
40 , m_file()
41#endif
42 , m_optMessageType()
43{
44}
45
46
47/**
48 * Does common message dumping.
49 */
50void DhcpMessage::dump() const RT_NOEXCEPT
51{
52 switch (m_optMessageType.value())
53 {
54 case RTNET_DHCP_MT_DISCOVER: LogRel(("DISCOVER")); break;
55 case RTNET_DHCP_MT_OFFER: LogRel(("OFFER")); break;
56 case RTNET_DHCP_MT_REQUEST: LogRel(("REQUEST")); break;
57 case RTNET_DHCP_MT_DECLINE: LogRel(("DECLINE")); break;
58 case RTNET_DHCP_MT_ACK: LogRel(("ACK")); break;
59 case RTNET_DHCP_MT_NAC: LogRel(("NAC")); break;
60 case RTNET_DHCP_MT_RELEASE: LogRel(("RELEASE")); break;
61 case RTNET_DHCP_MT_INFORM: LogRel(("INFORM")); break;
62 default:
63 LogRel(("<Unknown Mesage Type %d>", m_optMessageType.value()));
64 break;
65 }
66
67 LogRel((" xid 0x%08x", m_xid));
68 LogRel((" chaddr %RTmac\n", &m_mac));
69 LogRel((" ciaddr %RTnaipv4", m_ciaddr.u));
70 if (m_yiaddr.u != 0)
71 LogRel((" yiaddr %RTnaipv4", m_yiaddr.u));
72 if (m_siaddr.u != 0)
73 LogRel((" siaddr %RTnaipv4", m_siaddr.u));
74 if (m_giaddr.u != 0)
75 LogRel((" giaddr %RTnaipv4", m_giaddr.u));
76 if (broadcast())
77 LogRel((" broadcast\n"));
78 else
79 LogRel(("\n"));
80}
81
82
83/*********************************************************************************************************************************
84* DhcpClientMessage Implementation *
85*********************************************************************************************************************************/
86
87/* static */
88DhcpClientMessage *DhcpClientMessage::parse(bool broadcasted, const void *buf, size_t buflen)
89{
90 /*
91 * Validate the request.
92 */
93 if (buflen < RT_OFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts))
94 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: %zu bytes datagram is too short\n", buflen));
95
96 PCRTNETBOOTP bp = (PCRTNETBOOTP)buf;
97
98 if (bp->bp_op != RTNETBOOTP_OP_REQUEST)
99 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: bad opcode: %d\n", bp->bp_op));
100
101 if (bp->bp_htype != RTNET_ARP_ETHER)
102 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: unsupported htype %d\n", bp->bp_htype));
103
104 if (bp->bp_hlen != sizeof(RTMAC))
105 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: unexpected hlen %d\n", bp->bp_hlen));
106
107 if ( (bp->bp_chaddr.Mac.au8[0] & 0x01) != 0
108 && (bp->bp_flags & RTNET_DHCP_FLAG_BROADCAST) == 0)
109 LogRel2(("DhcpClientMessage::parse: multicast chaddr %RTmac without broadcast flag\n", &bp->bp_chaddr.Mac));
110
111 /* we don't want to deal with forwarding */
112 if (bp->bp_giaddr.u != 0)
113 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: giaddr %RTnaipv4\n", bp->bp_giaddr.u));
114
115 if (bp->bp_hops != 0)
116 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: non-zero hops %d\n", bp->bp_hops));
117
118 if (bp->bp_vend.Dhcp.dhcp_cookie != RT_H2N_U32_C(RTNET_DHCP_COOKIE))
119 DHCP_LOG2_RET_NULL(("DhcpClientMessage::parse: bad cookie %#RX32\n", bp->bp_vend.Dhcp.dhcp_cookie));
120
121 /*
122 * Convert it into a DhcpClientMessage instance.
123 */
124 std::unique_ptr<DhcpClientMessage> msg(new DhcpClientMessage());
125
126 msg->m_broadcasted = broadcasted;
127 msg->m_xid = bp->bp_xid;
128 msg->m_flags = bp->bp_flags;
129 msg->m_mac = bp->bp_chaddr.Mac;
130 msg->m_ciaddr = bp->bp_ciaddr;
131 msg->m_yiaddr = bp->bp_yiaddr;
132 msg->m_siaddr = bp->bp_siaddr;
133 msg->m_giaddr = bp->bp_giaddr;
134
135 int fOptOverload = msg->i_parseOptions(&bp->bp_vend.Dhcp.dhcp_opts[0],
136 buflen - RT_OFFSETOF(RTNETBOOTP, bp_vend.Dhcp.dhcp_opts));
137 if (fOptOverload < 0)
138 return NULL;
139
140 /* "The 'file' field MUST be interpreted next ..." */
141 if (fOptOverload & RTNET_DHCP_OPTION_OVERLOAD_FILE)
142 {
143 int status = msg->i_parseOptions(bp->bp_file, sizeof(bp->bp_file));
144 if (status != 0)
145 return NULL;
146 }
147#if 0 /* not currently unused */
148 else if (bp->bp_file[0] != '\0')
149 {
150 /* must be zero terminated, ignore if not */
151 const char *pszFile = (const char *)bp->bp_file;
152 size_t len = RTStrNLen(pszFile, sizeof(bp->bp_file));
153 if (len < sizeof(bp->bp_file))
154 msg->m_file.assign(pszFile, len);
155 }
156#endif
157
158 /* "... followed by the 'sname' field." */
159 if (fOptOverload & RTNET_DHCP_OPTION_OVERLOAD_SNAME)
160 {
161 int status = msg->i_parseOptions(bp->bp_sname, sizeof(bp->bp_sname));
162 if (status != 0) /* NB: this includes "nested" Option Overload */
163 return NULL;
164 }
165#if 0 /* not currently unused */
166 else if (bp->bp_sname[0] != '\0')
167 {
168 /* must be zero terminated, ignore if not */
169 const char *pszSName = (const char *)bp->bp_sname;
170 size_t len = RTStrNLen(pszSName, sizeof(bp->bp_sname));
171 if (len < sizeof(bp->bp_sname))
172 msg->m_sname.assign(pszSName, len);
173 }
174#endif
175
176 msg->m_optMessageType = OptMessageType(*msg);
177 if (!msg->m_optMessageType.present())
178 return NULL;
179
180 msg->m_id = ClientId(msg->m_mac, OptClientId(*msg));
181
182 return msg.release();
183}
184
185
186int DhcpClientMessage::i_parseOptions(const uint8_t *pbBuf, size_t cbBuf) RT_NOEXCEPT
187{
188 int fOptOverload = 0;
189 while (cbBuf > 0)
190 {
191 uint8_t const bOpt = *pbBuf++;
192 --cbBuf;
193
194 if (bOpt == RTNET_DHCP_OPT_PAD)
195 continue;
196
197 if (bOpt == RTNET_DHCP_OPT_END)
198 break;
199
200 if (cbBuf == 0)
201 DHCP_LOG_RET(-1, ("option %d has no length field\n", bOpt));
202
203 uint8_t const cbOpt = *pbBuf++;
204 --cbBuf;
205
206 if (cbOpt > cbBuf)
207 DHCP_LOG_RET(-1, ("option %d truncated (length %d, but only %zu bytes left)\n", bOpt, cbOpt, cbBuf));
208
209#if 0
210 rawopts_t::const_iterator it(m_optmap.find(bOpt));
211 if (it != m_optmap.cend())
212 return -1;
213#endif
214 if (bOpt == RTNET_DHCP_OPT_OPTION_OVERLOAD)
215 {
216 if (cbOpt != 1)
217 DHCP_LOG_RET(-1, ("Overload Option (option %d) has invalid length %d\n", bOpt, cbOpt));
218
219 fOptOverload = *pbBuf;
220
221 if ((fOptOverload & ~RTNET_DHCP_OPTION_OVERLOAD_MASK) != 0)
222 DHCP_LOG_RET(-1, ("Overload Option (option %d) has invalid value 0x%x\n", bOpt, fOptOverload));
223 }
224 else
225 m_rawopts.insert(std::make_pair(bOpt, octets_t(pbBuf, pbBuf + cbOpt)));
226
227 pbBuf += cbOpt;
228 cbBuf -= cbOpt;
229 }
230
231 return fOptOverload;
232}
233
234
235/**
236 * Dumps the message.
237 */
238void DhcpClientMessage::dump() const RT_NOEXCEPT
239{
240 DhcpMessage::dump();
241
242 if (OptRapidCommit(*this).present())
243 LogRel((" (rapid commit)"));
244
245 try
246 {
247 const OptServerId sid(*this);
248 if (sid.present())
249 LogRel((" for server %RTnaipv4", sid.value().u));
250
251 const OptClientId cid(*this);
252 if (cid.present())
253 {
254 if (cid.value().size() > 0)
255 LogRel((" client id: %.*Rhxs\n", cid.value().size(), &cid.value().front()));
256 else
257 LogRel((" client id: <empty>\n"));
258 }
259
260 const OptRequestedAddress reqAddr(*this);
261 if (reqAddr.present())
262 LogRel((" requested address %RTnaipv4", reqAddr.value().u));
263 const OptLeaseTime reqLeaseTime(*this);
264 if (reqLeaseTime.present())
265 LogRel((" requested lease time %d", reqAddr.value()));
266 if (reqAddr.present() || reqLeaseTime.present())
267 LogRel(("\n"));
268
269 const OptParameterRequest params(*this);
270 if (params.present())
271 {
272 LogRel((" params {"));
273 typedef OptParameterRequest::value_t::const_iterator it_t;
274 for (it_t it = params.value().begin(); it != params.value().end(); ++it)
275 LogRel((" %d", *it));
276 LogRel((" }\n"));
277 }
278 }
279 catch (std::bad_alloc &)
280 {
281 LogRel(("bad_alloc during dumping\n"));
282 }
283
284 for (rawopts_t::const_iterator it = m_rawopts.begin(); it != m_rawopts.end(); ++it)
285 {
286 const uint8_t optcode = (*it).first;
287 switch (optcode)
288 {
289 case OptMessageType::optcode: /* FALLTHROUGH */
290 case OptClientId::optcode: /* FALLTHROUGH */
291 case OptRequestedAddress::optcode: /* FALLTHROUGH */
292 case OptLeaseTime::optcode: /* FALLTHROUGH */
293 case OptParameterRequest::optcode: /* FALLTHROUGH */
294 case OptRapidCommit::optcode:
295 break;
296
297 default:
298 {
299 size_t const cbBytes = it->second.size();
300 uint8_t const *pbBytes = &it->second.front();
301 bool fAllPrintable = true;
302 for (size_t off = 0; off < cbBytes; off++)
303 if (!RT_C_IS_PRINT((char )pbBytes[off]))
304 {
305 fAllPrintable = false;
306 break;
307 }
308 if (fAllPrintable)
309 LogRel((" %2d: '%.*s'\n", optcode, cbBytes, pbBytes));
310 else
311 LogRel((" %2d: %.*Rhxs\n", optcode, cbBytes, pbBytes));
312 }
313 }
314 }
315}
316
317
318
319/*********************************************************************************************************************************
320* DhcpServerMessage Implementation *
321*********************************************************************************************************************************/
322
323DhcpServerMessage::DhcpServerMessage(const DhcpClientMessage &req, uint8_t messageTypeParam, RTNETADDRIPV4 serverAddr)
324 : DhcpMessage()
325 , m_optServerId(serverAddr)
326{
327 m_dst.u = 0xffffffff; /* broadcast */
328
329 m_optMessageType = OptMessageType(messageTypeParam);
330
331 /* copy values from the request (cf. RFC2131 Table 3) */
332 m_xid = req.xid();
333 m_flags = req.flags();
334 m_giaddr = req.giaddr();
335 m_mac = req.mac();
336
337 if (req.messageType() == RTNET_DHCP_MT_REQUEST)
338 m_ciaddr = req.ciaddr();
339}
340
341
342void DhcpServerMessage::maybeUnicast(const DhcpClientMessage &req) RT_NOEXCEPT
343{
344 if (!req.broadcast() && req.ciaddr().u != 0)
345 setDst(req.ciaddr());
346}
347
348
349/**
350 * @throws std::bad_alloc
351 */
352void DhcpServerMessage::addOption(DhcpOption *opt)
353{
354 m_optmap << opt;
355}
356
357
358/**
359 * @throws std::bad_alloc
360 */
361void DhcpServerMessage::addOptions(const optmap_t &optmap)
362{
363 for (optmap_t::const_iterator it = optmap.begin(); it != optmap.end(); ++it)
364 m_optmap << it->second;
365}
366
367
368/**
369 * @throws std::bad_alloc
370 */
371int DhcpServerMessage::encode(octets_t &data)
372{
373 /*
374 * Header, including DHCP cookie.
375 */
376 RTNETBOOTP bp;
377 RT_ZERO(bp);
378
379 bp.bp_op = RTNETBOOTP_OP_REPLY;
380 bp.bp_htype = RTNET_ARP_ETHER;
381 bp.bp_hlen = sizeof(RTMAC);
382
383 bp.bp_xid = m_xid;
384
385 bp.bp_ciaddr = m_ciaddr;
386 bp.bp_yiaddr = m_yiaddr;
387 bp.bp_siaddr = m_siaddr;
388 bp.bp_giaddr = m_giaddr;
389
390 bp.bp_chaddr.Mac = m_mac;
391
392 bp.bp_vend.Dhcp.dhcp_cookie = RT_H2N_U32_C(RTNET_DHCP_COOKIE);
393
394 data.insert(data.end(), (uint8_t *)&bp, (uint8_t *)&bp.bp_vend.Dhcp.dhcp_opts);
395
396 /** @todo TFTP, bootfile name, etc. pick from extended options if no
397 * override in effect? */
398
399 /*
400 * Options
401 */
402 data << m_optServerId
403 << m_optMessageType;
404
405 for (optmap_t::const_iterator it = m_optmap.begin(); it != m_optmap.end(); ++it)
406 {
407 LogRel3(("encoding option %d (%s)\n", it->first, DhcpOption::name(it->first)));
408 DhcpOption &opt = *it->second;
409 data << opt;
410 }
411
412 data << OptEnd();
413
414 AssertCompile(RTNET_DHCP_NORMAL_SIZE == 548);
415 if (data.size() < RTNET_DHCP_NORMAL_SIZE)
416 data.resize(RTNET_DHCP_NORMAL_SIZE);
417
418 /** @todo dump it */
419 if ((LogRelIs4Enabled() && LogRelIsEnabled()) || LogIsEnabled())
420 dump();
421 if ((LogRelIs5Enabled() && LogRelIsEnabled()) || LogIs5Enabled())
422 LogRel5(("encoded message: %u bytes\n%.*Rhxd\n", data.size(), data.size(), &data.front()));
423
424 return VINF_SUCCESS;
425}
426
427
428/**
429 * Dumps a server message to the log.
430 */
431void DhcpServerMessage::dump() const RT_NOEXCEPT
432{
433 DhcpMessage::dump();
434
435 /** @todo dump option details. */
436}
437
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