VirtualBox

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

Last change on this file since 35819 was 35819, checked in by vboxsync, 14 years ago

VBoxNetAdpCtl: be more verbose on error

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