VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp@ 91483

Last change on this file since 91483 was 91416, checked in by vboxsync, 3 years ago

Devices: bugref:9932 DrvVMNet and host-only network initial implementation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: VBoxManageHostonly.cpp 91416 2021-09-28 06:15:49Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of hostonlyif command.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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/param.h>
32#include <iprt/path.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/net.h>
36#include <iprt/getopt.h>
37#include <iprt/ctype.h>
38
39#include <VBox/log.h>
40
41#include "VBoxManage.h"
42
43#ifndef VBOX_ONLY_DOCS
44using namespace com;
45
46static const RTGETOPTDEF g_aHostOnlyCreateOptions[] =
47{
48 { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
49};
50
51#if defined(VBOX_WITH_NETFLT) && !defined(RT_OS_SOLARIS)
52static RTEXITCODE handleCreate(HandlerArg *a)
53{
54 /*
55 * Parse input.
56 */
57 bool fMachineReadable = false;
58 RTGETOPTUNION ValueUnion;
59 RTGETOPTSTATE GetState;
60 RTGetOptInit(&GetState, a->argc, a->argv, g_aHostOnlyCreateOptions,
61 RT_ELEMENTS(g_aHostOnlyCreateOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
62 int c;
63 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
64 {
65 switch (c)
66 {
67 case 'M': // --machinereadable
68 fMachineReadable = true;
69 break;
70
71 default:
72 return errorGetOpt(USAGE_HOSTONLYIFS, c, &ValueUnion);
73 }
74 }
75
76 /*
77 * Do the work.
78 */
79 ComPtr<IHost> host;
80 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
81
82 ComPtr<IHostNetworkInterface> hif;
83 ComPtr<IProgress> progress;
84
85 CHECK_ERROR2I_RET(host, CreateHostOnlyNetworkInterface(hif.asOutParam(), progress.asOutParam()), RTEXITCODE_FAILURE);
86
87 if (fMachineReadable)
88 {
89 progress->WaitForCompletion(10000); /* Ten seconds should probably be enough. */
90 CHECK_PROGRESS_ERROR_RET(progress, (""), RTEXITCODE_FAILURE);
91 }
92 else
93 {
94 /*HRESULT hrc =*/ showProgress(progress);
95 CHECK_PROGRESS_ERROR_RET(progress, ("Failed to create the host-only adapter"), RTEXITCODE_FAILURE);
96 }
97
98 Bstr bstrName;
99 CHECK_ERROR2I(hif, COMGETTER(Name)(bstrName.asOutParam()));
100
101 if (fMachineReadable)
102 RTPrintf("%ls", bstrName.raw());
103 else
104 RTPrintf("Interface '%ls' was successfully created\n", bstrName.raw());
105 return RTEXITCODE_SUCCESS;
106}
107
108static RTEXITCODE handleRemove(HandlerArg *a)
109{
110 /*
111 * Parse input.
112 */
113 const char *pszName = NULL;
114 int ch;
115 RTGETOPTUNION ValueUnion;
116 RTGETOPTSTATE GetState;
117 RTGetOptInit(&GetState, a->argc, a->argv, NULL, 0, 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
118 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
119 switch (ch)
120 {
121 case VINF_GETOPT_NOT_OPTION:
122 if (pszName)
123 return errorSyntax(USAGE_HOSTONLYIFS, "Only one interface name can be specified");
124 pszName = ValueUnion.psz;
125 break;
126
127 default:
128 return errorGetOpt(USAGE_HOSTONLYIFS, ch, &ValueUnion);
129 }
130 if (!pszName)
131 return errorSyntax(USAGE_HOSTONLYIFS, "No interface name was specified");
132
133 /*
134 * Do the work.
135 */
136 ComPtr<IHost> host;
137 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
138
139 ComPtr<IHostNetworkInterface> hif;
140 CHECK_ERROR2I_RET(host, FindHostNetworkInterfaceByName(Bstr(pszName).raw(), hif.asOutParam()), RTEXITCODE_FAILURE);
141
142 Bstr guid;
143 CHECK_ERROR2I_RET(hif, COMGETTER(Id)(guid.asOutParam()), RTEXITCODE_FAILURE);
144
145 ComPtr<IProgress> progress;
146 CHECK_ERROR2I_RET(host, RemoveHostOnlyNetworkInterface(guid.raw(), progress.asOutParam()), RTEXITCODE_FAILURE);
147
148 /*HRESULT hrc =*/ showProgress(progress);
149 CHECK_PROGRESS_ERROR_RET(progress, ("Failed to remove the host-only adapter"), RTEXITCODE_FAILURE);
150
151 return RTEXITCODE_SUCCESS;
152}
153#endif
154
155static const RTGETOPTDEF g_aHostOnlyIPOptions[]
156 = {
157 { "--dhcp", 'd', RTGETOPT_REQ_NOTHING },
158 { "-dhcp", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
159 { "--ip", 'a', RTGETOPT_REQ_STRING },
160 { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
161 { "--netmask", 'm', RTGETOPT_REQ_STRING },
162 { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
163 { "--ipv6", 'b', RTGETOPT_REQ_STRING },
164 { "-ipv6", 'b', RTGETOPT_REQ_STRING }, // deprecated
165 { "--netmasklengthv6", 'l', RTGETOPT_REQ_UINT8 },
166 { "-netmasklengthv6", 'l', RTGETOPT_REQ_UINT8 } // deprecated
167 };
168
169static RTEXITCODE handleIpConfig(HandlerArg *a)
170{
171 bool fDhcp = false;
172 bool fNetmasklengthv6 = false;
173 uint32_t uNetmasklengthv6 = UINT32_MAX;
174 const char *pszIpv6 = NULL;
175 const char *pszIp = NULL;
176 const char *pszNetmask = NULL;
177 const char *pszName = NULL;
178
179 int c;
180 RTGETOPTUNION ValueUnion;
181 RTGETOPTSTATE GetState;
182 RTGetOptInit(&GetState, a->argc, a->argv, g_aHostOnlyIPOptions, RT_ELEMENTS(g_aHostOnlyIPOptions),
183 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
184 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
185 {
186 switch (c)
187 {
188 case 'd': // --dhcp
189 fDhcp = true;
190 break;
191 case 'a': // --ip
192 if (pszIp)
193 RTMsgWarning("The --ip option is specified more than once");
194 pszIp = ValueUnion.psz;
195 break;
196 case 'm': // --netmask
197 if (pszNetmask)
198 RTMsgWarning("The --netmask option is specified more than once");
199 pszNetmask = ValueUnion.psz;
200 break;
201 case 'b': // --ipv6
202 if (pszIpv6)
203 RTMsgWarning("The --ipv6 option is specified more than once");
204 pszIpv6 = ValueUnion.psz;
205 break;
206 case 'l': // --netmasklengthv6
207 if (fNetmasklengthv6)
208 RTMsgWarning("The --netmasklengthv6 option is specified more than once");
209 fNetmasklengthv6 = true;
210 uNetmasklengthv6 = ValueUnion.u8;
211 break;
212 case VINF_GETOPT_NOT_OPTION:
213 if (pszName)
214 return errorSyntax(USAGE_HOSTONLYIFS, "Only one interface name can be specified");
215 pszName = ValueUnion.psz;
216 break;
217 default:
218 return errorGetOpt(USAGE_HOSTONLYIFS, c, &ValueUnion);
219 }
220 }
221
222 /* parameter sanity check */
223 if (fDhcp && (fNetmasklengthv6 || pszIpv6 || pszIp || pszNetmask))
224 return errorSyntax(USAGE_HOSTONLYIFS, "You can not use --dhcp with static ip configuration parameters: --ip, --netmask, --ipv6 and --netmasklengthv6.");
225 if ((pszIp || pszNetmask) && (fNetmasklengthv6 || pszIpv6))
226 return errorSyntax(USAGE_HOSTONLYIFS, "You can not use ipv4 configuration (--ip and --netmask) with ipv6 (--ipv6 and --netmasklengthv6) simultaneously.");
227
228 ComPtr<IHost> host;
229 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
230
231 ComPtr<IHostNetworkInterface> hif;
232 CHECK_ERROR2I_RET(host, FindHostNetworkInterfaceByName(Bstr(pszName).raw(), hif.asOutParam()), RTEXITCODE_FAILURE);
233 if (hif.isNull())
234 return errorArgument("Could not find interface '%s'", pszName);
235
236 if (fDhcp)
237 CHECK_ERROR2I_RET(hif, EnableDynamicIPConfig(), RTEXITCODE_FAILURE);
238 else if (pszIp)
239 {
240 if (!pszNetmask)
241 pszNetmask = "255.255.255.0"; /* ?? */
242 CHECK_ERROR2I_RET(hif, EnableStaticIPConfig(Bstr(pszIp).raw(), Bstr(pszNetmask).raw()), RTEXITCODE_FAILURE);
243 }
244 else if (pszIpv6)
245 {
246 BOOL fIpV6Supported;
247 CHECK_ERROR2I_RET(hif, COMGETTER(IPV6Supported)(&fIpV6Supported), RTEXITCODE_FAILURE);
248 if (!fIpV6Supported)
249 {
250 RTMsgError("IPv6 setting is not supported for this adapter");
251 return RTEXITCODE_FAILURE;
252 }
253
254 if (uNetmasklengthv6 == UINT32_MAX)
255 uNetmasklengthv6 = 64; /* ?? */
256 CHECK_ERROR2I_RET(hif, EnableStaticIPConfigV6(Bstr(pszIpv6).raw(), (ULONG)uNetmasklengthv6), RTEXITCODE_FAILURE);
257 }
258 else
259 return errorSyntax(USAGE_HOSTONLYIFS, "Neither -dhcp nor -ip nor -ipv6 was specfified");
260
261 return RTEXITCODE_SUCCESS;
262}
263
264
265RTEXITCODE handleHostonlyIf(HandlerArg *a)
266{
267 if (a->argc < 1)
268 return errorSyntax(USAGE_HOSTONLYIFS, "No sub-command specified");
269
270 RTEXITCODE rcExit;
271 if (!strcmp(a->argv[0], "ipconfig"))
272 rcExit = handleIpConfig(a);
273#if defined(VBOX_WITH_NETFLT) && !defined(RT_OS_SOLARIS)
274 else if (!strcmp(a->argv[0], "create"))
275 rcExit = handleCreate(a);
276 else if (!strcmp(a->argv[0], "remove"))
277 rcExit = handleRemove(a);
278#endif
279 else
280 rcExit = errorSyntax(USAGE_HOSTONLYIFS, "Unknown sub-command '%s'", a->argv[0]);
281 return rcExit;
282}
283
284#ifdef VBOX_WITH_VMNET
285struct HostOnlyNetworkOptions
286{
287 bool fEnable;
288 bool fDisable;
289 Bstr bstrNetworkId;
290 Bstr bstrNetworkName;
291 Bstr bstrNetworkMask;
292 Bstr bstrLowerIp;
293 Bstr bstrUpperIp;
294 /* Initialize fEnable and fDisable */
295 HostOnlyNetworkOptions() : fEnable(false), fDisable(false) {};
296};
297typedef struct HostOnlyNetworkOptions HOSTONLYNETOPT;
298
299static RTEXITCODE createUpdateHostOnlyNetworkParse(HandlerArg *a, HOSTONLYNETOPT& options)
300{
301 static const RTGETOPTDEF s_aOptions[] =
302 {
303 { "--id", 'i', RTGETOPT_REQ_STRING },
304 { "--name", 'n', RTGETOPT_REQ_STRING },
305 { "--netmask", 'm', RTGETOPT_REQ_STRING },
306 { "--lower-ip", 'l', RTGETOPT_REQ_STRING },
307 { "--lowerip", 'l', RTGETOPT_REQ_STRING },
308 { "--upper-ip", 'u', RTGETOPT_REQ_STRING },
309 { "--upperip", 'u', RTGETOPT_REQ_STRING },
310 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
311 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
312 };
313
314 RTGETOPTSTATE GetState;
315 RTGETOPTUNION ValueUnion;
316 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* iFirst */, 0);
317 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
318
319 int c;
320 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
321 {
322 switch (c)
323 {
324 case 'i':
325 options.bstrNetworkId = ValueUnion.psz;
326 break;
327 case 'n':
328 options.bstrNetworkName = ValueUnion.psz;
329 break;
330 case 'm':
331 options.bstrNetworkMask = ValueUnion.psz;
332 break;
333 case 'l':
334 options.bstrLowerIp = ValueUnion.psz;
335 break;
336 case 'u':
337 options.bstrUpperIp = ValueUnion.psz;
338 break;
339 case 'e':
340 options.fEnable = true;
341 break;
342 case 'd':
343 options.fDisable = true;
344 break;
345 case VINF_GETOPT_NOT_OPTION:
346 return errorUnknownSubcommand(ValueUnion.psz);
347 default:
348 return errorGetOpt(c, &ValueUnion);
349 }
350 }
351 return RTEXITCODE_SUCCESS;
352}
353
354static RTEXITCODE createUpdateHostOnlyNetworkCommon(ComPtr<IHostOnlyNetwork> hostOnlyNetwork, HOSTONLYNETOPT& options)
355{
356 HRESULT hrc = S_OK;
357
358 if (options.bstrNetworkId.isNotEmpty())
359 {
360 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(Id)(options.bstrNetworkId.raw()), RTEXITCODE_FAILURE);
361 }
362 if (options.bstrNetworkName.isNotEmpty())
363 {
364 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(NetworkName)(options.bstrNetworkName.raw()), RTEXITCODE_FAILURE);
365 }
366 if (options.bstrNetworkMask.isNotEmpty())
367 {
368 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(NetworkMask)(options.bstrNetworkMask.raw()), RTEXITCODE_FAILURE);
369 }
370 if (options.bstrLowerIp.isNotEmpty())
371 {
372 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(LowerIP)(options.bstrLowerIp.raw()), RTEXITCODE_FAILURE);
373 }
374 if (options.bstrUpperIp.isNotEmpty())
375 {
376 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(UpperIP)(options.bstrUpperIp.raw()), RTEXITCODE_FAILURE);
377 }
378 if (options.fEnable)
379 {
380 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(Enabled)(TRUE), RTEXITCODE_FAILURE);
381 }
382 if (options.fDisable)
383 {
384 CHECK_ERROR2_RET(hrc, hostOnlyNetwork, COMSETTER(Enabled)(FALSE), RTEXITCODE_FAILURE);
385 }
386
387 return RTEXITCODE_SUCCESS;
388}
389
390static RTEXITCODE handleNetAdd(HandlerArg *a)
391{
392 HRESULT hrc = S_OK;
393
394 HOSTONLYNETOPT options;
395 hrc = createUpdateHostOnlyNetworkParse(a, options);
396
397 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
398 ComPtr<IHostOnlyNetwork> hostOnlyNetwork;
399
400 if (options.bstrNetworkName.isEmpty())
401 return errorArgument("The --name parameter must be specified");
402 if (options.bstrNetworkMask.isEmpty())
403 return errorArgument("The --netmask parameter must be specified");
404 if (options.bstrLowerIp.isEmpty())
405 return errorArgument("The --lower-ip parameter must be specified");
406 if (options.bstrUpperIp.isEmpty())
407 return errorArgument("The --upper-ip parameter must be specified");
408
409 CHECK_ERROR2_RET(hrc, pVirtualBox,
410 CreateHostOnlyNetwork(options.bstrNetworkName.raw(), hostOnlyNetwork.asOutParam()),
411 RTEXITCODE_FAILURE);
412 return createUpdateHostOnlyNetworkCommon(hostOnlyNetwork, options);
413}
414
415static RTEXITCODE handleNetModify(HandlerArg *a)
416{
417 HRESULT hrc = S_OK;
418
419 HOSTONLYNETOPT options;
420 hrc = createUpdateHostOnlyNetworkParse(a, options);
421
422 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
423 ComPtr<IHostOnlyNetwork> hostOnlyNetwork;
424
425 if (options.bstrNetworkName.isNotEmpty())
426 {
427 CHECK_ERROR2_RET(hrc, pVirtualBox,
428 FindHostOnlyNetworkByName(options.bstrNetworkName.raw(), hostOnlyNetwork.asOutParam()),
429 RTEXITCODE_FAILURE);
430 }
431 else if (options.bstrNetworkId.isNotEmpty())
432 {
433 CHECK_ERROR2_RET(hrc, pVirtualBox,
434 FindHostOnlyNetworkById(options.bstrNetworkId.raw(), hostOnlyNetwork.asOutParam()),
435 RTEXITCODE_FAILURE);
436 }
437 else
438 return errorArgument("Either --name or --id parameter must be specified");
439
440 return createUpdateHostOnlyNetworkCommon(hostOnlyNetwork, options);
441}
442
443static RTEXITCODE handleNetRemove(HandlerArg *a)
444{
445 HRESULT hrc = S_OK;
446
447 static const RTGETOPTDEF s_aOptions[] =
448 {
449 { "--id", 'i', RTGETOPT_REQ_STRING },
450 { "--name", 'n', RTGETOPT_REQ_STRING },
451 };
452
453 RTGETOPTSTATE GetState;
454 RTGETOPTUNION ValueUnion;
455 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /* iFirst */, 0);
456 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
457
458 Bstr strNetworkId, strNetworkName;
459
460 int c;
461 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
462 {
463 switch (c)
464 {
465 case 'i':
466 strNetworkId=ValueUnion.psz;
467 break;
468 case 'n':
469 strNetworkName=ValueUnion.psz;
470 break;
471 case VINF_GETOPT_NOT_OPTION:
472 return errorUnknownSubcommand(ValueUnion.psz);
473 default:
474 return errorGetOpt(c, &ValueUnion);
475 }
476 }
477
478 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
479 ComPtr<IHostOnlyNetwork> hostOnlyNetwork;
480
481 if (!strNetworkName.isEmpty())
482 {
483 CHECK_ERROR2_RET(hrc, pVirtualBox,
484 FindHostOnlyNetworkByName(strNetworkName.raw(), hostOnlyNetwork.asOutParam()),
485 RTEXITCODE_FAILURE);
486 }
487 else if (!strNetworkId.isEmpty())
488 {
489 CHECK_ERROR2_RET(hrc, pVirtualBox,
490 FindHostOnlyNetworkById(strNetworkId.raw(), hostOnlyNetwork.asOutParam()),
491 RTEXITCODE_FAILURE);
492 }
493 else
494 return errorArgument("Either --name or --id parameter must be specified");
495
496 CHECK_ERROR2_RET(hrc, pVirtualBox,
497 RemoveHostOnlyNetwork(hostOnlyNetwork),
498 RTEXITCODE_FAILURE);
499 return RTEXITCODE_SUCCESS;
500}
501
502RTEXITCODE handleHostonlyNet(HandlerArg *a)
503{
504 if (a->argc < 1)
505 return errorSyntax("No sub-command specified");
506
507 RTEXITCODE rcExit;
508 if (!strcmp(a->argv[0], "add"))
509 {
510 setCurrentSubcommand(HELP_SCOPE_HOSTONLYNET_ADD);
511 rcExit = handleNetAdd(a);
512 }
513 else if (!strcmp(a->argv[0], "modify"))
514 {
515 setCurrentSubcommand(HELP_SCOPE_HOSTONLYNET_MODIFY);
516 rcExit = handleNetModify(a);
517 }
518 else if (!strcmp(a->argv[0], "remove"))
519 {
520 setCurrentSubcommand(HELP_SCOPE_HOSTONLYNET_REMOVE);
521 rcExit = handleNetRemove(a);
522 }
523 else
524 rcExit = errorSyntax("Unknown sub-command '%s'", a->argv[0]);
525 return rcExit;
526}
527#endif /* VBOX_WITH_VMNET */
528
529#endif /* !VBOX_ONLY_DOCS */
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