#include #include #include #include #include #include #include #include #include #include #include /* 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; }