This commit was manufactured by cvs2svn to create branch
[iptables.git] / ippool / ippool.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <ctype.h>
7 #include <stdlib.h>
8 #include <getopt.h>
9 #include <arpa/inet.h>
10 #include <linux/netfilter_ipv4/ip_pool.h>
11 #include <libippool/ip_pool_support.h>
12
13 /* translate errnos, as returned by our *sockopt() functions */
14 static char *errno2msg(int op, int err)
15 {
16         switch(err) {
17         case ERANGE:
18                 return "Address out of pool range";
19         }
20         return strerror(err);
21 }
22
23 static void usage(char *msg)
24 {
25         if (msg) fprintf(stderr, "ERROR: %s\n", msg);
26         fprintf(stderr,
27 "Usage: ippool [-LADCNF] [POOL] [args]\n"
28 "The POOL argument is either a number, or a name from %s\n"
29 , IPPOOL_CONF);
30         fprintf(stderr,
31 "ippool [-n] [-l|-L POOL] [-u] [-v]\n"
32         "\tLists all (-l), or a single (-L) pool.\n"
33         "\tWith -u, summarized usage counts are listed.\n"
34         "\tWith -v, each pool membership is shown, one per line.\n"
35 "ippool [-n] [-f|-F [POOL]]\n"
36         "\tflushes POOL (or all pools.)\n"
37 "ippool [-n] [-x|-X [POOL]]\n"
38         "\tremoves POOL (or all pools.)\n"
39 "ippool [-n] -B\n"
40         "\tcreates all fully specified pools found in the config file.\n"
41 "ippool [-n] -N POOL [-t type] [FIRST LAST]\n"
42         "\tcreates POOL of IP addresses FIRST to LAST (inclusive).  If a\n"
43         "\tpool name from the config file %s is used, type and\n"
44         "\taddress information can be defined there.  The -t argument\n"
45         "\tgives the type (default bitmap).\n"
46 "ippool [-n] -A POOL ADDR\n"
47         "\tadds ADDR to POOL\n"
48 "ippool [-n] -D POOL ADDR\n"
49         "\tremoves ADDR from POOL\n"
50 "ippool [-n] -C POOL ADDR\n"
51         "\ttests ADDR against membership in POOL\n"
52 , IPPOOL_CONF);
53         exit(1);
54 }
55
56 /* config file parsing */
57
58 #define IP_POOL_T_NONE          0
59 #define IP_POOL_T_BITMAP        1
60
61 static int conf_type = IP_POOL_T_NONE;
62 static unsigned long conf_addr = 0;
63 static unsigned long conf_addr2 = 0;
64
65 #define SCAN_EOF (IP_POOL_NONE-1)
66
67 static ip_pool_t get_index_line(
68         FILE *fp,
69         char **namep,
70         char **typep,
71         char **argp
72 ) {
73         char *p;
74         ip_pool_t index;
75         static char buf[256];
76
77         if (namep) *namep = 0;
78         if (typep) *typep = 0;
79         if (argp) *argp = 0;
80
81         if (!fgets(buf, sizeof(buf), fp)) return SCAN_EOF;
82
83         p = strtok(buf, " \t\n");
84         if (!p || *p == '#') return IP_POOL_NONE;
85         index = atoi(p);
86
87         p = strtok(0, " \t\n");
88         if (!p || *p == '#') return index;
89         if (namep) *namep = p;
90
91         p = strtok(0, " \t\n");
92         if (!p || *p == '#') return index;
93         if (typep) *typep = p;
94
95         p = strtok(0, "#\n");
96         if (argp) *argp = p;
97
98         return index;
99 }
100
101 static ip_pool_t get_index(char *name)
102 {
103         FILE *fp;
104         char *poolname, *type, *arg, *p;
105         ip_pool_t res;
106
107         if (isdigit(*name))
108                 return atoi(name);
109         fp = fopen(IPPOOL_CONF, "r");
110         if (!fp) {
111                 fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
112                 exit(1);
113         }
114         while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
115                 if (poolname && 0 == strcmp(poolname, name)) {
116                         if (!type || (0 == strcmp(type, "bitmap"))) {
117                                 conf_type = IP_POOL_T_BITMAP;
118                                 p = strtok(arg, " \t");
119                                 if (p) {
120                                         conf_addr = inet_addr(p);
121                                         p = strtok(0, " \t");
122                                         if (p)
123                                                 conf_addr2 = inet_addr(p);
124                                         else
125                                                 conf_addr = 0;
126                                 }
127                         }
128                         break;
129                 }
130         }
131         fclose(fp);
132         if (res == SCAN_EOF) {
133                 fprintf(stderr, "pool '%s' not found in %s\n",
134                         name, IPPOOL_CONF);
135                 exit(1);
136         }
137         return res;
138 }
139
140 static char *get_name(ip_pool_t index)
141 {
142         FILE *fp;
143         static char sbuf[256];
144         int ok = 0;
145
146         fp = fopen(IPPOOL_CONF, "r");
147         if (fp) {
148                 while (fgets(sbuf, sizeof(sbuf), fp)) {
149                         char *p = strtok(sbuf, " \t\n");
150
151                         if (!p || *p == '#') continue;
152                         if (index != atoi(p)) continue;
153                         p = strtok(0, " \t\n");
154                         if (!p || *p == '#') continue;
155                         memmove(sbuf, p, strlen(p)+1);
156                         ok = 1;
157                         break;
158                 }
159                 fclose(fp);
160         }
161         if (!ok) sprintf(sbuf, "%d", index);
162         return sbuf;
163 }
164
165 /* user/kernel interaction */
166
167 static int fd = -1;
168 static int flag_n = 0;          /* do not do anything; just brag about it */
169 static int flag_v = 0;          /* be verbose (list members) */
170 static int flag_q = 0;          /* be quiet */
171 static int flag_u = 0;          /* show usage counts in listings */
172 static char *flag_t = "bitmap"; /* pool type */
173
174 static ip_pool_t high_nr(void)
175 {
176         struct ip_pool_request req;
177         int reqlen = sizeof(req);
178
179         req.op = IP_POOL_HIGH_NR;
180         if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
181                 fprintf(stderr,
182                         "IP_POOL_HIGH_NR failed: %s\n",
183                         errno2msg(IP_POOL_HIGH_NR, errno));
184                 exit(1);
185         }
186         return req.index;
187 }
188
189 static void do_list(ip_pool_t pool)
190 {
191         struct ip_pool_request req;
192         int reqlen = sizeof(req);
193         u_int32_t first_ip;
194         u_int32_t last_ip;
195
196         req.op = IP_POOL_LOOKUP;
197         req.index = pool;
198         req.addr = req.addr2 = 0;
199         reqlen = sizeof(req);
200         if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
201                 struct in_addr ina;
202                 ina.s_addr = req.addr;
203                 printf("%s %s", get_name(req.index), inet_ntoa(ina));
204                 ina.s_addr = req.addr2;
205                 printf(" %s", inet_ntoa(ina));
206                 first_ip = ntohl(req.addr);
207                 last_ip = ntohl(req.addr2);
208                 if (flag_u) {
209                         req.op = IP_POOL_USAGE;
210                         req.index = pool;
211                         reqlen = sizeof(req);
212                         if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
213                                                 &req, &reqlen)) {
214                                 printf(" %d %d", req.addr, req.addr2);
215                         } else {
216                                 printf(" - -");
217                         }
218                 }
219                 printf("\n");
220                 if (flag_v) {
221                         while (first_ip <= last_ip) {
222                                 req.op = IP_POOL_TEST_ADDR;
223                                 req.index = pool;
224                                 ina.s_addr = req.addr = htonl(first_ip);
225                                 reqlen = sizeof(req);
226                                 if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
227                                                         &req, &reqlen)) {
228                                         if (req.addr) printf("\t%s\n",
229                                                         inet_ntoa(ina));
230                                 }
231                                 first_ip++;
232                         }
233                 }
234         }
235 }
236
237 static void do_list_all(void)
238 {
239         ip_pool_t i, highest = high_nr();
240
241         for (i=0; i<=highest; i++)
242                 do_list(i);
243 }
244
245 static void do_flush(ip_pool_t pool)
246 {
247         struct ip_pool_request req;
248         int reqlen = sizeof(req);
249
250         req.op = IP_POOL_FLUSH;
251         req.index = pool;
252         if (flag_n) {
253                 printf("ippool -F %d\n", req.index);
254                 return;
255         }
256         if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
257                 int err = errno;
258                 fprintf(stderr,
259                         "IP_POOL_FLUSH %s failed: %s\n",
260                         get_name(pool), errno2msg(IP_POOL_FLUSH, err));
261                 exit(1);
262         }
263 }
264
265 static void do_flush_all(void)
266 {
267         ip_pool_t i, highest = high_nr();
268
269         for (i=0; i<=highest; i++)
270                 do_flush(i);
271 }
272
273 static void do_destroy(ip_pool_t pool)
274 {
275         struct ip_pool_request req;
276         int reqlen = sizeof(req);
277
278         req.op = IP_POOL_DESTROY;
279         req.index = pool;
280         if (flag_n) {
281                 printf("ippool -X %d\n", req.index);
282                 return;
283         }
284         if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
285                 int err = errno;
286                 fprintf(stderr,
287                         "IP_POOL_DESTROY %s failed: %s\n",
288                         get_name(pool), errno2msg(IP_POOL_DESTROY, err));
289                 exit(1);
290         }
291 }
292
293 static void do_destroy_all(void)
294 {
295         ip_pool_t i, highest = high_nr();
296
297         for (i=0; i<=highest; i++)
298                 do_destroy(i);
299 }
300
301 static int do_adddel(ip_pool_t pool, char *straddr, int op)
302 {
303         struct ip_pool_request req;
304         int res;
305         int reqlen = sizeof(req);
306
307         req.op = op;
308         req.index = pool;
309         req.addr = inet_addr(straddr);
310         req.addr2 = 0;
311         if (flag_n) {
312                 printf("ippool -%c %s %s\n",
313                                 (op == IP_POOL_ADD_ADDR) ? 'A' : 'D',
314                                 get_name(req.index), straddr);
315                 return 0;
316         }
317         res = getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen);
318         if (0 > res) {
319                 int err = errno;
320                 fprintf(stderr,
321                         "IP_POOL_ADD/DEL %s in %s failed: %s\n",
322                         straddr, get_name(pool), errno2msg(op, err));
323                 exit(1);
324         }
325         if (!flag_q)
326                 printf("%s %s %d %d\n", get_name(pool), straddr, req.addr,
327                         op == IP_POOL_ADD_ADDR ? 1 : 0);
328         return req.addr;
329 }
330
331 static int do_check(ip_pool_t pool, char *straddr)
332 {
333         struct ip_pool_request req;
334         int reqlen = sizeof(req);
335
336         req.op = IP_POOL_TEST_ADDR;
337         req.index = pool;
338         req.addr = inet_addr(straddr);
339         if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
340                 int err = errno;
341                 fprintf(stderr,
342                         "IP_POOL_TEST_ADDR %s in %s failed: %s\n",
343                         straddr, get_name(pool),
344                         errno2msg(IP_POOL_TEST_ADDR, err));
345                 exit(1);
346         }
347         if (!flag_q)
348                 printf("%s %s %d\n", get_name(req.index), straddr, req.addr);
349         return !req.addr;
350 }
351
352 static void do_new(ip_pool_t pool, int argc, char **argv)
353 {
354         struct ip_pool_request req;
355         int reqlen = sizeof(req);
356
357         req.op = IP_POOL_INIT;
358         req.index = pool;
359         if (argc >= 2) {
360                 conf_type = IP_POOL_T_BITMAP;
361                 conf_addr = inet_addr(argv[0]);
362                 conf_addr2 = inet_addr(argv[1]);
363         }
364         if (conf_type != IP_POOL_T_BITMAP || conf_addr == 0 || conf_addr2 == 0)
365                 usage("bad pool specification");
366         req.addr = conf_addr;
367         req.addr2 = conf_addr2;
368         if (flag_n) {
369                 printf("ippool -N %s [-T %s] ...\n", get_name(pool),
370                                         conf_type == IP_POOL_T_BITMAP
371                                                 ? "bitmap"
372                                                 : "???");
373                 return;
374         }
375         if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
376                 struct in_addr ina;
377                 int err = errno;
378                 ina.s_addr = conf_addr;
379                 fprintf(stderr,
380                         "IP_POOL_INIT %s [%s-",
381                         get_name(pool), inet_ntoa(ina));
382                 ina.s_addr = conf_addr2;
383                 fprintf(stderr,
384                         "%s] failed: %s\n",
385                         inet_ntoa(ina), errno2msg(IP_POOL_INIT, err));
386                 exit(1);
387         }
388 }
389
390 static void do_new_all(void)
391 {
392         FILE *fp;
393         char *poolname, *type, *arg, *p;
394         int res;
395
396         fp = fopen(IPPOOL_CONF, "r");
397         if (!fp) {
398                 fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
399                 exit(1);
400         }
401         while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
402                 if (poolname && type && (0 == strcmp(type, "bitmap"))) {
403                         conf_type = IP_POOL_T_BITMAP;
404                         p = strtok(arg, " \t");
405                         if (p) {
406                                 conf_addr = inet_addr(p);
407                                 p = strtok(0, " \t");
408                                 if (p)
409                                         conf_addr2 = inet_addr(p);
410                                 else
411                                         conf_addr = 0;
412                         }
413                         if (conf_addr != 0) {
414                                 if (flag_v)
415                                         printf("ippool -N %s (%s) [%s]\n",
416                                                 poolname, type, arg);
417                                 do_new(res, 0, 0);
418                         }
419                 }
420         }
421         fclose(fp);
422 }
423
424 int main(int argc, char **argv)
425 {
426         int opt;
427         int op;
428 #define OP_NONE         0
429 #define OP_LIST         1
430 #define OP_LIST_ALL     2
431 #define OP_FLUSH        3
432 #define OP_FLUSH_ALL    4
433 #define OP_DESTROY      5
434 #define OP_DESTROY_ALL  6
435 #define OP_ADD          7
436 #define OP_DEL          8
437 #define OP_CHECK        9
438 #define OP_NEW          10
439 #define OP_NEW_ALL      11
440 #define OP_HIGHEST      12
441         char *op_pool;
442
443         fd = socket(AF_INET, SOCK_DGRAM, 0);
444         if (fd < 0) {
445                 fprintf(stderr, "cannot get DGRAM socket: %s\n",
446                         strerror(errno));
447                 exit(1);
448         }
449         op_pool = 0;
450         op = OP_NONE;
451         /* GRRR. I thought getopt() would allow an "L*" specifier, for an -L
452          * taking an optional argument. Does not work. Bad.
453          * Adding -l for -L without argument, also -f/-F and -x/-X.
454          */
455         while (EOF != (opt=getopt( argc, argv, "HhnvuqA:D:C:N:t:L:F:X:lfxB")))
456         switch(opt) {
457                 case 'l':
458                         if (op != OP_NONE) usage("conflicting operations");
459                         op = OP_LIST_ALL;
460                         break;
461                 case 'L':
462                         if (op != OP_NONE) usage("conflicting operations");
463                         op = OP_LIST;
464                         op_pool = optarg;
465                         break;
466                 case 'f':
467                         if (op != OP_NONE) usage("conflicting operations");
468                         op = OP_FLUSH_ALL;
469                         break;
470                 case 'F':
471                         if (op != OP_NONE) usage("conflicting operations");
472                         op = OP_FLUSH;
473                         op_pool = optarg;
474                         break;
475                 case 'x':
476                         if (op != OP_NONE) usage("conflicting operations");
477                         op = OP_DESTROY_ALL;
478                         break;
479                 case 'X':
480                         if (op != OP_NONE) usage("conflicting operations");
481                         op = OP_DESTROY;
482                         op_pool = optarg;
483                         break;
484                 case 'A':
485                         if (op != OP_NONE) usage("conflicting operations");
486                         op = OP_ADD;
487                         op_pool = optarg;
488                         break;
489                 case 'D':
490                         if (op != OP_NONE) usage("conflicting operations");
491                         op = OP_DEL;
492                         op_pool = optarg;
493                         break;
494                 case 'C':
495                         if (op != OP_NONE) usage("conflicting operations");
496                         op = OP_CHECK;
497                         op_pool = optarg;
498                         break;
499                 case 'B':
500                         if (op != OP_NONE) usage("conflicting operations");
501                         op = OP_NEW_ALL;
502                         break;
503                 case 'N':
504                         if (op != OP_NONE) usage("conflicting operations");
505                         op = OP_NEW;
506                         op_pool = optarg;
507                         break;
508                 case 'H':
509                         if (op != OP_NONE) usage("conflicting operations");
510                         op = OP_HIGHEST;
511                         break;
512                 case 't':
513                         flag_t = optarg;
514                         break;
515                 case 'n':
516                         flag_n = 1;
517                         break;
518                 case 'v':
519                         flag_v = 1;
520                         break;
521                 case 'u':
522                         flag_u = 1;
523                         break;
524                 case 'q':
525                         flag_q = 1;
526                         break;
527                 case 'h':
528                         usage(0);
529                 default:
530                         usage("bad option");
531         }
532         if (op == OP_NONE)
533                 usage("no operation specified");
534         if (op == OP_LIST_ALL) {
535                 do_list_all();
536                 return 0;
537         }
538         if (op == OP_LIST) {
539                 do_list(get_index(op_pool));
540                 return 0;
541         }
542         if (op == OP_FLUSH_ALL) {
543                 do_flush_all();
544                 return 0;
545         }
546         if (op == OP_FLUSH) {
547                 do_flush(get_index(op_pool));
548                 return 0;
549         }
550         if (op == OP_DESTROY_ALL) {
551                 do_destroy_all();
552                 return 0;
553         }
554         if (op == OP_DESTROY) {
555                 do_destroy(get_index(op_pool));
556                 return 0;
557         }
558         if (op == OP_CHECK) {
559                 if (optind >= argc)
560                         usage("missing address to check");
561                 return do_check(get_index(op_pool), argv[optind]);
562         }
563         if (op == OP_NEW_ALL) {
564                 do_new_all();
565                 return 0;
566         }
567         if (op == OP_NEW) {
568                 do_new(get_index(op_pool), argc-optind, argv+optind);
569                 return 0;
570         }
571         if (op == OP_ADD) {
572                 if (optind >= argc)
573                         usage("missing address to add");
574                 return do_adddel(get_index(op_pool),
575                                 argv[optind], IP_POOL_ADD_ADDR);
576         }
577         if (op == OP_DEL) {
578                 if (optind >= argc)
579                         usage("missing address to delete");
580                 return do_adddel(get_index(op_pool),
581                                 argv[optind], IP_POOL_DEL_ADDR);
582         }
583         if (op == OP_HIGHEST) {
584                 printf("%d\n", high_nr());
585                 return 0;
586         }
587         usage("no operation specified");
588         return 0;
589 }