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