VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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