VirtualBox

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

Last change on this file since 78401 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: 6.8 KB
Line 
1/* $Id: DHCPD.cpp 76553 2019-01-01 01:45:53Z 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.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 "DHCPD.h"
19#include "DhcpOptions.h"
20
21#include <iprt/path.h>
22
23
24DHCPD::DHCPD()
25 : m_pConfig(NULL), m_db()
26{
27}
28
29
30int DHCPD::init(const Config *pConfig)
31{
32 int rc;
33
34 if (m_pConfig != NULL)
35 return VERR_INVALID_STATE;
36
37 /* leases file name */
38 m_strLeasesFileName = pConfig->getHome();
39 m_strLeasesFileName += RTPATH_DELIMITER;
40 m_strLeasesFileName += pConfig->getBaseName();
41 m_strLeasesFileName += "-Dhcpd.leases";
42
43 rc = m_db.init(pConfig);
44 if (RT_FAILURE(rc))
45 return rc;
46
47 loadLeases();
48
49 m_pConfig = pConfig;
50 return VINF_SUCCESS;
51}
52
53
54void DHCPD::loadLeases()
55{
56 m_db.loadLeases(m_strLeasesFileName);
57}
58
59
60void DHCPD::saveLeases()
61{
62 m_db.expire();
63 m_db.writeLeases(m_strLeasesFileName);
64}
65
66
67DhcpServerMessage *DHCPD::process(DhcpClientMessage &req)
68{
69 DhcpServerMessage *reply = NULL;
70
71 req.dump();
72
73 OptServerId sid(req);
74 if (sid.present() && sid.value().u != m_pConfig->getIPv4Address().u)
75 {
76 if (req.broadcasted() && req.messageType() == RTNET_DHCP_MT_REQUEST)
77 m_db.cancelOffer(req);
78
79 return NULL;
80 }
81
82 switch (req.messageType())
83 {
84 /*
85 * Requests that require server's reply.
86 */
87 case RTNET_DHCP_MT_DISCOVER:
88 reply = doDiscover(req);
89 break;
90
91 case RTNET_DHCP_MT_REQUEST:
92 reply = doRequest(req);
93 break;
94
95 case RTNET_DHCP_MT_INFORM:
96 reply = doInform(req);
97 break;
98
99 /*
100 * Requests that don't have a reply.
101 */
102 case RTNET_DHCP_MT_DECLINE:
103 doDecline(req);
104 break;
105
106 case RTNET_DHCP_MT_RELEASE:
107 doRelease(req);
108 break;
109
110 /*
111 * Unexpected or unknown message types.
112 */
113 case RTNET_DHCP_MT_OFFER: /* FALLTHROUGH */
114 case RTNET_DHCP_MT_ACK: /* FALLTHROUGH */
115 case RTNET_DHCP_MT_NAC: /* FALLTHROUGH */
116 default:
117 break;
118 }
119
120 return reply;
121}
122
123
124DhcpServerMessage *DHCPD::createMessage(int type, DhcpClientMessage &req)
125{
126 return new DhcpServerMessage(req, type, m_pConfig->getIPv4Address());
127}
128
129
130DhcpServerMessage *DHCPD::doDiscover(DhcpClientMessage &req)
131{
132 /*
133 * XXX: TODO: Windows iSCSI initiator sends DHCPDISCOVER first and
134 * it has ciaddr filled. Shouldn't let it screw up the normal
135 * lease we already have for that client, but we should probably
136 * reply with a pro-forma offer.
137 */
138 if (req.ciaddr().u != 0)
139 return NULL;
140
141 Binding *b = m_db.allocateBinding(req);
142 if (b == NULL)
143 return NULL;
144
145
146 std::unique_ptr<DhcpServerMessage> reply;
147
148 bool fRapidCommit = OptRapidCommit(req).present();
149 if (!fRapidCommit)
150 {
151 reply.reset(createMessage(RTNET_DHCP_MT_OFFER, req));
152
153 if (b->state() < Binding::OFFERED)
154 b->setState(Binding::OFFERED);
155
156 /* use small lease time internally to quickly free unclaimed offers? */
157 }
158 else
159 {
160 reply.reset(createMessage(RTNET_DHCP_MT_ACK, req));
161 reply->addOption(OptRapidCommit(true));
162
163 b->setState(Binding::ACKED);
164 saveLeases();
165 }
166
167 reply->setYiaddr(b->addr());
168 reply->addOption(OptLeaseTime(b->leaseTime()));
169
170
171 OptParameterRequest optlist(req);
172 reply->addOptions(m_pConfig->getOptions(optlist, req.clientId()));
173
174 // reply->maybeUnicast(req); /* XXX: we reject ciaddr != 0 above */
175 return reply.release();
176}
177
178
179DhcpServerMessage *DHCPD::doRequest(DhcpClientMessage &req)
180{
181 OptRequestedAddress reqAddr(req);
182 if (req.ciaddr().u != 0 && reqAddr.present() && reqAddr.value().u != req.ciaddr().u)
183 {
184 std::unique_ptr<DhcpServerMessage> nak (
185 createMessage(RTNET_DHCP_MT_NAC, req)
186 );
187 nak->addOption(OptMessage("Requested address does not match ciaddr"));
188 return nak.release();
189 }
190
191
192 Binding *b = m_db.allocateBinding(req);
193 if (b == NULL)
194 {
195 return createMessage(RTNET_DHCP_MT_NAC, req);
196 }
197
198
199 std::unique_ptr<DhcpServerMessage> ack (
200 createMessage(RTNET_DHCP_MT_ACK, req)
201 );
202
203 b->setState(Binding::ACKED);
204 saveLeases();
205
206 ack->setYiaddr(b->addr());
207 ack->addOption(OptLeaseTime(b->leaseTime()));
208
209 OptParameterRequest optlist(req);
210 ack->addOptions(m_pConfig->getOptions(optlist, req.clientId()));
211
212 ack->addOption(OptMessage("Ok, ok, here it is"));
213
214 ack->maybeUnicast(req);
215 return ack.release();
216}
217
218
219/*
220 * 4.3.5 DHCPINFORM message
221 *
222 * The server responds to a DHCPINFORM message by sending a DHCPACK
223 * message directly to the address given in the 'ciaddr' field of the
224 * DHCPINFORM message. The server MUST NOT send a lease expiration time
225 * to the client and SHOULD NOT fill in 'yiaddr'. The server includes
226 * other parameters in the DHCPACK message as defined in section 4.3.1.
227 */
228DhcpServerMessage *DHCPD::doInform(DhcpClientMessage &req)
229{
230 if (req.ciaddr().u == 0)
231 return NULL;
232
233 const OptParameterRequest params(req);
234 if (!params.present())
235 return NULL;
236
237 optmap_t info(m_pConfig->getOptions(params, req.clientId()));
238 if (info.empty())
239 return NULL;
240
241 std::unique_ptr<DhcpServerMessage> ack (
242 createMessage(RTNET_DHCP_MT_ACK, req)
243 );
244
245 ack->addOptions(info);
246
247 ack->maybeUnicast(req);
248 return ack.release();
249}
250
251
252/*
253 * 4.3.3 DHCPDECLINE message
254 *
255 * If the server receives a DHCPDECLINE message, the client has
256 * discovered through some other means that the suggested network
257 * address is already in use. The server MUST mark the network address
258 * as not available and SHOULD notify the local system administrator of
259 * a possible configuration problem.
260 */
261DhcpServerMessage *DHCPD::doDecline(DhcpClientMessage &req)
262{
263 RT_NOREF(req);
264 return NULL;
265}
266
267
268/*
269 * 4.3.4 DHCPRELEASE message
270 *
271 * Upon receipt of a DHCPRELEASE message, the server marks the network
272 * address as not allocated. The server SHOULD retain a record of the
273 * client's initialization parameters for possible reuse in response to
274 * subsequent requests from the client.
275 */
276DhcpServerMessage *DHCPD::doRelease(DhcpClientMessage &req)
277{
278 if (req.ciaddr().u == 0)
279 return NULL;
280
281 bool released = m_db.releaseBinding(req);
282 if (released)
283 saveLeases();
284
285 return NULL;
286}
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