VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 4 weeks ago

Copyright year updates by scm.

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