VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp@ 76366

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

include/VBox/com/Guid.h: Don't include iprt/err.h for no good reason. bugref:9344

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.2 KB
Line 
1/* $Id: VBoxManageDHCPServer.cpp 76366 2018-12-22 02:16:26Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of dhcpserver command.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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#ifndef VBOX_ONLY_DOCS
23#include <VBox/com/com.h>
24#include <VBox/com/array.h>
25#include <VBox/com/ErrorInfo.h>
26#include <VBox/com/errorprint.h>
27#include <VBox/com/VirtualBox.h>
28#endif /* !VBOX_ONLY_DOCS */
29
30#include <iprt/cidr.h>
31#include <iprt/err.h>
32#include <iprt/param.h>
33#include <iprt/path.h>
34#include <iprt/stream.h>
35#include <iprt/string.h>
36#include <iprt/net.h>
37#include <iprt/getopt.h>
38#include <iprt/ctype.h>
39
40#include <VBox/log.h>
41
42#include "VBoxManage.h"
43
44#include <string>
45#include <vector>
46#include <map>
47
48#ifndef VBOX_ONLY_DOCS
49using namespace com;
50
51typedef enum enMainOpCodes
52{
53 OP_ADD = 1000,
54 OP_REMOVE,
55 OP_MODIFY,
56 OP_RESTART
57} OPCODE;
58
59typedef std::pair<DhcpOpt_T, std::string> DhcpOptSpec;
60typedef std::vector<DhcpOptSpec> DhcpOpts;
61typedef DhcpOpts::iterator DhcpOptIterator;
62
63typedef std::vector<DhcpOpt_T> DhcpOptIds;
64typedef DhcpOptIds::iterator DhcpOptIdIterator;
65
66struct VmNameSlotKey
67{
68 const std::string VmName;
69 uint8_t u8Slot;
70
71 VmNameSlotKey(const std::string &aVmName, uint8_t aSlot)
72 : VmName(aVmName)
73 , u8Slot(aSlot)
74 {}
75
76 bool operator< (const VmNameSlotKey& that) const
77 {
78 if (VmName == that.VmName)
79 return u8Slot < that.u8Slot;
80 else
81 return VmName < that.VmName;
82 }
83};
84
85typedef std::map<VmNameSlotKey, DhcpOpts> VmSlot2OptionsM;
86typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator;
87typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair;
88
89typedef std::map<VmNameSlotKey, DhcpOptIds> VmSlot2OptionIdsM;
90typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator;
91
92typedef std::vector<VmNameSlotKey> VmConfigs;
93
94static const RTGETOPTDEF g_aDHCPIPOptions[] =
95{
96 { "--netname", 't', RTGETOPT_REQ_STRING }, /* we use 't' instead of 'n' to avoid
97 * 1. the misspelled "-enable" long option to be treated as 'e' (for -enable) + 'n' (for -netname) + "<the_rest_opt>" (for net name)
98 * 2. the misspelled "-netmask" to be treated as 'n' (for -netname) + "<the_rest_opt>" (for net name)
99 */
100 { "-netname", 't', RTGETOPT_REQ_STRING }, // deprecated (if removed check below)
101 { "--ifname", 'f', RTGETOPT_REQ_STRING }, /* we use 'f' instead of 'i' to avoid
102 * 1. the misspelled "-disable" long option to be treated as 'd' (for -disable) + 'i' (for -ifname) + "<the_rest_opt>" (for if name)
103 */
104 { "-ifname", 'f', RTGETOPT_REQ_STRING }, // deprecated
105 { "--ip", 'a', RTGETOPT_REQ_STRING },
106 { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
107 { "--netmask", 'm', RTGETOPT_REQ_STRING },
108 { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
109 { "--lowerip", 'l', RTGETOPT_REQ_STRING },
110 { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated
111 { "--upperip", 'u', RTGETOPT_REQ_STRING },
112 { "-upperip", 'u', RTGETOPT_REQ_STRING }, // deprecated
113 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
114 { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated
115 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
116 { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
117 { "--options", 'o', RTGETOPT_REQ_NOTHING },
118 { "--vm", 'M', RTGETOPT_REQ_STRING}, /* only with -o */
119 { "--nic", 'n', RTGETOPT_REQ_UINT8}, /* only with -o and -M */
120 { "--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
121 { "--value", 'p', RTGETOPT_REQ_STRING}, /* only with -i */
122 { "--remove", 'r', RTGETOPT_REQ_NOTHING} /* only with -i */
123};
124
125static RTEXITCODE handleOp(HandlerArg *a, OPCODE enmCode, int iStart)
126{
127 if (a->argc - iStart < 2)
128 return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
129
130 int index = iStart;
131 HRESULT rc;
132 bool fOptionsRead = false;
133 bool fVmOptionRead = false;
134
135 const char *pszVmName = NULL;
136 const char *pNetName = NULL;
137 const char *pIfName = NULL;
138 const char * pIp = NULL;
139 const char * pNetmask = NULL;
140 const char * pLowerIp = NULL;
141 const char * pUpperIp = NULL;
142
143 uint8_t u8OptId = (uint8_t)~0;
144 uint8_t u8Slot = (uint8_t)~0;
145
146 int enable = -1;
147
148 DhcpOpts GlobalDhcpOptions;
149 DhcpOptIds GlobalDhcpOptions2Delete;
150 VmSlot2OptionsM VmSlot2Options;
151 VmSlot2OptionIdsM VmSlot2Options2Delete;
152 VmConfigs VmConfigs2Delete;
153
154 int c;
155 RTGETOPTUNION ValueUnion;
156 RTGETOPTSTATE GetState;
157 RTGetOptInit(&GetState,
158 a->argc,
159 a->argv,
160 g_aDHCPIPOptions,
161 enmCode != OP_REMOVE ? RT_ELEMENTS(g_aDHCPIPOptions) : 4, /* we use only --netname and --ifname for remove*/
162 index,
163 RTGETOPTINIT_FLAGS_NO_STD_OPTS);
164 while ((c = RTGetOpt(&GetState, &ValueUnion)))
165 {
166 switch (c)
167 {
168 case 't': // --netname
169 if(pNetName)
170 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netname once.");
171 else if (pIfName)
172 return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ifname for identifying the DHCP server.");
173 else
174 {
175 pNetName = ValueUnion.psz;
176 }
177 break;
178 case 'f': // --ifname
179 if(pIfName)
180 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ifname once.");
181 else if (pNetName)
182 return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ipname for identifying the DHCP server.");
183 else
184 {
185 pIfName = ValueUnion.psz;
186 }
187 break;
188 case 'a': // -ip
189 if(pIp)
190 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ip once.");
191 else
192 {
193 pIp = ValueUnion.psz;
194 }
195 break;
196 case 'm': // --netmask
197 if(pNetmask)
198 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netmask once.");
199 else
200 {
201 pNetmask = ValueUnion.psz;
202 }
203 break;
204 case 'l': // --lowerip
205 if(pLowerIp)
206 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --lowerip once.");
207 else
208 {
209 pLowerIp = ValueUnion.psz;
210 }
211 break;
212 case 'u': // --upperip
213 if(pUpperIp)
214 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --upperip once.");
215 else
216 {
217 pUpperIp = ValueUnion.psz;
218 }
219 break;
220 case 'e': // --enable
221 if(enable >= 0)
222 return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
223 else
224 {
225 enable = 1;
226 }
227 break;
228 case 'd': // --disable
229 if(enable >= 0)
230 return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
231 else
232 {
233 enable = 0;
234 }
235 break;
236 case VINF_GETOPT_NOT_OPTION:
237 return errorSyntax(USAGE_DHCPSERVER, "unhandled parameter: %s", ValueUnion.psz);
238 break;
239
240 case 'o': // --options
241 {
242 // {"--vm", 'n', RTGETOPT_REQ_STRING}, /* only with -o */
243 // {"--nic", 'c', RTGETOPT_REQ_UINT8}, /* only with -o and -n*/
244 // {"--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
245 // {"--value", 'p', RTGETOPT_REQ_STRING} /* only with -i */
246 if (fOptionsRead)
247 return errorSyntax(USAGE_DHCPSERVER,
248 "previos option edition wasn't finished");
249 fOptionsRead = true;
250 fVmOptionRead = false; /* we want specify new global or vm option*/
251 u8Slot = (uint8_t)~0;
252 u8OptId = (uint8_t)~0;
253 pszVmName = NULL;
254 } /* end of --options */
255 break;
256
257 case 'M': // --vm
258 {
259 if (fVmOptionRead)
260 return errorSyntax(USAGE_DHCPSERVER,
261 "previous vm option edition wasn't finished");
262 else
263 fVmOptionRead = true;
264 u8Slot = (uint8_t)~0; /* clear slor */
265 pszVmName = RTStrDup(ValueUnion.psz);
266 }
267 break; /* end of --vm */
268
269 case 'n': // --nic
270 {
271 if (!fVmOptionRead)
272 return errorSyntax(USAGE_DHCPSERVER,
273 "vm name wasn't specified");
274
275 u8Slot = ValueUnion.u8;
276
277 if (u8Slot < 1)
278 return errorSyntax(USAGE_DHCPSERVER,
279 "invalid NIC number: %u", u8Slot);
280 --u8Slot;
281 }
282 break; /* end of --nic */
283
284 case 'i': // --id
285 {
286 if (!fOptionsRead)
287 return errorSyntax(USAGE_DHCPSERVER,
288 "-o wasn't found");
289
290 u8OptId = ValueUnion.u8;
291 }
292 break; /* end of --id */
293
294 case 'p': // --value
295 {
296 if (!fOptionsRead)
297 return errorSyntax(USAGE_DHCPSERVER,
298 "-o wasn't found");
299
300 if (u8OptId == (uint8_t)~0)
301 return errorSyntax(USAGE_DHCPSERVER,
302 "--id wasn't found");
303 if ( fVmOptionRead
304 && u8Slot == (uint8_t)~0)
305 return errorSyntax(USAGE_DHCPSERVER,
306 "--nic wasn't found");
307
308 DhcpOpts &opts = fVmOptionRead ? VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)]
309 : GlobalDhcpOptions;
310 std::string strVal = ValueUnion.psz;
311 opts.push_back(DhcpOptSpec((DhcpOpt_T)u8OptId, strVal));
312
313 }
314 break; // --end of value
315
316 case 'r': /* --remove */
317 {
318 if (!fOptionsRead)
319 return errorSyntax(USAGE_DHCPSERVER,
320 "-o wasn't found");
321
322 if (u8OptId == (uint8_t)~0)
323 return errorSyntax(USAGE_DHCPSERVER,
324 "--id wasn't found");
325 if ( fVmOptionRead
326 && u8Slot == (uint8_t)~0)
327 return errorSyntax(USAGE_DHCPSERVER,
328 "--nic wasn't found");
329
330 DhcpOptIds &optIds = fVmOptionRead ? VmSlot2Options2Delete[VmNameSlotKey(pszVmName, u8Slot)]
331 : GlobalDhcpOptions2Delete;
332 optIds.push_back((DhcpOpt_T)u8OptId);
333 }
334 break; /* --end of remove */
335
336 default:
337 if (c > 0)
338 {
339 if (RT_C_IS_GRAPH(c))
340 return errorSyntax(USAGE_DHCPSERVER, "unhandled option: -%c", c);
341 else
342 return errorSyntax(USAGE_DHCPSERVER, "unhandled option: %i", c);
343 }
344 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
345 return errorSyntax(USAGE_DHCPSERVER, "unknown option: %s", ValueUnion.psz);
346 else if (ValueUnion.pDef)
347 return errorSyntax(USAGE_DHCPSERVER, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
348 else
349 return errorSyntax(USAGE_DHCPSERVER, "%Rrs", c);
350 }
351 }
352
353 if(! pNetName && !pIfName)
354 return errorSyntax(USAGE_DHCPSERVER, "You need to specify either --netname or --ifname to identify the DHCP server");
355
356 if( enmCode != OP_REMOVE
357 && enmCode != OP_RESTART
358 && GlobalDhcpOptions.empty()
359 && VmSlot2Options.empty()
360 && GlobalDhcpOptions2Delete.empty()
361 && VmSlot2Options2Delete.empty())
362 {
363 if(enable < 0 || pIp || pNetmask || pLowerIp || pUpperIp)
364 {
365 if(!pIp)
366 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --ip option");
367
368 if(!pNetmask)
369 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --netmask option");
370
371 if(!pLowerIp)
372 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --lowerip option");
373
374 if(!pUpperIp)
375 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --upperip option");
376 }
377 }
378
379 Bstr NetName;
380 if(!pNetName)
381 {
382 ComPtr<IHost> host;
383 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
384
385 ComPtr<IHostNetworkInterface> hif;
386 CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pIfName).mutableRaw(), hif.asOutParam()));
387 if (FAILED(rc))
388 return errorArgument("Could not find interface '%s'", pIfName);
389
390 CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam()));
391 if (FAILED(rc))
392 return errorArgument("Could not get network name for the interface '%s'", pIfName);
393 }
394 else
395 {
396 NetName = Bstr(pNetName);
397 }
398
399 ComPtr<IDHCPServer> svr;
400 rc = a->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), svr.asOutParam());
401 if(enmCode == OP_ADD)
402 {
403 if (SUCCEEDED(rc))
404 return errorArgument("DHCP server already exists");
405
406 CHECK_ERROR(a->virtualBox, CreateDHCPServer(NetName.mutableRaw(), svr.asOutParam()));
407 if (FAILED(rc))
408 return errorArgument("Failed to create the DHCP server");
409 }
410 else if (FAILED(rc))
411 {
412 return errorArgument("DHCP server does not exist");
413 }
414
415 if (enmCode == OP_RESTART)
416 {
417 CHECK_ERROR(svr, Restart());
418 if(FAILED(rc))
419 return errorArgument("Failed to restart server");
420 }
421 else if (enmCode == OP_REMOVE)
422 {
423 CHECK_ERROR(a->virtualBox, RemoveDHCPServer(svr));
424 if(FAILED(rc))
425 return errorArgument("Failed to remove server");
426 }
427 else
428 {
429 if (pIp || pNetmask || pLowerIp || pUpperIp)
430 {
431 CHECK_ERROR(svr, SetConfiguration (
432 Bstr(pIp).mutableRaw(),
433 Bstr(pNetmask).mutableRaw(),
434 Bstr(pLowerIp).mutableRaw(),
435 Bstr(pUpperIp).mutableRaw()));
436 if(FAILED(rc))
437 return errorArgument("Failed to set configuration");
438 }
439
440 if(enable >= 0)
441 {
442 CHECK_ERROR(svr, COMSETTER(Enabled) ((BOOL)enable));
443 }
444
445 /* remove specified options */
446 DhcpOptIdIterator itOptId;
447 for (itOptId = GlobalDhcpOptions2Delete.begin();
448 itOptId != GlobalDhcpOptions2Delete.end();
449 ++itOptId)
450 {
451 CHECK_ERROR(svr, RemoveGlobalOption(*itOptId));
452 }
453 VmSlot2OptionIdsIterator itIdVector;
454 for (itIdVector = VmSlot2Options2Delete.begin();
455 itIdVector != VmSlot2Options2Delete.end();
456 ++itIdVector)
457 {
458 for(itOptId = itIdVector->second.begin();
459 itOptId != itIdVector->second.end();
460 ++itOptId)
461 {
462 CHECK_ERROR(svr,
463 RemoveVmSlotOption(Bstr(itIdVector->first.VmName.c_str()).raw(),
464 itIdVector->first.u8Slot,
465 *itOptId));
466 }
467 }
468
469 /* option processing */
470 DhcpOptIterator itOpt;
471 VmSlot2OptionsIterator it;
472
473 /* Global Options */
474 for(itOpt = GlobalDhcpOptions.begin();
475 itOpt != GlobalDhcpOptions.end();
476 ++itOpt)
477 {
478 CHECK_ERROR(svr,
479 AddGlobalOption(
480 itOpt->first,
481 com::Bstr(itOpt->second.c_str()).raw()));
482 }
483
484 /* heh, vm slot options. */
485
486 for (it = VmSlot2Options.begin();
487 it != VmSlot2Options.end();
488 ++it)
489 {
490 for(itOpt = it->second.begin();
491 itOpt != it->second.end();
492 ++itOpt)
493 {
494 CHECK_ERROR(svr,
495 AddVmSlotOption(Bstr(it->first.VmName.c_str()).raw(),
496 it->first.u8Slot,
497 itOpt->first,
498 com::Bstr(itOpt->second.c_str()).raw()));
499 }
500 }
501 }
502
503 return RTEXITCODE_SUCCESS;
504}
505
506
507RTEXITCODE handleDHCPServer(HandlerArg *a)
508{
509 if (a->argc < 1)
510 return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
511
512 RTEXITCODE rcExit;
513 if (strcmp(a->argv[0], "modify") == 0)
514 rcExit = handleOp(a, OP_MODIFY, 1);
515 else if (strcmp(a->argv[0], "add") == 0)
516 rcExit = handleOp(a, OP_ADD, 1);
517 else if (strcmp(a->argv[0], "remove") == 0)
518 rcExit = handleOp(a, OP_REMOVE, 1);
519 else if (strcmp(a->argv[0], "restart") == 0)
520 rcExit = handleOp(a, OP_RESTART, 1);
521 else
522 rcExit = errorSyntax(USAGE_DHCPSERVER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
523
524 return rcExit;
525}
526
527#endif /* !VBOX_ONLY_DOCS */
528
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