VirtualBox

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

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

Network: Increased the limit of host-only interfaces to 128.

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