VirtualBox

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

最後變更 在這個檔案從79563是 79563,由 vboxsync 提交於 6 年 前

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

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 12.5 KB
 
1/* $Id: DHCPD.cpp 79563 2019-07-06 01:22:56Z vboxsync $ */
2/** @file
3 * DHCP server - protocol logic
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.alldomusa.eu.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 "DHCPD.h"
24#include "DhcpOptions.h"
25
26#include <iprt/message.h>
27#include <iprt/cpp/path.h>
28
29
30DHCPD::DHCPD()
31 : m_pConfig(NULL), m_db()
32{
33}
34
35
36/**
37 * Initializes the DHCPD with the given config.
38 *
39 * @returns VBox status code.
40 * @param pConfig The configuration to use.
41 */
42int DHCPD::init(const Config *pConfig) RT_NOEXCEPT
43{
44 Assert(pConfig);
45 AssertReturn(!m_pConfig, VERR_INVALID_STATE);
46
47 /** @todo r=bird: This must be configurable so main can read the database and
48 * fish assignments out of it. (That's the most efficient and accurate way of
49 * figuring out the IP address of a VM.) */
50
51 /* leases filename */
52 int rc = m_strLeasesFilename.assignNoThrow(pConfig->getHome());
53 if (RT_SUCCESS(rc))
54 rc = RTPathAppendCxx(m_strLeasesFilename, pConfig->getBaseName());
55 if (RT_SUCCESS(rc))
56 rc = m_strLeasesFilename.appendNoThrow("-Dhcpd.leases");
57 if (RT_SUCCESS(rc))
58 {
59 /* Load the lease database, ignoring most issues except being out of memory: */
60 rc = m_db.init(pConfig);
61 if (RT_SUCCESS(rc))
62 {
63 rc = i_loadLeases();
64 if (rc != VERR_NO_MEMORY)
65 {
66 m_pConfig = pConfig;
67 rc = VINF_SUCCESS;
68 }
69 else
70 {
71 LogRel(("Ran out of memory loading leases from '%s'. Try rename or delete the file.\n",
72 m_strLeasesFilename.c_str()));
73 RTMsgError("Ran out of memory loading leases from '%s'. Try rename or delete the file.\n",
74 m_strLeasesFilename.c_str());
75 }
76 }
77 }
78 return rc;
79}
80
81
82/**
83 * Load leases from m_strLeasesFilename.
84 */
85int DHCPD::i_loadLeases() RT_NOEXCEPT
86{
87 return m_db.loadLeases(m_strLeasesFilename);
88}
89
90
91/**
92 * Save the current leases to m_strLeasesFilename, doing expiry first.
93 *
94 * This is called after m_db is updated during a client request, so the on disk
95 * database is always up-to-date. This means it doesn't matter if we're
96 * terminated with extreme prejudice, and it allows Main to look up IP addresses
97 * for VMs.
98 *
99 * @throws nothing
100 */
101void DHCPD::i_saveLeases() RT_NOEXCEPT
102{
103 m_db.expire();
104 m_db.writeLeases(m_strLeasesFilename);
105}
106
107
108/**
109 * Process a DHCP client message.
110 *
111 * Called by VBoxNetDhcpd::dhcp4Recv().
112 *
113 * @returns Pointer to DHCP reply (caller deletes this). NULL if no reply
114 * warranted or we're out of memory.
115 * @param req The client message.
116 * @throws nothing
117 */
118DhcpServerMessage *DHCPD::process(DhcpClientMessage &req) RT_NOEXCEPT
119{
120 /*
121 * Dump the package if release log level 3+1 are enable or if debug logging is
122 * enabled. We don't normally want to do this at the default log level, of course.
123 */
124 if ((LogRelIs3Enabled() && LogRelIsEnabled()) || LogIsEnabled())
125 req.dump();
126
127 /*
128 * Fend off requests that are not for us.
129 */
130 OptServerId sid(req);
131 if (sid.present() && sid.value().u != m_pConfig->getIPv4Address().u)
132 {
133 if (req.broadcasted() && req.messageType() == RTNET_DHCP_MT_REQUEST)
134 {
135 LogRel2(("Message is not for us, canceling any pending offer.\n"));
136 m_db.cancelOffer(req);
137 }
138 else
139 LogRel2(("Message is not for us.\n"));
140 return NULL;
141 }
142
143 /*
144 * Process it.
145 */
146 DhcpServerMessage *reply = NULL;
147
148 switch (req.messageType())
149 {
150 /*
151 * Requests that require server's reply.
152 */
153 case RTNET_DHCP_MT_DISCOVER:
154 try
155 {
156 reply = i_doDiscover(req);
157 }
158 catch (std::bad_alloc &)
159 {
160 LogRelFunc(("i_doDiscover threw bad_alloc\n"));
161 }
162 break;
163
164 case RTNET_DHCP_MT_REQUEST:
165 try
166 {
167 reply = i_doRequest(req);
168 }
169 catch (std::bad_alloc &)
170 {
171 LogRelFunc(("i_doRequest threw bad_alloc\n"));
172 }
173 break;
174
175 case RTNET_DHCP_MT_INFORM:
176 try
177 {
178 reply = i_doInform(req);
179 }
180 catch (std::bad_alloc &)
181 {
182 LogRelFunc(("i_doInform threw bad_alloc\n"));
183 }
184 break;
185
186 /*
187 * Requests that don't have a reply.
188 */
189 case RTNET_DHCP_MT_DECLINE:
190 i_doDecline(req);
191 break;
192
193 case RTNET_DHCP_MT_RELEASE:
194 i_doRelease(req);
195 break;
196
197 /*
198 * Unexpected or unknown message types.
199 */
200 case RTNET_DHCP_MT_OFFER:
201 LogRel2(("Ignoring unexpected message of type RTNET_DHCP_MT_OFFER!\n"));
202 break;
203 case RTNET_DHCP_MT_ACK:
204 LogRel2(("Ignoring unexpected message of type RTNET_DHCP_MT_ACK!\n"));
205 break;
206 case RTNET_DHCP_MT_NAC:
207 LogRel2(("Ignoring unexpected message of type RTNET_DHCP_MT_NAC!\n"));
208 break;
209 default:
210 LogRel2(("Ignoring unexpected message of unknown type: %d (%#x)!\n", req.messageType(), req.messageType()));
211 break;
212 }
213
214 return reply;
215}
216
217
218/**
219 * Internal helper.
220 *
221 * @throws std::bad_alloc
222 */
223DhcpServerMessage *DHCPD::i_createMessage(int type, DhcpClientMessage &req)
224{
225 return new DhcpServerMessage(req, type, m_pConfig->getIPv4Address());
226}
227
228
229/**
230 * 4.3.1 DHCPDISCOVER message
231 *
232 * When a server receives a DHCPDISCOVER message from a client, the server
233 * chooses a network address for the requesting client. If no address is
234 * available, the server may choose to report the problem to the system
235 * administrator. If an address is available, the new address SHOULD be chosen
236 * as follows:
237 * - The client's current address as recorded in the client's current binding,
238 * ELSE
239 * - The client's previous address as recorded in the client's (now expired or
240 * released) binding, if that address is in the server's pool of available
241 * addresses and not already allocated, ELSE
242 * - The address requested in the 'Requested IP Address' option, if that
243 * address is valid and not already allocated, ELSE
244 * - A new address allocated from the server's pool of available addresses;
245 * the address is selected based on the subnet from which the message was
246 * received (if 'giaddr' is 0) or on the address of the relay agent that
247 * forwarded the message ('giaddr' when not 0).
248 *
249 * ...
250 *
251 * @throws std::bad_alloc
252 */
253DhcpServerMessage *DHCPD::i_doDiscover(DhcpClientMessage &req)
254{
255 /** @todo
256 * XXX: TODO: Windows iSCSI initiator sends DHCPDISCOVER first and
257 * it has ciaddr filled. Shouldn't let it screw up the normal
258 * lease we already have for that client, but we should probably
259 * reply with a pro-forma offer.
260 */
261 if (req.ciaddr().u != 0)
262 return NULL;
263
264 Binding *b = m_db.allocateBinding(req);
265 if (b == NULL)
266 return NULL;
267
268 std::unique_ptr<DhcpServerMessage> reply;
269
270 bool fRapidCommit = OptRapidCommit(req).present();
271 if (!fRapidCommit)
272 {
273 reply.reset(i_createMessage(RTNET_DHCP_MT_OFFER, req));
274
275 if (b->state() < Binding::OFFERED)
276 b->setState(Binding::OFFERED);
277
278 /** @todo use small lease time internally to quickly free unclaimed offers? */
279 }
280 else
281 {
282 reply.reset(i_createMessage(RTNET_DHCP_MT_ACK, req));
283 reply->addOption(OptRapidCommit(true));
284
285 b->setState(Binding::ACKED);
286 i_saveLeases();
287 }
288
289 reply->setYiaddr(b->addr());
290 reply->addOption(OptLeaseTime(b->leaseTime()));
291
292
293 OptParameterRequest optlist(req);
294 optmap_t replyOptions;
295 reply->addOptions(m_pConfig->getOptions(replyOptions, optlist, req.clientId()));
296
297 // reply->maybeUnicast(req); /** @todo XXX: we reject ciaddr != 0 above */
298 return reply.release();
299}
300
301
302/**
303 * 4.3.2 DHCPREQUEST message
304 *
305 * A DHCPREQUEST message may come from a client responding to a DHCPOFFER
306 * message from a server, from a client verifying a previously allocated IP
307 * address or from a client extending the lease on a network address. If the
308 * DHCPREQUEST message contains a 'server identifier' option, the message is in
309 * response to a DHCPOFFER message. Otherwise, the message is a request to
310 * verify or extend an existing lease. If the client uses a 'client identifier'
311 * in a DHCPREQUEST message, it MUST use that same 'client identifier' in all
312 * subsequent messages. If the client included a list of requested parameters in
313 * a DHCPDISCOVER message, it MUST include that list in all subsequent messages.
314 *
315 * ...
316 *
317 * @throws std::bad_alloc
318 */
319DhcpServerMessage *DHCPD::i_doRequest(DhcpClientMessage &req)
320{
321 OptRequestedAddress reqAddr(req);
322 if (req.ciaddr().u != 0 && reqAddr.present() && reqAddr.value().u != req.ciaddr().u)
323 {
324 std::unique_ptr<DhcpServerMessage> nak (
325 i_createMessage(RTNET_DHCP_MT_NAC, req)
326 );
327 nak->addOption(OptMessage("Requested address does not match ciaddr"));
328 return nak.release();
329 }
330
331
332 Binding *b = m_db.allocateBinding(req);
333 if (b == NULL)
334 {
335 return i_createMessage(RTNET_DHCP_MT_NAC, req);
336 }
337
338
339 std::unique_ptr<DhcpServerMessage> ack(i_createMessage(RTNET_DHCP_MT_ACK, req));
340
341 b->setState(Binding::ACKED);
342 i_saveLeases();
343
344 ack->setYiaddr(b->addr());
345 ack->addOption(OptLeaseTime(b->leaseTime()));
346
347 OptParameterRequest optlist(req);
348 optmap_t replyOptions;
349 ack->addOptions(m_pConfig->getOptions(replyOptions, optlist, req.clientId()));
350
351 ack->addOption(OptMessage("Ok, ok, here it is"));
352
353 ack->maybeUnicast(req);
354 return ack.release();
355}
356
357
358/**
359 * 4.3.5 DHCPINFORM message
360 *
361 * The server responds to a DHCPINFORM message by sending a DHCPACK message
362 * directly to the address given in the 'ciaddr' field of the DHCPINFORM
363 * message. The server MUST NOT send a lease expiration time to the client and
364 * SHOULD NOT fill in 'yiaddr'. The server includes other parameters in the
365 * DHCPACK message as defined in section 4.3.1.
366 *
367 * @throws std::bad_alloc
368 */
369DhcpServerMessage *DHCPD::i_doInform(DhcpClientMessage &req)
370{
371 if (req.ciaddr().u == 0)
372 return NULL;
373
374 const OptParameterRequest params(req);
375 if (!params.present())
376 return NULL;
377
378 optmap_t info;
379 m_pConfig->getOptions(info, params, req.clientId());
380 if (info.empty())
381 return NULL;
382
383 std::unique_ptr<DhcpServerMessage> ack(i_createMessage(RTNET_DHCP_MT_ACK, req));
384 ack->addOptions(info);
385 ack->maybeUnicast(req);
386 return ack.release();
387}
388
389
390/**
391 * 4.3.3 DHCPDECLINE message
392 *
393 * If the server receives a DHCPDECLINE message, the client has discovered
394 * through some other means that the suggested network address is already in
395 * use. The server MUST mark the network address as not available and SHOULD
396 * notify the local system administrator of a possible configuration problem.
397 *
398 * @throws nothing
399 */
400DhcpServerMessage *DHCPD::i_doDecline(const DhcpClientMessage &req) RT_NOEXCEPT
401{
402 RT_NOREF(req);
403 return NULL;
404}
405
406
407/**
408 * 4.3.4 DHCPRELEASE message
409 *
410 * Upon receipt of a DHCPRELEASE message, the server marks the network address
411 * as not allocated. The server SHOULD retain a record of the client's
412 * initialization parameters for possible reuse in response to subsequent
413 * requests from the client.
414 *
415 * @throws nothing
416 */
417DhcpServerMessage *DHCPD::i_doRelease(const DhcpClientMessage &req) RT_NOEXCEPT
418{
419 if (req.ciaddr().u != 0)
420 {
421 bool fReleased = m_db.releaseBinding(req);
422 if (fReleased)
423 i_saveLeases();
424 }
425
426 return NULL;
427}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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