This commit was manufactured by cvs2svn to create branch
[iptables.git] / ippool / ippool.c
diff --git a/ippool/ippool.c b/ippool/ippool.c
new file mode 100644 (file)
index 0000000..3a680d3
--- /dev/null
@@ -0,0 +1,589 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <linux/netfilter_ipv4/ip_pool.h>
+#include <libippool/ip_pool_support.h>
+
+/* translate errnos, as returned by our *sockopt() functions */
+static char *errno2msg(int op, int err)
+{
+       switch(err) {
+       case ERANGE:
+               return "Address out of pool range";
+       }
+       return strerror(err);
+}
+
+static void usage(char *msg)
+{
+       if (msg) fprintf(stderr, "ERROR: %s\n", msg);
+       fprintf(stderr,
+"Usage: ippool [-LADCNF] [POOL] [args]\n"
+"The POOL argument is either a number, or a name from %s\n"
+, IPPOOL_CONF);
+       fprintf(stderr,
+"ippool [-n] [-l|-L POOL] [-u] [-v]\n"
+       "\tLists all (-l), or a single (-L) pool.\n"
+       "\tWith -u, summarized usage counts are listed.\n"
+       "\tWith -v, each pool membership is shown, one per line.\n"
+"ippool [-n] [-f|-F [POOL]]\n"
+       "\tflushes POOL (or all pools.)\n"
+"ippool [-n] [-x|-X [POOL]]\n"
+       "\tremoves POOL (or all pools.)\n"
+"ippool [-n] -B\n"
+       "\tcreates all fully specified pools found in the config file.\n"
+"ippool [-n] -N POOL [-t type] [FIRST LAST]\n"
+       "\tcreates POOL of IP addresses FIRST to LAST (inclusive).  If a\n"
+       "\tpool name from the config file %s is used, type and\n"
+       "\taddress information can be defined there.  The -t argument\n"
+       "\tgives the type (default bitmap).\n"
+"ippool [-n] -A POOL ADDR\n"
+       "\tadds ADDR to POOL\n"
+"ippool [-n] -D POOL ADDR\n"
+       "\tremoves ADDR from POOL\n"
+"ippool [-n] -C POOL ADDR\n"
+       "\ttests ADDR against membership in POOL\n"
+, IPPOOL_CONF);
+       exit(1);
+}
+
+/* config file parsing */
+
+#define IP_POOL_T_NONE         0
+#define IP_POOL_T_BITMAP       1
+
+static int conf_type = IP_POOL_T_NONE;
+static unsigned long conf_addr = 0;
+static unsigned long conf_addr2 = 0;
+
+#define SCAN_EOF (IP_POOL_NONE-1)
+
+static ip_pool_t get_index_line(
+       FILE *fp,
+       char **namep,
+       char **typep,
+       char **argp
+) {
+       char *p;
+       ip_pool_t index;
+       static char buf[256];
+
+       if (namep) *namep = 0;
+       if (typep) *typep = 0;
+       if (argp) *argp = 0;
+
+       if (!fgets(buf, sizeof(buf), fp)) return SCAN_EOF;
+
+       p = strtok(buf, " \t\n");
+       if (!p || *p == '#') return IP_POOL_NONE;
+       index = atoi(p);
+
+       p = strtok(0, " \t\n");
+       if (!p || *p == '#') return index;
+       if (namep) *namep = p;
+
+       p = strtok(0, " \t\n");
+       if (!p || *p == '#') return index;
+       if (typep) *typep = p;
+
+       p = strtok(0, "#\n");
+       if (argp) *argp = p;
+
+       return index;
+}
+
+static ip_pool_t get_index(char *name)
+{
+       FILE *fp;
+       char *poolname, *type, *arg, *p;
+       ip_pool_t res;
+
+       if (isdigit(*name))
+               return atoi(name);
+       fp = fopen(IPPOOL_CONF, "r");
+       if (!fp) {
+               fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
+               exit(1);
+       }
+       while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
+               if (poolname && 0 == strcmp(poolname, name)) {
+                       if (!type || (0 == strcmp(type, "bitmap"))) {
+                               conf_type = IP_POOL_T_BITMAP;
+                               p = strtok(arg, " \t");
+                               if (p) {
+                                       conf_addr = inet_addr(p);
+                                       p = strtok(0, " \t");
+                                       if (p)
+                                               conf_addr2 = inet_addr(p);
+                                       else
+                                               conf_addr = 0;
+                               }
+                       }
+                       break;
+               }
+       }
+       fclose(fp);
+       if (res == SCAN_EOF) {
+               fprintf(stderr, "pool '%s' not found in %s\n",
+                       name, IPPOOL_CONF);
+               exit(1);
+       }
+       return res;
+}
+
+static char *get_name(ip_pool_t index)
+{
+       FILE *fp;
+       static char sbuf[256];
+       int ok = 0;
+
+       fp = fopen(IPPOOL_CONF, "r");
+       if (fp) {
+               while (fgets(sbuf, sizeof(sbuf), fp)) {
+                       char *p = strtok(sbuf, " \t\n");
+
+                       if (!p || *p == '#') continue;
+                       if (index != atoi(p)) continue;
+                       p = strtok(0, " \t\n");
+                       if (!p || *p == '#') continue;
+                       memmove(sbuf, p, strlen(p)+1);
+                       ok = 1;
+                       break;
+               }
+               fclose(fp);
+       }
+       if (!ok) sprintf(sbuf, "%d", index);
+       return sbuf;
+}
+
+/* user/kernel interaction */
+
+static int fd = -1;
+static int flag_n = 0;         /* do not do anything; just brag about it */
+static int flag_v = 0;         /* be verbose (list members) */
+static int flag_q = 0;         /* be quiet */
+static int flag_u = 0;         /* show usage counts in listings */
+static char *flag_t = "bitmap";        /* pool type */
+
+static ip_pool_t high_nr(void)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_HIGH_NR;
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               fprintf(stderr,
+                       "IP_POOL_HIGH_NR failed: %s\n",
+                       errno2msg(IP_POOL_HIGH_NR, errno));
+               exit(1);
+       }
+       return req.index;
+}
+
+static void do_list(ip_pool_t pool)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+       u_int32_t first_ip;
+       u_int32_t last_ip;
+
+       req.op = IP_POOL_LOOKUP;
+       req.index = pool;
+       req.addr = req.addr2 = 0;
+       reqlen = sizeof(req);
+       if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               struct in_addr ina;
+               ina.s_addr = req.addr;
+               printf("%s %s", get_name(req.index), inet_ntoa(ina));
+               ina.s_addr = req.addr2;
+               printf(" %s", inet_ntoa(ina));
+               first_ip = ntohl(req.addr);
+               last_ip = ntohl(req.addr2);
+               if (flag_u) {
+                       req.op = IP_POOL_USAGE;
+                       req.index = pool;
+                       reqlen = sizeof(req);
+                       if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
+                                               &req, &reqlen)) {
+                               printf(" %d %d", req.addr, req.addr2);
+                       } else {
+                               printf(" - -");
+                       }
+               }
+               printf("\n");
+               if (flag_v) {
+                       while (first_ip <= last_ip) {
+                               req.op = IP_POOL_TEST_ADDR;
+                               req.index = pool;
+                               ina.s_addr = req.addr = htonl(first_ip);
+                               reqlen = sizeof(req);
+                               if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
+                                                       &req, &reqlen)) {
+                                       if (req.addr) printf("\t%s\n",
+                                                       inet_ntoa(ina));
+                               }
+                               first_ip++;
+                       }
+               }
+       }
+}
+
+static void do_list_all(void)
+{
+       ip_pool_t i, highest = high_nr();
+
+       for (i=0; i<=highest; i++)
+               do_list(i);
+}
+
+static void do_flush(ip_pool_t pool)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_FLUSH;
+       req.index = pool;
+       if (flag_n) {
+               printf("ippool -F %d\n", req.index);
+               return;
+       }
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_FLUSH %s failed: %s\n",
+                       get_name(pool), errno2msg(IP_POOL_FLUSH, err));
+               exit(1);
+       }
+}
+
+static void do_flush_all(void)
+{
+       ip_pool_t i, highest = high_nr();
+
+       for (i=0; i<=highest; i++)
+               do_flush(i);
+}
+
+static void do_destroy(ip_pool_t pool)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_DESTROY;
+       req.index = pool;
+       if (flag_n) {
+               printf("ippool -X %d\n", req.index);
+               return;
+       }
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_DESTROY %s failed: %s\n",
+                       get_name(pool), errno2msg(IP_POOL_DESTROY, err));
+               exit(1);
+       }
+}
+
+static void do_destroy_all(void)
+{
+       ip_pool_t i, highest = high_nr();
+
+       for (i=0; i<=highest; i++)
+               do_destroy(i);
+}
+
+static int do_adddel(ip_pool_t pool, char *straddr, int op)
+{
+       struct ip_pool_request req;
+       int res;
+       int reqlen = sizeof(req);
+
+       req.op = op;
+       req.index = pool;
+       req.addr = inet_addr(straddr);
+       req.addr2 = 0;
+       if (flag_n) {
+               printf("ippool -%c %s %s\n",
+                               (op == IP_POOL_ADD_ADDR) ? 'A' : 'D',
+                               get_name(req.index), straddr);
+               return 0;
+       }
+       res = getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen);
+       if (0 > res) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_ADD/DEL %s in %s failed: %s\n",
+                       straddr, get_name(pool), errno2msg(op, err));
+               exit(1);
+       }
+       if (!flag_q)
+               printf("%s %s %d %d\n", get_name(pool), straddr, req.addr,
+                       op == IP_POOL_ADD_ADDR ? 1 : 0);
+       return req.addr;
+}
+
+static int do_check(ip_pool_t pool, char *straddr)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_TEST_ADDR;
+       req.index = pool;
+       req.addr = inet_addr(straddr);
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               int err = errno;
+               fprintf(stderr,
+                       "IP_POOL_TEST_ADDR %s in %s failed: %s\n",
+                       straddr, get_name(pool),
+                       errno2msg(IP_POOL_TEST_ADDR, err));
+               exit(1);
+       }
+       if (!flag_q)
+               printf("%s %s %d\n", get_name(req.index), straddr, req.addr);
+       return !req.addr;
+}
+
+static void do_new(ip_pool_t pool, int argc, char **argv)
+{
+       struct ip_pool_request req;
+       int reqlen = sizeof(req);
+
+       req.op = IP_POOL_INIT;
+       req.index = pool;
+       if (argc >= 2) {
+               conf_type = IP_POOL_T_BITMAP;
+               conf_addr = inet_addr(argv[0]);
+               conf_addr2 = inet_addr(argv[1]);
+       }
+       if (conf_type != IP_POOL_T_BITMAP || conf_addr == 0 || conf_addr2 == 0)
+               usage("bad pool specification");
+       req.addr = conf_addr;
+       req.addr2 = conf_addr2;
+       if (flag_n) {
+               printf("ippool -N %s [-T %s] ...\n", get_name(pool),
+                                       conf_type == IP_POOL_T_BITMAP
+                                               ? "bitmap"
+                                               : "???");
+               return;
+       }
+       if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+               struct in_addr ina;
+               int err = errno;
+               ina.s_addr = conf_addr;
+               fprintf(stderr,
+                       "IP_POOL_INIT %s [%s-",
+                       get_name(pool), inet_ntoa(ina));
+               ina.s_addr = conf_addr2;
+               fprintf(stderr,
+                       "%s] failed: %s\n",
+                       inet_ntoa(ina), errno2msg(IP_POOL_INIT, err));
+               exit(1);
+       }
+}
+
+static void do_new_all(void)
+{
+       FILE *fp;
+       char *poolname, *type, *arg, *p;
+       int res;
+
+       fp = fopen(IPPOOL_CONF, "r");
+       if (!fp) {
+               fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
+               exit(1);
+       }
+       while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
+               if (poolname && type && (0 == strcmp(type, "bitmap"))) {
+                       conf_type = IP_POOL_T_BITMAP;
+                       p = strtok(arg, " \t");
+                       if (p) {
+                               conf_addr = inet_addr(p);
+                               p = strtok(0, " \t");
+                               if (p)
+                                       conf_addr2 = inet_addr(p);
+                               else
+                                       conf_addr = 0;
+                       }
+                       if (conf_addr != 0) {
+                               if (flag_v)
+                                       printf("ippool -N %s (%s) [%s]\n",
+                                               poolname, type, arg);
+                               do_new(res, 0, 0);
+                       }
+               }
+       }
+       fclose(fp);
+}
+
+int main(int argc, char **argv)
+{
+       int opt;
+       int op;
+#define OP_NONE                0
+#define OP_LIST                1
+#define OP_LIST_ALL    2
+#define OP_FLUSH       3
+#define OP_FLUSH_ALL   4
+#define OP_DESTROY     5
+#define OP_DESTROY_ALL 6
+#define OP_ADD         7
+#define OP_DEL         8
+#define OP_CHECK       9
+#define OP_NEW         10
+#define OP_NEW_ALL     11
+#define OP_HIGHEST     12
+       char *op_pool;
+
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               fprintf(stderr, "cannot get DGRAM socket: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+       op_pool = 0;
+       op = OP_NONE;
+       /* GRRR. I thought getopt() would allow an "L*" specifier, for an -L
+        * taking an optional argument. Does not work. Bad.
+        * Adding -l for -L without argument, also -f/-F and -x/-X.
+        */
+       while (EOF != (opt=getopt( argc, argv, "HhnvuqA:D:C:N:t:L:F:X:lfxB")))
+       switch(opt) {
+               case 'l':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_LIST_ALL;
+                       break;
+               case 'L':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_LIST;
+                       op_pool = optarg;
+                       break;
+               case 'f':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_FLUSH_ALL;
+                       break;
+               case 'F':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_FLUSH;
+                       op_pool = optarg;
+                       break;
+               case 'x':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_DESTROY_ALL;
+                       break;
+               case 'X':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_DESTROY;
+                       op_pool = optarg;
+                       break;
+               case 'A':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_ADD;
+                       op_pool = optarg;
+                       break;
+               case 'D':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_DEL;
+                       op_pool = optarg;
+                       break;
+               case 'C':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_CHECK;
+                       op_pool = optarg;
+                       break;
+               case 'B':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_NEW_ALL;
+                       break;
+               case 'N':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_NEW;
+                       op_pool = optarg;
+                       break;
+               case 'H':
+                       if (op != OP_NONE) usage("conflicting operations");
+                       op = OP_HIGHEST;
+                       break;
+               case 't':
+                       flag_t = optarg;
+                       break;
+               case 'n':
+                       flag_n = 1;
+                       break;
+               case 'v':
+                       flag_v = 1;
+                       break;
+               case 'u':
+                       flag_u = 1;
+                       break;
+               case 'q':
+                       flag_q = 1;
+                       break;
+               case 'h':
+                       usage(0);
+               default:
+                       usage("bad option");
+       }
+       if (op == OP_NONE)
+               usage("no operation specified");
+       if (op == OP_LIST_ALL) {
+               do_list_all();
+               return 0;
+       }
+       if (op == OP_LIST) {
+               do_list(get_index(op_pool));
+               return 0;
+       }
+       if (op == OP_FLUSH_ALL) {
+               do_flush_all();
+               return 0;
+       }
+       if (op == OP_FLUSH) {
+               do_flush(get_index(op_pool));
+               return 0;
+       }
+       if (op == OP_DESTROY_ALL) {
+               do_destroy_all();
+               return 0;
+       }
+       if (op == OP_DESTROY) {
+               do_destroy(get_index(op_pool));
+               return 0;
+       }
+       if (op == OP_CHECK) {
+               if (optind >= argc)
+                       usage("missing address to check");
+               return do_check(get_index(op_pool), argv[optind]);
+       }
+       if (op == OP_NEW_ALL) {
+               do_new_all();
+               return 0;
+       }
+       if (op == OP_NEW) {
+               do_new(get_index(op_pool), argc-optind, argv+optind);
+               return 0;
+       }
+       if (op == OP_ADD) {
+               if (optind >= argc)
+                       usage("missing address to add");
+               return do_adddel(get_index(op_pool),
+                               argv[optind], IP_POOL_ADD_ADDR);
+       }
+       if (op == OP_DEL) {
+               if (optind >= argc)
+                       usage("missing address to delete");
+               return do_adddel(get_index(op_pool),
+                               argv[optind], IP_POOL_DEL_ADDR);
+       }
+       if (op == OP_HIGHEST) {
+               printf("%d\n", high_nr());
+               return 0;
+       }
+       usage("no operation specified");
+       return 0;
+}