VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/adpctl/VBoxNetAdpCtl.cpp@ 91616

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

VBoxNetAdpCtl: bugref:8093. Rewrite command line processing to be
easier to follow, add getopt support.

Add --verbose and --dry-run options to be able to see which commands
are/will be executed to effect the requested change. --dry-run is not
supported by the actions that do VBox ioctls (it's not yet clear to me
what would be the best way to report the intended action here), but
since these options are mostly for internal use, it's not that much of
problem, I think.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.0 KB
Line 
1/* $Id: VBoxNetAdpCtl.cpp 90773 2021-08-21 13:55:41Z vboxsync $ */
2/** @file
3 * Apps - VBoxAdpCtl, Configuration tool for vboxnetX adapters.
4 */
5
6/*
7 * Copyright (C) 2009-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/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23#include <list>
24#include <errno.h>
25#include <getopt.h>
26#include <stdio.h>
27#include <stdarg.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/wait.h>
32#include <sys/ioctl.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#ifdef RT_OS_LINUX
36# include <arpa/inet.h>
37# include <net/if.h>
38# include <linux/types.h>
39/* Older versions of ethtool.h rely on these: */
40typedef unsigned long long u64;
41typedef __uint32_t u32;
42typedef __uint16_t u16;
43typedef __uint8_t u8;
44# include <limits.h> /* for INT_MAX */
45# include <linux/ethtool.h>
46# include <linux/sockios.h>
47#endif
48#ifdef RT_OS_SOLARIS
49# include <sys/ioccom.h>
50#endif
51
52#define NOREF(x) (void)x
53
54/** @todo Error codes must be moved to some header file */
55#define ADPCTLERR_BAD_NAME 2
56#define ADPCTLERR_NO_CTL_DEV 3
57#define ADPCTLERR_IOCTL_FAILED 4
58#define ADPCTLERR_SOCKET_FAILED 5
59
60/** @todo These are duplicates from src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h */
61#define VBOXNETADP_CTL_DEV_NAME "/dev/vboxnetctl"
62#define VBOXNETADP_MAX_INSTANCES 128
63#define VBOXNETADP_NAME "vboxnet"
64#define VBOXNETADP_MAX_NAME_LEN 32
65#define VBOXNETADP_CTL_ADD _IOWR('v', 1, VBOXNETADPREQ)
66#define VBOXNETADP_CTL_REMOVE _IOW('v', 2, VBOXNETADPREQ)
67typedef struct VBoxNetAdpReq
68{
69 char szName[VBOXNETADP_MAX_NAME_LEN];
70} VBOXNETADPREQ;
71typedef VBOXNETADPREQ *PVBOXNETADPREQ;
72
73#define VBOXADPCTL_IFCONFIG_PATH1 "/sbin/ifconfig"
74#define VBOXADPCTL_IFCONFIG_PATH2 "/bin/ifconfig"
75
76bool verbose;
77bool dry_run;
78
79
80static int usage(void)
81{
82 fprintf(stderr, "Usage: VBoxNetAdpCtl <adapter> <address> ([netmask <address>] | remove)\n");
83 fprintf(stderr, " | VBoxNetAdpCtl [<adapter>] add\n");
84 fprintf(stderr, " | VBoxNetAdpCtl <adapter> remove\n");
85 return EXIT_FAILURE;
86}
87
88
89/*
90 * A wrapper on standard list that provides '<<' operator for adding several list members in a single
91 * line dynamically. For example: "CmdList(arg1) << arg2 << arg3" produces a list with three members.
92 */
93class CmdList
94{
95public:
96 /** Creates an empty list. */
97 CmdList() {};
98 /** Creates a list with a single member. */
99 CmdList(const char *pcszCommand) { m_list.push_back(pcszCommand); };
100 /** Provides access to the underlying standard list. */
101 const std::list<const char *>& getList(void) const { return m_list; };
102 /** Adds a member to the list. */
103 CmdList& operator<<(const char *pcszArgument);
104private:
105 std::list<const char *>m_list;
106};
107
108CmdList& CmdList::operator<<(const char *pcszArgument)
109{
110 m_list.push_back(pcszArgument);
111 return *this;
112}
113
114/** Simple helper to distinguish IPv4 and IPv6 addresses. */
115inline bool isAddrV6(const char *pcszAddress)
116{
117 return !!(strchr(pcszAddress, ':'));
118}
119
120
121/*********************************************************************************************************************************
122* Generic address commands. *
123*********************************************************************************************************************************/
124
125/**
126 * The base class for all address manipulation commands. While being an abstract class,
127 * it provides a generic implementation of 'set' and 'remove' methods, which rely on
128 * pure virtual methods like 'addV4' and 'removeV4' to perform actual command execution.
129 */
130class AddressCommand
131{
132public:
133 AddressCommand() : m_pszPath(0) {};
134 virtual ~AddressCommand() {};
135
136 /** returns true if underlying command (executable) is present in the system. */
137 bool isAvailable(void)
138 { struct stat s; return (!stat(m_pszPath, &s) && S_ISREG(s.st_mode)); };
139
140 /*
141 * Someday we may want to support several IP addresses per adapter, but for now we
142 * have 'set' method only, which replaces all addresses with the one specifed.
143 *
144 * virtual int add(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
145 */
146 /** replace existing address(es) */
147 virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0);
148 /** remove address */
149 virtual int remove(const char *pcszAdapter, const char *pcszAddress);
150protected:
151 /** IPv4-specific handler used by generic implementation of 'set' method if 'setV4' is not supported. */
152 virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
153 /** IPv6-specific handler used by generic implementation of 'set' method. */
154 virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
155 /** IPv4-specific handler used by generic implementation of 'set' method. */
156 virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0) = 0;
157 /** IPv4-specific handler used by generic implementation of 'remove' method. */
158 virtual int removeV4(const char *pcszAdapter, const char *pcszAddress) = 0;
159 /** IPv6-specific handler used by generic implementation of 'remove' method. */
160 virtual int removeV6(const char *pcszAdapter, const char *pcszAddress) = 0;
161 /** Composes the argument list of command that obtains all addresses assigned to the adapter. */
162 virtual CmdList getShowCommand(const char *pcszAdapter) const = 0;
163
164 /** Prepares an array of C strings needed for 'exec' call. */
165 char * const * allocArgv(const CmdList& commandList);
166 /** Hides process creation details. To be used in derived classes. */
167 int execute(CmdList& commandList);
168
169 /** A path to executable command. */
170 const char *m_pszPath;
171private:
172 /** Removes all previously asssigned addresses of a particular protocol family. */
173 int removeAddresses(const char *pcszAdapter, const char *pcszFamily);
174};
175
176/*
177 * A generic implementation of 'ifconfig' command for all platforms.
178 */
179class CmdIfconfig : public AddressCommand
180{
181public:
182 CmdIfconfig()
183 {
184 struct stat s;
185 if ( !stat(VBOXADPCTL_IFCONFIG_PATH1, &s)
186 && S_ISREG(s.st_mode))
187 m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH1;
188 else
189 m_pszPath = (char*)VBOXADPCTL_IFCONFIG_PATH2;
190 };
191
192protected:
193 /** Returns platform-specific subcommand to add an address. */
194 virtual const char *addCmdArg(void) const = 0;
195 /** Returns platform-specific subcommand to remove an address. */
196 virtual const char *delCmdArg(void) const = 0;
197 virtual CmdList getShowCommand(const char *pcszAdapter) const
198 { return CmdList(pcszAdapter); };
199 virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
200 { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); };
201 virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
202 {
203 return execute(CmdList(pcszAdapter) << "inet6" << addCmdArg() << pcszAddress);
204 NOREF(pcszNetmask);
205 };
206 virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
207 {
208 if (!pcszNetmask)
209 return execute(CmdList(pcszAdapter) << pcszAddress);
210 return execute(CmdList(pcszAdapter) << pcszAddress << "netmask" << pcszNetmask);
211 };
212 virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
213 { return execute(CmdList(pcszAdapter) << delCmdArg() << pcszAddress); };
214 virtual int removeV6(const char *pcszAdapter, const char *pcszAddress)
215 { return execute(CmdList(pcszAdapter) << "inet6" << delCmdArg() << pcszAddress); };
216};
217
218
219/*********************************************************************************************************************************
220* Platform-specific commands *
221*********************************************************************************************************************************/
222
223class CmdIfconfigLinux : public CmdIfconfig
224{
225protected:
226 virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
227 { return execute(CmdList(pcszAdapter) << "0.0.0.0"); NOREF(pcszAddress); };
228 virtual const char *addCmdArg(void) const { return "add"; };
229 virtual const char *delCmdArg(void) const { return "del"; };
230};
231
232class CmdIfconfigDarwin : public CmdIfconfig
233{
234protected:
235 virtual const char *addCmdArg(void) const { return "add"; };
236 virtual const char *delCmdArg(void) const { return "delete"; };
237};
238
239class CmdIfconfigSolaris : public CmdIfconfig
240{
241public:
242 virtual int set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
243 {
244 const char *pcszFamily = isAddrV6(pcszAddress) ? "inet6" : "inet";
245 int status;
246
247 status = execute(CmdList(pcszAdapter) << pcszFamily);
248 if (status != EXIT_SUCCESS)
249 status = execute(CmdList(pcszAdapter) << pcszFamily << "plumb" << "up");
250 if (status != EXIT_SUCCESS)
251 return status;
252
253 return CmdIfconfig::set(pcszAdapter, pcszAddress, pcszNetmask);
254 };
255protected:
256 /* We can umplumb IPv4 interfaces only! */
257 virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
258 {
259 int rc = CmdIfconfig::removeV4(pcszAdapter, pcszAddress);
260
261 /** @todo Do we really need to unplumb inet here? */
262 execute(CmdList(pcszAdapter) << "inet" << "unplumb");
263 return rc;
264 };
265 virtual const char *addCmdArg(void) const { return "addif"; };
266 virtual const char *delCmdArg(void) const { return "removeif"; };
267};
268
269
270#ifdef RT_OS_LINUX
271/*
272 * Helper class to incapsulate IPv4 address conversion.
273 */
274class AddressIPv4
275{
276public:
277 AddressIPv4(const char *pcszAddress, const char *pcszNetmask = 0)
278 {
279 if (pcszNetmask)
280 m_Prefix = maskToPrefix(pcszNetmask);
281 else
282 {
283 /*
284 * Since guessing network mask is probably futile we simply use 24,
285 * as it matches our defaults. When non-default values are used
286 * providing a proper netmask is up to the user.
287 */
288 m_Prefix = 24;
289 }
290 inet_pton(AF_INET, pcszAddress, &(m_Address.sin_addr));
291 snprintf(m_szAddressAndMask, sizeof(m_szAddressAndMask), "%s/%d", pcszAddress, m_Prefix);
292 m_Broadcast.sin_addr.s_addr = computeBroadcast(m_Address.sin_addr.s_addr, m_Prefix);
293 inet_ntop(AF_INET, &(m_Broadcast.sin_addr), m_szBroadcast, sizeof(m_szBroadcast));
294 }
295 const char *getBroadcast() const { return m_szBroadcast; };
296 const char *getAddressAndMask() const { return m_szAddressAndMask; };
297private:
298 unsigned int maskToPrefix(const char *pcszNetmask);
299 unsigned long computeBroadcast(unsigned long ulAddress, unsigned int uPrefix);
300
301 unsigned int m_Prefix;
302 struct sockaddr_in m_Address;
303 struct sockaddr_in m_Broadcast;
304 char m_szAddressAndMask[INET_ADDRSTRLEN + 3]; /* e.g. 192.168.56.101/24 */
305 char m_szBroadcast[INET_ADDRSTRLEN];
306};
307
308unsigned int AddressIPv4::maskToPrefix(const char *pcszNetmask)
309{
310 unsigned cBits = 0;
311 unsigned m[4];
312
313 if (sscanf(pcszNetmask, "%u.%u.%u.%u", &m[0], &m[1], &m[2], &m[3]) == 4)
314 {
315 for (int i = 0; i < 4 && m[i]; ++i)
316 {
317 int mask = m[i];
318 while (mask & 0x80)
319 {
320 cBits++;
321 mask <<= 1;
322 }
323 }
324 }
325 return cBits;
326}
327
328unsigned long AddressIPv4::computeBroadcast(unsigned long ulAddress, unsigned int uPrefix)
329{
330 /* Note: the address is big-endian. */
331 unsigned long ulNetworkMask = (1l << uPrefix) - 1;
332 return (ulAddress & ulNetworkMask) | ~ulNetworkMask;
333}
334
335
336/*
337 * Linux-specific implementation of 'ip' command, as other platforms do not support it.
338 */
339class CmdIpLinux : public AddressCommand
340{
341public:
342 CmdIpLinux() { m_pszPath = "/sbin/ip"; };
343 /**
344 * IPv4 and IPv6 syntax is the same, so we override `remove` instead of implementing
345 * family-specific commands. It would be easier to use the same body in both
346 * 'removeV4' and 'removeV6', so we override 'remove' to illustrate how to do common
347 * implementation.
348 */
349 virtual int remove(const char *pcszAdapter, const char *pcszAddress)
350 { return execute(CmdList("addr") << "del" << pcszAddress << "dev" << pcszAdapter); };
351protected:
352 virtual int addV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
353 {
354 AddressIPv4 addr(pcszAddress, pcszNetmask);
355 bringUp(pcszAdapter);
356 return execute(CmdList("addr") << "add" << addr.getAddressAndMask() <<
357 "broadcast" << addr.getBroadcast() << "dev" << pcszAdapter);
358 };
359 virtual int addV6(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
360 {
361 bringUp(pcszAdapter);
362 return execute(CmdList("addr") << "add" << pcszAddress << "dev" << pcszAdapter);
363 NOREF(pcszNetmask);
364 };
365 /**
366 * Our command does not support 'replacing' addresses. Reporting this fact to generic implementation
367 * of 'set' causes it to remove all assigned addresses, then 'add' the new one.
368 */
369 virtual int setV4(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask = 0)
370 { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); NOREF(pcszNetmask); };
371 /** We use family-agnostic command syntax. See 'remove' above. */
372 virtual int removeV4(const char *pcszAdapter, const char *pcszAddress)
373 { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); };
374 /** We use family-agnostic command syntax. See 'remove' above. */
375 virtual int removeV6(const char *pcszAdapter, const char *pcszAddress)
376 { return ENOTSUP; NOREF(pcszAdapter); NOREF(pcszAddress); };
377 virtual CmdList getShowCommand(const char *pcszAdapter) const
378 { return CmdList("addr") << "show" << "dev" << pcszAdapter; };
379private:
380 /** Brings up the adapter */
381 void bringUp(const char *pcszAdapter)
382 { execute(CmdList("link") << "set" << "dev" << pcszAdapter << "up"); };
383};
384#endif /* RT_OS_LINUX */
385
386
387/*********************************************************************************************************************************
388* Generic address command implementations *
389*********************************************************************************************************************************/
390
391int AddressCommand::set(const char *pcszAdapter, const char *pcszAddress, const char *pcszNetmask)
392{
393 if (isAddrV6(pcszAddress))
394 {
395 removeAddresses(pcszAdapter, "inet6");
396 return addV6(pcszAdapter, pcszAddress, pcszNetmask);
397 }
398 int rc = setV4(pcszAdapter, pcszAddress, pcszNetmask);
399 if (rc == ENOTSUP)
400 {
401 removeAddresses(pcszAdapter, "inet");
402 rc = addV4(pcszAdapter, pcszAddress, pcszNetmask);
403 }
404 return rc;
405}
406
407int AddressCommand::remove(const char *pcszAdapter, const char *pcszAddress)
408{
409 if (isAddrV6(pcszAddress))
410 return removeV6(pcszAdapter, pcszAddress);
411 return removeV4(pcszAdapter, pcszAddress);
412}
413
414/*
415 * Allocate an array of exec arguments. In addition to arguments provided
416 * we need to include the full path to the executable as well as "terminating"
417 * null pointer marking the end of the array.
418 */
419char * const * AddressCommand::allocArgv(const CmdList& list)
420{
421 int i = 0;
422 std::list<const char *>::const_iterator it;
423 const char **argv = (const char **)calloc(list.getList().size() + 2, sizeof(const char *));
424 if (argv)
425 {
426 argv[i++] = m_pszPath;
427 for (it = list.getList().begin(); it != list.getList().end(); ++it)
428 argv[i++] = *it;
429 argv[i++] = NULL;
430 }
431 return (char * const*)argv;
432}
433
434int AddressCommand::execute(CmdList& list)
435{
436 char * const pEnv[] = { (char*)"LC_ALL=C", NULL };
437 char * const* argv = allocArgv(list);
438 if (argv == NULL)
439 return EXIT_FAILURE;
440
441 if (verbose)
442 {
443 const char *sep = "";
444 for (const char * const *pArg = argv; *pArg != NULL; ++pArg)
445 {
446 printf("%s%s", sep, *pArg);
447 sep = " ";
448 }
449 printf("\n");
450 }
451 if (dry_run)
452 return EXIT_SUCCESS;
453
454 int rc = EXIT_FAILURE; /* o/~ hope for the best, expect the worst */
455 pid_t childPid = fork();
456 switch (childPid)
457 {
458 case -1: /* Something went wrong. */
459 perror("fork");
460 break;
461
462 case 0: /* Child process. */
463 if (execve(argv[0], argv, pEnv) == -1)
464 {
465 perror("execve");
466 exit(EXIT_FAILURE);
467 /* NOTREACHED */
468 }
469 break;
470
471 default: /* Parent process. */
472 {
473 int status;
474 pid_t waited = waitpid(childPid, &status, 0);
475 if (waited == childPid) /* likely*/
476 {
477 if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
478 rc = EXIT_SUCCESS;
479 }
480 else if (waited == (pid_t)-1)
481 {
482 perror("waitpid");
483 }
484 else
485 {
486 /* should never happen */
487 fprintf(stderr, "waitpid: unexpected pid %lld\n",
488 (long long int)waited);
489 }
490 break;
491 }
492 }
493
494 free((void*)argv);
495 return rc;
496}
497
498#define MAX_ADDRESSES 128
499#define MAX_ADDRLEN 64
500
501int AddressCommand::removeAddresses(const char *pcszAdapter, const char *pcszFamily)
502{
503 char szBuf[1024];
504 char aszAddresses[MAX_ADDRESSES][MAX_ADDRLEN];
505 int rc = EXIT_SUCCESS;
506 int fds[2];
507 char * const * argv = allocArgv(getShowCommand(pcszAdapter));
508 char * const envp[] = { (char*)"LC_ALL=C", NULL };
509
510 memset(aszAddresses, 0, sizeof(aszAddresses));
511
512 rc = pipe(fds);
513 if (rc < 0)
514 return errno;
515
516 pid_t pid = fork();
517 if (pid < 0)
518 return errno;
519
520 if (pid == 0)
521 {
522 /* child */
523 close(fds[0]);
524 close(STDOUT_FILENO);
525 rc = dup2(fds[1], STDOUT_FILENO);
526 if (rc >= 0)
527 if (execve(argv[0], argv, envp) == -1)
528 return errno;
529 return rc;
530 }
531
532 /* parent */
533 close(fds[1]);
534 FILE *fp = fdopen(fds[0], "r");
535 if (!fp)
536 return false;
537
538 int cAddrs;
539 for (cAddrs = 0; cAddrs < MAX_ADDRESSES && fgets(szBuf, sizeof(szBuf), fp);)
540 {
541 int cbSkipWS = strspn(szBuf, " \t");
542 char *pszWord = strtok(szBuf + cbSkipWS, " ");
543 /* We are concerned with particular family address lines only. */
544 if (!pszWord || strcmp(pszWord, pcszFamily))
545 continue;
546
547 pszWord = strtok(NULL, " ");
548
549 /* Skip "addr:" word if present. */
550 if (pszWord && !strcmp(pszWord, "addr:"))
551 pszWord = strtok(NULL, " ");
552
553 /* Skip link-local address lines. */
554 if (!pszWord || !strncmp(pszWord, "fe80", 4))
555 continue;
556 strncpy(aszAddresses[cAddrs++], pszWord, MAX_ADDRLEN-1);
557 }
558 fclose(fp);
559
560 for (int i = 0; i < cAddrs && rc == EXIT_SUCCESS; i++)
561 rc = remove(pcszAdapter, aszAddresses[i]);
562
563 return rc;
564}
565
566
567/*********************************************************************************************************************************
568* Adapter creation/removal implementations *
569*********************************************************************************************************************************/
570
571/*
572 * A generic implementation of adapter creation/removal ioctl calls.
573 */
574class Adapter
575{
576public:
577 int add(char *pszNameInOut);
578 int remove(const char *pcszName);
579 int checkName(const char *pcszNameIn, char *pszNameOut, size_t cbNameOut);
580protected:
581 virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq);
582};
583
584/*
585 * Solaris does not support dynamic creation/removal of adapters.
586 */
587class AdapterSolaris : public Adapter
588{
589protected:
590 virtual int doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
591 { return 1 /*ENOTSUP*/; NOREF(iCmd); NOREF(pReq); };
592};
593
594#if defined(RT_OS_LINUX)
595/*
596 * Linux implementation provides a 'workaround' to obtain adapter speed.
597 */
598class AdapterLinux : public Adapter
599{
600public:
601 int getSpeed(const char *pszName, unsigned *puSpeed);
602};
603
604int AdapterLinux::getSpeed(const char *pszName, unsigned *puSpeed)
605{
606 struct ifreq IfReq;
607 struct ethtool_value EthToolVal;
608 struct ethtool_cmd EthToolReq;
609 int fd = socket(AF_INET, SOCK_DGRAM, 0);
610 if (fd < 0)
611 {
612 fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
613 "speed for %s: ", pszName);
614 perror("VBoxNetAdpCtl: failed to open control socket");
615 return ADPCTLERR_SOCKET_FAILED;
616 }
617 /* Get link status first. */
618 memset(&EthToolVal, 0, sizeof(EthToolVal));
619 memset(&IfReq, 0, sizeof(IfReq));
620 snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName);
621
622 EthToolVal.cmd = ETHTOOL_GLINK;
623 IfReq.ifr_data = (caddr_t)&EthToolVal;
624 int rc = ioctl(fd, SIOCETHTOOL, &IfReq);
625 if (rc == 0)
626 {
627 if (EthToolVal.data)
628 {
629 memset(&IfReq, 0, sizeof(IfReq));
630 snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszName);
631 EthToolReq.cmd = ETHTOOL_GSET;
632 IfReq.ifr_data = (caddr_t)&EthToolReq;
633 rc = ioctl(fd, SIOCETHTOOL, &IfReq);
634 if (rc == 0)
635 {
636 *puSpeed = EthToolReq.speed;
637 }
638 else
639 {
640 fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
641 "speed for %s: ", pszName);
642 perror("VBoxNetAdpCtl: ioctl failed");
643 rc = ADPCTLERR_IOCTL_FAILED;
644 }
645 }
646 else
647 *puSpeed = 0;
648 }
649 else
650 {
651 fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
652 "status for %s: ", pszName);
653 perror("VBoxNetAdpCtl: ioctl failed");
654 rc = ADPCTLERR_IOCTL_FAILED;
655 }
656
657 close(fd);
658 return rc;
659}
660#endif /* defined(RT_OS_LINUX) */
661
662int Adapter::add(char *pszName /* in/out */)
663{
664 VBOXNETADPREQ Req;
665 memset(&Req, '\0', sizeof(Req));
666 snprintf(Req.szName, sizeof(Req.szName), "%s", pszName);
667 int rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
668 if (rc == 0)
669 strncpy(pszName, Req.szName, VBOXNETADP_MAX_NAME_LEN);
670 return rc;
671}
672
673int Adapter::remove(const char *pcszName)
674{
675 VBOXNETADPREQ Req;
676 memset(&Req, '\0', sizeof(Req));
677 snprintf(Req.szName, sizeof(Req.szName), "%s", pcszName);
678 return doIOCtl(VBOXNETADP_CTL_REMOVE, &Req);
679}
680
681int Adapter::checkName(const char *pcszNameIn, char *pszNameOut, size_t cbNameOut)
682{
683 int iAdapterIndex = -1;
684
685 if ( strlen(pcszNameIn) >= VBOXNETADP_MAX_NAME_LEN
686 || sscanf(pcszNameIn, "vboxnet%d", &iAdapterIndex) != 1
687 || iAdapterIndex < 0 || iAdapterIndex >= VBOXNETADP_MAX_INSTANCES )
688 {
689 fprintf(stderr, "VBoxNetAdpCtl: Setting configuration for '%s' is not supported.\n", pcszNameIn);
690 return ADPCTLERR_BAD_NAME;
691 }
692 snprintf(pszNameOut, cbNameOut, "vboxnet%d", iAdapterIndex);
693 if (strcmp(pszNameOut, pcszNameIn))
694 {
695 fprintf(stderr, "VBoxNetAdpCtl: Invalid adapter name '%s'.\n", pcszNameIn);
696 return ADPCTLERR_BAD_NAME;
697 }
698
699 return 0;
700}
701
702int Adapter::doIOCtl(unsigned long iCmd, VBOXNETADPREQ *pReq)
703{
704 int fd = open(VBOXNETADP_CTL_DEV_NAME, O_RDWR);
705 if (fd == -1)
706 {
707 fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ",
708 iCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding",
709 pReq->szName[0] ? pReq->szName : "new interface");
710 perror("failed to open " VBOXNETADP_CTL_DEV_NAME);
711 return ADPCTLERR_NO_CTL_DEV;
712 }
713
714 int rc = ioctl(fd, iCmd, pReq);
715 if (rc == -1)
716 {
717 fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ",
718 iCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding",
719 pReq->szName[0] ? pReq->szName : "new interface");
720 perror("VBoxNetAdpCtl: ioctl failed for " VBOXNETADP_CTL_DEV_NAME);
721 rc = ADPCTLERR_IOCTL_FAILED;
722 }
723
724 close(fd);
725
726 return rc;
727}
728
729
730/*********************************************************************************************************************************
731* Main logic, argument parsing, etc. *
732*********************************************************************************************************************************/
733
734#if defined(RT_OS_LINUX)
735static CmdIfconfigLinux g_ifconfig;
736static AdapterLinux g_adapter;
737#elif defined(RT_OS_SOLARIS)
738static CmdIfconfigSolaris g_ifconfig;
739static AdapterSolaris g_adapter;
740#else
741static CmdIfconfigDarwin g_ifconfig;
742static Adapter g_adapter;
743#endif
744
745static AddressCommand& chooseAddressCommand()
746{
747#if defined(RT_OS_LINUX)
748 static CmdIpLinux g_ip;
749 if (g_ip.isAvailable())
750 return g_ip;
751#endif
752 return g_ifconfig;
753}
754
755int main(int argc, char *argv[])
756{
757 char szAdapterName[VBOXNETADP_MAX_NAME_LEN];
758 int rc;
759
760 AddressCommand& cmd = chooseAddressCommand();
761
762
763 static const struct option options[] = {
764 { "dry-run", no_argument, NULL, 'n' },
765 { "verbose", no_argument, NULL, 'v' },
766 { NULL, 0, NULL, 0 }
767 };
768
769 int ch;
770 while ((ch = getopt_long(argc, argv, "nv", options, NULL)) != -1)
771 {
772 switch (ch)
773 {
774 case 'n':
775 dry_run = true;
776 verbose = true;
777 break;
778
779 case 'v':
780 verbose = true;
781 break;
782
783 case '?':
784 default:
785 return usage();
786 }
787 }
788 argc -= optind;
789 argv += optind;
790
791 if (argc == 0)
792 return usage();
793
794
795 /*
796 * VBoxNetAdpCtl add
797 */
798 if (strcmp(argv[0], "add") == 0)
799 {
800 if (argc > 1) /* extraneous args */
801 return usage();
802
803 /* Create a new interface, print its name. */
804 *szAdapterName = '\0';
805 rc = g_adapter.add(szAdapterName);
806 if (rc == EXIT_SUCCESS)
807 puts(szAdapterName);
808
809 return rc;
810 }
811
812
813 /*
814 * All other variants are of the form:
815 * VBoxNetAdpCtl if0 ...action...
816 */
817 const char * const ifname = argv[0];
818 const char * const action = argv[1];
819 if (argc < 2)
820 return usage();
821
822
823#ifdef RT_OS_LINUX
824 /*
825 * VBoxNetAdpCtl iface42 speed
826 *
827 * This ugly hack is needed for retrieving the link speed on
828 * pre-2.6.33 kernels (see @bugref{6345}).
829 *
830 * This variant is used with any interface, not just host-only.
831 */
832 if (strcmp(action, "speed") == 0)
833 {
834 if (argc > 2) /* extraneous args */
835 return usage();
836
837 if (strlen(ifname) >= IFNAMSIZ)
838 {
839 fprintf(stderr, "Interface name too long\n");
840 return EXIT_FAILURE;
841 }
842
843 unsigned uSpeed = 0;
844 rc = g_adapter.getSpeed(ifname, &uSpeed);
845 if (rc == EXIT_SUCCESS)
846 printf("%u", uSpeed);
847
848 return rc;
849 }
850#endif /* RT_OS_LINUX */
851
852
853 /*
854 * The rest of the actions only operate on host-only interfaces.
855 */
856 /** @todo Why the code below uses both ifname and szAdapterName? */
857 rc = g_adapter.checkName(ifname, szAdapterName, sizeof(szAdapterName));
858 if (rc != EXIT_SUCCESS)
859 return rc;
860
861
862 /*
863 * VBoxNetAdpCtl vboxnetN remove
864 */
865 if (strcmp(action, "remove") == 0)
866 {
867 if (argc > 2) /* extraneous args */
868 return usage();
869
870 /* Remove an existing interface */
871 return g_adapter.remove(ifname);
872 }
873
874 /*
875 * VBoxNetAdpCtl vboxnetN add
876 */
877 if (strcmp(action, "add") == 0)
878 {
879 if (argc > 2) /* extraneous args */
880 return usage();
881
882 /* Create an interface with the given name, print its name. */
883 rc = g_adapter.add(szAdapterName);
884 if (rc == EXIT_SUCCESS)
885 puts(szAdapterName);
886
887 return rc;
888 }
889
890
891 /*
892 * The rest of the actions are of the form
893 * VBoxNetAdpCtl vboxnetN $addr [...]
894 *
895 * Use the argument after the address to select the action.
896 */
897 /** @todo Do early verification of addr format here? */
898 const char * const addr = argv[1];
899 const char * const keyword = argv[2];
900
901
902 /*
903 * VBoxNetAdpCtl vboxnetN 1.2.3.4
904 */
905 if (keyword == NULL)
906 {
907 return cmd.set(ifname, addr);
908 }
909
910 /*
911 * VBoxNetAdpCtl vboxnetN 1.2.3.4 netmask 255.255.255.0
912 */
913 if (strcmp(keyword, "netmask") == 0)
914 {
915 if (argc != 4) /* too few or too many args */
916 return usage();
917
918 const char * const mask = argv[3];
919 return cmd.set(ifname, addr, mask);
920 }
921
922 /*
923 * VBoxNetAdpCtl vboxnetN 1.2.3.4 remove
924 */
925 if (strcmp(keyword, "remove") == 0)
926 {
927 if (argc > 3) /* extraneous args */
928 return usage();
929
930 return cmd.remove(ifname, addr);
931 }
932
933 return usage();
934}
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