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