]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ipfw/tables.c
Update mandoc to cvs snaphot from 20150302
[FreeBSD/FreeBSD.git] / sbin / ipfw / tables.c
1 /*
2  * Copyright (c) 2014 Yandex LLC
3  * Copyright (c) 2014 Alexander V. Chernikov
4  *
5  * Redistribution and use in source forms, with and without modification,
6  * are permitted provided that this entire comment appears intact.
7  *
8  * Redistribution in binary form may occur without any restrictions.
9  * Obviously, it would be nice if you gave credit where credit is due
10  * but requiring it would be too onerous.
11  *
12  * This software is provided ``AS IS'' without any warranties of any kind.
13  *
14  * in-kernel ipfw tables support.
15  *
16  * $FreeBSD$
17  */
18
19
20 #include <sys/types.h>
21 #include <sys/param.h>
22 #include <sys/socket.h>
23 #include <sys/sysctl.h>
24
25 #include <ctype.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <netdb.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sysexits.h>
33
34 #include <net/if.h>
35 #include <netinet/in.h>
36 #include <netinet/ip_fw.h>
37 #include <arpa/inet.h>
38
39 #include "ipfw2.h"
40
41 static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
42     int add, int quiet, int update, int atomic);
43 static int table_flush(ipfw_obj_header *oh);
44 static int table_destroy(ipfw_obj_header *oh);
45 static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
46 static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i);
47 static int table_do_swap(ipfw_obj_header *oh, char *second);
48 static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
49 static void table_modify(ipfw_obj_header *oh, int ac, char *av[]);
50 static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]);
51 static void table_lock(ipfw_obj_header *oh, int lock);
52 static int table_swap(ipfw_obj_header *oh, char *second);
53 static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
54 static int table_show_info(ipfw_xtable_info *i, void *arg);
55 static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set,
56     uint16_t uidx);
57
58 static int table_flush_one(ipfw_xtable_info *i, void *arg);
59 static int table_show_one(ipfw_xtable_info *i, void *arg);
60 static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh);
61 static void table_show_list(ipfw_obj_header *oh, int need_header);
62 static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent);
63
64 static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
65     char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi);
66 static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
67     char *arg, uint8_t type, uint32_t vmask);
68 static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
69     uint32_t vmask, int print_ip);
70
71 typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
72 static int tables_foreach(table_cb_t *f, void *arg, int sort);
73
74 #ifndef s6_addr32
75 #define s6_addr32 __u6_addr.__u6_addr32
76 #endif
77
78 static struct _s_x tabletypes[] = {
79       { "addr",         IPFW_TABLE_ADDR },
80       { "iface",        IPFW_TABLE_INTERFACE },
81       { "number",       IPFW_TABLE_NUMBER },
82       { "flow",         IPFW_TABLE_FLOW },
83       { NULL, 0 }
84 };
85
86 static struct _s_x tablevaltypes[] = {
87       { "skipto",       IPFW_VTYPE_SKIPTO },
88       { "pipe",         IPFW_VTYPE_PIPE },
89       { "fib",          IPFW_VTYPE_FIB },
90       { "nat",          IPFW_VTYPE_NAT },
91       { "dscp",         IPFW_VTYPE_DSCP },
92       { "tag",          IPFW_VTYPE_TAG },
93       { "divert",       IPFW_VTYPE_DIVERT },
94       { "netgraph",     IPFW_VTYPE_NETGRAPH },
95       { "limit",        IPFW_VTYPE_LIMIT },
96       { "ipv4",         IPFW_VTYPE_NH4 },
97       { "ipv6",         IPFW_VTYPE_NH6 },
98       { NULL, 0 }
99 };
100
101 static struct _s_x tablecmds[] = {
102       { "add",          TOK_ADD },
103       { "delete",       TOK_DEL },
104       { "create",       TOK_CREATE },
105       { "destroy",      TOK_DESTROY },
106       { "flush",        TOK_FLUSH },
107       { "modify",       TOK_MODIFY },
108       { "swap",         TOK_SWAP },
109       { "info",         TOK_INFO },
110       { "detail",       TOK_DETAIL },
111       { "list",         TOK_LIST },
112       { "lookup",       TOK_LOOKUP },
113       { "atomic",       TOK_ATOMIC },
114       { "lock",         TOK_LOCK },
115       { "unlock",       TOK_UNLOCK },
116       { NULL, 0 }
117 };
118
119 static int
120 lookup_host (char *host, struct in_addr *ipaddr)
121 {
122         struct hostent *he;
123
124         if (!inet_aton(host, ipaddr)) {
125                 if ((he = gethostbyname(host)) == NULL)
126                         return(-1);
127                 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
128         }
129         return(0);
130 }
131
132 static int
133 get_token(struct _s_x *table, char *string, char *errbase)
134 {
135         int tcmd;
136
137         if ((tcmd = match_token_relaxed(table, string)) < 0)
138                 errx(EX_USAGE, "%s %s %s",
139                     (tcmd == 0) ? "invalid" : "ambiguous", errbase, string);
140
141         return (tcmd);
142 }
143
144 /*
145  * This one handles all table-related commands
146  *      ipfw table NAME create ...
147  *      ipfw table NAME modify ...
148  *      ipfw table NAME destroy
149  *      ipfw table NAME swap NAME
150  *      ipfw table NAME lock
151  *      ipfw table NAME unlock
152  *      ipfw table NAME add addr[/masklen] [value] 
153  *      ipfw table NAME add [addr[/masklen] value] [addr[/masklen] value] ..
154  *      ipfw table NAME delete addr[/masklen] [addr[/masklen]] ..
155  *      ipfw table NAME lookup addr
156  *      ipfw table {NAME | all} flush
157  *      ipfw table {NAME | all} list
158  *      ipfw table {NAME | all} info
159  *      ipfw table {NAME | all} detail
160  */
161 void
162 ipfw_table_handler(int ac, char *av[])
163 {
164         int do_add, is_all;
165         int atomic, error, tcmd;
166         ipfw_xtable_info i;
167         ipfw_obj_header oh;
168         char *tablename;
169         uint32_t set;
170         void *arg;
171
172         memset(&oh, 0, sizeof(oh));
173         is_all = 0;
174         if (co.use_set != 0)
175                 set = co.use_set - 1;
176         else
177                 set = 0;
178
179         ac--; av++;
180         NEED1("table needs name");
181         tablename = *av;
182
183         if (table_check_name(tablename) == 0) {
184                 table_fill_ntlv(&oh.ntlv, *av, set, 1);
185                 oh.idx = 1;
186         } else {
187                 if (strcmp(tablename, "all") == 0)
188                         is_all = 1;
189                 else
190                         errx(EX_USAGE, "table name %s is invalid", tablename);
191         }
192         ac--; av++;
193         NEED1("table needs command");
194
195         tcmd = get_token(tablecmds, *av, "table command");
196         /* Check if atomic operation was requested */
197         atomic = 0;
198         if (tcmd == TOK_ATOMIC) {
199                 ac--; av++;
200                 NEED1("atomic needs command");
201                 tcmd = get_token(tablecmds, *av, "table command");
202                 switch (tcmd) {
203                 case TOK_ADD:
204                         break;
205                 default:
206                         errx(EX_USAGE, "atomic is not compatible with %s", *av);
207                 }
208                 atomic = 1;
209         }
210
211         switch (tcmd) {
212         case TOK_LIST:
213         case TOK_INFO:
214         case TOK_DETAIL:
215         case TOK_FLUSH:
216                 break;
217         default:
218                 if (is_all != 0)
219                         errx(EX_USAGE, "table name required");
220         }
221
222         switch (tcmd) {
223         case TOK_ADD:
224         case TOK_DEL:
225                 do_add = **av == 'a';
226                 ac--; av++;
227                 table_modify_record(&oh, ac, av, do_add, co.do_quiet,
228                     co.do_quiet, atomic);
229                 break;
230         case TOK_CREATE:
231                 ac--; av++;
232                 table_create(&oh, ac, av);
233                 break;
234         case TOK_MODIFY:
235                 ac--; av++;
236                 table_modify(&oh, ac, av);
237                 break;
238         case TOK_DESTROY:
239                 if (table_destroy(&oh) != 0)
240                         err(EX_OSERR, "failed to destroy table %s", tablename);
241                 break;
242         case TOK_FLUSH:
243                 if (is_all == 0) {
244                         if ((error = table_flush(&oh)) != 0)
245                                 err(EX_OSERR, "failed to flush table %s info",
246                                     tablename);
247                 } else {
248                         error = tables_foreach(table_flush_one, &oh, 1);
249                         if (error != 0)
250                                 err(EX_OSERR, "failed to flush tables list");
251                 }
252                 break;
253         case TOK_SWAP:
254                 ac--; av++;
255                 NEED1("second table name required");
256                 table_swap(&oh, *av);
257                 break;
258         case TOK_LOCK:
259         case TOK_UNLOCK:
260                 table_lock(&oh, (tcmd == TOK_LOCK));
261                 break;
262         case TOK_DETAIL:
263         case TOK_INFO:
264                 arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
265                 if (is_all == 0) {
266                         if ((error = table_get_info(&oh, &i)) != 0)
267                                 err(EX_OSERR, "failed to request table info");
268                         table_show_info(&i, arg);
269                 } else {
270                         error = tables_foreach(table_show_info, arg, 1);
271                         if (error != 0)
272                                 err(EX_OSERR, "failed to request tables list");
273                 }
274                 break;
275         case TOK_LIST:
276                 if (is_all == 0) {
277                         ipfw_xtable_info i;
278                         if ((error = table_get_info(&oh, &i)) != 0)
279                                 err(EX_OSERR, "failed to request table info");
280                         table_show_one(&i, NULL);
281                 } else {
282                         error = tables_foreach(table_show_one, NULL, 1);
283                         if (error != 0)
284                                 err(EX_OSERR, "failed to request tables list");
285                 }
286                 break;
287         case TOK_LOOKUP:
288                 ac--; av++;
289                 table_lookup(&oh, ac, av);
290                 break;
291         }
292 }
293
294 static void
295 table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx)
296 {
297
298         ntlv->head.type = IPFW_TLV_TBL_NAME;
299         ntlv->head.length = sizeof(ipfw_obj_ntlv);
300         ntlv->idx = uidx;
301         ntlv->set = set;
302         strlcpy(ntlv->name, name, sizeof(ntlv->name));
303 }
304
305 static void
306 table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
307 {
308
309         oh->idx = 1;
310         table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
311 }
312
313 static struct _s_x tablenewcmds[] = {
314       { "type",         TOK_TYPE },
315       { "valtype",      TOK_VALTYPE },
316       { "algo",         TOK_ALGO },
317       { "limit",        TOK_LIMIT },
318       { "locked",       TOK_LOCK },
319       { NULL, 0 }
320 };
321
322 static struct _s_x flowtypecmds[] = {
323       { "src-ip",       IPFW_TFFLAG_SRCIP },
324       { "proto",        IPFW_TFFLAG_PROTO },
325       { "src-port",     IPFW_TFFLAG_SRCPORT },
326       { "dst-ip",       IPFW_TFFLAG_DSTIP },
327       { "dst-port",     IPFW_TFFLAG_DSTPORT },
328       { NULL, 0 }
329 };
330
331 int
332 table_parse_type(uint8_t ttype, char *p, uint8_t *tflags)
333 {
334         uint32_t fset, fclear;
335         char *e;
336
337         /* Parse type options */
338         switch(ttype) {
339         case IPFW_TABLE_FLOW:
340                 fset = fclear = 0;
341                 if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0)
342                         errx(EX_USAGE,
343                             "unable to parse flow option %s", e);
344                 *tflags = fset;
345                 break;
346         default:
347                 return (EX_USAGE);
348         }
349
350         return (0);
351 }
352
353 void
354 table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags)
355 {
356         const char *tname;
357         int l;
358
359         if ((tname = match_value(tabletypes, type)) == NULL)
360                 tname = "unknown";
361
362         l = snprintf(tbuf, size, "%s", tname);
363         tbuf += l;
364         size -= l;
365
366         switch(type) {
367         case IPFW_TABLE_FLOW:
368                 if (tflags != 0) {
369                         *tbuf++ = ':';
370                         l--;
371                         print_flags_buffer(tbuf, size, flowtypecmds, tflags);
372                 }
373                 break;
374         }
375 }
376
377 /*
378  * Creates new table
379  *
380  * ipfw table NAME create [ type { addr | iface | number | flow } ]
381  *     [ algo algoname ]
382  */
383 static void
384 table_create(ipfw_obj_header *oh, int ac, char *av[])
385 {
386         ipfw_xtable_info xi;
387         int error, tcmd, val;
388         uint32_t fset, fclear;
389         size_t sz;
390         char *e, *p;
391         char tbuf[128];
392
393         sz = sizeof(tbuf);
394         memset(&xi, 0, sizeof(xi));
395
396         while (ac > 0) {
397                 tcmd = get_token(tablenewcmds, *av, "option");
398                 ac--; av++;
399
400                 switch (tcmd) {
401                 case TOK_LIMIT:
402                         NEED1("limit value required");
403                         xi.limit = strtol(*av, NULL, 10);
404                         ac--; av++;
405                         break;
406                 case TOK_TYPE:
407                         NEED1("table type required");
408                         /* Type may have suboptions after ':' */
409                         if ((p = strchr(*av, ':')) != NULL)
410                                 *p++ = '\0';
411                         val = match_token(tabletypes, *av);
412                         if (val == -1) {
413                                 concat_tokens(tbuf, sizeof(tbuf), tabletypes,
414                                     ", ");
415                                 errx(EX_USAGE,
416                                     "Unknown tabletype: %s. Supported: %s",
417                                     *av, tbuf);
418                         }
419                         xi.type = val;
420                         if (p != NULL) {
421                                 error = table_parse_type(val, p, &xi.tflags);
422                                 if (error != 0)
423                                         errx(EX_USAGE,
424                                             "Unsupported suboptions: %s", p);
425                         }
426                         ac--; av++;
427                         break;
428                 case TOK_VALTYPE:
429                         NEED1("table value type required");
430                         fset = fclear = 0;
431                         val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear);
432                         if (val != -1) {
433                                 xi.vmask = fset;
434                                 ac--; av++;
435                                 break;
436                         }
437                         concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
438                         errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
439                             e, tbuf);
440                         break;
441                 case TOK_ALGO:
442                         NEED1("table algorithm name required");
443                         if (strlen(*av) > sizeof(xi.algoname))
444                                 errx(EX_USAGE, "algorithm name too long");
445                         strlcpy(xi.algoname, *av, sizeof(xi.algoname));
446                         ac--; av++;
447                         break;
448                 case TOK_LOCK:
449                         xi.flags |= IPFW_TGFLAGS_LOCKED;
450                         break;
451                 }
452         }
453
454         /* Set some defaults to preserve compability */
455         if (xi.algoname[0] == '\0' && xi.type == 0)
456                 xi.type = IPFW_TABLE_ADDR;
457         if (xi.vmask == 0)
458                 xi.vmask = IPFW_VTYPE_LEGACY;
459
460         if ((error = table_do_create(oh, &xi)) != 0)
461                 err(EX_OSERR, "Table creation failed");
462 }
463
464 /*
465  * Creates new table
466  *
467  * Request: [ ipfw_obj_header ipfw_xtable_info ]
468  *
469  * Returns 0 on success.
470  */
471 static int
472 table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
473 {
474         char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
475         int error;
476
477         memcpy(tbuf, oh, sizeof(*oh));
478         memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
479         oh = (ipfw_obj_header *)tbuf;
480
481         error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf));
482
483         return (error);
484 }
485
486 /*
487  * Modifies existing table
488  *
489  * ipfw table NAME modify [ limit number ]
490  */
491 static void
492 table_modify(ipfw_obj_header *oh, int ac, char *av[])
493 {
494         ipfw_xtable_info xi;
495         int tcmd;
496         size_t sz;
497         char tbuf[128];
498
499         sz = sizeof(tbuf);
500         memset(&xi, 0, sizeof(xi));
501
502         while (ac > 0) {
503                 tcmd = get_token(tablenewcmds, *av, "option");
504                 ac--; av++;
505
506                 switch (tcmd) {
507                 case TOK_LIMIT:
508                         NEED1("limit value required");
509                         xi.limit = strtol(*av, NULL, 10);
510                         xi.mflags |= IPFW_TMFLAGS_LIMIT;
511                         ac--; av++;
512                         break;
513                 default:
514                         errx(EX_USAGE, "cmd is not supported for modificatiob");
515                 }
516         }
517
518         if (table_do_modify(oh, &xi) != 0)
519                 err(EX_OSERR, "Table modification failed");
520 }
521
522 /*
523  * Modifies existing table.
524  *
525  * Request: [ ipfw_obj_header ipfw_xtable_info ]
526  *
527  * Returns 0 on success.
528  */
529 static int
530 table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i)
531 {
532         char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
533         int error;
534
535         memcpy(tbuf, oh, sizeof(*oh));
536         memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
537         oh = (ipfw_obj_header *)tbuf;
538
539         error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf));
540
541         return (error);
542 }
543
544 /*
545  * Locks or unlocks given table
546  */
547 static void
548 table_lock(ipfw_obj_header *oh, int lock)
549 {
550         ipfw_xtable_info xi;
551
552         memset(&xi, 0, sizeof(xi));
553
554         xi.mflags |= IPFW_TMFLAGS_LOCK;
555         xi.flags |= (lock != 0) ? IPFW_TGFLAGS_LOCKED : 0;
556
557         if (table_do_modify(oh, &xi) != 0)
558                 err(EX_OSERR, "Table %s failed", lock != 0 ? "lock" : "unlock");
559 }
560
561 /*
562  * Destroys given table specified by @oh->ntlv.
563  * Returns 0 on success.
564  */
565 static int
566 table_destroy(ipfw_obj_header *oh)
567 {
568
569         if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0)
570                 return (-1);
571
572         return (0);
573 }
574
575 /*
576  * Flushes given table specified by @oh->ntlv.
577  * Returns 0 on success.
578  */
579 static int
580 table_flush(ipfw_obj_header *oh)
581 {
582
583         if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0)
584                 return (-1);
585
586         return (0);
587 }
588
589 static int
590 table_do_swap(ipfw_obj_header *oh, char *second)
591 {
592         char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)];
593         int error;
594
595         memset(tbuf, 0, sizeof(tbuf));
596         memcpy(tbuf, oh, sizeof(*oh));
597         oh = (ipfw_obj_header *)tbuf;
598         table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1);
599
600         error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf));
601
602         return (error);
603 }
604
605 /*
606  * Swaps given table with @second one.
607  */
608 static int
609 table_swap(ipfw_obj_header *oh, char *second)
610 {
611         int error;
612
613         if (table_check_name(second) != 0)
614                 errx(EX_USAGE, "table name %s is invalid", second);
615
616         error = table_do_swap(oh, second);
617
618         switch (error) {
619         case EINVAL:
620                 errx(EX_USAGE, "Unable to swap table: check types");
621         case EFBIG:
622                 errx(EX_USAGE, "Unable to swap table: check limits");
623         }
624
625         return (0);
626 }
627
628
629 /*
630  * Retrieves table in given table specified by @oh->ntlv.
631  * it inside @i.
632  * Returns 0 on success.
633  */
634 static int
635 table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i)
636 {
637         char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
638         size_t sz;
639
640         sz = sizeof(tbuf);
641         memset(tbuf, 0, sizeof(tbuf));
642         memcpy(tbuf, oh, sizeof(*oh));
643         oh = (ipfw_obj_header *)tbuf;
644
645         if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) != 0)
646                 return (errno);
647
648         if (sz < sizeof(tbuf))
649                 return (EINVAL);
650
651         *i = *(ipfw_xtable_info *)(oh + 1);
652
653         return (0);
654 }
655
656 static struct _s_x tablealgoclass[] = {
657       { "hash",         IPFW_TACLASS_HASH },
658       { "array",        IPFW_TACLASS_ARRAY },
659       { "radix",        IPFW_TACLASS_RADIX },
660       { NULL, 0 }
661 };
662
663 struct ta_cldata {
664         uint8_t         taclass;
665         uint8_t         spare4;
666         uint16_t        itemsize;
667         uint16_t        itemsize6;
668         uint32_t        size;   
669         uint32_t        count;
670 };
671
672 /*
673  * Print global/per-AF table @i algorithm info.
674  */
675 static void
676 table_show_tainfo(ipfw_xtable_info *i, struct ta_cldata *d,
677     const char *af, const char *taclass)
678 {
679
680         switch (d->taclass) {
681         case IPFW_TACLASS_HASH:
682         case IPFW_TACLASS_ARRAY:
683                 printf(" %salgorithm %s info\n", af, taclass);
684                 if (d->itemsize == d->itemsize6)
685                         printf("  size: %u items: %u itemsize: %u\n",
686                             d->size, d->count, d->itemsize);
687                 else
688                         printf("  size: %u items: %u "
689                             "itemsize4: %u itemsize6: %u\n",
690                             d->size, d->count,
691                             d->itemsize, d->itemsize6);
692                 break;
693         case IPFW_TACLASS_RADIX:
694                 printf(" %salgorithm %s info\n", af, taclass);
695                 if (d->itemsize == d->itemsize6)
696                         printf("  items: %u itemsize: %u\n",
697                             d->count, d->itemsize);
698                 else
699                         printf("  items: %u "
700                             "itemsize4: %u itemsize6: %u\n",
701                             d->count, d->itemsize, d->itemsize6);
702                 break;
703         default:
704                 printf(" algo class: %s\n", taclass);
705         }
706 }
707
708 static void
709 table_print_valheader(char *buf, size_t bufsize, uint32_t vmask)
710 {
711
712         if (vmask == IPFW_VTYPE_LEGACY) {
713                 snprintf(buf, bufsize, "legacy");
714                 return;
715         }
716
717         print_flags_buffer(buf, bufsize, tablevaltypes, vmask);
718 }
719
720 /*
721  * Prints table info struct @i in human-readable form.
722  */
723 static int
724 table_show_info(ipfw_xtable_info *i, void *arg)
725 {
726         const char *vtype;
727         ipfw_ta_tinfo *tainfo;
728         int afdata, afitem;
729         struct ta_cldata d;
730         char ttype[64], tvtype[64];
731
732         table_print_type(ttype, sizeof(ttype), i->type, i->tflags);
733         table_print_valheader(tvtype, sizeof(tvtype), i->vmask);
734
735         printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
736         if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0)
737                 printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype);
738         else
739                 printf(" kindex: %d, type: %s\n", i->kidx, ttype);
740         printf(" references: %u, valtype: %s\n", i->refcnt, tvtype);
741         printf(" algorithm: %s\n", i->algoname);
742         printf(" items: %u, size: %u\n", i->count, i->size);
743         if (i->limit > 0)
744                 printf(" limit: %u\n", i->limit);
745
746         /* Print algo-specific info if requested & set  */
747         if (arg == NULL)
748                 return (0);
749
750         if ((i->ta_info.flags & IPFW_TATFLAGS_DATA) == 0)
751                 return (0);
752         tainfo = &i->ta_info;
753
754         afdata = 0;
755         afitem = 0;
756         if (tainfo->flags & IPFW_TATFLAGS_AFDATA)
757                 afdata = 1;
758         if (tainfo->flags & IPFW_TATFLAGS_AFITEM)
759                 afitem = 1;
760
761         memset(&d, 0, sizeof(d));
762         d.taclass = tainfo->taclass4;
763         d.size = tainfo->size4;
764         d.count = tainfo->count4;
765         d.itemsize = tainfo->itemsize4;
766         if (afdata == 0 && afitem != 0)
767                 d.itemsize6 = tainfo->itemsize6;
768         else
769                 d.itemsize6 = d.itemsize;
770         if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
771                 vtype = "unknown";
772
773         if (afdata == 0) {
774                 table_show_tainfo(i, &d, "", vtype);
775         } else {
776                 table_show_tainfo(i, &d, "IPv4 ", vtype);
777                 memset(&d, 0, sizeof(d));
778                 d.taclass = tainfo->taclass6;
779                 if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
780                         vtype = "unknown";
781                 d.size = tainfo->size6;
782                 d.count = tainfo->count6;
783                 d.itemsize = tainfo->itemsize6;
784                 d.itemsize6 = d.itemsize;
785                 table_show_tainfo(i, &d, "IPv6 ", vtype);
786         }
787
788         return (0);
789 }
790
791
792 /*
793  * Function wrappers which can be used either
794  * as is or as foreach function parameter.
795  */
796
797 static int
798 table_show_one(ipfw_xtable_info *i, void *arg)
799 {
800         ipfw_obj_header *oh;
801         int error;
802
803         if ((error = table_do_get_list(i, &oh)) != 0) {
804                 err(EX_OSERR, "Error requesting table %s list", i->tablename);
805                 return (error);
806         }
807
808         table_show_list(oh, 1);
809
810         free(oh);
811         return (0);     
812 }
813
814 static int
815 table_flush_one(ipfw_xtable_info *i, void *arg)
816 {
817         ipfw_obj_header *oh;
818
819         oh = (ipfw_obj_header *)arg;
820
821         table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
822
823         return (table_flush(oh));
824 }
825
826 static int
827 table_do_modify_record(int cmd, ipfw_obj_header *oh,
828     ipfw_obj_tentry *tent, int count, int atomic)
829 {
830         ipfw_obj_ctlv *ctlv;
831         ipfw_obj_tentry *tent_base;
832         caddr_t pbuf;
833         char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)];
834         int error, i;
835         size_t sz;
836
837         sz = sizeof(*ctlv) + sizeof(*tent) * count;
838         if (count == 1) {
839                 memset(xbuf, 0, sizeof(xbuf));
840                 pbuf = xbuf;
841         } else {
842                 if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL)
843                         return (ENOMEM);
844         }
845
846         memcpy(pbuf, oh, sizeof(*oh));
847         oh = (ipfw_obj_header *)pbuf;
848         oh->opheader.version = 1;
849
850         ctlv = (ipfw_obj_ctlv *)(oh + 1);
851         ctlv->count = count;
852         ctlv->head.length = sz;
853         if (atomic != 0)
854                 ctlv->flags |= IPFW_CTF_ATOMIC;
855
856         tent_base = tent;
857         memcpy(ctlv + 1, tent, sizeof(*tent) * count);
858         tent = (ipfw_obj_tentry *)(ctlv + 1);
859         for (i = 0; i < count; i++, tent++) {
860                 tent->head.length = sizeof(ipfw_obj_tentry);
861                 tent->idx = oh->idx;
862         }
863
864         sz += sizeof(*oh);
865         error = do_get3(cmd, &oh->opheader, &sz);
866         tent = (ipfw_obj_tentry *)(ctlv + 1);
867         /* Copy result back to provided buffer */
868         memcpy(tent_base, ctlv + 1, sizeof(*tent) * count);
869
870         if (pbuf != xbuf)
871                 free(pbuf);
872
873         return (error);
874 }
875
876 static void
877 table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
878     int quiet, int update, int atomic)
879 {
880         ipfw_obj_tentry *ptent, tent, *tent_buf;
881         ipfw_xtable_info xi;
882         uint8_t type;
883         uint32_t vmask;
884         int cmd, count, error, i, ignored;
885         char *texterr, *etxt, *px;
886
887         if (ac == 0)
888                 errx(EX_USAGE, "address required");
889         
890         if (add != 0) {
891                 cmd = IP_FW_TABLE_XADD;
892                 texterr = "Adding record failed";
893         } else {
894                 cmd = IP_FW_TABLE_XDEL;
895                 texterr = "Deleting record failed";
896         }
897
898         /*
899          * Calculate number of entries:
900          * Assume [key val] x N for add
901          * and
902          * key x N for delete
903          */
904         count = (add != 0) ? ac / 2 + 1 : ac;
905
906         if (count <= 1) {
907                 /* Adding single entry with/without value */
908                 memset(&tent, 0, sizeof(tent));
909                 tent_buf = &tent;
910         } else {
911                 
912                 if ((tent_buf = calloc(count, sizeof(tent))) == NULL)
913                         errx(EX_OSERR,
914                             "Unable to allocate memory for all entries");
915         }
916         ptent = tent_buf;
917
918         memset(&xi, 0, sizeof(xi));
919         count = 0;
920         while (ac > 0) {
921                 tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi);
922
923                 /*
924                  * compability layer: auto-create table if not exists
925                  */
926                 if (xi.tablename[0] == '\0') {
927                         xi.type = type;
928                         xi.vmask = vmask;
929                         strlcpy(xi.tablename, oh->ntlv.name,
930                             sizeof(xi.tablename));
931                         fprintf(stderr, "DEPRECATED: inserting data info "
932                             "non-existent table %s. (auto-created)\n",
933                             xi.tablename);
934                         table_do_create(oh, &xi);
935                 }
936         
937                 oh->ntlv.type = type;
938                 ac--; av++;
939         
940                 if (add != 0 && ac > 0) {
941                         tentry_fill_value(oh, ptent, *av, type, vmask);
942                         ac--; av++;
943                 }
944
945                 if (update != 0)
946                         ptent->head.flags |= IPFW_TF_UPDATE;
947
948                 count++;
949                 ptent++;
950         }
951
952         error = table_do_modify_record(cmd, oh, tent_buf, count, atomic);
953
954         quiet = 0;
955
956         /*
957          * Compatibility stuff: do not yell on duplicate keys or
958          * failed deletions.
959          */
960         if (error == 0 || (error == EEXIST && add != 0) ||
961             (error == ENOENT && add == 0)) {
962                 if (quiet != 0) {
963                         if (tent_buf != &tent)
964                                 free(tent_buf);
965                         return;
966                 }
967         }
968
969         /* Report results back */
970         ptent = tent_buf;
971         for (i = 0; i < count; ptent++, i++) {
972                 ignored = 0;
973                 switch (ptent->result) {
974                 case IPFW_TR_ADDED:
975                         px = "added";
976                         break;
977                 case IPFW_TR_DELETED:
978                         px = "deleted";
979                         break;
980                 case IPFW_TR_UPDATED:
981                         px = "updated";
982                         break;
983                 case IPFW_TR_LIMIT:
984                         px = "limit";
985                         ignored = 1;
986                         break;
987                 case IPFW_TR_ERROR:
988                         px = "error";
989                         ignored = 1;
990                         break;
991                 case IPFW_TR_NOTFOUND:
992                         px = "notfound";
993                         ignored = 1;
994                         break;
995                 case IPFW_TR_EXISTS:
996                         px = "exists";
997                         ignored = 1;
998                         break;
999                 case IPFW_TR_IGNORED:
1000                         px = "ignored";
1001                         ignored = 1;
1002                         break;
1003                 default:
1004                         px = "unknown";
1005                         ignored = 1;
1006                 }
1007
1008                 if (error != 0 && atomic != 0 && ignored == 0)
1009                         printf("%s(reverted): ", px);
1010                 else
1011                         printf("%s: ", px);
1012
1013                 table_show_entry(&xi, ptent);
1014         }
1015
1016         if (tent_buf != &tent)
1017                 free(tent_buf);
1018
1019         if (error == 0)
1020                 return;
1021         /* Get real OS error */
1022         error = errno;
1023
1024         /* Try to provide more human-readable error */
1025         switch (error) {
1026         case EEXIST:
1027                 etxt = "record already exists";
1028                 break;
1029         case EFBIG:
1030                 etxt = "limit hit";
1031                 break;
1032         case ESRCH:
1033                 etxt = "table not found";
1034                 break;
1035         case ENOENT:
1036                 etxt = "record not found";
1037                 break;
1038         case EACCES:
1039                 etxt = "table is locked";
1040                 break;
1041         default:
1042                 etxt = strerror(error);
1043         }
1044
1045         errx(EX_OSERR, "%s: %s", texterr, etxt);
1046 }
1047
1048 static int
1049 table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi,
1050     ipfw_obj_tentry *xtent)
1051 {
1052         char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
1053         ipfw_obj_tentry *tent;
1054         uint8_t type;
1055         uint32_t vmask;
1056         size_t sz;
1057
1058         memcpy(xbuf, oh, sizeof(*oh));
1059         oh = (ipfw_obj_header *)xbuf;
1060         tent = (ipfw_obj_tentry *)(oh + 1);
1061
1062         memset(tent, 0, sizeof(*tent));
1063         tent->head.length = sizeof(*tent);
1064         tent->idx = 1;
1065
1066         tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi);
1067         oh->ntlv.type = type;
1068
1069         sz = sizeof(xbuf);
1070         if (do_get3(IP_FW_TABLE_XFIND, &oh->opheader, &sz) != 0)
1071                 return (errno);
1072
1073         if (sz < sizeof(xbuf))
1074                 return (EINVAL);
1075
1076         *xtent = *tent;
1077
1078         return (0);
1079 }
1080
1081 static void
1082 table_lookup(ipfw_obj_header *oh, int ac, char *av[])
1083 {
1084         ipfw_obj_tentry xtent;
1085         ipfw_xtable_info xi;
1086         char key[64];
1087         int error;
1088
1089         if (ac == 0)
1090                 errx(EX_USAGE, "address required");
1091
1092         strlcpy(key, *av, sizeof(key));
1093
1094         memset(&xi, 0, sizeof(xi));
1095         error = table_do_lookup(oh, key, &xi, &xtent);
1096
1097         switch (error) {
1098         case 0:
1099                 break;
1100         case ESRCH:
1101                 errx(EX_UNAVAILABLE, "Table %s not found", oh->ntlv.name);
1102         case ENOENT:
1103                 errx(EX_UNAVAILABLE, "Entry %s not found", *av);
1104         case ENOTSUP:
1105                 errx(EX_UNAVAILABLE, "Table %s algo does not support "
1106                     "\"lookup\" method", oh->ntlv.name);
1107         default:
1108                 err(EX_OSERR, "getsockopt(IP_FW_TABLE_XFIND)");
1109         }
1110
1111         table_show_entry(&xi, &xtent);
1112 }
1113
1114 static void
1115 tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
1116     uint8_t tflags)
1117 {
1118         char *p, *pp;
1119         int mask, af;
1120         struct in6_addr *paddr, tmp;
1121         struct tflow_entry *tfe;
1122         uint32_t key, *pkey;
1123         uint16_t port;
1124         struct protoent *pent;
1125         struct servent *sent;
1126         int masklen;
1127
1128         masklen = 0;
1129         af = 0;
1130         paddr = (struct in6_addr *)&tentry->k;
1131
1132         switch (type) {
1133         case IPFW_TABLE_ADDR:
1134                 /* Remove / if exists */
1135                 if ((p = strchr(arg, '/')) != NULL) {
1136                         *p = '\0';
1137                         mask = atoi(p + 1);
1138                 }
1139
1140                 if (inet_pton(AF_INET, arg, paddr) == 1) {
1141                         if (p != NULL && mask > 32)
1142                                 errx(EX_DATAERR, "bad IPv4 mask width: %s",
1143                                     p + 1);
1144
1145                         masklen = p ? mask : 32;
1146                         af = AF_INET;
1147                 } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
1148                         if (IN6_IS_ADDR_V4COMPAT(paddr))
1149                                 errx(EX_DATAERR,
1150                                     "Use IPv4 instead of v4-compatible");
1151                         if (p != NULL && mask > 128)
1152                                 errx(EX_DATAERR, "bad IPv6 mask width: %s",
1153                                     p + 1);
1154
1155                         masklen = p ? mask : 128;
1156                         af = AF_INET6;
1157                 } else {
1158                         /* Assume FQDN */
1159                         if (lookup_host(arg, (struct in_addr *)paddr) != 0)
1160                                 errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
1161
1162                         masklen = 32;
1163                         type = IPFW_TABLE_ADDR;
1164                         af = AF_INET;
1165                 }
1166                 break;
1167         case IPFW_TABLE_INTERFACE:
1168                 /* Assume interface name. Copy significant data only */
1169                 mask = MIN(strlen(arg), IF_NAMESIZE - 1);
1170                 memcpy(paddr, arg, mask);
1171                 /* Set mask to exact match */
1172                 masklen = 8 * IF_NAMESIZE;
1173                 break;
1174         case IPFW_TABLE_NUMBER:
1175                 /* Port or any other key */
1176                 key = strtol(arg, &p, 10);
1177                 if (*p != '\0')
1178                         errx(EX_DATAERR, "Invalid number: %s", arg);
1179
1180                 pkey = (uint32_t *)paddr;
1181                 *pkey = key;
1182                 masklen = 32;
1183                 break;
1184         case IPFW_TABLE_FLOW:
1185                 /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */
1186                 tfe = &tentry->k.flow;
1187                 af = 0;
1188
1189                 /* Handle <ipv4|ipv6> */
1190                 if ((tflags & IPFW_TFFLAG_SRCIP) != 0) {
1191                         if ((p = strchr(arg, ',')) != NULL)
1192                                 *p++ = '\0';
1193                         /* Determine family using temporary storage */
1194                         if (inet_pton(AF_INET, arg, &tmp) == 1) {
1195                                 if (af != 0 && af != AF_INET)
1196                                         errx(EX_DATAERR,
1197                                             "Inconsistent address family\n");
1198                                 af = AF_INET;
1199                                 memcpy(&tfe->a.a4.sip, &tmp, 4);
1200                         } else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
1201                                 if (af != 0 && af != AF_INET6)
1202                                         errx(EX_DATAERR,
1203                                             "Inconsistent address family\n");
1204                                 af = AF_INET6;
1205                                 memcpy(&tfe->a.a6.sip6, &tmp, 16);
1206                         }
1207
1208                         arg = p;
1209                 }
1210
1211                 /* Handle <proto-num|proto-name> */
1212                 if ((tflags & IPFW_TFFLAG_PROTO) != 0) {
1213                         if (arg == NULL)
1214                                 errx(EX_DATAERR, "invalid key: proto missing");
1215                         if ((p = strchr(arg, ',')) != NULL)
1216                                 *p++ = '\0';
1217
1218                         key = strtol(arg, &pp, 10);
1219                         if (*pp != '\0') {
1220                                 if ((pent = getprotobyname(arg)) == NULL)
1221                                         errx(EX_DATAERR, "Unknown proto: %s",
1222                                             arg);
1223                                 else
1224                                         key = pent->p_proto;
1225                         }
1226                         
1227                         if (key > 255)
1228                                 errx(EX_DATAERR, "Bad protocol number: %u",key);
1229
1230                         tfe->proto = key;
1231
1232                         arg = p;
1233                 }
1234
1235                 /* Handle <port-num|service-name> */
1236                 if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) {
1237                         if (arg == NULL)
1238                                 errx(EX_DATAERR, "invalid key: src port missing");
1239                         if ((p = strchr(arg, ',')) != NULL)
1240                                 *p++ = '\0';
1241
1242                         if ((port = htons(strtol(arg, NULL, 10))) == 0) {
1243                                 if ((sent = getservbyname(arg, NULL)) == NULL)
1244                                         errx(EX_DATAERR, "Unknown service: %s",
1245                                             arg);
1246                                 else
1247                                         key = sent->s_port;
1248                         }
1249                         
1250                         tfe->sport = port;
1251
1252                         arg = p;
1253                 }
1254
1255                 /* Handle <ipv4|ipv6>*/
1256                 if ((tflags & IPFW_TFFLAG_DSTIP) != 0) {
1257                         if (arg == NULL)
1258                                 errx(EX_DATAERR, "invalid key: dst ip missing");
1259                         if ((p = strchr(arg, ',')) != NULL)
1260                                 *p++ = '\0';
1261                         /* Determine family using temporary storage */
1262                         if (inet_pton(AF_INET, arg, &tmp) == 1) {
1263                                 if (af != 0 && af != AF_INET)
1264                                         errx(EX_DATAERR,
1265                                             "Inconsistent address family");
1266                                 af = AF_INET;
1267                                 memcpy(&tfe->a.a4.dip, &tmp, 4);
1268                         } else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
1269                                 if (af != 0 && af != AF_INET6)
1270                                         errx(EX_DATAERR,
1271                                             "Inconsistent address family");
1272                                 af = AF_INET6;
1273                                 memcpy(&tfe->a.a6.dip6, &tmp, 16);
1274                         }
1275
1276                         arg = p;
1277                 }
1278
1279                 /* Handle <port-num|service-name> */
1280                 if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) {
1281                         if (arg == NULL)
1282                                 errx(EX_DATAERR, "invalid key: dst port missing");
1283                         if ((p = strchr(arg, ',')) != NULL)
1284                                 *p++ = '\0';
1285
1286                         if ((port = htons(strtol(arg, NULL, 10))) == 0) {
1287                                 if ((sent = getservbyname(arg, NULL)) == NULL)
1288                                         errx(EX_DATAERR, "Unknown service: %s",
1289                                             arg);
1290                                 else
1291                                         key = sent->s_port;
1292                         }
1293                         
1294                         tfe->dport = port;
1295
1296                         arg = p;
1297                 }
1298
1299                 tfe->af = af;
1300
1301                 break;
1302         
1303         default:
1304                 errx(EX_DATAERR, "Unsupported table type: %d", type);
1305         }
1306
1307         tentry->subtype = af;
1308         tentry->masklen = masklen;
1309 }
1310
1311 static void
1312 tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
1313     int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi)
1314 {
1315         uint8_t type, tflags;
1316         uint32_t vmask;
1317         int error;
1318         char *del;
1319
1320         type = 0;
1321         tflags = 0;
1322         vmask = 0;
1323
1324         if (xi->tablename[0] == '\0')
1325                 error = table_get_info(oh, xi);
1326         else
1327                 error = 0;
1328
1329         if (error == 0) {
1330                 /* Table found. */
1331                 type = xi->type;
1332                 tflags = xi->tflags;
1333                 vmask = xi->vmask;
1334         } else {
1335                 if (error != ESRCH)
1336                         errx(EX_OSERR, "Error requesting table %s info",
1337                             oh->ntlv.name);
1338                 if (add == 0)
1339                         errx(EX_DATAERR, "Table %s does not exist",
1340                             oh->ntlv.name);
1341                 /*
1342                  * Table does not exist.
1343                  * Compability layer: try to interpret data as ADDR
1344                  * before failing.
1345                  */
1346                 if ((del = strchr(key, '/')) != NULL)
1347                         *del = '\0';
1348                 if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 ||
1349                     inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
1350                         /* OK Prepare and send */
1351                         type = IPFW_TABLE_ADDR;
1352                         vmask = IPFW_VTYPE_LEGACY;
1353                 } else {
1354                         /* Inknown key */
1355                         errx(EX_USAGE, "Table %s does not exist, cannot guess "
1356                             "key '%s' type", oh->ntlv.name, key);
1357                 }
1358                 if (del != NULL)
1359                         *del = '/';
1360         }
1361
1362         tentry_fill_key_type(key, tent, type, tflags);
1363
1364         *ptype = type;
1365         *pvmask = vmask;
1366 }
1367
1368 static void
1369 set_legacy_value(uint32_t val, ipfw_table_value *v)
1370 {
1371         v->tag = val;
1372         v->pipe = val;
1373         v->divert = val;
1374         v->skipto = val;
1375         v->netgraph = val;
1376         v->fib = val;
1377         v->nat = val;
1378         v->nh4 = val;
1379         v->dscp = (uint8_t)val;
1380         v->limit = val;
1381 }
1382
1383 static void
1384 tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg,
1385     uint8_t type, uint32_t vmask)
1386 {
1387         uint32_t a4, flag, val, vm;
1388         ipfw_table_value *v;
1389         uint32_t i;
1390         int dval;
1391         char *comma, *e, *etype, *n, *p;
1392
1393         v = &tent->v.value;
1394         vm = vmask;
1395
1396         /* Compat layer: keep old behavior for legacy value types */
1397         if (vmask == IPFW_VTYPE_LEGACY) {
1398                 /* Try to interpret as number first */
1399                 val = strtoul(arg, &p, 0);
1400                 if (*p == '\0') {
1401                         set_legacy_value(val, v);
1402                         return;
1403                 }
1404                 if (inet_pton(AF_INET, arg, &val) == 1) {
1405                         set_legacy_value(ntohl(val), v);
1406                         return;
1407                 }
1408                 /* Try hostname */
1409                 if (lookup_host(arg, (struct in_addr *)&val) == 0) {
1410                         set_legacy_value(val, v);
1411                         return;
1412                 }
1413                 errx(EX_OSERR, "Unable to parse value %s", arg);
1414         }
1415
1416         /*
1417          * Shorthands: handle single value if vmask consists
1418          * of numbers only. e.g.:
1419          * vmask = "fib,skipto" -> treat input "1" as "1,1"
1420          */
1421
1422         n = arg;
1423         etype = NULL;
1424         for (i = 1; i < (1 << 31); i *= 2) {
1425                 if ((flag = (vmask & i)) == 0)
1426                         continue;
1427                 vmask &= ~flag;
1428
1429                 if ((comma = strchr(n, ',')) != NULL)
1430                         *comma = '\0';
1431
1432                 switch (flag) {
1433                 case IPFW_VTYPE_TAG:
1434                         v->tag = strtol(n, &e, 10);
1435                         if (*e != '\0')
1436                                 etype = "tag";
1437                         break;
1438                 case IPFW_VTYPE_PIPE:
1439                         v->pipe = strtol(n, &e, 10);
1440                         if (*e != '\0')
1441                                 etype = "pipe";
1442                         break;
1443                 case IPFW_VTYPE_DIVERT:
1444                         v->divert = strtol(n, &e, 10);
1445                         if (*e != '\0')
1446                                 etype = "divert";
1447                         break;
1448                 case IPFW_VTYPE_SKIPTO:
1449                         v->skipto = strtol(n, &e, 10);
1450                         if (*e != '\0')
1451                                 etype = "skipto";
1452                         break;
1453                 case IPFW_VTYPE_NETGRAPH:
1454                         v->netgraph = strtol(n, &e, 10);
1455                         if (*e != '\0')
1456                                 etype = "netgraph";
1457                         break;
1458                 case IPFW_VTYPE_FIB:
1459                         v->fib = strtol(n, &e, 10);
1460                         if (*e != '\0')
1461                                 etype = "fib";
1462                         break;
1463                 case IPFW_VTYPE_NAT:
1464                         v->nat = strtol(n, &e, 10);
1465                         if (*e != '\0')
1466                                 etype = "nat";
1467                         break;
1468                 case IPFW_VTYPE_LIMIT:
1469                         v->limit = strtol(n, &e, 10);
1470                         if (*e != '\0')
1471                                 etype = "limit";
1472                         break;
1473                 case IPFW_VTYPE_NH4:
1474                         if (strchr(n, '.') != NULL &&
1475                             inet_pton(AF_INET, n, &a4) == 1) {
1476                                 v->nh4 = ntohl(a4);
1477                                 break;
1478                         }
1479                         if (lookup_host(n, (struct in_addr *)&v->nh4) == 0)
1480                                 break;
1481                         etype = "ipv4";
1482                         break;
1483                 case IPFW_VTYPE_DSCP:
1484                         if (isalpha(*n)) {
1485                                 if ((dval = match_token(f_ipdscp, n)) != -1) {
1486                                         v->dscp = dval;
1487                                         break;
1488                                 } else
1489                                         etype = "DSCP code";
1490                         } else {
1491                                 v->dscp = strtol(n, &e, 10);
1492                                 if (v->dscp > 63 || *e != '\0')
1493                                         etype = "DSCP value";
1494                         }
1495                         break;
1496                 case IPFW_VTYPE_NH6:
1497                         if (strchr(n, ':') != NULL &&
1498                             inet_pton(AF_INET6, n, &v->nh6) == 1)
1499                                 break;
1500                         etype = "ipv6";
1501                         break;
1502                 }
1503
1504                 if (etype != NULL)
1505                         errx(EX_USAGE, "Unable to parse %s as %s", n, etype);
1506
1507                 if (comma != NULL)
1508                         *comma++ = ',';
1509
1510                 if ((n = comma) != NULL)
1511                         continue;
1512
1513                 /* End of input. */
1514                 if (vmask != 0)
1515                         errx(EX_USAGE, "Not enough fields inside value");
1516         }
1517 }
1518
1519 /*
1520  * Compare table names.
1521  * Honor number comparison.
1522  */
1523 static int
1524 tablename_cmp(const void *a, const void *b)
1525 {
1526         ipfw_xtable_info *ia, *ib;
1527
1528         ia = (ipfw_xtable_info *)a;
1529         ib = (ipfw_xtable_info *)b;
1530
1531         return (stringnum_cmp(ia->tablename, ib->tablename));
1532 }
1533
1534 /*
1535  * Retrieves table list from kernel,
1536  * optionally sorts it and calls requested function for each table.
1537  * Returns 0 on success.
1538  */
1539 static int
1540 tables_foreach(table_cb_t *f, void *arg, int sort)
1541 {
1542         ipfw_obj_lheader *olh;
1543         ipfw_xtable_info *info;
1544         size_t sz;
1545         int i, error;
1546
1547         /* Start with reasonable default */
1548         sz = sizeof(*olh) + 16 * sizeof(ipfw_xtable_info);
1549
1550         for (;;) {
1551                 if ((olh = calloc(1, sz)) == NULL)
1552                         return (ENOMEM);
1553
1554                 olh->size = sz;
1555                 if (do_get3(IP_FW_TABLES_XLIST, &olh->opheader, &sz) != 0) {
1556                         sz = olh->size;
1557                         free(olh);
1558                         if (errno != ENOMEM)
1559                                 return (errno);
1560                         continue;
1561                 }
1562
1563                 if (sort != 0)
1564                         qsort(olh + 1, olh->count, olh->objsize, tablename_cmp);
1565
1566                 info = (ipfw_xtable_info *)(olh + 1);
1567                 for (i = 0; i < olh->count; i++) {
1568                         error = f(info, arg); /* Ignore errors for now */
1569                         info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize);
1570                 }
1571
1572                 free(olh);
1573                 break;
1574         }
1575
1576         return (0);
1577 }
1578
1579
1580 /*
1581  * Retrieves all entries for given table @i in
1582  * eXtended format. Allocate buffer large enough
1583  * to store result. Called needs to free it later.
1584  *
1585  * Returns 0 on success.
1586  */
1587 static int
1588 table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh)
1589 {
1590         ipfw_obj_header *oh;
1591         size_t sz;
1592         int c;
1593
1594         sz = 0;
1595         oh = NULL;
1596         for (c = 0; c < 8; c++) {
1597                 if (sz < i->size)
1598                         sz = i->size + 44;
1599                 if (oh != NULL)
1600                         free(oh);
1601                 if ((oh = calloc(1, sz)) == NULL)
1602                         continue;
1603                 table_fill_objheader(oh, i);
1604                 oh->opheader.version = 1; /* Current version */
1605                 if (do_get3(IP_FW_TABLE_XLIST, &oh->opheader, &sz) == 0) {
1606                         *poh = oh;
1607                         return (0);
1608                 }
1609
1610                 if (errno != ENOMEM)
1611                         break;
1612         }
1613         free(oh);
1614
1615         return (errno);
1616 }
1617
1618 /*
1619  * Shows all entries from @oh in human-readable format
1620  */
1621 static void
1622 table_show_list(ipfw_obj_header *oh, int need_header)
1623 {
1624         ipfw_obj_tentry *tent;
1625         uint32_t count;
1626         ipfw_xtable_info *i;
1627
1628         i = (ipfw_xtable_info *)(oh + 1);
1629         tent = (ipfw_obj_tentry *)(i + 1);
1630
1631         if (need_header)
1632                 printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
1633
1634         count = i->count;
1635         while (count > 0) {
1636                 table_show_entry(i, tent);
1637                 tent = (ipfw_obj_tentry *)((caddr_t)tent + tent->head.length);
1638                 count--;
1639         }
1640 }
1641
1642 static void
1643 table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
1644     uint32_t vmask, int print_ip)
1645 {
1646         uint32_t flag, i, l;
1647         size_t sz;
1648         struct in_addr a4;
1649         char abuf[INET6_ADDRSTRLEN];
1650
1651         sz = bufsize;
1652
1653         /*
1654          * Some shorthands for printing values:
1655          * legacy assumes all values are equal, so keep the first one.
1656          */
1657         if (vmask == IPFW_VTYPE_LEGACY) {
1658                 if (print_ip != 0) {
1659                         flag = htonl(v->tag);
1660                         inet_ntop(AF_INET, &flag, buf, sz);
1661                 } else
1662                         snprintf(buf, sz, "%u", v->tag);
1663                 return;
1664         }
1665
1666         for (i = 1; i < (1 << 31); i *= 2) {
1667                 if ((flag = (vmask & i)) == 0)
1668                         continue;
1669                 l = 0;
1670
1671                 switch (flag) {
1672                 case IPFW_VTYPE_TAG:
1673                         l = snprintf(buf, sz, "%u,", v->tag);
1674                         break;
1675                 case IPFW_VTYPE_PIPE:
1676                         l = snprintf(buf, sz, "%u,", v->pipe);
1677                         break;
1678                 case IPFW_VTYPE_DIVERT:
1679                         l = snprintf(buf, sz, "%d,", v->divert);
1680                         break;
1681                 case IPFW_VTYPE_SKIPTO:
1682                         l = snprintf(buf, sz, "%d,", v->skipto);
1683                         break;
1684                 case IPFW_VTYPE_NETGRAPH:
1685                         l = snprintf(buf, sz, "%u,", v->netgraph);
1686                         break;
1687                 case IPFW_VTYPE_FIB:
1688                         l = snprintf(buf, sz, "%u,", v->fib);
1689                         break;
1690                 case IPFW_VTYPE_NAT:
1691                         l = snprintf(buf, sz, "%u,", v->nat);
1692                         break;
1693                 case IPFW_VTYPE_LIMIT:
1694                         l = snprintf(buf, sz, "%u,", v->limit);
1695                         break;
1696                 case IPFW_VTYPE_NH4:
1697                         a4.s_addr = htonl(v->nh4);
1698                         inet_ntop(AF_INET, &a4, abuf, sizeof(abuf));
1699                         l = snprintf(buf, sz, "%s,", abuf);
1700                         break;
1701                 case IPFW_VTYPE_DSCP:
1702                         l = snprintf(buf, sz, "%d,", v->dscp);
1703                         break;
1704                 case IPFW_VTYPE_NH6:
1705                         inet_ntop(AF_INET6, &v->nh6, abuf, sizeof(abuf));
1706                         l = snprintf(buf, sz, "%s,", abuf);
1707                         break;
1708                 }
1709
1710                 buf += l;
1711                 sz -= l;
1712         }
1713
1714         if (sz != bufsize)
1715                 *(buf - 1) = '\0';
1716 }
1717
1718 static void
1719 table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
1720 {
1721         char *comma, tbuf[128], pval[128];
1722         void *paddr;
1723         struct tflow_entry *tfe;
1724
1725         table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask,
1726             co.do_value_as_ip);
1727
1728         switch (i->type) {
1729         case IPFW_TABLE_ADDR:
1730                 /* IPv4 or IPv6 prefixes */
1731                 inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
1732                 printf("%s/%u %s\n", tbuf, tent->masklen, pval);
1733                 break;
1734         case IPFW_TABLE_INTERFACE:
1735                 /* Interface names */
1736                 printf("%s %s\n", tent->k.iface, pval);
1737                 break;
1738         case IPFW_TABLE_NUMBER:
1739                 /* numbers */
1740                 printf("%u %s\n", tent->k.key, pval);
1741                 break;
1742         case IPFW_TABLE_FLOW:
1743                 /* flows */
1744                 tfe = &tent->k.flow;
1745                 comma = "";
1746
1747                 if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) {
1748                         if (tfe->af == AF_INET)
1749                                 paddr = &tfe->a.a4.sip;
1750                         else
1751                                 paddr = &tfe->a.a6.sip6;
1752
1753                         inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
1754                         printf("%s%s", comma, tbuf);
1755                         comma = ",";
1756                 }
1757
1758                 if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) {
1759                         printf("%s%d", comma, tfe->proto);
1760                         comma = ",";
1761                 }
1762
1763                 if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) {
1764                         printf("%s%d", comma, ntohs(tfe->sport));
1765                         comma = ",";
1766                 }
1767                 if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) {
1768                         if (tfe->af == AF_INET)
1769                                 paddr = &tfe->a.a4.dip;
1770                         else
1771                                 paddr = &tfe->a.a6.dip6;
1772
1773                         inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
1774                         printf("%s%s", comma, tbuf);
1775                         comma = ",";
1776                 }
1777
1778                 if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) {
1779                         printf("%s%d", comma, ntohs(tfe->dport));
1780                         comma = ",";
1781                 }
1782
1783                 printf(" %s\n", pval);
1784         }
1785 }
1786
1787 static int
1788 table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh)
1789 {
1790         ipfw_obj_lheader req, *olh;
1791         size_t sz;
1792
1793         memset(&req, 0, sizeof(req));
1794         sz = sizeof(req);
1795
1796         if (do_get3(opcode, &req.opheader, &sz) != 0)
1797                 if (errno != ENOMEM)
1798                         return (errno);
1799
1800         sz = req.size;
1801         if ((olh = calloc(1, sz)) == NULL)
1802                 return (ENOMEM);
1803
1804         olh->size = sz;
1805         if (do_get3(opcode, &olh->opheader, &sz) != 0) {
1806                 free(olh);
1807                 return (errno);
1808         }
1809
1810         *polh = olh;
1811         return (0);
1812 }
1813
1814 static int
1815 table_do_get_algolist(ipfw_obj_lheader **polh)
1816 {
1817
1818         return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh));
1819 }
1820
1821 static int
1822 table_do_get_vlist(ipfw_obj_lheader **polh)
1823 {
1824
1825         return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh));
1826 }
1827
1828 void
1829 ipfw_list_ta(int ac, char *av[])
1830 {
1831         ipfw_obj_lheader *olh;
1832         ipfw_ta_info *info;
1833         int error, i;
1834         const char *atype;
1835
1836         error = table_do_get_algolist(&olh);
1837         if (error != 0)
1838                 err(EX_OSERR, "Unable to request algorithm list");
1839
1840         info = (ipfw_ta_info *)(olh + 1);
1841         for (i = 0; i < olh->count; i++) {
1842                 if ((atype = match_value(tabletypes, info->type)) == NULL)
1843                         atype = "unknown";
1844                 printf("--- %s ---\n", info->algoname);
1845                 printf(" type: %s\n refcount: %u\n", atype, info->refcnt);
1846
1847                 info = (ipfw_ta_info *)((caddr_t)info + olh->objsize);
1848         }
1849
1850         free(olh);
1851 }
1852
1853
1854 /* Copy of current kernel table_value structure */
1855 struct _table_value {
1856         uint32_t        tag;            /* O_TAG/O_TAGGED */
1857         uint32_t        pipe;           /* O_PIPE/O_QUEUE */
1858         uint16_t        divert;         /* O_DIVERT/O_TEE */
1859         uint16_t        skipto;         /* skipto, CALLRET */
1860         uint32_t        netgraph;       /* O_NETGRAPH/O_NGTEE */
1861         uint32_t        fib;            /* O_SETFIB */
1862         uint32_t        nat;            /* O_NAT */
1863         uint32_t        nh4;
1864         uint8_t         dscp;
1865         uint8_t         spare0[3];
1866         /* -- 32 bytes -- */
1867         struct in6_addr nh6;
1868         uint32_t        limit;          /* O_LIMIT */
1869         uint32_t        spare1;
1870         uint64_t        refcnt;         /* Number of references */
1871 };
1872
1873 int
1874 compare_values(const void *_a, const void *_b)
1875 {
1876         struct _table_value *a, *b;
1877
1878         a = (struct _table_value *)_a;
1879         b = (struct _table_value *)_b;
1880
1881         if (a->spare1 < b->spare1)
1882                 return (-1);
1883         else if (a->spare1 > b->spare1)
1884                 return (1);
1885
1886         return (0);
1887 }
1888
1889 void
1890 ipfw_list_values(int ac, char *av[])
1891 {
1892         ipfw_obj_lheader *olh;
1893         struct _table_value *v;
1894         int error, i;
1895         uint32_t vmask;
1896         char buf[128];
1897
1898         error = table_do_get_vlist(&olh);
1899         if (error != 0)
1900                 err(EX_OSERR, "Unable to request value list");
1901
1902         vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */
1903
1904         table_print_valheader(buf, sizeof(buf), vmask);
1905         printf("HEADER: %s\n", buf);
1906         v = (struct _table_value *)(olh + 1);
1907         qsort(v, olh->count, olh->objsize, compare_values);
1908         for (i = 0; i < olh->count; i++) {
1909                 table_show_value(buf, sizeof(buf), (ipfw_table_value *)v,
1910                     vmask, 0);
1911                 printf("[%u] refs=%lu %s\n", v->spare1, (u_long)v->refcnt, buf);
1912                 v = (struct _table_value *)((caddr_t)v + olh->objsize);
1913         }
1914
1915         free(olh);
1916 }
1917
1918 int
1919 compare_ntlv(const void *_a, const void *_b)
1920 {
1921         ipfw_obj_ntlv *a, *b;
1922
1923         a = (ipfw_obj_ntlv *)_a;
1924         b = (ipfw_obj_ntlv *)_b;
1925
1926         if (a->set < b->set)
1927                 return (-1);
1928         else if (a->set > b->set)
1929                 return (1);
1930
1931         if (a->idx < b->idx)
1932                 return (-1);
1933         else if (a->idx > b->idx)
1934                 return (1);
1935
1936         return (0);
1937 }
1938
1939 int
1940 compare_kntlv(const void *k, const void *v)
1941 {
1942         ipfw_obj_ntlv *ntlv;
1943         uint16_t key;
1944
1945         key = *((uint16_t *)k);
1946         ntlv = (ipfw_obj_ntlv *)v;
1947
1948         if (key < ntlv->idx)
1949                 return (-1);
1950         else if (key > ntlv->idx)
1951                 return (1);
1952         
1953         return (0);
1954 }
1955
1956 /*
1957  * Finds table name in @ctlv by @idx.
1958  * Uses the following facts:
1959  * 1) All TLVs are the same size
1960  * 2) Kernel implementation provides already sorted list.
1961  *
1962  * Returns table name or NULL.
1963  */
1964 char *
1965 table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx)
1966 {
1967         ipfw_obj_ntlv *ntlv;
1968
1969         ntlv = bsearch(&idx, (ctlv + 1), ctlv->count, ctlv->objsize,
1970             compare_kntlv);
1971
1972         if (ntlv != 0)
1973                 return (ntlv->name);
1974
1975         return (NULL);
1976 }
1977
1978 void
1979 table_sort_ctlv(ipfw_obj_ctlv *ctlv)
1980 {
1981
1982         qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv);
1983 }
1984
1985 int
1986 table_check_name(char *tablename)
1987 {
1988         int c, i, l;
1989
1990         /*
1991          * Check if tablename is null-terminated and contains
1992          * valid symbols only. Valid mask is:
1993          * [a-zA-Z0-9\-_\.]{1,63}
1994          */
1995         l = strlen(tablename);
1996         if (l == 0 || l >= 64)
1997                 return (EINVAL);
1998         for (i = 0; i < l; i++) {
1999                 c = tablename[i];
2000                 if (isalpha(c) || isdigit(c) || c == '_' ||
2001                     c == '-' || c == '.')
2002                         continue;
2003                 return (EINVAL);        
2004         }
2005
2006         /* Restrict some 'special' names */
2007         if (strcmp(tablename, "all") == 0)
2008                 return (EINVAL);
2009
2010         return (0);
2011 }
2012