VirtualBox

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

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

scm --update-copyright-year

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