VirtualBox

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

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

Dhcpd: DHCP_LOG_MSG_ERROR. bugref:9288

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

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