VirtualBox

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

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

Dhcpd: a couple of more todos. bugref:9288

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