2 * Copyright (c) 2014 Yandex LLC
3 * Copyright (c) 2014 Alexander V. Chernikov
5 * Redistribution and use in source forms, with and without modification,
6 * are permitted provided that this entire comment appears intact.
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.
12 * This software is provided ``AS IS'' without any warranties of any kind.
14 * in-kernel ipfw tables support.
20 #include <sys/types.h>
21 #include <sys/param.h>
22 #include <sys/socket.h>
23 #include <sys/sysctl.h>
34 #include <net/ethernet.h>
36 #include <netinet/in.h>
37 #include <netinet/ip_fw.h>
38 #include <arpa/inet.h>
43 static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
44 int add, int quiet, int update, int atomic);
45 static int table_flush(ipfw_obj_header *oh);
46 static int table_destroy(ipfw_obj_header *oh);
47 static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
48 static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i);
49 static int table_do_swap(ipfw_obj_header *oh, char *second);
50 static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
51 static void table_modify(ipfw_obj_header *oh, int ac, char *av[]);
52 static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]);
53 static void table_lock(ipfw_obj_header *oh, int lock);
54 static int table_swap(ipfw_obj_header *oh, char *second);
55 static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
56 static int table_show_info(ipfw_xtable_info *i, void *arg);
58 static int table_destroy_one(ipfw_xtable_info *i, void *arg);
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);
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);
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);
76 #define s6_addr32 __u6_addr.__u6_addr32
79 static struct _s_x tabletypes[] = {
80 { "addr", IPFW_TABLE_ADDR },
81 { "mac", IPFW_TABLE_MAC },
82 { "iface", IPFW_TABLE_INTERFACE },
83 { "number", IPFW_TABLE_NUMBER },
84 { "flow", IPFW_TABLE_FLOW },
88 /* Default algorithms for various table types */
89 static struct _s_x tablealgos[] = {
90 { "addr:radix", IPFW_TABLE_ADDR },
91 { "flow:hash", IPFW_TABLE_FLOW },
92 { "iface:array", IPFW_TABLE_INTERFACE },
93 { "number:array", IPFW_TABLE_NUMBER },
97 static struct _s_x tablevaltypes[] = {
98 { "skipto", IPFW_VTYPE_SKIPTO },
99 { "pipe", IPFW_VTYPE_PIPE },
100 { "fib", IPFW_VTYPE_FIB },
101 { "nat", IPFW_VTYPE_NAT },
102 { "dscp", IPFW_VTYPE_DSCP },
103 { "tag", IPFW_VTYPE_TAG },
104 { "divert", IPFW_VTYPE_DIVERT },
105 { "netgraph", IPFW_VTYPE_NETGRAPH },
106 { "limit", IPFW_VTYPE_LIMIT },
107 { "ipv4", IPFW_VTYPE_NH4 },
108 { "ipv6", IPFW_VTYPE_NH6 },
109 { "mark", IPFW_VTYPE_MARK },
113 static struct _s_x tablecmds[] = {
115 { "delete", TOK_DEL },
116 { "create", TOK_CREATE },
117 { "destroy", TOK_DESTROY },
118 { "flush", TOK_FLUSH },
119 { "modify", TOK_MODIFY },
120 { "swap", TOK_SWAP },
121 { "info", TOK_INFO },
122 { "detail", TOK_DETAIL },
123 { "list", TOK_LIST },
124 { "lookup", TOK_LOOKUP },
125 { "atomic", TOK_ATOMIC },
126 { "lock", TOK_LOCK },
127 { "unlock", TOK_UNLOCK },
132 lookup_host (char *host, struct in_addr *ipaddr)
136 if (!inet_aton(host, ipaddr)) {
137 if ((he = gethostbyname(host)) == NULL)
139 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
145 * This one handles all table-related commands
146 * ipfw table NAME create ...
147 * ipfw table NAME modify ...
148 * ipfw table {NAME | all} destroy
149 * ipfw table NAME swap NAME
150 * ipfw table NAME lock
151 * ipfw table NAME unlock
152 * ipfw table NAME add addr[/masklen] [value]
153 * ipfw table NAME add [addr[/masklen] value] [addr[/masklen] value] ..
154 * ipfw table NAME delete addr[/masklen] [addr[/masklen]] ..
155 * ipfw table NAME lookup addr
156 * ipfw table {NAME | all} flush
157 * ipfw table {NAME | all} list
158 * ipfw table {NAME | all} info
159 * ipfw table {NAME | all} detail
162 ipfw_table_handler(int ac, char *av[])
165 int atomic, error, tcmd;
172 memset(&oh, 0, sizeof(oh));
174 if (g_co.use_set != 0)
175 set = g_co.use_set - 1;
180 NEED1("table needs name");
183 if (table_check_name(tablename) == 0) {
184 table_fill_ntlv(&oh.ntlv, *av, set, 1);
187 if (strcmp(tablename, "all") == 0)
190 errx(EX_USAGE, "table name %s is invalid", tablename);
193 NEED1("table needs command");
195 tcmd = get_token(tablecmds, *av, "table command");
196 /* Check if atomic operation was requested */
198 if (tcmd == TOK_ATOMIC) {
200 NEED1("atomic needs command");
201 tcmd = get_token(tablecmds, *av, "table command");
206 errx(EX_USAGE, "atomic is not compatible with %s", *av);
220 errx(EX_USAGE, "table name required");
226 do_add = **av == 'a';
228 table_modify_record(&oh, ac, av, do_add, g_co.do_quiet,
229 g_co.do_quiet, atomic);
233 table_create(&oh, ac, av);
237 table_modify(&oh, ac, av);
241 if (table_destroy(&oh) == 0)
244 err(EX_OSERR, "failed to destroy table %s",
246 /* ESRCH isn't fatal, warn if not quiet mode */
247 if (g_co.do_quiet == 0)
248 warn("failed to destroy table %s", tablename);
250 error = tables_foreach(table_destroy_one, &oh, 1);
253 "failed to destroy tables list");
258 if ((error = table_flush(&oh)) == 0)
261 err(EX_OSERR, "failed to flush table %s info",
263 /* ESRCH isn't fatal, warn if not quiet mode */
264 if (g_co.do_quiet == 0)
265 warn("failed to flush table %s info",
268 error = tables_foreach(table_flush_one, &oh, 1);
270 err(EX_OSERR, "failed to flush tables list");
271 /* XXX: we ignore errors here */
276 NEED1("second table name required");
277 table_swap(&oh, *av);
281 table_lock(&oh, (tcmd == TOK_LOCK));
285 arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
287 if ((error = table_get_info(&oh, &i)) != 0)
288 err(EX_OSERR, "failed to request table info");
289 table_show_info(&i, arg);
291 error = tables_foreach(table_show_info, arg, 1);
293 err(EX_OSERR, "failed to request tables list");
297 arg = is_all ? (void*)1 : NULL;
299 if ((error = table_get_info(&oh, &i)) != 0)
300 err(EX_OSERR, "failed to request table info");
301 table_show_one(&i, arg);
303 error = tables_foreach(table_show_one, arg, 1);
305 err(EX_OSERR, "failed to request tables list");
310 table_lookup(&oh, ac, av);
316 table_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set,
320 ntlv->head.type = IPFW_TLV_TBL_NAME;
321 ntlv->head.length = sizeof(ipfw_obj_ntlv);
324 strlcpy(ntlv->name, name, sizeof(ntlv->name));
328 table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
332 table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
335 static struct _s_x tablenewcmds[] = {
336 { "type", TOK_TYPE },
337 { "valtype", TOK_VALTYPE },
338 { "algo", TOK_ALGO },
339 { "limit", TOK_LIMIT },
340 { "locked", TOK_LOCK },
341 { "missing", TOK_MISSING },
342 { "or-flush", TOK_ORFLUSH },
346 static struct _s_x flowtypecmds[] = {
347 { "src-ip", IPFW_TFFLAG_SRCIP },
348 { "proto", IPFW_TFFLAG_PROTO },
349 { "src-port", IPFW_TFFLAG_SRCPORT },
350 { "dst-ip", IPFW_TFFLAG_DSTIP },
351 { "dst-port", IPFW_TFFLAG_DSTPORT },
356 table_parse_type(uint8_t ttype, char *p, uint8_t *tflags)
358 uint32_t fset, fclear;
361 /* Parse type options */
363 case IPFW_TABLE_FLOW:
365 if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0)
367 "unable to parse flow option %s", e);
378 table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags)
383 if ((tname = match_value(tabletypes, type)) == NULL)
386 l = snprintf(tbuf, size, "%s", tname);
391 case IPFW_TABLE_FLOW:
395 print_flags_buffer(tbuf, size, flowtypecmds, tflags);
404 * ipfw table NAME create [ type { addr | iface | number | flow } ]
405 * [ algo algoname ] [missing] [or-flush]
408 table_create(ipfw_obj_header *oh, int ac, char *av[])
410 ipfw_xtable_info xi, xie;
411 int error, missing, orflush, tcmd, val;
412 uint32_t fset, fclear;
416 missing = orflush = 0;
417 memset(&xi, 0, sizeof(xi));
419 tcmd = get_token(tablenewcmds, *av, "option");
424 NEED1("limit value required");
425 xi.limit = strtol(*av, NULL, 10);
429 NEED1("table type required");
430 /* Type may have suboptions after ':' */
431 if ((p = strchr(*av, ':')) != NULL)
433 val = match_token(tabletypes, *av);
435 concat_tokens(tbuf, sizeof(tbuf), tabletypes,
438 "Unknown tabletype: %s. Supported: %s",
443 error = table_parse_type(val, p, &xi.tflags);
446 "Unsupported suboptions: %s", p);
451 NEED1("table value type required");
453 val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear);
459 concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
460 errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
464 NEED1("table algorithm name required");
465 if (strlen(*av) > sizeof(xi.algoname))
466 errx(EX_USAGE, "algorithm name too long");
467 strlcpy(xi.algoname, *av, sizeof(xi.algoname));
471 xi.flags |= IPFW_TGFLAGS_LOCKED;
482 /* Set some defaults to preserve compatibility. */
483 if (xi.algoname[0] == '\0') {
487 xi.type = IPFW_TABLE_ADDR;
488 algo = match_value(tablealgos, xi.type);
490 strlcpy(xi.algoname, algo, sizeof(xi.algoname));
493 xi.vmask = IPFW_VTYPE_LEGACY;
495 error = table_do_create(oh, &xi);
500 if (errno != EEXIST || missing == 0)
501 err(EX_OSERR, "Table creation failed");
503 /* Check that existing table is the same we are trying to create */
504 if (table_get_info(oh, &xie) != 0)
505 err(EX_OSERR, "Existing table check failed");
507 if (xi.limit != xie.limit || xi.type != xie.type ||
508 xi.tflags != xie.tflags || xi.vmask != xie.vmask || (
509 xi.algoname[0] != '\0' && strcmp(xi.algoname,
510 xie.algoname) != 0) || xi.flags != xie.flags)
511 errx(EX_DATAERR, "The existing table is not compatible "
512 "with one you are creating.");
514 /* Flush existing table if instructed to do so */
515 if (orflush != 0 && table_flush(oh) != 0)
516 err(EX_OSERR, "Table flush on creation failed");
522 * Request: [ ipfw_obj_header ipfw_xtable_info ]
524 * Returns 0 on success.
527 table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
529 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
532 memcpy(tbuf, oh, sizeof(*oh));
533 memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
534 oh = (ipfw_obj_header *)tbuf;
536 error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf));
542 * Modifies existing table
544 * ipfw table NAME modify [ limit number ]
547 table_modify(ipfw_obj_header *oh, int ac, char *av[])
552 memset(&xi, 0, sizeof(xi));
555 tcmd = get_token(tablenewcmds, *av, "option");
560 NEED1("limit value required");
561 xi.limit = strtol(*av, NULL, 10);
562 xi.mflags |= IPFW_TMFLAGS_LIMIT;
566 errx(EX_USAGE, "cmd is not supported for modification");
570 if (table_do_modify(oh, &xi) != 0)
571 err(EX_OSERR, "Table modification failed");
575 * Modifies existing table.
577 * Request: [ ipfw_obj_header ipfw_xtable_info ]
579 * Returns 0 on success.
582 table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i)
584 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
587 memcpy(tbuf, oh, sizeof(*oh));
588 memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
589 oh = (ipfw_obj_header *)tbuf;
591 error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf));
597 * Locks or unlocks given table
600 table_lock(ipfw_obj_header *oh, int lock)
604 memset(&xi, 0, sizeof(xi));
606 xi.mflags |= IPFW_TMFLAGS_LOCK;
607 xi.flags |= (lock != 0) ? IPFW_TGFLAGS_LOCKED : 0;
609 if (table_do_modify(oh, &xi) != 0)
610 err(EX_OSERR, "Table %s failed", lock != 0 ? "lock" : "unlock");
614 * Destroys given table specified by @oh->ntlv.
615 * Returns 0 on success.
618 table_destroy(ipfw_obj_header *oh)
621 if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0)
628 table_destroy_one(ipfw_xtable_info *i, void *arg)
632 oh = (ipfw_obj_header *)arg;
633 table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
634 if (table_destroy(oh) != 0) {
635 if (g_co.do_quiet == 0)
636 warn("failed to destroy table(%s) in set %u",
637 i->tablename, i->set);
644 * Flushes given table specified by @oh->ntlv.
645 * Returns 0 on success.
648 table_flush(ipfw_obj_header *oh)
651 if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0)
658 table_do_swap(ipfw_obj_header *oh, char *second)
660 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)];
663 memset(tbuf, 0, sizeof(tbuf));
664 memcpy(tbuf, oh, sizeof(*oh));
665 oh = (ipfw_obj_header *)tbuf;
666 table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1);
668 error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf));
674 * Swaps given table with @second one.
677 table_swap(ipfw_obj_header *oh, char *second)
680 if (table_check_name(second) != 0)
681 errx(EX_USAGE, "table name %s is invalid", second);
683 if (table_do_swap(oh, second) == 0)
688 errx(EX_USAGE, "Unable to swap table: check types");
690 errx(EX_USAGE, "Unable to swap table: check limits");
698 * Retrieves table in given table specified by @oh->ntlv.
700 * Returns 0 on success.
703 table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i)
705 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
709 memset(tbuf, 0, sizeof(tbuf));
710 memcpy(tbuf, oh, sizeof(*oh));
711 oh = (ipfw_obj_header *)tbuf;
713 if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) != 0)
716 if (sz < sizeof(tbuf))
719 *i = *(ipfw_xtable_info *)(oh + 1);
724 static struct _s_x tablealgoclass[] = {
725 { "hash", IPFW_TACLASS_HASH },
726 { "array", IPFW_TACLASS_ARRAY },
727 { "radix", IPFW_TACLASS_RADIX },
741 * Print global/per-AF table @i algorithm info.
744 table_show_tainfo(ipfw_xtable_info *i __unused, struct ta_cldata *d,
745 const char *af, const char *taclass)
748 switch (d->taclass) {
749 case IPFW_TACLASS_HASH:
750 case IPFW_TACLASS_ARRAY:
751 printf(" %salgorithm %s info\n", af, taclass);
752 if (d->itemsize == d->itemsize6)
753 printf(" size: %u items: %u itemsize: %u\n",
754 d->size, d->count, d->itemsize);
756 printf(" size: %u items: %u "
757 "itemsize4: %u itemsize6: %u\n",
759 d->itemsize, d->itemsize6);
761 case IPFW_TACLASS_RADIX:
762 printf(" %salgorithm %s info\n", af, taclass);
763 if (d->itemsize == d->itemsize6)
764 printf(" items: %u itemsize: %u\n",
765 d->count, d->itemsize);
768 "itemsize4: %u itemsize6: %u\n",
769 d->count, d->itemsize, d->itemsize6);
772 printf(" algo class: %s\n", taclass);
777 table_print_valheader(char *buf, size_t bufsize, uint32_t vmask)
780 if (vmask == IPFW_VTYPE_LEGACY) {
781 snprintf(buf, bufsize, "legacy");
785 memset(buf, 0, bufsize);
786 print_flags_buffer(buf, bufsize, tablevaltypes, vmask);
790 * Prints table info struct @i in human-readable form.
793 table_show_info(ipfw_xtable_info *i, void *arg)
796 ipfw_ta_tinfo *tainfo;
799 char ttype[64], tvtype[64];
801 table_print_type(ttype, sizeof(ttype), i->type, i->tflags);
802 table_print_valheader(tvtype, sizeof(tvtype), i->vmask);
804 printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
805 if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0)
806 printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype);
808 printf(" kindex: %d, type: %s\n", i->kidx, ttype);
809 printf(" references: %u, valtype: %s\n", i->refcnt, tvtype);
810 printf(" algorithm: %s\n", i->algoname);
811 printf(" items: %u, size: %u\n", i->count, i->size);
813 printf(" limit: %u\n", i->limit);
815 /* Print algo-specific info if requested & set */
819 if ((i->ta_info.flags & IPFW_TATFLAGS_DATA) == 0)
821 tainfo = &i->ta_info;
825 if (tainfo->flags & IPFW_TATFLAGS_AFDATA)
827 if (tainfo->flags & IPFW_TATFLAGS_AFITEM)
830 memset(&d, 0, sizeof(d));
831 d.taclass = tainfo->taclass4;
832 d.size = tainfo->size4;
833 d.count = tainfo->count4;
834 d.itemsize = tainfo->itemsize4;
835 if (afdata == 0 && afitem != 0)
836 d.itemsize6 = tainfo->itemsize6;
838 d.itemsize6 = d.itemsize;
839 if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
843 table_show_tainfo(i, &d, "", vtype);
845 table_show_tainfo(i, &d, "IPv4 ", vtype);
846 memset(&d, 0, sizeof(d));
847 d.taclass = tainfo->taclass6;
848 if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
850 d.size = tainfo->size6;
851 d.count = tainfo->count6;
852 d.itemsize = tainfo->itemsize6;
853 d.itemsize6 = d.itemsize;
854 table_show_tainfo(i, &d, "IPv6 ", vtype);
862 * Function wrappers which can be used either
863 * as is or as foreach function parameter.
867 table_show_one(ipfw_xtable_info *i, void *arg)
869 ipfw_obj_header *oh = NULL;
873 is_all = arg == NULL ? 0 : 1;
875 if ((error = table_do_get_list(i, &oh)) != 0) {
876 err(EX_OSERR, "Error requesting table %s list", i->tablename);
880 table_show_list(oh, is_all);
887 table_flush_one(ipfw_xtable_info *i, void *arg)
891 oh = (ipfw_obj_header *)arg;
893 table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
895 return (table_flush(oh));
899 table_do_modify_record(int cmd, ipfw_obj_header *oh,
900 ipfw_obj_tentry *tent, int count, int atomic)
903 ipfw_obj_tentry *tent_base;
905 char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)];
909 sz = sizeof(*ctlv) + sizeof(*tent) * count;
911 memset(xbuf, 0, sizeof(xbuf));
914 if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL)
918 memcpy(pbuf, oh, sizeof(*oh));
919 oh = (ipfw_obj_header *)pbuf;
920 oh->opheader.version = 1; /* Current version */
922 ctlv = (ipfw_obj_ctlv *)(oh + 1);
924 ctlv->head.length = sz;
926 ctlv->flags |= IPFW_CTF_ATOMIC;
929 memcpy(ctlv + 1, tent, sizeof(*tent) * count);
930 tent = (ipfw_obj_tentry *)(ctlv + 1);
931 for (i = 0; i < count; i++, tent++) {
932 tent->head.length = sizeof(ipfw_obj_tentry);
937 error = do_get3(cmd, &oh->opheader, &sz);
940 tent = (ipfw_obj_tentry *)(ctlv + 1);
941 /* Copy result back to provided buffer */
942 memcpy(tent_base, ctlv + 1, sizeof(*tent) * count);
951 table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
952 int quiet, int update, int atomic)
954 ipfw_obj_tentry *ptent, tent, *tent_buf;
956 const char *etxt, *px, *texterr;
959 int cmd, count, error, i, ignored;
962 errx(EX_USAGE, "address required");
965 cmd = IP_FW_TABLE_XADD;
966 texterr = "Adding record failed";
968 cmd = IP_FW_TABLE_XDEL;
969 texterr = "Deleting record failed";
973 * Calculate number of entries:
974 * Assume [key val] x N for add
978 count = (add != 0) ? ac / 2 + 1 : ac;
981 /* Adding single entry with/without value */
982 memset(&tent, 0, sizeof(tent));
986 if ((tent_buf = calloc(count, sizeof(tent))) == NULL)
988 "Unable to allocate memory for all entries");
992 memset(&xi, 0, sizeof(xi));
995 tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi);
998 * Compatibility layer: auto-create table if not exists.
1000 if (xi.tablename[0] == '\0') {
1003 strlcpy(xi.tablename, oh->ntlv.name,
1004 sizeof(xi.tablename));
1006 warnx("DEPRECATED: inserting data into "
1007 "non-existent table %s. (auto-created)",
1009 table_do_create(oh, &xi);
1012 oh->ntlv.type = type;
1015 if (add != 0 && ac > 0) {
1016 tentry_fill_value(oh, ptent, *av, type, vmask);
1021 ptent->head.flags |= IPFW_TF_UPDATE;
1027 error = table_do_modify_record(cmd, oh, tent_buf, count, atomic);
1030 * Compatibility stuff: do not yell on duplicate keys or
1033 if (error == 0 || (error == EEXIST && add != 0) ||
1034 (error == ENOENT && add == 0)) {
1036 if (tent_buf != &tent)
1042 /* Report results back */
1044 for (i = 0; i < count; ptent++, i++) {
1046 switch (ptent->result) {
1050 case IPFW_TR_DELETED:
1053 case IPFW_TR_UPDATED:
1064 case IPFW_TR_NOTFOUND:
1068 case IPFW_TR_EXISTS:
1072 case IPFW_TR_IGNORED:
1081 if (error != 0 && atomic != 0 && ignored == 0)
1082 printf("%s(reverted): ", px);
1086 table_show_entry(&xi, ptent);
1089 if (tent_buf != &tent)
1094 /* Get real OS error */
1097 /* Try to provide more human-readable error */
1100 etxt = "record already exists";
1106 etxt = "table not found";
1109 etxt = "record not found";
1112 etxt = "table is locked";
1115 etxt = strerror(error);
1118 errx(EX_OSERR, "%s: %s", texterr, etxt);
1122 table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi,
1123 ipfw_obj_tentry *xtent)
1125 char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
1126 ipfw_obj_tentry *tent;
1131 memcpy(xbuf, oh, sizeof(*oh));
1132 oh = (ipfw_obj_header *)xbuf;
1133 tent = (ipfw_obj_tentry *)(oh + 1);
1135 memset(tent, 0, sizeof(*tent));
1136 tent->head.length = sizeof(*tent);
1139 tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi);
1140 oh->ntlv.type = type;
1143 if (do_get3(IP_FW_TABLE_XFIND, &oh->opheader, &sz) != 0)
1146 if (sz < sizeof(xbuf))
1155 table_lookup(ipfw_obj_header *oh, int ac, char *av[])
1157 ipfw_obj_tentry xtent;
1158 ipfw_xtable_info xi;
1163 errx(EX_USAGE, "address required");
1165 strlcpy(key, *av, sizeof(key));
1167 memset(&xi, 0, sizeof(xi));
1168 error = table_do_lookup(oh, key, &xi, &xtent);
1174 errx(EX_UNAVAILABLE, "Table %s not found", oh->ntlv.name);
1176 errx(EX_UNAVAILABLE, "Entry %s not found", *av);
1178 errx(EX_UNAVAILABLE, "Table %s algo does not support "
1179 "\"lookup\" method", oh->ntlv.name);
1181 err(EX_OSERR, "getsockopt(IP_FW_TABLE_XFIND)");
1184 table_show_entry(&xi, &xtent);
1188 tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
1193 struct in6_addr *paddr, tmp;
1194 struct ether_addr *mac;
1195 struct tflow_entry *tfe;
1196 uint32_t key, *pkey;
1198 struct protoent *pent;
1199 struct servent *sent;
1204 paddr = (struct in6_addr *)&tentry->k;
1207 case IPFW_TABLE_ADDR:
1208 /* Remove / if exists */
1209 if ((p = strchr(arg, '/')) != NULL) {
1214 if (inet_pton(AF_INET, arg, paddr) == 1) {
1215 if (p != NULL && mask > 32)
1216 errx(EX_DATAERR, "bad IPv4 mask width: %s",
1219 masklen = p ? mask : 32;
1221 } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
1222 if (IN6_IS_ADDR_V4COMPAT(paddr))
1224 "Use IPv4 instead of v4-compatible");
1225 if (p != NULL && mask > 128)
1226 errx(EX_DATAERR, "bad IPv6 mask width: %s",
1229 masklen = p ? mask : 128;
1233 if (lookup_host(arg, (struct in_addr *)paddr) != 0)
1234 errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
1237 type = IPFW_TABLE_ADDR;
1241 case IPFW_TABLE_MAC:
1242 /* Remove / if exists */
1243 if ((p = strchr(arg, '/')) != NULL) {
1248 if (p != NULL && mask > 8 * ETHER_ADDR_LEN)
1249 errx(EX_DATAERR, "bad MAC mask width: %s",
1252 if ((mac = ether_aton(arg)) == NULL)
1253 errx(EX_DATAERR, "Incorrect MAC address");
1255 memcpy(tentry->k.mac, mac->octet, ETHER_ADDR_LEN);
1256 masklen = p ? mask : 8 * ETHER_ADDR_LEN;
1259 case IPFW_TABLE_INTERFACE:
1260 /* Assume interface name. Copy significant data only */
1261 mask = MIN(strlen(arg), IF_NAMESIZE - 1);
1262 memcpy(paddr, arg, mask);
1263 /* Set mask to exact match */
1264 masklen = 8 * IF_NAMESIZE;
1266 case IPFW_TABLE_NUMBER:
1267 /* Port or any other key */
1268 key = strtol(arg, &p, 10);
1270 errx(EX_DATAERR, "Invalid number: %s", arg);
1272 pkey = (uint32_t *)paddr;
1276 case IPFW_TABLE_FLOW:
1277 /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */
1278 tfe = &tentry->k.flow;
1281 /* Handle <ipv4|ipv6> */
1282 if ((tflags & IPFW_TFFLAG_SRCIP) != 0) {
1283 if ((p = strchr(arg, ',')) != NULL)
1285 /* Determine family using temporary storage */
1286 if (inet_pton(AF_INET, arg, &tmp) == 1) {
1287 if (af != 0 && af != AF_INET)
1289 "Inconsistent address family\n");
1291 memcpy(&tfe->a.a4.sip, &tmp, 4);
1292 } else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
1293 if (af != 0 && af != AF_INET6)
1295 "Inconsistent address family\n");
1297 memcpy(&tfe->a.a6.sip6, &tmp, 16);
1303 /* Handle <proto-num|proto-name> */
1304 if ((tflags & IPFW_TFFLAG_PROTO) != 0) {
1306 errx(EX_DATAERR, "invalid key: proto missing");
1307 if ((p = strchr(arg, ',')) != NULL)
1310 key = strtol(arg, &pp, 10);
1312 if ((pent = getprotobyname(arg)) == NULL)
1313 errx(EX_DATAERR, "Unknown proto: %s",
1316 key = pent->p_proto;
1320 errx(EX_DATAERR, "Bad protocol number: %u",key);
1327 /* Handle <port-num|service-name> */
1328 if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) {
1330 errx(EX_DATAERR, "invalid key: src port missing");
1331 if ((p = strchr(arg, ',')) != NULL)
1334 port = htons(strtol(arg, &pp, 10));
1336 if ((sent = getservbyname(arg, NULL)) == NULL)
1337 errx(EX_DATAERR, "Unknown service: %s",
1339 port = sent->s_port;
1345 /* Handle <ipv4|ipv6>*/
1346 if ((tflags & IPFW_TFFLAG_DSTIP) != 0) {
1348 errx(EX_DATAERR, "invalid key: dst ip missing");
1349 if ((p = strchr(arg, ',')) != NULL)
1351 /* Determine family using temporary storage */
1352 if (inet_pton(AF_INET, arg, &tmp) == 1) {
1353 if (af != 0 && af != AF_INET)
1355 "Inconsistent address family");
1357 memcpy(&tfe->a.a4.dip, &tmp, 4);
1358 } else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
1359 if (af != 0 && af != AF_INET6)
1361 "Inconsistent address family");
1363 memcpy(&tfe->a.a6.dip6, &tmp, 16);
1369 /* Handle <port-num|service-name> */
1370 if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) {
1372 errx(EX_DATAERR, "invalid key: dst port missing");
1373 if ((p = strchr(arg, ',')) != NULL)
1376 port = htons(strtol(arg, &pp, 10));
1378 if ((sent = getservbyname(arg, NULL)) == NULL)
1379 errx(EX_DATAERR, "Unknown service: %s",
1381 port = sent->s_port;
1392 errx(EX_DATAERR, "Unsupported table type: %d", type);
1395 tentry->subtype = af;
1396 tentry->masklen = masklen;
1400 * Tries to guess table key type.
1401 * This procedure is used in legacy table auto-create
1402 * code AND in `ipfw -n` ruleset checking.
1404 * Imported from old table_fill_xentry() parse code.
1407 guess_key_type(char *key, uint8_t *ptype)
1410 struct in6_addr addr;
1413 if (ishexnumber(*key) != 0 || *key == ':') {
1414 /* Remove / if exists */
1415 if ((p = strchr(key, '/')) != NULL)
1418 if ((inet_pton(AF_INET, key, &addr) == 1) ||
1419 (inet_pton(AF_INET6, key, &addr) == 1)) {
1420 *ptype = IPFW_TABLE_CIDR;
1425 /* Port or any other key */
1426 /* Skip non-base 10 entries like 'fa1' */
1427 kv = strtol(key, &p, 10);
1429 *ptype = IPFW_TABLE_NUMBER;
1431 } else if ((p != key) && (*p == '.')) {
1433 * Warn on IPv4 address strings
1434 * which are "valid" for inet_aton() but not
1437 * Typical examples: '10.5' or '10.0.0.05'
1444 if (strchr(key, '.') == NULL) {
1445 *ptype = IPFW_TABLE_INTERFACE;
1449 if (lookup_host(key, (struct in_addr *)&addr) != 0)
1452 *ptype = IPFW_TABLE_CIDR;
1457 tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
1458 int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi)
1460 uint8_t type, tflags;
1468 if (xi->tablename[0] == '\0')
1469 error = table_get_info(oh, xi);
1474 if (g_co.test_only == 0) {
1477 tflags = xi->tflags;
1481 * We're running `ipfw -n`
1482 * Compatibility layer: try to guess key type
1485 if (guess_key_type(key, &type) != 0) {
1487 errx(EX_USAGE, "Cannot guess "
1488 "key '%s' type", key);
1490 vmask = IPFW_VTYPE_LEGACY;
1494 errx(EX_OSERR, "Error requesting table %s info",
1497 errx(EX_DATAERR, "Table %s does not exist",
1500 * Table does not exist
1501 * Compatibility layer: try to guess key type before failing.
1503 if (guess_key_type(key, &type) != 0) {
1505 errx(EX_USAGE, "Table %s does not exist, cannot guess "
1506 "key '%s' type", oh->ntlv.name, key);
1509 vmask = IPFW_VTYPE_LEGACY;
1512 tentry_fill_key_type(key, tent, type, tflags);
1519 set_legacy_value(uint32_t val, ipfw_table_value *v)
1529 v->dscp = (uint8_t)val;
1534 tentry_fill_value(ipfw_obj_header *oh __unused, ipfw_obj_tentry *tent,
1535 char *arg, uint8_t type __unused, uint32_t vmask)
1537 struct addrinfo hints, *res;
1538 struct in_addr ipaddr;
1540 char *comma, *e, *n, *p;
1541 uint32_t a4, flag, val;
1542 ipfw_table_value *v;
1548 /* Compat layer: keep old behavior for legacy value types */
1549 if (vmask == IPFW_VTYPE_LEGACY) {
1550 /* Try to interpret as number first */
1551 val = strtoul(arg, &p, 0);
1553 set_legacy_value(val, v);
1556 if (inet_pton(AF_INET, arg, &val) == 1) {
1557 set_legacy_value(ntohl(val), v);
1561 if (lookup_host(arg, &ipaddr) == 0) {
1562 set_legacy_value(ntohl(ipaddr.s_addr), v);
1565 errx(EX_OSERR, "Unable to parse value %s", arg);
1569 * Shorthands: handle single value if vmask consists
1570 * of numbers only. e.g.:
1571 * vmask = "fib,skipto" -> treat input "1" as "1,1"
1576 for (i = 1; i < (1u << 31); i *= 2) {
1577 if ((flag = (vmask & i)) == 0)
1581 if ((comma = strchr(n, ',')) != NULL)
1585 case IPFW_VTYPE_TAG:
1586 v->tag = strtol(n, &e, 10);
1590 case IPFW_VTYPE_PIPE:
1591 v->pipe = strtol(n, &e, 10);
1595 case IPFW_VTYPE_DIVERT:
1596 v->divert = strtol(n, &e, 10);
1600 case IPFW_VTYPE_SKIPTO:
1601 v->skipto = strtol(n, &e, 10);
1605 case IPFW_VTYPE_NETGRAPH:
1606 v->netgraph = strtol(n, &e, 10);
1610 case IPFW_VTYPE_FIB:
1611 v->fib = strtol(n, &e, 10);
1615 case IPFW_VTYPE_NAT:
1616 v->nat = strtol(n, &e, 10);
1620 case IPFW_VTYPE_LIMIT:
1621 v->limit = strtol(n, &e, 10);
1625 case IPFW_VTYPE_NH4:
1626 if (strchr(n, '.') != NULL &&
1627 inet_pton(AF_INET, n, &a4) == 1) {
1631 if (lookup_host(n, &ipaddr) == 0) {
1632 v->nh4 = ntohl(ipaddr.s_addr);
1637 case IPFW_VTYPE_DSCP:
1639 if ((dval = match_token(f_ipdscp, n)) != -1) {
1643 etype = "DSCP code";
1645 v->dscp = strtol(n, &e, 10);
1646 if (v->dscp > 63 || *e != '\0')
1647 etype = "DSCP value";
1650 case IPFW_VTYPE_NH6:
1651 if (strchr(n, ':') != NULL) {
1652 memset(&hints, 0, sizeof(hints));
1653 hints.ai_family = AF_INET6;
1654 hints.ai_flags = AI_NUMERICHOST;
1655 if (getaddrinfo(n, NULL, &hints, &res) == 0) {
1656 v->nh6 = ((struct sockaddr_in6 *)
1657 res->ai_addr)->sin6_addr;
1658 v->zoneid = ((struct sockaddr_in6 *)
1659 res->ai_addr)->sin6_scope_id;
1666 case IPFW_VTYPE_MARK:
1667 v->mark = strtol(n, &e, 16);
1674 errx(EX_USAGE, "Unable to parse %s as %s", n, etype);
1679 if ((n = comma) != NULL)
1684 errx(EX_USAGE, "Not enough fields inside value");
1689 * Compare table names.
1690 * Honor number comparison.
1693 tablename_cmp(const void *a, const void *b)
1695 const ipfw_xtable_info *ia, *ib;
1697 ia = (const ipfw_xtable_info *)a;
1698 ib = (const ipfw_xtable_info *)b;
1700 return (stringnum_cmp(ia->tablename, ib->tablename));
1704 * Retrieves table list from kernel,
1705 * optionally sorts it and calls requested function for each table.
1706 * Returns 0 on success.
1709 tables_foreach(table_cb_t *f, void *arg, int sort)
1711 ipfw_obj_lheader *olh;
1712 ipfw_xtable_info *info;
1717 /* Start with reasonable default */
1718 sz = sizeof(*olh) + 16 * sizeof(ipfw_xtable_info);
1721 if ((olh = calloc(1, sz)) == NULL)
1725 if (do_get3(IP_FW_TABLES_XLIST, &olh->opheader, &sz) != 0) {
1728 if (errno != ENOMEM)
1734 qsort(olh + 1, olh->count, olh->objsize,
1737 info = (ipfw_xtable_info *)(olh + 1);
1738 for (i = 0; i < olh->count; i++) {
1739 if (g_co.use_set == 0 || info->set == g_co.use_set - 1)
1740 error = f(info, arg);
1741 info = (ipfw_xtable_info *)((caddr_t)info +
1752 * Retrieves all entries for given table @i in
1753 * eXtended format. Allocate buffer large enough
1754 * to store result. Called needs to free it later.
1756 * Returns 0 on success.
1759 table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh)
1761 ipfw_obj_header *oh;
1767 for (c = 0; c < 8; c++) {
1772 if ((oh = calloc(1, sz)) == NULL)
1774 table_fill_objheader(oh, i);
1775 oh->opheader.version = 1; /* Current version */
1776 if (do_get3(IP_FW_TABLE_XLIST, &oh->opheader, &sz) == 0) {
1781 if (errno != ENOMEM)
1790 * Shows all entries from @oh in human-readable format
1793 table_show_list(ipfw_obj_header *oh, int need_header)
1795 ipfw_obj_tentry *tent;
1797 ipfw_xtable_info *i;
1799 i = (ipfw_xtable_info *)(oh + 1);
1800 tent = (ipfw_obj_tentry *)(i + 1);
1803 printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
1807 table_show_entry(i, tent);
1808 tent = (ipfw_obj_tentry *)((caddr_t)tent + tent->head.length);
1814 table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
1815 uint32_t vmask, int print_ip)
1817 char abuf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2];
1818 struct sockaddr_in6 sa6;
1819 uint32_t flag, i, l;
1826 * Some shorthands for printing values:
1827 * legacy assumes all values are equal, so keep the first one.
1829 if (vmask == IPFW_VTYPE_LEGACY) {
1830 if (print_ip != 0) {
1831 flag = htonl(v->tag);
1832 inet_ntop(AF_INET, &flag, buf, sz);
1834 snprintf(buf, sz, "%u", v->tag);
1838 for (i = 1; i < (1u << 31); i *= 2) {
1839 if ((flag = (vmask & i)) == 0)
1844 case IPFW_VTYPE_TAG:
1845 l = snprintf(buf, sz, "%u,", v->tag);
1847 case IPFW_VTYPE_PIPE:
1848 l = snprintf(buf, sz, "%u,", v->pipe);
1850 case IPFW_VTYPE_DIVERT:
1851 l = snprintf(buf, sz, "%d,", v->divert);
1853 case IPFW_VTYPE_SKIPTO:
1854 l = snprintf(buf, sz, "%d,", v->skipto);
1856 case IPFW_VTYPE_NETGRAPH:
1857 l = snprintf(buf, sz, "%u,", v->netgraph);
1859 case IPFW_VTYPE_FIB:
1860 l = snprintf(buf, sz, "%u,", v->fib);
1862 case IPFW_VTYPE_NAT:
1863 l = snprintf(buf, sz, "%u,", v->nat);
1865 case IPFW_VTYPE_LIMIT:
1866 l = snprintf(buf, sz, "%u,", v->limit);
1868 case IPFW_VTYPE_NH4:
1869 a4.s_addr = htonl(v->nh4);
1870 inet_ntop(AF_INET, &a4, abuf, sizeof(abuf));
1871 l = snprintf(buf, sz, "%s,", abuf);
1873 case IPFW_VTYPE_DSCP:
1874 l = snprintf(buf, sz, "%d,", v->dscp);
1876 case IPFW_VTYPE_NH6:
1877 sa6.sin6_family = AF_INET6;
1878 sa6.sin6_len = sizeof(sa6);
1879 sa6.sin6_addr = v->nh6;
1881 sa6.sin6_scope_id = v->zoneid;
1882 if (getnameinfo((const struct sockaddr *)&sa6,
1883 sa6.sin6_len, abuf, sizeof(abuf), NULL, 0,
1884 NI_NUMERICHOST) == 0)
1885 l = snprintf(buf, sz, "%s,", abuf);
1887 case IPFW_VTYPE_MARK:
1888 l = snprintf(buf, sz, "%#x,", v->mark);
1901 table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
1903 char tbuf[128], pval[128];
1907 struct tflow_entry *tfe;
1909 table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask,
1910 g_co.do_value_as_ip);
1913 case IPFW_TABLE_ADDR:
1914 /* IPv4 or IPv6 prefixes */
1915 inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
1916 printf("%s/%u %s\n", tbuf, tent->masklen, pval);
1918 case IPFW_TABLE_MAC:
1921 printf("%02x:%02x:%02x:%02x:%02x:%02x/%u %s\n",
1922 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
1923 tent->masklen, pval);
1925 case IPFW_TABLE_INTERFACE:
1926 /* Interface names */
1927 printf("%s %s\n", tent->k.iface, pval);
1929 case IPFW_TABLE_NUMBER:
1931 printf("%u %s\n", tent->k.key, pval);
1933 case IPFW_TABLE_FLOW:
1935 tfe = &tent->k.flow;
1938 if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) {
1939 if (tfe->af == AF_INET)
1940 paddr = &tfe->a.a4.sip;
1942 paddr = &tfe->a.a6.sip6;
1944 inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
1945 printf("%s%s", comma, tbuf);
1949 if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) {
1950 printf("%s%d", comma, tfe->proto);
1954 if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) {
1955 printf("%s%d", comma, ntohs(tfe->sport));
1958 if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) {
1959 if (tfe->af == AF_INET)
1960 paddr = &tfe->a.a4.dip;
1962 paddr = &tfe->a.a6.dip6;
1964 inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
1965 printf("%s%s", comma, tbuf);
1969 if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) {
1970 printf("%s%d", comma, ntohs(tfe->dport));
1974 printf(" %s\n", pval);
1979 table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh)
1981 ipfw_obj_lheader req, *olh;
1984 memset(&req, 0, sizeof(req));
1987 if (do_get3(opcode, &req.opheader, &sz) != 0)
1988 if (errno != ENOMEM)
1992 if ((olh = calloc(1, sz)) == NULL)
1996 if (do_get3(opcode, &olh->opheader, &sz) != 0) {
2006 table_do_get_algolist(ipfw_obj_lheader **polh)
2009 return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh));
2013 table_do_get_vlist(ipfw_obj_lheader **polh)
2016 return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh));
2020 ipfw_list_ta(int ac __unused, char *av[] __unused)
2022 ipfw_obj_lheader *olh;
2028 error = table_do_get_algolist(&olh);
2030 err(EX_OSERR, "Unable to request algorithm list");
2032 info = (ipfw_ta_info *)(olh + 1);
2033 for (i = 0; i < olh->count; i++) {
2034 if ((atype = match_value(tabletypes, info->type)) == NULL)
2036 printf("--- %s ---\n", info->algoname);
2037 printf(" type: %s\n refcount: %u\n", atype, info->refcnt);
2039 info = (ipfw_ta_info *)((caddr_t)info + olh->objsize);
2047 compare_values(const void *_a, const void *_b)
2049 const ipfw_table_value *a, *b;
2051 a = (const ipfw_table_value *)_a;
2052 b = (const ipfw_table_value *)_b;
2054 if (a->kidx < b->kidx)
2056 else if (a->kidx > b->kidx)
2063 ipfw_list_values(int ac __unused, char *av[] __unused)
2066 ipfw_obj_lheader *olh;
2067 ipfw_table_value *v;
2071 error = table_do_get_vlist(&olh);
2073 err(EX_OSERR, "Unable to request value list");
2075 vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */
2077 table_print_valheader(buf, sizeof(buf), vmask);
2078 printf("HEADER: %s\n", buf);
2079 v = (ipfw_table_value *)(olh + 1);
2080 qsort(v, olh->count, olh->objsize, compare_values);
2081 for (i = 0; i < olh->count; i++) {
2082 table_show_value(buf, sizeof(buf), (ipfw_table_value *)v,
2084 printf("[%u] refs=%lu %s\n", v->kidx, (u_long)v->refcnt, buf);
2085 v = (ipfw_table_value *)((caddr_t)v + olh->objsize);
2092 table_check_name(const char *tablename)
2095 if (ipfw_check_object_name(tablename) != 0)
2097 /* Restrict some 'special' names */
2098 if (strcmp(tablename, "all") == 0)