VirtualBox

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

Last change on this file since 76690 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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