VirtualBox

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

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

Main/Metrics: Alternative way to get link speed for old kernels (#6345)

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