VirtualBox

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

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

doc/manual,FE/VBoxManage: Convert hostonlyif command to refentry documentation, ​bugref:9186

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

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