3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
5 // based on chbind.cc by Jacques Gelinas
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2, or (at your option)
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include <lib/internal.h>
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
44 #define ENSC_WRAPPERS_PREFIX "naddress: "
45 #define ENSC_WRAPPERS_IO 1
46 #define ENSC_WRAPPERS_UNISTD 1
47 #define ENSC_WRAPPERS_VSERVER 1
50 #define CMD_HELP 0x1000
51 #define CMD_VERSION 0x1001
53 #define CMD_SILENT 0x2000
54 #define CMD_NID 0x2001
55 #define CMD_ADD 0x2002
56 #define CMD_REMOVE 0x2003
57 #define CMD_SET 0x2004
59 #define CMD_BCAST 0x2011
61 int wrapper_exit_code = 255;
64 static struct option const
66 { "help", no_argument, 0, CMD_HELP },
67 { "version", no_argument, 0, CMD_VERSION },
68 { "silent", no_argument, 0, CMD_SILENT },
69 { "add", no_argument, 0, CMD_ADD },
70 { "remove", no_argument, 0, CMD_REMOVE },
71 { "set", no_argument, 0, CMD_SET },
72 { "nid", required_argument, 0, CMD_NID },
73 { "ip", required_argument, 0, CMD_IP },
74 { "bcast", required_argument, 0, CMD_BCAST },
93 showHelp(int fd, char const *cmd, int res)
95 WRITE_MSG(fd, "Usage:\n ");
98 " (--add|--remove|--set) [--silent] [--nid <nid>]\n"
99 " [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
100 "Please report bugs to " PACKAGE_BUGREPORT "\n");
109 "naddress " VERSION " -- bind to an ip and execute a program\n"
110 "This program is part of " PACKAGE_STRING "\n\n"
111 "Copyright (C) 2003,2004 Enrico Scholz\n"
112 "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
113 VERSION_COPYRIGHT_DISCLAIMER);
118 Check if a network device exist in /proc/net/dev.
119 This is used because ifconfig_ioctl triggers modprobe if requesting
120 information about non existant devices.
122 Return != 0 if the device exist.
125 existsDevice(char const *dev_raw)
127 size_t buf_size=8192;
128 char dev[strlen(dev_raw)+2];
130 strcpy(dev, dev_raw);
136 int fd=open("/proc/net/dev", O_RDONLY);
138 if (fd==-1) return false;
139 too_small = EreadAll(fd, buf, buf_size);
147 pos = strstr(buf, dev);
148 return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
152 static int ifconfig_ioctl(
158 strcpy(ifr->ifr_name, ifname);
159 return ioctl(fd, cmd, ifr);
163 Fetch the IP number of an interface from the kernel.
164 Assume the device is already available in the kernel
165 Return -1 if any error.
167 int ifconfig_getaddr (
174 if (existsDevice(ifname)){
175 int skfd = socket(AF_INET, SOCK_DGRAM, 0);
178 if (addr != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
179 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
180 *addr = sin->sin_addr.s_addr;
183 if (mask != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
184 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
185 *mask = sin->sin_addr.s_addr;
188 if (bcast != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
189 struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
190 *bcast = sin->sin_addr.s_addr;
200 convertAddress(const char *str, vc_net_nx_type *type, void *dst)
203 if (type) *type = vcNET_IPV4;
204 ret = inet_pton(AF_INET, str, dst);
206 if (type) *type = vcNET_IPV6;
207 ret = inet_pton(AF_INET6, str, dst);
209 return ret > 0 ? 0 : -1;
213 readIP(char const *str, struct vc_ips **ips)
215 if (ifconfig_getaddr(str, &(*ips)->a.ip[0], &(*ips)->a.mask[0], NULL)==-1) {
217 char tmpopt[strlen(str)+1];
218 uint32_t *mask = (*ips)->a.mask;
221 pt = strchr(tmpopt,'/');
225 if (convertAddress(tmpopt, &(*ips)->a.type, (*ips)->a.ip) == -1) {
226 WRITE_MSG(2, "Invalid IP number '");
227 WRITE_STR(2, tmpopt);
229 exit(wrapper_exit_code);
233 switch ((*ips)->a.type) {
235 mask[0] = htonl(0xffffff00);
244 // Ok, we have a network size, not a netmask
245 if (strchr(pt,'.')==0 && strchr(pt,':')==0) {
246 unsigned long sz, limit = 0;
248 switch ((*ips)->a.type) {
249 case vcNET_IPV4: limit = 32; break;
250 case vcNET_IPV6: limit = 128; break;
254 if (!isNumberUnsigned(pt, &sz, true) || sz > limit) {
255 WRITE_MSG(2, "Invalid prefix '");
258 exit(wrapper_exit_code);
261 switch ((*ips)->a.type) {
263 mask[0] = htonl(~((1 << (32 - sz)) - 1));
272 if (convertAddress(pt, NULL, &(*ips)->a.mask) == -1) {
273 WRITE_MSG(2, "Invalid netmask '");
276 exit(wrapper_exit_code);
282 (*ips)->a.type = vcNET_IPV4;
285 (*ips)->next = calloc(1, sizeof(struct vc_ips));
290 readBcast(char const *str, struct vc_ips **ips)
293 if (ifconfig_getaddr(str, NULL, NULL, &bcast)==-1){
294 if (convertAddress(str, NULL, &bcast) == -1) {
295 WRITE_MSG(2, "Invalid broadcast number '");
296 WRITE_STR(2, optarg);
298 exit(wrapper_exit_code);
301 (*ips)->a.ip[0] = bcast;
303 (*ips)->a.type = vcNET_IPV4B;
304 (*ips)->next = calloc(1, sizeof(struct vc_ips));
309 tellAddress(struct vc_net_nx *addr, bool silent)
314 if (inet_ntop(addr->type == vcNET_IPV6 ? AF_INET6 : AF_INET,
315 addr->ip, buf, sizeof(buf)) == NULL) {
316 WRITE_MSG(1, " <conversion failed>");
324 doit(struct Arguments *args)
329 struct vc_net_nx remove = { .type = vcNET_ANY };
330 if (vc_net_remove(args->nid, &remove) == -1) {
331 perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
332 exit(wrapper_exit_code);
336 if (args->do_add || args->do_set) {
337 if (!args->is_silent)
338 WRITE_MSG(1, "Adding");
339 for (ips = &args->head; ips->next; ips = ips->next) {
340 tellAddress(&ips->a, args->is_silent);
341 if (vc_net_add(args->nid, &ips->a) != (int)ips->a.count) {
342 perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
343 exit(wrapper_exit_code);
346 if (!args->is_silent)
349 else if (args->do_remove) {
350 if (!args->is_silent)
351 WRITE_MSG(1, "Removing");
352 for (ips = &args->head; ips->next; ips = ips->next) {
353 tellAddress(&ips->a, args->is_silent);
354 if (vc_net_remove(args->nid, &ips->a) != (int)ips->a.count) {
355 perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
356 exit(wrapper_exit_code);
359 if (!args->is_silent)
364 int main (int argc, char *argv[])
366 struct Arguments args = {
372 .head = { .next = NULL },
374 struct vc_ips *ips = &args.head;
377 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
381 case CMD_HELP : showHelp(1, argv[0], 0);
382 case CMD_VERSION : showVersion();
383 case CMD_SILENT : args.is_silent = true; break;
384 case CMD_NID : args.nid = Evc_nidopt2nid(optarg,true); break;
385 case CMD_ADD : args.do_add = true; break;
386 case CMD_REMOVE : args.do_remove = true; break;
387 case CMD_SET : args.do_set = true; break;
388 case CMD_IP : readIP(optarg, &ips); break;
389 case CMD_BCAST : readBcast(optarg, &ips); break;
391 WRITE_MSG(2, "Try '");
392 WRITE_STR(2, argv[0]);
393 WRITE_MSG(2, " --help' for more information.\n");
394 exit(wrapper_exit_code);
399 if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
401 if (!args.do_add && !args.do_remove && !args.do_set) {
402 WRITE_MSG(2, "No operation specified; try '--help' for more information\n");
403 exit(wrapper_exit_code);
405 else if (((args.do_add ? 1 : 0) + (args.do_remove ? 1 : 0) + (args.do_set ? 1 : 0)) > 1) {
406 WRITE_MSG(2, "Multiple operations specified; try '--help' for more information\n");
407 exit(wrapper_exit_code);
413 Eexecvp (argv[optind],argv+optind);
417 #ifdef ENSC_TESTSUITE
422 struct vc_ip_mask_pair ip;
426 readIP("1.2.3.4", &ip, &bcast);
427 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
429 readIP("1.2.3.4/8", &ip, &bcast);
430 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
432 readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
433 assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
435 readIP("localhost", &ip, &bcast);
436 assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
439 if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
440 readIP("lo", &ip, &bcast);
441 assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));