VirtualBox

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

Last change on this file since 47470 was 44529, checked in by vboxsync, 12 years ago

header (C) fixes

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