--- /dev/null
+// $Id: naddress.c 2387 2006-11-20 15:01:44Z dhozac $
+
+// Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+// Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
+// based on chbind.cc by Jacques Gelinas
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "vserver.h"
+#include "util.h"
+
+#include <lib/internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define ENSC_WRAPPERS_PREFIX "naddress: "
+#define ENSC_WRAPPERS_IO 1
+#define ENSC_WRAPPERS_UNISTD 1
+#define ENSC_WRAPPERS_VSERVER 1
+#include "wrappers.h"
+
+#define CMD_HELP 0x1000
+#define CMD_VERSION 0x1001
+
+#define CMD_SILENT 0x2000
+#define CMD_NID 0x2001
+#define CMD_ADD 0x2002
+#define CMD_REMOVE 0x2003
+#define CMD_SET 0x2004
+#define CMD_IP 0x2010
+#define CMD_BCAST 0x2011
+
+int wrapper_exit_code = 255;
+
+
+static struct option const
+CMDLINE_OPTIONS[] = {
+ { "help", no_argument, 0, CMD_HELP },
+ { "version", no_argument, 0, CMD_VERSION },
+ { "silent", no_argument, 0, CMD_SILENT },
+ { "add", no_argument, 0, CMD_ADD },
+ { "remove", no_argument, 0, CMD_REMOVE },
+ { "set", no_argument, 0, CMD_SET },
+ { "nid", required_argument, 0, CMD_NID },
+ { "ip", required_argument, 0, CMD_IP },
+ { "bcast", required_argument, 0, CMD_BCAST },
+ { 0,0,0,0 }
+};
+
+struct vc_ips {
+ struct vc_net_nx a;
+ struct vc_ips *next;
+};
+
+struct Arguments {
+ nid_t nid;
+ struct vc_ips head;
+ bool is_silent;
+ bool do_add;
+ bool do_remove;
+ bool do_set;
+};
+
+static void
+showHelp(int fd, char const *cmd, int res)
+{
+ WRITE_MSG(fd, "Usage:\n ");
+ WRITE_STR(fd, cmd);
+ WRITE_MSG(fd,
+ " (--add|--remove|--set) [--silent] [--nid <nid>]\n"
+ " [--ip <ip_num>[/<mask>]] [--bcast <broadcast>] [--] <commands> <args>*\n\n"
+ "Please report bugs to " PACKAGE_BUGREPORT "\n");
+
+ exit(res);
+}
+
+static void
+showVersion()
+{
+ WRITE_MSG(1,
+ "naddress " VERSION " -- bind to an ip and execute a program\n"
+ "This program is part of " PACKAGE_STRING "\n\n"
+ "Copyright (C) 2003,2004 Enrico Scholz\n"
+ "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
+ VERSION_COPYRIGHT_DISCLAIMER);
+ exit(0);
+}
+
+/*
+ Check if a network device exist in /proc/net/dev.
+ This is used because ifconfig_ioctl triggers modprobe if requesting
+ information about non existant devices.
+
+ Return != 0 if the device exist.
+*/
+static bool
+existsDevice(char const *dev_raw)
+{
+ size_t buf_size=8192;
+ char dev[strlen(dev_raw)+2];
+
+ strcpy(dev, dev_raw);
+ strcat(dev, ":");
+ for (;;) {
+ char buf[buf_size];
+ char * pos;
+ bool too_small;
+ int fd=open("/proc/net/dev", O_RDONLY);
+
+ if (fd==-1) return false;
+ too_small = EreadAll(fd, buf, buf_size);
+ close(fd);
+
+ if (too_small) {
+ buf_size *= 2;
+ continue;
+ }
+
+ pos = strstr(buf, dev);
+ return (pos && (pos==buf || pos[-1]==' ' || pos[-1]=='\n'));
+ }
+}
+
+static int ifconfig_ioctl(
+ int fd,
+ const char *ifname,
+ int cmd,
+ struct ifreq *ifr)
+{
+ strcpy(ifr->ifr_name, ifname);
+ return ioctl(fd, cmd, ifr);
+}
+
+/*
+ Fetch the IP number of an interface from the kernel.
+ Assume the device is already available in the kernel
+ Return -1 if any error.
+*/
+int ifconfig_getaddr (
+ const char *ifname,
+ uint32_t *addr,
+ uint32_t *mask,
+ uint32_t *bcast)
+{
+ int ret = -1;
+ if (existsDevice(ifname)){
+ int skfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (skfd != -1){
+ struct ifreq ifr;
+ if (addr != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
+ struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
+ *addr = sin->sin_addr.s_addr;
+ ret = 0;
+ }
+ if (mask != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
+ struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
+ *mask = sin->sin_addr.s_addr;
+ ret = 0;
+ }
+ if (bcast != NULL && ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
+ struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
+ *bcast = sin->sin_addr.s_addr;
+ ret = 0;
+ }
+ close (skfd);
+ }
+ }
+ return ret;
+}
+
+static int
+convertAddress(const char *str, vc_net_nx_type *type, void *dst)
+{
+ int ret;
+ if (type) *type = vcNET_IPV4;
+ ret = inet_pton(AF_INET, str, dst);
+ if (ret==0) {
+ if (type) *type = vcNET_IPV6;
+ ret = inet_pton(AF_INET6, str, dst);
+ }
+ return ret > 0 ? 0 : -1;
+}
+
+static void
+readIP(char const *str, struct vc_ips **ips)
+{
+ if (ifconfig_getaddr(str, &(*ips)->a.ip[0], &(*ips)->a.mask[0], NULL)==-1) {
+ char *pt;
+ char tmpopt[strlen(str)+1];
+ uint32_t *mask = (*ips)->a.mask;
+
+ strcpy(tmpopt,str);
+ pt = strchr(tmpopt,'/');
+ if (pt)
+ *pt++ = '\0';
+
+ if (convertAddress(tmpopt, &(*ips)->a.type, (*ips)->a.ip) == -1) {
+ WRITE_MSG(2, "Invalid IP number '");
+ WRITE_STR(2, tmpopt);
+ WRITE_MSG(2, "'\n");
+ exit(wrapper_exit_code);
+ }
+
+ if (pt==0) {
+ switch ((*ips)->a.type) {
+ case vcNET_IPV4:
+ mask[0] = htonl(0xffffff00);
+ break;
+ case vcNET_IPV6:
+ mask[0] = 64;
+ break;
+ default: break;
+ }
+ }
+ else {
+ // Ok, we have a network size, not a netmask
+ if (strchr(pt,'.')==0 && strchr(pt,':')==0) {
+ unsigned long sz, limit = 0;
+
+ switch ((*ips)->a.type) {
+ case vcNET_IPV4: limit = 32; break;
+ case vcNET_IPV6: limit = 128; break;
+ default: break;
+ }
+
+ if (!isNumberUnsigned(pt, &sz, true) || sz > limit) {
+ WRITE_MSG(2, "Invalid prefix '");
+ WRITE_STR(2, pt);
+ WRITE_MSG(2, "'\n");
+ exit(wrapper_exit_code);
+ }
+
+ switch ((*ips)->a.type) {
+ case vcNET_IPV4:
+ mask[0] = htonl(~((1 << (32 - sz)) - 1));
+ break;
+ case vcNET_IPV6:
+ mask[0] = sz;
+ break;
+ default: break;
+ }
+ }
+ else {
+ if (convertAddress(pt, NULL, &(*ips)->a.mask) == -1) {
+ WRITE_MSG(2, "Invalid netmask '");
+ WRITE_STR(2, pt);
+ WRITE_MSG(2, "'\n");
+ exit(wrapper_exit_code);
+ }
+ }
+ }
+ }
+ else
+ (*ips)->a.type = vcNET_IPV4;
+
+ (*ips)->a.count = 1;
+ (*ips)->next = calloc(1, sizeof(struct vc_ips));
+ *ips = (*ips)->next;
+}
+
+static void
+readBcast(char const *str, struct vc_ips **ips)
+{
+ uint32_t bcast;
+ if (ifconfig_getaddr(str, NULL, NULL, &bcast)==-1){
+ if (convertAddress(str, NULL, &bcast) == -1) {
+ WRITE_MSG(2, "Invalid broadcast number '");
+ WRITE_STR(2, optarg);
+ WRITE_MSG(2, "'\n");
+ exit(wrapper_exit_code);
+ }
+ }
+ (*ips)->a.ip[0] = bcast;
+ (*ips)->a.count = 1;
+ (*ips)->a.type = vcNET_IPV4B;
+ (*ips)->next = calloc(1, sizeof(struct vc_ips));
+ *ips = (*ips)->next;
+}
+
+static void
+tellAddress(struct vc_net_nx *addr, bool silent)
+{
+ char buf[41];
+ if (silent)
+ return;
+ if (inet_ntop(addr->type == vcNET_IPV6 ? AF_INET6 : AF_INET,
+ addr->ip, buf, sizeof(buf)) == NULL) {
+ WRITE_MSG(1, " <conversion failed>");
+ return;
+ }
+ WRITE_MSG(1, " ");
+ WRITE_STR(1, buf);
+}
+
+static inline void
+doit(struct Arguments *args)
+{
+ struct vc_ips *ips;
+
+ if (args->do_set) {
+ struct vc_net_nx remove = { .type = vcNET_ANY };
+ if (vc_net_remove(args->nid, &remove) == -1) {
+ perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
+ exit(wrapper_exit_code);
+ }
+ }
+
+ if (args->do_add || args->do_set) {
+ if (!args->is_silent)
+ WRITE_MSG(1, "Adding");
+ for (ips = &args->head; ips->next; ips = ips->next) {
+ tellAddress(&ips->a, args->is_silent);
+ if (vc_net_add(args->nid, &ips->a) != (int)ips->a.count) {
+ perror(ENSC_WRAPPERS_PREFIX "vc_net_add()");
+ exit(wrapper_exit_code);
+ }
+ }
+ if (!args->is_silent)
+ WRITE_MSG(1, "\n");
+ }
+ else if (args->do_remove) {
+ if (!args->is_silent)
+ WRITE_MSG(1, "Removing");
+ for (ips = &args->head; ips->next; ips = ips->next) {
+ tellAddress(&ips->a, args->is_silent);
+ if (vc_net_remove(args->nid, &ips->a) != (int)ips->a.count) {
+ perror(ENSC_WRAPPERS_PREFIX "vc_net_remove()");
+ exit(wrapper_exit_code);
+ }
+ }
+ if (!args->is_silent)
+ WRITE_MSG(1, "\n");
+ }
+}
+
+int main (int argc, char *argv[])
+{
+ struct Arguments args = {
+ .nid = VC_NOCTX,
+ .is_silent = false,
+ .do_add = false,
+ .do_remove = false,
+ .do_set = false,
+ .head = { .next = NULL },
+ };
+ struct vc_ips *ips = &args.head;
+
+ while (1) {
+ int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
+ if (c==-1) break;
+
+ switch (c) {
+ case CMD_HELP : showHelp(1, argv[0], 0);
+ case CMD_VERSION : showVersion();
+ case CMD_SILENT : args.is_silent = true; break;
+ case CMD_NID : args.nid = Evc_nidopt2nid(optarg,true); break;
+ case CMD_ADD : args.do_add = true; break;
+ case CMD_REMOVE : args.do_remove = true; break;
+ case CMD_SET : args.do_set = true; break;
+ case CMD_IP : readIP(optarg, &ips); break;
+ case CMD_BCAST : readBcast(optarg, &ips); break;
+ default :
+ WRITE_MSG(2, "Try '");
+ WRITE_STR(2, argv[0]);
+ WRITE_MSG(2, " --help' for more information.\n");
+ exit(wrapper_exit_code);
+ break;
+ }
+ }
+
+ if (args.nid == VC_NOCTX) args.nid = Evc_get_task_nid(0);
+
+ if (!args.do_add && !args.do_remove && !args.do_set) {
+ WRITE_MSG(2, "No operation specified; try '--help' for more information\n");
+ exit(wrapper_exit_code);
+ }
+ else if (((args.do_add ? 1 : 0) + (args.do_remove ? 1 : 0) + (args.do_set ? 1 : 0)) > 1) {
+ WRITE_MSG(2, "Multiple operations specified; try '--help' for more information\n");
+ exit(wrapper_exit_code);
+ }
+
+ doit(&args);
+
+ if (optind != argc)
+ Eexecvp (argv[optind],argv+optind);
+ return EXIT_SUCCESS;
+}
+
+#ifdef ENSC_TESTSUITE
+#include <assert.h>
+
+void test()
+{
+ struct vc_ip_mask_pair ip;
+ uint32_t bcast;
+
+ bcast = 0;
+ readIP("1.2.3.4", &ip, &bcast);
+ assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffffff00) && bcast==0);
+
+ readIP("1.2.3.4/8", &ip, &bcast);
+ assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xff000000) && bcast==0);
+
+ readIP("1.2.3.4/255.255.0.0", &ip, &bcast);
+ assert(ip.ip==ntohl(0x01020304) && ip.mask==ntohl(0xffff0000) && bcast==0);
+
+ readIP("localhost", &ip, &bcast);
+ assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xffffff00) && bcast==0);
+
+#if 0
+ if (ifconfig_getaddr("lo", &tmp, &tmp, &tmp)!=-1) {
+ readIP("lo", &ip, &bcast);
+ assert(ip.ip==ntohl(0x7f000001) && ip.mask==ntohl(0xff000000) && bcast==ntohl(0x7fffffff));
+ }
+#endif
+}
+#endif