VirtualBox

source: vbox/trunk/src/apps/adpctl/VBoxNetAdpCtl.cpp@ 51651

Last change on this file since 51651 was 50551, checked in by vboxsync, 11 years ago

src/adpctl: if /sbin/ifconfig does not exist, fall back to /bin/ifconfig

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: VBoxNetAdpCtl.cpp 50551 2014-02-24 09:31:41Z vboxsync $ */
2/** @file
3 * Apps - VBoxAdpCtl, Configuration tool for vboxnetX adapters.
4 */
5
6/*
7 * Copyright (C) 2009-2012 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 <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <sys/wait.h>
28#include <sys/ioctl.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#ifdef RT_OS_LINUX
32# include <net/if.h>
33# include <linux/types.h>
34/* Older versions of ethtool.h rely on these: */
35typedef unsigned long long u64;
36typedef __uint32_t u32;
37typedef __uint16_t u16;
38typedef __uint8_t u8;
39# include <linux/ethtool.h>
40# include <linux/sockios.h>
41#endif
42#ifdef RT_OS_SOLARIS
43# include <sys/ioccom.h>
44#endif
45
46/** @todo Error codes must be moved to some header file */
47#define ADPCTLERR_BAD_NAME 2
48#define ADPCTLERR_NO_CTL_DEV 3
49#define ADPCTLERR_IOCTL_FAILED 4
50#define ADPCTLERR_SOCKET_FAILED 5
51
52/** @todo These are duplicates from src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h */
53#define VBOXNETADP_CTL_DEV_NAME "/dev/vboxnetctl"
54#define VBOXNETADP_MAX_INSTANCES 128
55#define VBOXNETADP_NAME "vboxnet"
56#define VBOXNETADP_MAX_NAME_LEN 32
57#define VBOXNETADP_CTL_ADD _IOWR('v', 1, VBOXNETADPREQ)
58#define VBOXNETADP_CTL_REMOVE _IOW('v', 2, VBOXNETADPREQ)
59typedef struct VBoxNetAdpReq
60{
61 char szName[VBOXNETADP_MAX_NAME_LEN];
62} VBOXNETADPREQ;
63typedef VBOXNETADPREQ *PVBOXNETADPREQ;
64
65#define VBOXADPCTL_IFCONFIG_PATH1 "/sbin/ifconfig"
66#define VBOXADPCTL_IFCONFIG_PATH2 "/bin/ifconfig"
67static char *g_pszIfConfig;
68
69#if defined(RT_OS_LINUX)
70# define VBOXADPCTL_DEL_CMD "del"
71# define VBOXADPCTL_ADD_CMD "add"
72#elif defined(RT_OS_SOLARIS)
73# define VBOXADPCTL_DEL_CMD "removeif"
74# define VBOXADPCTL_ADD_CMD "addif"
75#else
76# define VBOXADPCTL_DEL_CMD "delete"
77# define VBOXADPCTL_ADD_CMD "add"
78#endif
79
80static void showUsage(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}
86
87static void setPathIfConfig(void)
88{
89 struct stat s;
90 if ( !stat(VBOXADPCTL_IFCONFIG_PATH1, &s)
91 && S_ISREG(s.st_mode))
92 g_pszIfConfig = (char*)VBOXADPCTL_IFCONFIG_PATH1;
93 else
94 g_pszIfConfig = (char*)VBOXADPCTL_IFCONFIG_PATH2;
95}
96
97static int executeIfconfig(const char *pcszAdapterName, const char *pcszArg1,
98 const char *pcszArg2 = NULL,
99 const char *pcszArg3 = NULL,
100 const char *pcszArg4 = NULL,
101 const char *pcszArg5 = NULL)
102{
103 const char * const argv[] =
104 {
105 g_pszIfConfig,
106 pcszAdapterName,
107 pcszArg1, /* [address family] */
108 pcszArg2, /* address */
109 pcszArg3, /* ['netmask'] */
110 pcszArg4, /* [network mask] */
111 pcszArg5, /* [network mask] */
112 NULL /* terminator */
113 };
114 char * const envp[] = { (char*)"LC_ALL=C", NULL };
115 int rc = EXIT_SUCCESS;
116 pid_t childPid = fork();
117 switch (childPid)
118 {
119 case -1: /* Something went wrong. */
120 perror("fork() failed");
121 rc = EXIT_FAILURE;
122 break;
123 case 0: /* Child process. */
124 if (execve(argv[0], (char * const*)argv, envp) == -1)
125 rc = EXIT_FAILURE;
126 break;
127 default: /* Parent process. */
128 waitpid(childPid, &rc, 0);
129 break;
130 }
131
132 return rc;
133}
134
135#define MAX_ADDRESSES 128
136#define MAX_ADDRLEN 64
137
138static bool removeAddresses(char *pszAdapterName)
139{
140 char szBuf[1024];
141 char aszAddresses[MAX_ADDRESSES][MAX_ADDRLEN];
142 int rc;
143 int fds[2];
144 char * const argv[] = { g_pszIfConfig, pszAdapterName, NULL };
145 char * const envp[] = { (char*)"LC_ALL=C", NULL };
146
147 memset(aszAddresses, 0, sizeof(aszAddresses));
148
149 rc = pipe(fds);
150 if (rc < 0)
151 return false;
152
153 pid_t pid = fork();
154 if (pid < 0)
155 return false;
156
157 if (pid == 0)
158 {
159 /* child */
160 close(fds[0]);
161 close(STDOUT_FILENO);
162 rc = dup2(fds[1], STDOUT_FILENO);
163 if (rc >= 0)
164 execve(argv[0], argv, envp);
165 return false;
166 }
167
168 /* parent */
169 close(fds[1]);
170 FILE *fp = fdopen(fds[0], "r");
171 if (!fp)
172 return false;
173
174 int cAddrs;
175 for (cAddrs = 0; cAddrs < MAX_ADDRESSES && fgets(szBuf, sizeof(szBuf), fp);)
176 {
177 int cbSkipWS = strspn(szBuf, " \t");
178 char *pszWord = strtok(szBuf + cbSkipWS, " ");
179 /* We are concerned with IPv6 address lines only. */
180 if (!pszWord || strcmp(pszWord, "inet6"))
181 continue;
182#ifdef RT_OS_LINUX
183 pszWord = strtok(NULL, " ");
184 /* Skip "addr:". */
185 if (!pszWord || strcmp(pszWord, "addr:"))
186 continue;
187#endif
188 pszWord = strtok(NULL, " ");
189 /* Skip link-local addresses. */
190 if (!pszWord || !strncmp(pszWord, "fe80", 4))
191 continue;
192 strncpy(aszAddresses[cAddrs++], pszWord, MAX_ADDRLEN-1);
193 }
194 fclose(fp);
195
196 for (int i = 0; i < cAddrs; i++)
197 {
198 if (executeIfconfig(pszAdapterName, "inet6",
199 VBOXADPCTL_DEL_CMD, aszAddresses[i]) != EXIT_SUCCESS)
200 return false;
201 }
202
203 return true;
204}
205
206static int doIOCtl(unsigned long uCmd, VBOXNETADPREQ *pReq)
207{
208 int fd = open(VBOXNETADP_CTL_DEV_NAME, O_RDWR);
209 if (fd == -1)
210 {
211 fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ",
212 uCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding",
213 pReq->szName[0] ? pReq->szName : "new interface");
214 perror("failed to open " VBOXNETADP_CTL_DEV_NAME);
215 return ADPCTLERR_NO_CTL_DEV;
216 }
217
218 int rc = ioctl(fd, uCmd, pReq);
219 if (rc == -1)
220 {
221 fprintf(stderr, "VBoxNetAdpCtl: Error while %s %s: ",
222 uCmd == VBOXNETADP_CTL_REMOVE ? "removing" : "adding",
223 pReq->szName[0] ? pReq->szName : "new interface");
224 perror("VBoxNetAdpCtl: ioctl failed for " VBOXNETADP_CTL_DEV_NAME);
225 rc = ADPCTLERR_IOCTL_FAILED;
226 }
227
228 close(fd);
229
230 return rc;
231}
232
233static int checkAdapterName(const char *pcszNameIn, char *pszNameOut)
234{
235 int iAdapterIndex = -1;
236
237 if ( strlen(pcszNameIn) >= VBOXNETADP_MAX_NAME_LEN
238 || sscanf(pcszNameIn, "vboxnet%d", &iAdapterIndex) != 1
239 || iAdapterIndex < 0 || iAdapterIndex >= VBOXNETADP_MAX_INSTANCES )
240 {
241 fprintf(stderr, "VBoxNetAdpCtl: Setting configuration for '%s' is not supported.\n", pcszNameIn);
242 return ADPCTLERR_BAD_NAME;
243 }
244 sprintf(pszNameOut, "vboxnet%d", iAdapterIndex);
245 if (strcmp(pszNameOut, pcszNameIn))
246 {
247 fprintf(stderr, "VBoxNetAdpCtl: Invalid adapter name '%s'.\n", pcszNameIn);
248 return ADPCTLERR_BAD_NAME;
249 }
250
251 return 0;
252}
253
254int main(int argc, char *argv[])
255{
256 char szAdapterName[VBOXNETADP_MAX_NAME_LEN];
257 char *pszAdapterName = NULL;
258 const char *pszAddress = NULL;
259 const char *pszNetworkMask = NULL;
260 const char *pszOption = NULL;
261 int rc = EXIT_SUCCESS;
262 bool fRemove = false;
263 VBOXNETADPREQ Req;
264
265 setPathIfConfig();
266
267 switch (argc)
268 {
269 case 5:
270 {
271 /* Add a netmask to existing interface */
272 if (strcmp("netmask", argv[3]))
273 {
274 fprintf(stderr, "Invalid argument: %s\n\n", argv[3]);
275 showUsage();
276 return 1;
277 }
278 pszOption = "netmask";
279 pszNetworkMask = argv[4];
280 pszAdapterName = argv[1];
281 pszAddress = argv[2];
282 break;
283 }
284
285 case 4:
286 {
287 /* Remove a single address from existing interface */
288 if (strcmp("remove", argv[3]))
289 {
290 fprintf(stderr, "Invalid argument: %s\n\n", argv[3]);
291 showUsage();
292 return 1;
293 }
294 fRemove = true;
295 pszAdapterName = argv[1];
296 pszAddress = argv[2];
297 break;
298 }
299
300 case 3:
301 {
302 pszAdapterName = argv[1];
303 memset(&Req, '\0', sizeof(Req));
304#ifdef RT_OS_LINUX
305 if (strcmp("speed", argv[2]) == 0)
306 {
307 /*
308 * This ugly hack is needed for retrieving the link speed on
309 * pre-2.6.33 kernels (see @bugref{6345}).
310 */
311 if (strlen(pszAdapterName) >= IFNAMSIZ)
312 {
313 showUsage();
314 return -1;
315 }
316 struct ifreq IfReq;
317 struct ethtool_value EthToolVal;
318 struct ethtool_cmd EthToolReq;
319 int fd = socket(AF_INET, SOCK_DGRAM, 0);
320 if (fd < 0)
321 {
322 fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
323 "speed for %s: ", pszAdapterName);
324 perror("VBoxNetAdpCtl: failed to open control socket");
325 return ADPCTLERR_SOCKET_FAILED;
326 }
327 /* Get link status first. */
328 memset(&EthToolVal, 0, sizeof(EthToolVal));
329 memset(&IfReq, 0, sizeof(IfReq));
330 snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszAdapterName);
331
332 EthToolVal.cmd = ETHTOOL_GLINK;
333 IfReq.ifr_data = (caddr_t)&EthToolVal;
334 rc = ioctl(fd, SIOCETHTOOL, &IfReq);
335 if (rc == 0)
336 {
337 if (EthToolVal.data)
338 {
339 memset(&IfReq, 0, sizeof(IfReq));
340 snprintf(IfReq.ifr_name, sizeof(IfReq.ifr_name), "%s", pszAdapterName);
341 EthToolReq.cmd = ETHTOOL_GSET;
342 IfReq.ifr_data = (caddr_t)&EthToolReq;
343 rc = ioctl(fd, SIOCETHTOOL, &IfReq);
344 if (rc == 0)
345 {
346 printf("%u", EthToolReq.speed);
347 }
348 else
349 {
350 fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
351 "speed for %s: ", pszAdapterName);
352 perror("VBoxNetAdpCtl: ioctl failed");
353 rc = ADPCTLERR_IOCTL_FAILED;
354 }
355 }
356 else
357 printf("0");
358 }
359 else
360 {
361 fprintf(stderr, "VBoxNetAdpCtl: Error while retrieving link "
362 "status for %s: ", pszAdapterName);
363 perror("VBoxNetAdpCtl: ioctl failed");
364 rc = ADPCTLERR_IOCTL_FAILED;
365 }
366
367 close(fd);
368 return rc;
369 }
370#endif
371 rc = checkAdapterName(pszAdapterName, szAdapterName);
372 if (rc)
373 return rc;
374 snprintf(Req.szName, sizeof(Req.szName), "%s", szAdapterName);
375 pszAddress = argv[2];
376 if (strcmp("remove", pszAddress) == 0)
377 {
378 /* Remove an existing interface */
379#ifdef RT_OS_SOLARIS
380 return 1;
381#else
382 return doIOCtl(VBOXNETADP_CTL_REMOVE, &Req);
383#endif
384 }
385 else if (strcmp("add", pszAddress) == 0)
386 {
387 /* Create an interface with given name */
388#ifdef RT_OS_SOLARIS
389 return 1;
390#else
391 rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
392 if (rc == 0)
393 puts(Req.szName);
394#endif
395 return rc;
396 }
397 break;
398 }
399
400 case 2:
401 {
402 /* Create a new interface */
403 if (strcmp("add", argv[1]) == 0)
404 {
405#ifdef RT_OS_SOLARIS
406 return 1;
407#else
408 memset(&Req, '\0', sizeof(Req));
409 rc = doIOCtl(VBOXNETADP_CTL_ADD, &Req);
410 if (rc == 0)
411 puts(Req.szName);
412#endif
413 return rc;
414 }
415 /* Fall through */
416 }
417
418 default:
419 fprintf(stderr, "Invalid number of arguments.\n\n");
420 /* Fall through */
421 case 1:
422 showUsage();
423 return 1;
424 }
425
426 rc = checkAdapterName(pszAdapterName, szAdapterName);
427 if (rc)
428 return rc;
429
430 pszAdapterName = szAdapterName;
431
432 if (fRemove)
433 {
434 if (strchr(pszAddress, ':'))
435 rc = executeIfconfig(pszAdapterName, "inet6", VBOXADPCTL_DEL_CMD, pszAddress);
436 else
437 {
438#if defined(RT_OS_LINUX)
439 rc = executeIfconfig(pszAdapterName, "0.0.0.0");
440#else
441 rc = executeIfconfig(pszAdapterName, VBOXADPCTL_DEL_CMD, pszAddress);
442#endif
443
444#ifdef RT_OS_SOLARIS
445 /* On Solaris we can unplumb the ipv4 interface */
446 executeIfconfig(pszAdapterName, "inet", "unplumb");
447#endif
448 }
449 }
450 else
451 {
452 /* We are setting/replacing address. */
453 if (strchr(pszAddress, ':'))
454 {
455#ifdef RT_OS_SOLARIS
456 /* On Solaris we need to plumb the interface first if it's not already plumbed. */
457 if (executeIfconfig(pszAdapterName, "inet6") != 0)
458 executeIfconfig(pszAdapterName, "inet6", "plumb", "up");
459#endif
460 /*
461 * Before we set IPv6 address we'd like to remove
462 * all previously assigned addresses except the
463 * self-assigned one.
464 */
465 if (!removeAddresses(pszAdapterName))
466 rc = EXIT_FAILURE;
467 else
468 rc = executeIfconfig(pszAdapterName, "inet6", VBOXADPCTL_ADD_CMD, pszAddress, pszOption, pszNetworkMask);
469 }
470 else
471 {
472#ifdef RT_OS_SOLARIS
473 /* On Solaris we need to plumb the interface first if it's not already plumbed. */
474 if (executeIfconfig(pszAdapterName, "inet") != 0)
475 executeIfconfig(pszAdapterName, "plumb", "up");
476#endif
477 rc = executeIfconfig(pszAdapterName, pszAddress, pszOption, pszNetworkMask);
478 }
479 }
480 return rc;
481}
482
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