1127dd473Swhl739 /*
2127dd473Swhl739 * Copyright (c) 2014 Yandex LLC
3127dd473Swhl739 * Copyright (c) 2014 Alexander V. Chernikov
4127dd473Swhl739 *
5127dd473Swhl739 * Redistribution and use in source forms, with and without modification,
6127dd473Swhl739 * are permitted provided that this entire comment appears intact.
7127dd473Swhl739 *
8127dd473Swhl739 * Redistribution in binary form may occur without any restrictions.
9127dd473Swhl739 * Obviously, it would be nice if you gave credit where credit is due
10127dd473Swhl739 * but requiring it would be too onerous.
11127dd473Swhl739 *
12127dd473Swhl739 * This software is provided ``AS IS'' without any warranties of any kind.
13127dd473Swhl739 *
14127dd473Swhl739 * in-kernel ipfw tables support.
15127dd473Swhl739 *
16127dd473Swhl739 * $FreeBSD$
17127dd473Swhl739 */
18127dd473Swhl739
19127dd473Swhl739
20127dd473Swhl739 #include <sys/types.h>
21127dd473Swhl739 #include <sys/param.h>
22127dd473Swhl739 #include <sys/socket.h>
23127dd473Swhl739 #include <sys/sysctl.h>
24127dd473Swhl739
25127dd473Swhl739 #include <ctype.h>
26127dd473Swhl739 #include <err.h>
27127dd473Swhl739 #include <errno.h>
28127dd473Swhl739 #include <netdb.h>
29127dd473Swhl739 #include <stdio.h>
30127dd473Swhl739 #include <stdlib.h>
31127dd473Swhl739 #include <string.h>
32127dd473Swhl739 #include <sysexits.h>
33127dd473Swhl739
34127dd473Swhl739 #include <net/if.h>
35127dd473Swhl739 #include <netinet/in.h>
36127dd473Swhl739 #include <netinet/ip_fw.h>
37127dd473Swhl739 #include <arpa/inet.h>
38127dd473Swhl739 #include <netdb.h>
39127dd473Swhl739
40127dd473Swhl739 #include "ipfw2.h"
41127dd473Swhl739
42*d4a07e70Sfengbojiang #ifdef FSTACK
43*d4a07e70Sfengbojiang #ifndef __unused
44*d4a07e70Sfengbojiang #define __unused __attribute__((__unused__))
45*d4a07e70Sfengbojiang #endif
46*d4a07e70Sfengbojiang #endif
47*d4a07e70Sfengbojiang
48127dd473Swhl739 static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
49127dd473Swhl739 int add, int quiet, int update, int atomic);
50127dd473Swhl739 static int table_flush(ipfw_obj_header *oh);
51127dd473Swhl739 static int table_destroy(ipfw_obj_header *oh);
52127dd473Swhl739 static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
53127dd473Swhl739 static int table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i);
54127dd473Swhl739 static int table_do_swap(ipfw_obj_header *oh, char *second);
55127dd473Swhl739 static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
56127dd473Swhl739 static void table_modify(ipfw_obj_header *oh, int ac, char *av[]);
57127dd473Swhl739 static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]);
58127dd473Swhl739 static void table_lock(ipfw_obj_header *oh, int lock);
59127dd473Swhl739 static int table_swap(ipfw_obj_header *oh, char *second);
60127dd473Swhl739 static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
61127dd473Swhl739 static int table_show_info(ipfw_xtable_info *i, void *arg);
62127dd473Swhl739
6322ce4affSfengbojiang static int table_destroy_one(ipfw_xtable_info *i, void *arg);
64127dd473Swhl739 static int table_flush_one(ipfw_xtable_info *i, void *arg);
65127dd473Swhl739 static int table_show_one(ipfw_xtable_info *i, void *arg);
66127dd473Swhl739 static int table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh);
67127dd473Swhl739 static void table_show_list(ipfw_obj_header *oh, int need_header);
68127dd473Swhl739 static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent);
69127dd473Swhl739
70127dd473Swhl739 static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
71127dd473Swhl739 char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi);
72127dd473Swhl739 static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
73127dd473Swhl739 char *arg, uint8_t type, uint32_t vmask);
74127dd473Swhl739 static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
75127dd473Swhl739 uint32_t vmask, int print_ip);
76127dd473Swhl739
77127dd473Swhl739 typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
78127dd473Swhl739 static int tables_foreach(table_cb_t *f, void *arg, int sort);
79127dd473Swhl739
80127dd473Swhl739 #ifndef s6_addr32
81127dd473Swhl739 #define s6_addr32 __u6_addr.__u6_addr32
82127dd473Swhl739 #endif
83127dd473Swhl739
84127dd473Swhl739 static struct _s_x tabletypes[] = {
85127dd473Swhl739 { "addr", IPFW_TABLE_ADDR },
86127dd473Swhl739 { "iface", IPFW_TABLE_INTERFACE },
87127dd473Swhl739 { "number", IPFW_TABLE_NUMBER },
88127dd473Swhl739 { "flow", IPFW_TABLE_FLOW },
89127dd473Swhl739 { NULL, 0 }
90127dd473Swhl739 };
91127dd473Swhl739
92127dd473Swhl739 static struct _s_x tablevaltypes[] = {
93127dd473Swhl739 { "skipto", IPFW_VTYPE_SKIPTO },
94127dd473Swhl739 { "pipe", IPFW_VTYPE_PIPE },
95127dd473Swhl739 { "fib", IPFW_VTYPE_FIB },
96127dd473Swhl739 { "nat", IPFW_VTYPE_NAT },
97127dd473Swhl739 { "dscp", IPFW_VTYPE_DSCP },
98127dd473Swhl739 { "tag", IPFW_VTYPE_TAG },
99127dd473Swhl739 { "divert", IPFW_VTYPE_DIVERT },
100127dd473Swhl739 { "netgraph", IPFW_VTYPE_NETGRAPH },
101127dd473Swhl739 { "limit", IPFW_VTYPE_LIMIT },
102127dd473Swhl739 { "ipv4", IPFW_VTYPE_NH4 },
103127dd473Swhl739 { "ipv6", IPFW_VTYPE_NH6 },
104127dd473Swhl739 { NULL, 0 }
105127dd473Swhl739 };
106127dd473Swhl739
107127dd473Swhl739 static struct _s_x tablecmds[] = {
108127dd473Swhl739 { "add", TOK_ADD },
109127dd473Swhl739 { "delete", TOK_DEL },
110127dd473Swhl739 { "create", TOK_CREATE },
111127dd473Swhl739 { "destroy", TOK_DESTROY },
112127dd473Swhl739 { "flush", TOK_FLUSH },
113127dd473Swhl739 { "modify", TOK_MODIFY },
114127dd473Swhl739 { "swap", TOK_SWAP },
115127dd473Swhl739 { "info", TOK_INFO },
116127dd473Swhl739 { "detail", TOK_DETAIL },
117127dd473Swhl739 { "list", TOK_LIST },
118127dd473Swhl739 { "lookup", TOK_LOOKUP },
119127dd473Swhl739 { "atomic", TOK_ATOMIC },
120127dd473Swhl739 { "lock", TOK_LOCK },
121127dd473Swhl739 { "unlock", TOK_UNLOCK },
122127dd473Swhl739 { NULL, 0 }
123127dd473Swhl739 };
124127dd473Swhl739
125127dd473Swhl739 static int
lookup_host(char * host,struct in_addr * ipaddr)126127dd473Swhl739 lookup_host (char *host, struct in_addr *ipaddr)
127127dd473Swhl739 {
128127dd473Swhl739 struct hostent *he;
129127dd473Swhl739
130127dd473Swhl739 if (!inet_aton(host, ipaddr)) {
131*d4a07e70Sfengbojiang #ifndef FSTACK
132127dd473Swhl739 if ((he = gethostbyname(host)) == NULL)
133127dd473Swhl739 return(-1);
134127dd473Swhl739 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
135*d4a07e70Sfengbojiang #else
136*d4a07e70Sfengbojiang return (-1);
137*d4a07e70Sfengbojiang #endif
138127dd473Swhl739 }
139127dd473Swhl739 return(0);
140127dd473Swhl739 }
141127dd473Swhl739
142127dd473Swhl739 /*
143127dd473Swhl739 * This one handles all table-related commands
144127dd473Swhl739 * ipfw table NAME create ...
145127dd473Swhl739 * ipfw table NAME modify ...
14622ce4affSfengbojiang * ipfw table {NAME | all} destroy
147127dd473Swhl739 * ipfw table NAME swap NAME
148127dd473Swhl739 * ipfw table NAME lock
149127dd473Swhl739 * ipfw table NAME unlock
150127dd473Swhl739 * ipfw table NAME add addr[/masklen] [value]
151127dd473Swhl739 * ipfw table NAME add [addr[/masklen] value] [addr[/masklen] value] ..
152127dd473Swhl739 * ipfw table NAME delete addr[/masklen] [addr[/masklen]] ..
153127dd473Swhl739 * ipfw table NAME lookup addr
154127dd473Swhl739 * ipfw table {NAME | all} flush
155127dd473Swhl739 * ipfw table {NAME | all} list
156127dd473Swhl739 * ipfw table {NAME | all} info
157127dd473Swhl739 * ipfw table {NAME | all} detail
158127dd473Swhl739 */
159127dd473Swhl739 void
ipfw_table_handler(int ac,char * av[])160127dd473Swhl739 ipfw_table_handler(int ac, char *av[])
161127dd473Swhl739 {
162127dd473Swhl739 int do_add, is_all;
163127dd473Swhl739 int atomic, error, tcmd;
164127dd473Swhl739 ipfw_xtable_info i;
165127dd473Swhl739 ipfw_obj_header oh;
166127dd473Swhl739 char *tablename;
16722ce4affSfengbojiang uint8_t set;
168127dd473Swhl739 void *arg;
169127dd473Swhl739
170127dd473Swhl739 memset(&oh, 0, sizeof(oh));
171127dd473Swhl739 is_all = 0;
17222ce4affSfengbojiang if (g_co.use_set != 0)
17322ce4affSfengbojiang set = g_co.use_set - 1;
174127dd473Swhl739 else
175127dd473Swhl739 set = 0;
176127dd473Swhl739
177127dd473Swhl739 ac--; av++;
178127dd473Swhl739 NEED1("table needs name");
179127dd473Swhl739 tablename = *av;
180127dd473Swhl739
181127dd473Swhl739 if (table_check_name(tablename) == 0) {
182127dd473Swhl739 table_fill_ntlv(&oh.ntlv, *av, set, 1);
183127dd473Swhl739 oh.idx = 1;
184127dd473Swhl739 } else {
185127dd473Swhl739 if (strcmp(tablename, "all") == 0)
186127dd473Swhl739 is_all = 1;
187127dd473Swhl739 else
188127dd473Swhl739 errx(EX_USAGE, "table name %s is invalid", tablename);
189127dd473Swhl739 }
190127dd473Swhl739 ac--; av++;
191127dd473Swhl739 NEED1("table needs command");
192127dd473Swhl739
193127dd473Swhl739 tcmd = get_token(tablecmds, *av, "table command");
194127dd473Swhl739 /* Check if atomic operation was requested */
195127dd473Swhl739 atomic = 0;
196127dd473Swhl739 if (tcmd == TOK_ATOMIC) {
197127dd473Swhl739 ac--; av++;
198127dd473Swhl739 NEED1("atomic needs command");
199127dd473Swhl739 tcmd = get_token(tablecmds, *av, "table command");
200127dd473Swhl739 switch (tcmd) {
201127dd473Swhl739 case TOK_ADD:
202127dd473Swhl739 break;
203127dd473Swhl739 default:
204127dd473Swhl739 errx(EX_USAGE, "atomic is not compatible with %s", *av);
205127dd473Swhl739 }
206127dd473Swhl739 atomic = 1;
207127dd473Swhl739 }
208127dd473Swhl739
209127dd473Swhl739 switch (tcmd) {
210127dd473Swhl739 case TOK_LIST:
211127dd473Swhl739 case TOK_INFO:
212127dd473Swhl739 case TOK_DETAIL:
213127dd473Swhl739 case TOK_FLUSH:
21422ce4affSfengbojiang case TOK_DESTROY:
215127dd473Swhl739 break;
216127dd473Swhl739 default:
217127dd473Swhl739 if (is_all != 0)
218127dd473Swhl739 errx(EX_USAGE, "table name required");
219127dd473Swhl739 }
220127dd473Swhl739
221127dd473Swhl739 switch (tcmd) {
222127dd473Swhl739 case TOK_ADD:
223127dd473Swhl739 case TOK_DEL:
224127dd473Swhl739 do_add = **av == 'a';
225127dd473Swhl739 ac--; av++;
22622ce4affSfengbojiang table_modify_record(&oh, ac, av, do_add, g_co.do_quiet,
22722ce4affSfengbojiang g_co.do_quiet, atomic);
228127dd473Swhl739 break;
229127dd473Swhl739 case TOK_CREATE:
230127dd473Swhl739 ac--; av++;
231127dd473Swhl739 table_create(&oh, ac, av);
232127dd473Swhl739 break;
233127dd473Swhl739 case TOK_MODIFY:
234127dd473Swhl739 ac--; av++;
235127dd473Swhl739 table_modify(&oh, ac, av);
236127dd473Swhl739 break;
237127dd473Swhl739 case TOK_DESTROY:
23822ce4affSfengbojiang if (is_all == 0) {
239127dd473Swhl739 if (table_destroy(&oh) == 0)
240127dd473Swhl739 break;
241127dd473Swhl739 if (errno != ESRCH)
24222ce4affSfengbojiang err(EX_OSERR, "failed to destroy table %s",
24322ce4affSfengbojiang tablename);
244127dd473Swhl739 /* ESRCH isn't fatal, warn if not quiet mode */
24522ce4affSfengbojiang if (g_co.do_quiet == 0)
246127dd473Swhl739 warn("failed to destroy table %s", tablename);
24722ce4affSfengbojiang } else {
24822ce4affSfengbojiang error = tables_foreach(table_destroy_one, &oh, 1);
24922ce4affSfengbojiang if (error != 0)
25022ce4affSfengbojiang err(EX_OSERR,
25122ce4affSfengbojiang "failed to destroy tables list");
25222ce4affSfengbojiang }
253127dd473Swhl739 break;
254127dd473Swhl739 case TOK_FLUSH:
255127dd473Swhl739 if (is_all == 0) {
256127dd473Swhl739 if ((error = table_flush(&oh)) == 0)
257127dd473Swhl739 break;
258127dd473Swhl739 if (errno != ESRCH)
259127dd473Swhl739 err(EX_OSERR, "failed to flush table %s info",
260127dd473Swhl739 tablename);
261127dd473Swhl739 /* ESRCH isn't fatal, warn if not quiet mode */
26222ce4affSfengbojiang if (g_co.do_quiet == 0)
263127dd473Swhl739 warn("failed to flush table %s info",
264127dd473Swhl739 tablename);
265127dd473Swhl739 } else {
266127dd473Swhl739 error = tables_foreach(table_flush_one, &oh, 1);
267127dd473Swhl739 if (error != 0)
268127dd473Swhl739 err(EX_OSERR, "failed to flush tables list");
269127dd473Swhl739 /* XXX: we ignore errors here */
270127dd473Swhl739 }
271127dd473Swhl739 break;
272127dd473Swhl739 case TOK_SWAP:
273127dd473Swhl739 ac--; av++;
274127dd473Swhl739 NEED1("second table name required");
275127dd473Swhl739 table_swap(&oh, *av);
276127dd473Swhl739 break;
277127dd473Swhl739 case TOK_LOCK:
278127dd473Swhl739 case TOK_UNLOCK:
279127dd473Swhl739 table_lock(&oh, (tcmd == TOK_LOCK));
280127dd473Swhl739 break;
281127dd473Swhl739 case TOK_DETAIL:
282127dd473Swhl739 case TOK_INFO:
283127dd473Swhl739 arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
284127dd473Swhl739 if (is_all == 0) {
285127dd473Swhl739 if ((error = table_get_info(&oh, &i)) != 0)
286127dd473Swhl739 err(EX_OSERR, "failed to request table info");
287127dd473Swhl739 table_show_info(&i, arg);
288127dd473Swhl739 } else {
289127dd473Swhl739 error = tables_foreach(table_show_info, arg, 1);
290127dd473Swhl739 if (error != 0)
291127dd473Swhl739 err(EX_OSERR, "failed to request tables list");
292127dd473Swhl739 }
293127dd473Swhl739 break;
294127dd473Swhl739 case TOK_LIST:
29522ce4affSfengbojiang arg = is_all ? (void*)1 : NULL;
296127dd473Swhl739 if (is_all == 0) {
297127dd473Swhl739 if ((error = table_get_info(&oh, &i)) != 0)
298127dd473Swhl739 err(EX_OSERR, "failed to request table info");
29922ce4affSfengbojiang table_show_one(&i, arg);
300127dd473Swhl739 } else {
30122ce4affSfengbojiang error = tables_foreach(table_show_one, arg, 1);
302127dd473Swhl739 if (error != 0)
303127dd473Swhl739 err(EX_OSERR, "failed to request tables list");
304127dd473Swhl739 }
305127dd473Swhl739 break;
306127dd473Swhl739 case TOK_LOOKUP:
307127dd473Swhl739 ac--; av++;
308127dd473Swhl739 table_lookup(&oh, ac, av);
309127dd473Swhl739 break;
310127dd473Swhl739 }
311127dd473Swhl739 }
312127dd473Swhl739
31322ce4affSfengbojiang void
table_fill_ntlv(ipfw_obj_ntlv * ntlv,const char * name,uint8_t set,uint16_t uidx)31422ce4affSfengbojiang table_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set,
315127dd473Swhl739 uint16_t uidx)
316127dd473Swhl739 {
317127dd473Swhl739
318127dd473Swhl739 ntlv->head.type = IPFW_TLV_TBL_NAME;
319127dd473Swhl739 ntlv->head.length = sizeof(ipfw_obj_ntlv);
320127dd473Swhl739 ntlv->idx = uidx;
321127dd473Swhl739 ntlv->set = set;
322127dd473Swhl739 strlcpy(ntlv->name, name, sizeof(ntlv->name));
323127dd473Swhl739 }
324127dd473Swhl739
325127dd473Swhl739 static void
table_fill_objheader(ipfw_obj_header * oh,ipfw_xtable_info * i)326127dd473Swhl739 table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
327127dd473Swhl739 {
328127dd473Swhl739
329127dd473Swhl739 oh->idx = 1;
330127dd473Swhl739 table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
331127dd473Swhl739 }
332127dd473Swhl739
333127dd473Swhl739 static struct _s_x tablenewcmds[] = {
334127dd473Swhl739 { "type", TOK_TYPE },
335127dd473Swhl739 { "valtype", TOK_VALTYPE },
336127dd473Swhl739 { "algo", TOK_ALGO },
337127dd473Swhl739 { "limit", TOK_LIMIT },
338127dd473Swhl739 { "locked", TOK_LOCK },
33922ce4affSfengbojiang { "missing", TOK_MISSING },
34022ce4affSfengbojiang { "or-flush", TOK_ORFLUSH },
341127dd473Swhl739 { NULL, 0 }
342127dd473Swhl739 };
343127dd473Swhl739
344127dd473Swhl739 static struct _s_x flowtypecmds[] = {
345127dd473Swhl739 { "src-ip", IPFW_TFFLAG_SRCIP },
346127dd473Swhl739 { "proto", IPFW_TFFLAG_PROTO },
347127dd473Swhl739 { "src-port", IPFW_TFFLAG_SRCPORT },
348127dd473Swhl739 { "dst-ip", IPFW_TFFLAG_DSTIP },
349127dd473Swhl739 { "dst-port", IPFW_TFFLAG_DSTPORT },
350127dd473Swhl739 { NULL, 0 }
351127dd473Swhl739 };
352127dd473Swhl739
35322ce4affSfengbojiang static int
table_parse_type(uint8_t ttype,char * p,uint8_t * tflags)354127dd473Swhl739 table_parse_type(uint8_t ttype, char *p, uint8_t *tflags)
355127dd473Swhl739 {
356127dd473Swhl739 uint32_t fset, fclear;
357127dd473Swhl739 char *e;
358127dd473Swhl739
359127dd473Swhl739 /* Parse type options */
360127dd473Swhl739 switch(ttype) {
361127dd473Swhl739 case IPFW_TABLE_FLOW:
362127dd473Swhl739 fset = fclear = 0;
363127dd473Swhl739 if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0)
364127dd473Swhl739 errx(EX_USAGE,
365127dd473Swhl739 "unable to parse flow option %s", e);
366127dd473Swhl739 *tflags = fset;
367127dd473Swhl739 break;
368127dd473Swhl739 default:
369127dd473Swhl739 return (EX_USAGE);
370127dd473Swhl739 }
371127dd473Swhl739
372127dd473Swhl739 return (0);
373127dd473Swhl739 }
374127dd473Swhl739
37522ce4affSfengbojiang static void
table_print_type(char * tbuf,size_t size,uint8_t type,uint8_t tflags)376127dd473Swhl739 table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags)
377127dd473Swhl739 {
378127dd473Swhl739 const char *tname;
379127dd473Swhl739 int l;
380127dd473Swhl739
381127dd473Swhl739 if ((tname = match_value(tabletypes, type)) == NULL)
382127dd473Swhl739 tname = "unknown";
383127dd473Swhl739
384127dd473Swhl739 l = snprintf(tbuf, size, "%s", tname);
385127dd473Swhl739 tbuf += l;
386127dd473Swhl739 size -= l;
387127dd473Swhl739
388127dd473Swhl739 switch(type) {
389127dd473Swhl739 case IPFW_TABLE_FLOW:
390127dd473Swhl739 if (tflags != 0) {
391127dd473Swhl739 *tbuf++ = ':';
392127dd473Swhl739 l--;
393127dd473Swhl739 print_flags_buffer(tbuf, size, flowtypecmds, tflags);
394127dd473Swhl739 }
395127dd473Swhl739 break;
396127dd473Swhl739 }
397127dd473Swhl739 }
398127dd473Swhl739
399127dd473Swhl739 /*
400127dd473Swhl739 * Creates new table
401127dd473Swhl739 *
402127dd473Swhl739 * ipfw table NAME create [ type { addr | iface | number | flow } ]
40322ce4affSfengbojiang * [ algo algoname ] [missing] [or-flush]
404127dd473Swhl739 */
405127dd473Swhl739 static void
table_create(ipfw_obj_header * oh,int ac,char * av[])406127dd473Swhl739 table_create(ipfw_obj_header *oh, int ac, char *av[])
407127dd473Swhl739 {
40822ce4affSfengbojiang ipfw_xtable_info xi, xie;
40922ce4affSfengbojiang int error, missing, orflush, tcmd, val;
410127dd473Swhl739 uint32_t fset, fclear;
411127dd473Swhl739 char *e, *p;
412127dd473Swhl739 char tbuf[128];
413127dd473Swhl739
41422ce4affSfengbojiang missing = orflush = 0;
415127dd473Swhl739 memset(&xi, 0, sizeof(xi));
416127dd473Swhl739 while (ac > 0) {
417127dd473Swhl739 tcmd = get_token(tablenewcmds, *av, "option");
418127dd473Swhl739 ac--; av++;
419127dd473Swhl739
420127dd473Swhl739 switch (tcmd) {
421127dd473Swhl739 case TOK_LIMIT:
422127dd473Swhl739 NEED1("limit value required");
423127dd473Swhl739 xi.limit = strtol(*av, NULL, 10);
424127dd473Swhl739 ac--; av++;
425127dd473Swhl739 break;
426127dd473Swhl739 case TOK_TYPE:
427127dd473Swhl739 NEED1("table type required");
428127dd473Swhl739 /* Type may have suboptions after ':' */
429127dd473Swhl739 if ((p = strchr(*av, ':')) != NULL)
430127dd473Swhl739 *p++ = '\0';
431127dd473Swhl739 val = match_token(tabletypes, *av);
432127dd473Swhl739 if (val == -1) {
433127dd473Swhl739 concat_tokens(tbuf, sizeof(tbuf), tabletypes,
434127dd473Swhl739 ", ");
435127dd473Swhl739 errx(EX_USAGE,
436127dd473Swhl739 "Unknown tabletype: %s. Supported: %s",
437127dd473Swhl739 *av, tbuf);
438127dd473Swhl739 }
439127dd473Swhl739 xi.type = val;
440127dd473Swhl739 if (p != NULL) {
441127dd473Swhl739 error = table_parse_type(val, p, &xi.tflags);
442127dd473Swhl739 if (error != 0)
443127dd473Swhl739 errx(EX_USAGE,
444127dd473Swhl739 "Unsupported suboptions: %s", p);
445127dd473Swhl739 }
446127dd473Swhl739 ac--; av++;
447127dd473Swhl739 break;
448127dd473Swhl739 case TOK_VALTYPE:
449127dd473Swhl739 NEED1("table value type required");
450127dd473Swhl739 fset = fclear = 0;
451127dd473Swhl739 val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear);
452127dd473Swhl739 if (val != -1) {
453127dd473Swhl739 xi.vmask = fset;
454127dd473Swhl739 ac--; av++;
455127dd473Swhl739 break;
456127dd473Swhl739 }
457127dd473Swhl739 concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
458127dd473Swhl739 errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
459127dd473Swhl739 e, tbuf);
460127dd473Swhl739 break;
461127dd473Swhl739 case TOK_ALGO:
462127dd473Swhl739 NEED1("table algorithm name required");
463127dd473Swhl739 if (strlen(*av) > sizeof(xi.algoname))
464127dd473Swhl739 errx(EX_USAGE, "algorithm name too long");
465127dd473Swhl739 strlcpy(xi.algoname, *av, sizeof(xi.algoname));
466127dd473Swhl739 ac--; av++;
467127dd473Swhl739 break;
468127dd473Swhl739 case TOK_LOCK:
469127dd473Swhl739 xi.flags |= IPFW_TGFLAGS_LOCKED;
470127dd473Swhl739 break;
47122ce4affSfengbojiang case TOK_ORFLUSH:
47222ce4affSfengbojiang orflush = 1;
47322ce4affSfengbojiang /* FALLTHROUGH */
47422ce4affSfengbojiang case TOK_MISSING:
47522ce4affSfengbojiang missing = 1;
47622ce4affSfengbojiang break;
477127dd473Swhl739 }
478127dd473Swhl739 }
479127dd473Swhl739
480127dd473Swhl739 /* Set some defaults to preserve compatibility. */
481127dd473Swhl739 if (xi.algoname[0] == '\0' && xi.type == 0)
482127dd473Swhl739 xi.type = IPFW_TABLE_ADDR;
483127dd473Swhl739 if (xi.vmask == 0)
484127dd473Swhl739 xi.vmask = IPFW_VTYPE_LEGACY;
485127dd473Swhl739
48622ce4affSfengbojiang error = table_do_create(oh, &xi);
48722ce4affSfengbojiang
48822ce4affSfengbojiang if (error == 0)
48922ce4affSfengbojiang return;
49022ce4affSfengbojiang
49122ce4affSfengbojiang if (errno != EEXIST || missing == 0)
492127dd473Swhl739 err(EX_OSERR, "Table creation failed");
49322ce4affSfengbojiang
49422ce4affSfengbojiang /* Check that existing table is the same we are trying to create */
49522ce4affSfengbojiang if (table_get_info(oh, &xie) != 0)
49622ce4affSfengbojiang err(EX_OSERR, "Existing table check failed");
49722ce4affSfengbojiang
49822ce4affSfengbojiang if (xi.limit != xie.limit || xi.type != xie.type ||
49922ce4affSfengbojiang xi.tflags != xie.tflags || xi.vmask != xie.vmask || (
50022ce4affSfengbojiang xi.algoname[0] != '\0' && strcmp(xi.algoname,
50122ce4affSfengbojiang xie.algoname) != 0) || xi.flags != xie.flags)
50222ce4affSfengbojiang errx(EX_DATAERR, "The existing table is not compatible "
50322ce4affSfengbojiang "with one you are creating.");
50422ce4affSfengbojiang
50522ce4affSfengbojiang /* Flush existing table if instructed to do so */
50622ce4affSfengbojiang if (orflush != 0 && table_flush(oh) != 0)
50722ce4affSfengbojiang err(EX_OSERR, "Table flush on creation failed");
508127dd473Swhl739 }
509127dd473Swhl739
510127dd473Swhl739 /*
511127dd473Swhl739 * Creates new table
512127dd473Swhl739 *
513127dd473Swhl739 * Request: [ ipfw_obj_header ipfw_xtable_info ]
514127dd473Swhl739 *
515127dd473Swhl739 * Returns 0 on success.
516127dd473Swhl739 */
517127dd473Swhl739 static int
table_do_create(ipfw_obj_header * oh,ipfw_xtable_info * i)518127dd473Swhl739 table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
519127dd473Swhl739 {
520127dd473Swhl739 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
521127dd473Swhl739 int error;
522127dd473Swhl739
523127dd473Swhl739 memcpy(tbuf, oh, sizeof(*oh));
524127dd473Swhl739 memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
525127dd473Swhl739 oh = (ipfw_obj_header *)tbuf;
526127dd473Swhl739
527127dd473Swhl739 error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf));
528127dd473Swhl739
529127dd473Swhl739 return (error);
530127dd473Swhl739 }
531127dd473Swhl739
532127dd473Swhl739 /*
533127dd473Swhl739 * Modifies existing table
534127dd473Swhl739 *
535127dd473Swhl739 * ipfw table NAME modify [ limit number ]
536127dd473Swhl739 */
537127dd473Swhl739 static void
table_modify(ipfw_obj_header * oh,int ac,char * av[])538127dd473Swhl739 table_modify(ipfw_obj_header *oh, int ac, char *av[])
539127dd473Swhl739 {
540127dd473Swhl739 ipfw_xtable_info xi;
541127dd473Swhl739 int tcmd;
542127dd473Swhl739
543127dd473Swhl739 memset(&xi, 0, sizeof(xi));
544127dd473Swhl739
545127dd473Swhl739 while (ac > 0) {
546127dd473Swhl739 tcmd = get_token(tablenewcmds, *av, "option");
547127dd473Swhl739 ac--; av++;
548127dd473Swhl739
549127dd473Swhl739 switch (tcmd) {
550127dd473Swhl739 case TOK_LIMIT:
551127dd473Swhl739 NEED1("limit value required");
552127dd473Swhl739 xi.limit = strtol(*av, NULL, 10);
553127dd473Swhl739 xi.mflags |= IPFW_TMFLAGS_LIMIT;
554127dd473Swhl739 ac--; av++;
555127dd473Swhl739 break;
556127dd473Swhl739 default:
55722ce4affSfengbojiang errx(EX_USAGE, "cmd is not supported for modification");
558127dd473Swhl739 }
559127dd473Swhl739 }
560127dd473Swhl739
561127dd473Swhl739 if (table_do_modify(oh, &xi) != 0)
562127dd473Swhl739 err(EX_OSERR, "Table modification failed");
563127dd473Swhl739 }
564127dd473Swhl739
565127dd473Swhl739 /*
566127dd473Swhl739 * Modifies existing table.
567127dd473Swhl739 *
568127dd473Swhl739 * Request: [ ipfw_obj_header ipfw_xtable_info ]
569127dd473Swhl739 *
570127dd473Swhl739 * Returns 0 on success.
571127dd473Swhl739 */
572127dd473Swhl739 static int
table_do_modify(ipfw_obj_header * oh,ipfw_xtable_info * i)573127dd473Swhl739 table_do_modify(ipfw_obj_header *oh, ipfw_xtable_info *i)
574127dd473Swhl739 {
575127dd473Swhl739 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
576127dd473Swhl739 int error;
577127dd473Swhl739
578127dd473Swhl739 memcpy(tbuf, oh, sizeof(*oh));
579127dd473Swhl739 memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
580127dd473Swhl739 oh = (ipfw_obj_header *)tbuf;
581127dd473Swhl739
582127dd473Swhl739 error = do_set3(IP_FW_TABLE_XMODIFY, &oh->opheader, sizeof(tbuf));
583127dd473Swhl739
584127dd473Swhl739 return (error);
585127dd473Swhl739 }
586127dd473Swhl739
587127dd473Swhl739 /*
588127dd473Swhl739 * Locks or unlocks given table
589127dd473Swhl739 */
590127dd473Swhl739 static void
table_lock(ipfw_obj_header * oh,int lock)591127dd473Swhl739 table_lock(ipfw_obj_header *oh, int lock)
592127dd473Swhl739 {
593127dd473Swhl739 ipfw_xtable_info xi;
594127dd473Swhl739
595127dd473Swhl739 memset(&xi, 0, sizeof(xi));
596127dd473Swhl739
597127dd473Swhl739 xi.mflags |= IPFW_TMFLAGS_LOCK;
598127dd473Swhl739 xi.flags |= (lock != 0) ? IPFW_TGFLAGS_LOCKED : 0;
599127dd473Swhl739
600127dd473Swhl739 if (table_do_modify(oh, &xi) != 0)
601127dd473Swhl739 err(EX_OSERR, "Table %s failed", lock != 0 ? "lock" : "unlock");
602127dd473Swhl739 }
603127dd473Swhl739
604127dd473Swhl739 /*
605127dd473Swhl739 * Destroys given table specified by @oh->ntlv.
606127dd473Swhl739 * Returns 0 on success.
607127dd473Swhl739 */
608127dd473Swhl739 static int
table_destroy(ipfw_obj_header * oh)609127dd473Swhl739 table_destroy(ipfw_obj_header *oh)
610127dd473Swhl739 {
611127dd473Swhl739
612127dd473Swhl739 if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0)
613127dd473Swhl739 return (-1);
614127dd473Swhl739
615127dd473Swhl739 return (0);
616127dd473Swhl739 }
617127dd473Swhl739
61822ce4affSfengbojiang static int
table_destroy_one(ipfw_xtable_info * i,void * arg)61922ce4affSfengbojiang table_destroy_one(ipfw_xtable_info *i, void *arg)
62022ce4affSfengbojiang {
62122ce4affSfengbojiang ipfw_obj_header *oh;
62222ce4affSfengbojiang
62322ce4affSfengbojiang oh = (ipfw_obj_header *)arg;
62422ce4affSfengbojiang table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
62522ce4affSfengbojiang if (table_destroy(oh) != 0) {
62622ce4affSfengbojiang if (g_co.do_quiet == 0)
62722ce4affSfengbojiang warn("failed to destroy table(%s) in set %u",
62822ce4affSfengbojiang i->tablename, i->set);
62922ce4affSfengbojiang return (-1);
63022ce4affSfengbojiang }
63122ce4affSfengbojiang return (0);
63222ce4affSfengbojiang }
63322ce4affSfengbojiang
634127dd473Swhl739 /*
635127dd473Swhl739 * Flushes given table specified by @oh->ntlv.
636127dd473Swhl739 * Returns 0 on success.
637127dd473Swhl739 */
638127dd473Swhl739 static int
table_flush(ipfw_obj_header * oh)639127dd473Swhl739 table_flush(ipfw_obj_header *oh)
640127dd473Swhl739 {
641127dd473Swhl739
642127dd473Swhl739 if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0)
643127dd473Swhl739 return (-1);
644127dd473Swhl739
645127dd473Swhl739 return (0);
646127dd473Swhl739 }
647127dd473Swhl739
648127dd473Swhl739 static int
table_do_swap(ipfw_obj_header * oh,char * second)649127dd473Swhl739 table_do_swap(ipfw_obj_header *oh, char *second)
650127dd473Swhl739 {
651127dd473Swhl739 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)];
652127dd473Swhl739 int error;
653127dd473Swhl739
654127dd473Swhl739 memset(tbuf, 0, sizeof(tbuf));
655127dd473Swhl739 memcpy(tbuf, oh, sizeof(*oh));
656127dd473Swhl739 oh = (ipfw_obj_header *)tbuf;
657127dd473Swhl739 table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1);
658127dd473Swhl739
659127dd473Swhl739 error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf));
660127dd473Swhl739
661127dd473Swhl739 return (error);
662127dd473Swhl739 }
663127dd473Swhl739
664127dd473Swhl739 /*
665127dd473Swhl739 * Swaps given table with @second one.
666127dd473Swhl739 */
667127dd473Swhl739 static int
table_swap(ipfw_obj_header * oh,char * second)668127dd473Swhl739 table_swap(ipfw_obj_header *oh, char *second)
669127dd473Swhl739 {
670127dd473Swhl739
671127dd473Swhl739 if (table_check_name(second) != 0)
672127dd473Swhl739 errx(EX_USAGE, "table name %s is invalid", second);
673127dd473Swhl739
674127dd473Swhl739 if (table_do_swap(oh, second) == 0)
675127dd473Swhl739 return (0);
676127dd473Swhl739
677127dd473Swhl739 switch (errno) {
678127dd473Swhl739 case EINVAL:
679127dd473Swhl739 errx(EX_USAGE, "Unable to swap table: check types");
680127dd473Swhl739 case EFBIG:
681127dd473Swhl739 errx(EX_USAGE, "Unable to swap table: check limits");
682127dd473Swhl739 }
683127dd473Swhl739
684127dd473Swhl739 return (0);
685127dd473Swhl739 }
686127dd473Swhl739
687127dd473Swhl739
688127dd473Swhl739 /*
689127dd473Swhl739 * Retrieves table in given table specified by @oh->ntlv.
690127dd473Swhl739 * it inside @i.
691127dd473Swhl739 * Returns 0 on success.
692127dd473Swhl739 */
693127dd473Swhl739 static int
table_get_info(ipfw_obj_header * oh,ipfw_xtable_info * i)694127dd473Swhl739 table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i)
695127dd473Swhl739 {
696127dd473Swhl739 char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
697127dd473Swhl739 size_t sz;
698127dd473Swhl739
699127dd473Swhl739 sz = sizeof(tbuf);
700127dd473Swhl739 memset(tbuf, 0, sizeof(tbuf));
701127dd473Swhl739 memcpy(tbuf, oh, sizeof(*oh));
702127dd473Swhl739 oh = (ipfw_obj_header *)tbuf;
703127dd473Swhl739
704127dd473Swhl739 if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) != 0)
705127dd473Swhl739 return (errno);
706127dd473Swhl739
707127dd473Swhl739 if (sz < sizeof(tbuf))
708127dd473Swhl739 return (EINVAL);
709127dd473Swhl739
710127dd473Swhl739 *i = *(ipfw_xtable_info *)(oh + 1);
711127dd473Swhl739
712127dd473Swhl739 return (0);
713127dd473Swhl739 }
714127dd473Swhl739
715127dd473Swhl739 static struct _s_x tablealgoclass[] = {
716127dd473Swhl739 { "hash", IPFW_TACLASS_HASH },
717127dd473Swhl739 { "array", IPFW_TACLASS_ARRAY },
718127dd473Swhl739 { "radix", IPFW_TACLASS_RADIX },
719127dd473Swhl739 { NULL, 0 }
720127dd473Swhl739 };
721127dd473Swhl739
722127dd473Swhl739 struct ta_cldata {
723127dd473Swhl739 uint8_t taclass;
724127dd473Swhl739 uint8_t spare4;
725127dd473Swhl739 uint16_t itemsize;
726127dd473Swhl739 uint16_t itemsize6;
727127dd473Swhl739 uint32_t size;
728127dd473Swhl739 uint32_t count;
729127dd473Swhl739 };
730127dd473Swhl739
731127dd473Swhl739 /*
732127dd473Swhl739 * Print global/per-AF table @i algorithm info.
733127dd473Swhl739 */
734127dd473Swhl739 static void
table_show_tainfo(ipfw_xtable_info * i __unused,struct ta_cldata * d,const char * af,const char * taclass)73522ce4affSfengbojiang table_show_tainfo(ipfw_xtable_info *i __unused, struct ta_cldata *d,
736127dd473Swhl739 const char *af, const char *taclass)
737127dd473Swhl739 {
738127dd473Swhl739
739127dd473Swhl739 switch (d->taclass) {
740127dd473Swhl739 case IPFW_TACLASS_HASH:
741127dd473Swhl739 case IPFW_TACLASS_ARRAY:
742127dd473Swhl739 printf(" %salgorithm %s info\n", af, taclass);
743127dd473Swhl739 if (d->itemsize == d->itemsize6)
744127dd473Swhl739 printf(" size: %u items: %u itemsize: %u\n",
745127dd473Swhl739 d->size, d->count, d->itemsize);
746127dd473Swhl739 else
747127dd473Swhl739 printf(" size: %u items: %u "
748127dd473Swhl739 "itemsize4: %u itemsize6: %u\n",
749127dd473Swhl739 d->size, d->count,
750127dd473Swhl739 d->itemsize, d->itemsize6);
751127dd473Swhl739 break;
752127dd473Swhl739 case IPFW_TACLASS_RADIX:
753127dd473Swhl739 printf(" %salgorithm %s info\n", af, taclass);
754127dd473Swhl739 if (d->itemsize == d->itemsize6)
755127dd473Swhl739 printf(" items: %u itemsize: %u\n",
756127dd473Swhl739 d->count, d->itemsize);
757127dd473Swhl739 else
758127dd473Swhl739 printf(" items: %u "
759127dd473Swhl739 "itemsize4: %u itemsize6: %u\n",
760127dd473Swhl739 d->count, d->itemsize, d->itemsize6);
761127dd473Swhl739 break;
762127dd473Swhl739 default:
763127dd473Swhl739 printf(" algo class: %s\n", taclass);
764127dd473Swhl739 }
765127dd473Swhl739 }
766127dd473Swhl739
767127dd473Swhl739 static void
table_print_valheader(char * buf,size_t bufsize,uint32_t vmask)768127dd473Swhl739 table_print_valheader(char *buf, size_t bufsize, uint32_t vmask)
769127dd473Swhl739 {
770127dd473Swhl739
771127dd473Swhl739 if (vmask == IPFW_VTYPE_LEGACY) {
772127dd473Swhl739 snprintf(buf, bufsize, "legacy");
773127dd473Swhl739 return;
774127dd473Swhl739 }
775127dd473Swhl739
776127dd473Swhl739 memset(buf, 0, bufsize);
777127dd473Swhl739 print_flags_buffer(buf, bufsize, tablevaltypes, vmask);
778127dd473Swhl739 }
779127dd473Swhl739
780127dd473Swhl739 /*
781127dd473Swhl739 * Prints table info struct @i in human-readable form.
782127dd473Swhl739 */
783127dd473Swhl739 static int
table_show_info(ipfw_xtable_info * i,void * arg)784127dd473Swhl739 table_show_info(ipfw_xtable_info *i, void *arg)
785127dd473Swhl739 {
786127dd473Swhl739 const char *vtype;
787127dd473Swhl739 ipfw_ta_tinfo *tainfo;
788127dd473Swhl739 int afdata, afitem;
789127dd473Swhl739 struct ta_cldata d;
790127dd473Swhl739 char ttype[64], tvtype[64];
791127dd473Swhl739
792127dd473Swhl739 table_print_type(ttype, sizeof(ttype), i->type, i->tflags);
793127dd473Swhl739 table_print_valheader(tvtype, sizeof(tvtype), i->vmask);
794127dd473Swhl739
795127dd473Swhl739 printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
796127dd473Swhl739 if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0)
797127dd473Swhl739 printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype);
798127dd473Swhl739 else
799127dd473Swhl739 printf(" kindex: %d, type: %s\n", i->kidx, ttype);
800127dd473Swhl739 printf(" references: %u, valtype: %s\n", i->refcnt, tvtype);
801127dd473Swhl739 printf(" algorithm: %s\n", i->algoname);
802127dd473Swhl739 printf(" items: %u, size: %u\n", i->count, i->size);
803127dd473Swhl739 if (i->limit > 0)
804127dd473Swhl739 printf(" limit: %u\n", i->limit);
805127dd473Swhl739
806127dd473Swhl739 /* Print algo-specific info if requested & set */
807127dd473Swhl739 if (arg == NULL)
808127dd473Swhl739 return (0);
809127dd473Swhl739
810127dd473Swhl739 if ((i->ta_info.flags & IPFW_TATFLAGS_DATA) == 0)
811127dd473Swhl739 return (0);
812127dd473Swhl739 tainfo = &i->ta_info;
813127dd473Swhl739
814127dd473Swhl739 afdata = 0;
815127dd473Swhl739 afitem = 0;
816127dd473Swhl739 if (tainfo->flags & IPFW_TATFLAGS_AFDATA)
817127dd473Swhl739 afdata = 1;
818127dd473Swhl739 if (tainfo->flags & IPFW_TATFLAGS_AFITEM)
819127dd473Swhl739 afitem = 1;
820127dd473Swhl739
821127dd473Swhl739 memset(&d, 0, sizeof(d));
822127dd473Swhl739 d.taclass = tainfo->taclass4;
823127dd473Swhl739 d.size = tainfo->size4;
824127dd473Swhl739 d.count = tainfo->count4;
825127dd473Swhl739 d.itemsize = tainfo->itemsize4;
826127dd473Swhl739 if (afdata == 0 && afitem != 0)
827127dd473Swhl739 d.itemsize6 = tainfo->itemsize6;
828127dd473Swhl739 else
829127dd473Swhl739 d.itemsize6 = d.itemsize;
830127dd473Swhl739 if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
831127dd473Swhl739 vtype = "unknown";
832127dd473Swhl739
833127dd473Swhl739 if (afdata == 0) {
834127dd473Swhl739 table_show_tainfo(i, &d, "", vtype);
835127dd473Swhl739 } else {
836127dd473Swhl739 table_show_tainfo(i, &d, "IPv4 ", vtype);
837127dd473Swhl739 memset(&d, 0, sizeof(d));
838127dd473Swhl739 d.taclass = tainfo->taclass6;
839127dd473Swhl739 if ((vtype = match_value(tablealgoclass, d.taclass)) == NULL)
840127dd473Swhl739 vtype = "unknown";
841127dd473Swhl739 d.size = tainfo->size6;
842127dd473Swhl739 d.count = tainfo->count6;
843127dd473Swhl739 d.itemsize = tainfo->itemsize6;
844127dd473Swhl739 d.itemsize6 = d.itemsize;
845127dd473Swhl739 table_show_tainfo(i, &d, "IPv6 ", vtype);
846127dd473Swhl739 }
847127dd473Swhl739
848127dd473Swhl739 return (0);
849127dd473Swhl739 }
850127dd473Swhl739
851127dd473Swhl739
852127dd473Swhl739 /*
853127dd473Swhl739 * Function wrappers which can be used either
854127dd473Swhl739 * as is or as foreach function parameter.
855127dd473Swhl739 */
856127dd473Swhl739
857127dd473Swhl739 static int
table_show_one(ipfw_xtable_info * i,void * arg)858127dd473Swhl739 table_show_one(ipfw_xtable_info *i, void *arg)
859127dd473Swhl739 {
86022ce4affSfengbojiang ipfw_obj_header *oh = NULL;
861127dd473Swhl739 int error;
86222ce4affSfengbojiang int is_all;
86322ce4affSfengbojiang
86422ce4affSfengbojiang is_all = arg == NULL ? 0 : 1;
865127dd473Swhl739
866127dd473Swhl739 if ((error = table_do_get_list(i, &oh)) != 0) {
867127dd473Swhl739 err(EX_OSERR, "Error requesting table %s list", i->tablename);
868127dd473Swhl739 return (error);
869127dd473Swhl739 }
870127dd473Swhl739
87122ce4affSfengbojiang table_show_list(oh, is_all);
872127dd473Swhl739
873127dd473Swhl739 free(oh);
874127dd473Swhl739 return (0);
875127dd473Swhl739 }
876127dd473Swhl739
877127dd473Swhl739 static int
table_flush_one(ipfw_xtable_info * i,void * arg)878127dd473Swhl739 table_flush_one(ipfw_xtable_info *i, void *arg)
879127dd473Swhl739 {
880127dd473Swhl739 ipfw_obj_header *oh;
881127dd473Swhl739
882127dd473Swhl739 oh = (ipfw_obj_header *)arg;
883127dd473Swhl739
884127dd473Swhl739 table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
885127dd473Swhl739
886127dd473Swhl739 return (table_flush(oh));
887127dd473Swhl739 }
888127dd473Swhl739
889127dd473Swhl739 static int
table_do_modify_record(int cmd,ipfw_obj_header * oh,ipfw_obj_tentry * tent,int count,int atomic)890127dd473Swhl739 table_do_modify_record(int cmd, ipfw_obj_header *oh,
891127dd473Swhl739 ipfw_obj_tentry *tent, int count, int atomic)
892127dd473Swhl739 {
893127dd473Swhl739 ipfw_obj_ctlv *ctlv;
894127dd473Swhl739 ipfw_obj_tentry *tent_base;
895127dd473Swhl739 caddr_t pbuf;
896127dd473Swhl739 char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)];
897127dd473Swhl739 int error, i;
898127dd473Swhl739 size_t sz;
899127dd473Swhl739
900127dd473Swhl739 sz = sizeof(*ctlv) + sizeof(*tent) * count;
901127dd473Swhl739 if (count == 1) {
902127dd473Swhl739 memset(xbuf, 0, sizeof(xbuf));
903127dd473Swhl739 pbuf = xbuf;
904127dd473Swhl739 } else {
905127dd473Swhl739 if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL)
906127dd473Swhl739 return (ENOMEM);
907127dd473Swhl739 }
908127dd473Swhl739
909127dd473Swhl739 memcpy(pbuf, oh, sizeof(*oh));
910127dd473Swhl739 oh = (ipfw_obj_header *)pbuf;
911127dd473Swhl739 oh->opheader.version = 1;
912127dd473Swhl739
913127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)(oh + 1);
914127dd473Swhl739 ctlv->count = count;
915127dd473Swhl739 ctlv->head.length = sz;
916127dd473Swhl739 if (atomic != 0)
917127dd473Swhl739 ctlv->flags |= IPFW_CTF_ATOMIC;
918127dd473Swhl739
919127dd473Swhl739 tent_base = tent;
920127dd473Swhl739 memcpy(ctlv + 1, tent, sizeof(*tent) * count);
921127dd473Swhl739 tent = (ipfw_obj_tentry *)(ctlv + 1);
922127dd473Swhl739 for (i = 0; i < count; i++, tent++) {
923127dd473Swhl739 tent->head.length = sizeof(ipfw_obj_tentry);
924127dd473Swhl739 tent->idx = oh->idx;
925127dd473Swhl739 }
926127dd473Swhl739
927127dd473Swhl739 sz += sizeof(*oh);
928127dd473Swhl739 error = do_get3(cmd, &oh->opheader, &sz);
92922ce4affSfengbojiang if (error != 0)
93022ce4affSfengbojiang error = errno;
931127dd473Swhl739 tent = (ipfw_obj_tentry *)(ctlv + 1);
932127dd473Swhl739 /* Copy result back to provided buffer */
933127dd473Swhl739 memcpy(tent_base, ctlv + 1, sizeof(*tent) * count);
934127dd473Swhl739
935127dd473Swhl739 if (pbuf != xbuf)
936127dd473Swhl739 free(pbuf);
937127dd473Swhl739
938127dd473Swhl739 return (error);
939127dd473Swhl739 }
940127dd473Swhl739
941127dd473Swhl739 static void
table_modify_record(ipfw_obj_header * oh,int ac,char * av[],int add,int quiet,int update,int atomic)942127dd473Swhl739 table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
943127dd473Swhl739 int quiet, int update, int atomic)
944127dd473Swhl739 {
945127dd473Swhl739 ipfw_obj_tentry *ptent, tent, *tent_buf;
946127dd473Swhl739 ipfw_xtable_info xi;
94722ce4affSfengbojiang const char *etxt, *px, *texterr;
948127dd473Swhl739 uint8_t type;
949127dd473Swhl739 uint32_t vmask;
950127dd473Swhl739 int cmd, count, error, i, ignored;
951127dd473Swhl739
952127dd473Swhl739 if (ac == 0)
953127dd473Swhl739 errx(EX_USAGE, "address required");
954127dd473Swhl739
955127dd473Swhl739 if (add != 0) {
956127dd473Swhl739 cmd = IP_FW_TABLE_XADD;
957127dd473Swhl739 texterr = "Adding record failed";
958127dd473Swhl739 } else {
959127dd473Swhl739 cmd = IP_FW_TABLE_XDEL;
960127dd473Swhl739 texterr = "Deleting record failed";
961127dd473Swhl739 }
962127dd473Swhl739
963127dd473Swhl739 /*
964127dd473Swhl739 * Calculate number of entries:
965127dd473Swhl739 * Assume [key val] x N for add
966127dd473Swhl739 * and
967127dd473Swhl739 * key x N for delete
968127dd473Swhl739 */
969127dd473Swhl739 count = (add != 0) ? ac / 2 + 1 : ac;
970127dd473Swhl739
971127dd473Swhl739 if (count <= 1) {
972127dd473Swhl739 /* Adding single entry with/without value */
973127dd473Swhl739 memset(&tent, 0, sizeof(tent));
974127dd473Swhl739 tent_buf = &tent;
975127dd473Swhl739 } else {
976127dd473Swhl739
977127dd473Swhl739 if ((tent_buf = calloc(count, sizeof(tent))) == NULL)
978127dd473Swhl739 errx(EX_OSERR,
979127dd473Swhl739 "Unable to allocate memory for all entries");
980127dd473Swhl739 }
981127dd473Swhl739 ptent = tent_buf;
982127dd473Swhl739
983127dd473Swhl739 memset(&xi, 0, sizeof(xi));
984127dd473Swhl739 count = 0;
985127dd473Swhl739 while (ac > 0) {
986127dd473Swhl739 tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi);
987127dd473Swhl739
988127dd473Swhl739 /*
989127dd473Swhl739 * Compatibility layer: auto-create table if not exists.
990127dd473Swhl739 */
991127dd473Swhl739 if (xi.tablename[0] == '\0') {
992127dd473Swhl739 xi.type = type;
993127dd473Swhl739 xi.vmask = vmask;
994127dd473Swhl739 strlcpy(xi.tablename, oh->ntlv.name,
995127dd473Swhl739 sizeof(xi.tablename));
996127dd473Swhl739 if (quiet == 0)
997127dd473Swhl739 warnx("DEPRECATED: inserting data into "
998127dd473Swhl739 "non-existent table %s. (auto-created)",
999127dd473Swhl739 xi.tablename);
1000127dd473Swhl739 table_do_create(oh, &xi);
1001127dd473Swhl739 }
1002127dd473Swhl739
1003127dd473Swhl739 oh->ntlv.type = type;
1004127dd473Swhl739 ac--; av++;
1005127dd473Swhl739
1006127dd473Swhl739 if (add != 0 && ac > 0) {
1007127dd473Swhl739 tentry_fill_value(oh, ptent, *av, type, vmask);
1008127dd473Swhl739 ac--; av++;
1009127dd473Swhl739 }
1010127dd473Swhl739
1011127dd473Swhl739 if (update != 0)
1012127dd473Swhl739 ptent->head.flags |= IPFW_TF_UPDATE;
1013127dd473Swhl739
1014127dd473Swhl739 count++;
1015127dd473Swhl739 ptent++;
1016127dd473Swhl739 }
1017127dd473Swhl739
1018127dd473Swhl739 error = table_do_modify_record(cmd, oh, tent_buf, count, atomic);
1019127dd473Swhl739
1020127dd473Swhl739 /*
1021127dd473Swhl739 * Compatibility stuff: do not yell on duplicate keys or
1022127dd473Swhl739 * failed deletions.
1023127dd473Swhl739 */
1024127dd473Swhl739 if (error == 0 || (error == EEXIST && add != 0) ||
1025127dd473Swhl739 (error == ENOENT && add == 0)) {
1026127dd473Swhl739 if (quiet != 0) {
1027127dd473Swhl739 if (tent_buf != &tent)
1028127dd473Swhl739 free(tent_buf);
1029127dd473Swhl739 return;
1030127dd473Swhl739 }
1031127dd473Swhl739 }
1032127dd473Swhl739
1033127dd473Swhl739 /* Report results back */
1034127dd473Swhl739 ptent = tent_buf;
1035127dd473Swhl739 for (i = 0; i < count; ptent++, i++) {
1036127dd473Swhl739 ignored = 0;
1037127dd473Swhl739 switch (ptent->result) {
1038127dd473Swhl739 case IPFW_TR_ADDED:
1039127dd473Swhl739 px = "added";
1040127dd473Swhl739 break;
1041127dd473Swhl739 case IPFW_TR_DELETED:
1042127dd473Swhl739 px = "deleted";
1043127dd473Swhl739 break;
1044127dd473Swhl739 case IPFW_TR_UPDATED:
1045127dd473Swhl739 px = "updated";
1046127dd473Swhl739 break;
1047127dd473Swhl739 case IPFW_TR_LIMIT:
1048127dd473Swhl739 px = "limit";
1049127dd473Swhl739 ignored = 1;
1050127dd473Swhl739 break;
1051127dd473Swhl739 case IPFW_TR_ERROR:
1052127dd473Swhl739 px = "error";
1053127dd473Swhl739 ignored = 1;
1054127dd473Swhl739 break;
1055127dd473Swhl739 case IPFW_TR_NOTFOUND:
1056127dd473Swhl739 px = "notfound";
1057127dd473Swhl739 ignored = 1;
1058127dd473Swhl739 break;
1059127dd473Swhl739 case IPFW_TR_EXISTS:
1060127dd473Swhl739 px = "exists";
1061127dd473Swhl739 ignored = 1;
1062127dd473Swhl739 break;
1063127dd473Swhl739 case IPFW_TR_IGNORED:
1064127dd473Swhl739 px = "ignored";
1065127dd473Swhl739 ignored = 1;
1066127dd473Swhl739 break;
1067127dd473Swhl739 default:
1068127dd473Swhl739 px = "unknown";
1069127dd473Swhl739 ignored = 1;
1070127dd473Swhl739 }
1071127dd473Swhl739
1072127dd473Swhl739 if (error != 0 && atomic != 0 && ignored == 0)
1073127dd473Swhl739 printf("%s(reverted): ", px);
1074127dd473Swhl739 else
1075127dd473Swhl739 printf("%s: ", px);
1076127dd473Swhl739
1077127dd473Swhl739 table_show_entry(&xi, ptent);
1078127dd473Swhl739 }
1079127dd473Swhl739
1080127dd473Swhl739 if (tent_buf != &tent)
1081127dd473Swhl739 free(tent_buf);
1082127dd473Swhl739
1083127dd473Swhl739 if (error == 0)
1084127dd473Swhl739 return;
1085127dd473Swhl739 /* Get real OS error */
1086127dd473Swhl739 error = errno;
1087127dd473Swhl739
1088127dd473Swhl739 /* Try to provide more human-readable error */
1089127dd473Swhl739 switch (error) {
1090127dd473Swhl739 case EEXIST:
1091127dd473Swhl739 etxt = "record already exists";
1092127dd473Swhl739 break;
1093127dd473Swhl739 case EFBIG:
1094127dd473Swhl739 etxt = "limit hit";
1095127dd473Swhl739 break;
1096127dd473Swhl739 case ESRCH:
1097127dd473Swhl739 etxt = "table not found";
1098127dd473Swhl739 break;
1099127dd473Swhl739 case ENOENT:
1100127dd473Swhl739 etxt = "record not found";
1101127dd473Swhl739 break;
1102127dd473Swhl739 case EACCES:
1103127dd473Swhl739 etxt = "table is locked";
1104127dd473Swhl739 break;
1105127dd473Swhl739 default:
1106127dd473Swhl739 etxt = strerror(error);
1107127dd473Swhl739 }
1108127dd473Swhl739
1109127dd473Swhl739 errx(EX_OSERR, "%s: %s", texterr, etxt);
1110127dd473Swhl739 }
1111127dd473Swhl739
1112127dd473Swhl739 static int
table_do_lookup(ipfw_obj_header * oh,char * key,ipfw_xtable_info * xi,ipfw_obj_tentry * xtent)1113127dd473Swhl739 table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi,
1114127dd473Swhl739 ipfw_obj_tentry *xtent)
1115127dd473Swhl739 {
1116127dd473Swhl739 char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
1117127dd473Swhl739 ipfw_obj_tentry *tent;
1118127dd473Swhl739 uint8_t type;
1119127dd473Swhl739 uint32_t vmask;
1120127dd473Swhl739 size_t sz;
1121127dd473Swhl739
1122127dd473Swhl739 memcpy(xbuf, oh, sizeof(*oh));
1123127dd473Swhl739 oh = (ipfw_obj_header *)xbuf;
1124127dd473Swhl739 tent = (ipfw_obj_tentry *)(oh + 1);
1125127dd473Swhl739
1126127dd473Swhl739 memset(tent, 0, sizeof(*tent));
1127127dd473Swhl739 tent->head.length = sizeof(*tent);
1128127dd473Swhl739 tent->idx = 1;
1129127dd473Swhl739
1130127dd473Swhl739 tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi);
1131127dd473Swhl739 oh->ntlv.type = type;
1132127dd473Swhl739
1133127dd473Swhl739 sz = sizeof(xbuf);
1134127dd473Swhl739 if (do_get3(IP_FW_TABLE_XFIND, &oh->opheader, &sz) != 0)
1135127dd473Swhl739 return (errno);
1136127dd473Swhl739
1137127dd473Swhl739 if (sz < sizeof(xbuf))
1138127dd473Swhl739 return (EINVAL);
1139127dd473Swhl739
1140127dd473Swhl739 *xtent = *tent;
1141127dd473Swhl739
1142127dd473Swhl739 return (0);
1143127dd473Swhl739 }
1144127dd473Swhl739
1145127dd473Swhl739 static void
table_lookup(ipfw_obj_header * oh,int ac,char * av[])1146127dd473Swhl739 table_lookup(ipfw_obj_header *oh, int ac, char *av[])
1147127dd473Swhl739 {
1148127dd473Swhl739 ipfw_obj_tentry xtent;
1149127dd473Swhl739 ipfw_xtable_info xi;
1150127dd473Swhl739 char key[64];
1151127dd473Swhl739 int error;
1152127dd473Swhl739
1153127dd473Swhl739 if (ac == 0)
1154127dd473Swhl739 errx(EX_USAGE, "address required");
1155127dd473Swhl739
1156127dd473Swhl739 strlcpy(key, *av, sizeof(key));
1157127dd473Swhl739
1158127dd473Swhl739 memset(&xi, 0, sizeof(xi));
1159127dd473Swhl739 error = table_do_lookup(oh, key, &xi, &xtent);
1160127dd473Swhl739
1161127dd473Swhl739 switch (error) {
1162127dd473Swhl739 case 0:
1163127dd473Swhl739 break;
1164127dd473Swhl739 case ESRCH:
1165127dd473Swhl739 errx(EX_UNAVAILABLE, "Table %s not found", oh->ntlv.name);
1166127dd473Swhl739 case ENOENT:
1167127dd473Swhl739 errx(EX_UNAVAILABLE, "Entry %s not found", *av);
1168127dd473Swhl739 case ENOTSUP:
1169127dd473Swhl739 errx(EX_UNAVAILABLE, "Table %s algo does not support "
1170127dd473Swhl739 "\"lookup\" method", oh->ntlv.name);
1171127dd473Swhl739 default:
1172127dd473Swhl739 err(EX_OSERR, "getsockopt(IP_FW_TABLE_XFIND)");
1173127dd473Swhl739 }
1174127dd473Swhl739
1175127dd473Swhl739 table_show_entry(&xi, &xtent);
1176127dd473Swhl739 }
1177127dd473Swhl739
1178127dd473Swhl739 static void
tentry_fill_key_type(char * arg,ipfw_obj_tentry * tentry,uint8_t type,uint8_t tflags)1179127dd473Swhl739 tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
1180127dd473Swhl739 uint8_t tflags)
1181127dd473Swhl739 {
1182127dd473Swhl739 char *p, *pp;
1183127dd473Swhl739 int mask, af;
1184127dd473Swhl739 struct in6_addr *paddr, tmp;
1185127dd473Swhl739 struct tflow_entry *tfe;
1186127dd473Swhl739 uint32_t key, *pkey;
1187127dd473Swhl739 uint16_t port;
1188127dd473Swhl739 struct protoent *pent;
1189127dd473Swhl739 struct servent *sent;
1190127dd473Swhl739 int masklen;
1191127dd473Swhl739
119222ce4affSfengbojiang mask = masklen = 0;
1193127dd473Swhl739 af = 0;
1194127dd473Swhl739 paddr = (struct in6_addr *)&tentry->k;
1195127dd473Swhl739
1196127dd473Swhl739 switch (type) {
1197127dd473Swhl739 case IPFW_TABLE_ADDR:
1198127dd473Swhl739 /* Remove / if exists */
1199127dd473Swhl739 if ((p = strchr(arg, '/')) != NULL) {
1200127dd473Swhl739 *p = '\0';
1201127dd473Swhl739 mask = atoi(p + 1);
1202127dd473Swhl739 }
1203127dd473Swhl739
1204127dd473Swhl739 if (inet_pton(AF_INET, arg, paddr) == 1) {
1205127dd473Swhl739 if (p != NULL && mask > 32)
1206127dd473Swhl739 errx(EX_DATAERR, "bad IPv4 mask width: %s",
1207127dd473Swhl739 p + 1);
1208127dd473Swhl739
1209127dd473Swhl739 masklen = p ? mask : 32;
1210127dd473Swhl739 af = AF_INET;
1211*d4a07e70Sfengbojiang } else if (inet_pton(AF_INET6_LINUX, arg, paddr) == 1) {
1212127dd473Swhl739 if (IN6_IS_ADDR_V4COMPAT(paddr))
1213127dd473Swhl739 errx(EX_DATAERR,
1214127dd473Swhl739 "Use IPv4 instead of v4-compatible");
1215127dd473Swhl739 if (p != NULL && mask > 128)
1216127dd473Swhl739 errx(EX_DATAERR, "bad IPv6 mask width: %s",
1217127dd473Swhl739 p + 1);
1218127dd473Swhl739
1219127dd473Swhl739 masklen = p ? mask : 128;
122022ce4affSfengbojiang af = AF_INET6;
1221127dd473Swhl739 } else {
1222127dd473Swhl739 /* Assume FQDN */
1223127dd473Swhl739 if (lookup_host(arg, (struct in_addr *)paddr) != 0)
1224127dd473Swhl739 errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
1225127dd473Swhl739
1226127dd473Swhl739 masklen = 32;
1227127dd473Swhl739 type = IPFW_TABLE_ADDR;
1228127dd473Swhl739 af = AF_INET;
1229127dd473Swhl739 }
1230127dd473Swhl739 break;
1231127dd473Swhl739 case IPFW_TABLE_INTERFACE:
1232127dd473Swhl739 /* Assume interface name. Copy significant data only */
1233127dd473Swhl739 mask = MIN(strlen(arg), IF_NAMESIZE - 1);
1234127dd473Swhl739 memcpy(paddr, arg, mask);
1235127dd473Swhl739 /* Set mask to exact match */
1236127dd473Swhl739 masklen = 8 * IF_NAMESIZE;
1237127dd473Swhl739 break;
1238127dd473Swhl739 case IPFW_TABLE_NUMBER:
1239127dd473Swhl739 /* Port or any other key */
1240127dd473Swhl739 key = strtol(arg, &p, 10);
1241127dd473Swhl739 if (*p != '\0')
1242127dd473Swhl739 errx(EX_DATAERR, "Invalid number: %s", arg);
1243127dd473Swhl739
1244127dd473Swhl739 pkey = (uint32_t *)paddr;
1245127dd473Swhl739 *pkey = key;
1246127dd473Swhl739 masklen = 32;
1247127dd473Swhl739 break;
1248127dd473Swhl739 case IPFW_TABLE_FLOW:
1249127dd473Swhl739 /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */
1250127dd473Swhl739 tfe = &tentry->k.flow;
1251127dd473Swhl739 af = 0;
1252127dd473Swhl739
1253127dd473Swhl739 /* Handle <ipv4|ipv6> */
1254127dd473Swhl739 if ((tflags & IPFW_TFFLAG_SRCIP) != 0) {
1255127dd473Swhl739 if ((p = strchr(arg, ',')) != NULL)
1256127dd473Swhl739 *p++ = '\0';
1257127dd473Swhl739 /* Determine family using temporary storage */
1258127dd473Swhl739 if (inet_pton(AF_INET, arg, &tmp) == 1) {
1259127dd473Swhl739 if (af != 0 && af != AF_INET)
1260127dd473Swhl739 errx(EX_DATAERR,
1261127dd473Swhl739 "Inconsistent address family\n");
1262127dd473Swhl739 af = AF_INET;
1263127dd473Swhl739 memcpy(&tfe->a.a4.sip, &tmp, 4);
1264*d4a07e70Sfengbojiang } else if (inet_pton(AF_INET6_LINUX, arg, &tmp) == 1) {
126522ce4affSfengbojiang if (af != 0 && af != AF_INET6)
1266127dd473Swhl739 errx(EX_DATAERR,
1267127dd473Swhl739 "Inconsistent address family\n");
126822ce4affSfengbojiang af = AF_INET6;
1269127dd473Swhl739 memcpy(&tfe->a.a6.sip6, &tmp, 16);
1270127dd473Swhl739 }
1271127dd473Swhl739
1272127dd473Swhl739 arg = p;
1273127dd473Swhl739 }
1274127dd473Swhl739
1275127dd473Swhl739 /* Handle <proto-num|proto-name> */
1276127dd473Swhl739 if ((tflags & IPFW_TFFLAG_PROTO) != 0) {
1277127dd473Swhl739 if (arg == NULL)
1278127dd473Swhl739 errx(EX_DATAERR, "invalid key: proto missing");
1279127dd473Swhl739 if ((p = strchr(arg, ',')) != NULL)
1280127dd473Swhl739 *p++ = '\0';
1281127dd473Swhl739
1282127dd473Swhl739 key = strtol(arg, &pp, 10);
1283127dd473Swhl739 if (*pp != '\0') {
1284127dd473Swhl739 if ((pent = getprotobyname(arg)) == NULL)
1285127dd473Swhl739 errx(EX_DATAERR, "Unknown proto: %s",
1286127dd473Swhl739 arg);
1287127dd473Swhl739 else
1288127dd473Swhl739 key = pent->p_proto;
1289127dd473Swhl739 }
1290127dd473Swhl739
1291127dd473Swhl739 if (key > 255)
1292127dd473Swhl739 errx(EX_DATAERR, "Bad protocol number: %u",key);
1293127dd473Swhl739
1294127dd473Swhl739 tfe->proto = key;
1295127dd473Swhl739
1296127dd473Swhl739 arg = p;
1297127dd473Swhl739 }
1298127dd473Swhl739
1299127dd473Swhl739 /* Handle <port-num|service-name> */
1300127dd473Swhl739 if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) {
1301127dd473Swhl739 if (arg == NULL)
1302127dd473Swhl739 errx(EX_DATAERR, "invalid key: src port missing");
1303127dd473Swhl739 if ((p = strchr(arg, ',')) != NULL)
1304127dd473Swhl739 *p++ = '\0';
1305127dd473Swhl739
130622ce4affSfengbojiang port = htons(strtol(arg, &pp, 10));
130722ce4affSfengbojiang if (*pp != '\0') {
1308127dd473Swhl739 if ((sent = getservbyname(arg, NULL)) == NULL)
1309127dd473Swhl739 errx(EX_DATAERR, "Unknown service: %s",
1310127dd473Swhl739 arg);
131122ce4affSfengbojiang port = sent->s_port;
1312127dd473Swhl739 }
1313127dd473Swhl739 tfe->sport = port;
1314127dd473Swhl739 arg = p;
1315127dd473Swhl739 }
1316127dd473Swhl739
1317127dd473Swhl739 /* Handle <ipv4|ipv6>*/
1318127dd473Swhl739 if ((tflags & IPFW_TFFLAG_DSTIP) != 0) {
1319127dd473Swhl739 if (arg == NULL)
1320127dd473Swhl739 errx(EX_DATAERR, "invalid key: dst ip missing");
1321127dd473Swhl739 if ((p = strchr(arg, ',')) != NULL)
1322127dd473Swhl739 *p++ = '\0';
1323127dd473Swhl739 /* Determine family using temporary storage */
1324127dd473Swhl739 if (inet_pton(AF_INET, arg, &tmp) == 1) {
1325127dd473Swhl739 if (af != 0 && af != AF_INET)
1326127dd473Swhl739 errx(EX_DATAERR,
1327127dd473Swhl739 "Inconsistent address family");
1328127dd473Swhl739 af = AF_INET;
1329127dd473Swhl739 memcpy(&tfe->a.a4.dip, &tmp, 4);
1330*d4a07e70Sfengbojiang } else if (inet_pton(AF_INET6_LINUX, arg, &tmp) == 1) {
133122ce4affSfengbojiang if (af != 0 && af != AF_INET6)
1332127dd473Swhl739 errx(EX_DATAERR,
1333127dd473Swhl739 "Inconsistent address family");
133422ce4affSfengbojiang af = AF_INET6;
1335127dd473Swhl739 memcpy(&tfe->a.a6.dip6, &tmp, 16);
1336127dd473Swhl739 }
1337127dd473Swhl739
1338127dd473Swhl739 arg = p;
1339127dd473Swhl739 }
1340127dd473Swhl739
1341127dd473Swhl739 /* Handle <port-num|service-name> */
1342127dd473Swhl739 if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) {
1343127dd473Swhl739 if (arg == NULL)
1344127dd473Swhl739 errx(EX_DATAERR, "invalid key: dst port missing");
1345127dd473Swhl739 if ((p = strchr(arg, ',')) != NULL)
1346127dd473Swhl739 *p++ = '\0';
1347127dd473Swhl739
134822ce4affSfengbojiang port = htons(strtol(arg, &pp, 10));
134922ce4affSfengbojiang if (*pp != '\0') {
1350127dd473Swhl739 if ((sent = getservbyname(arg, NULL)) == NULL)
1351127dd473Swhl739 errx(EX_DATAERR, "Unknown service: %s",
1352127dd473Swhl739 arg);
135322ce4affSfengbojiang port = sent->s_port;
1354127dd473Swhl739 }
1355127dd473Swhl739 tfe->dport = port;
1356127dd473Swhl739 arg = p;
1357127dd473Swhl739 }
1358127dd473Swhl739
1359127dd473Swhl739 tfe->af = af;
1360127dd473Swhl739
1361127dd473Swhl739 break;
1362127dd473Swhl739
1363127dd473Swhl739 default:
1364127dd473Swhl739 errx(EX_DATAERR, "Unsupported table type: %d", type);
1365127dd473Swhl739 }
1366127dd473Swhl739
1367127dd473Swhl739 tentry->subtype = af;
1368127dd473Swhl739 tentry->masklen = masklen;
1369127dd473Swhl739 }
1370127dd473Swhl739
1371127dd473Swhl739 /*
1372127dd473Swhl739 * Tries to guess table key type.
1373127dd473Swhl739 * This procedure is used in legacy table auto-create
1374127dd473Swhl739 * code AND in `ipfw -n` ruleset checking.
1375127dd473Swhl739 *
1376127dd473Swhl739 * Imported from old table_fill_xentry() parse code.
1377127dd473Swhl739 */
1378127dd473Swhl739 static int
guess_key_type(char * key,uint8_t * ptype)1379127dd473Swhl739 guess_key_type(char *key, uint8_t *ptype)
1380127dd473Swhl739 {
1381127dd473Swhl739 char *p;
1382127dd473Swhl739 struct in6_addr addr;
1383127dd473Swhl739 uint32_t kv;
1384127dd473Swhl739
1385127dd473Swhl739 if (ishexnumber(*key) != 0 || *key == ':') {
1386127dd473Swhl739 /* Remove / if exists */
1387127dd473Swhl739 if ((p = strchr(key, '/')) != NULL)
1388127dd473Swhl739 *p = '\0';
1389127dd473Swhl739
1390127dd473Swhl739 if ((inet_pton(AF_INET, key, &addr) == 1) ||
1391*d4a07e70Sfengbojiang (inet_pton(AF_INET6_LINUX, key, &addr) == 1)) {
1392127dd473Swhl739 *ptype = IPFW_TABLE_CIDR;
1393127dd473Swhl739 if (p != NULL)
1394127dd473Swhl739 *p = '/';
1395127dd473Swhl739 return (0);
1396127dd473Swhl739 } else {
1397127dd473Swhl739 /* Port or any other key */
1398127dd473Swhl739 /* Skip non-base 10 entries like 'fa1' */
1399127dd473Swhl739 kv = strtol(key, &p, 10);
1400127dd473Swhl739 if (*p == '\0') {
1401127dd473Swhl739 *ptype = IPFW_TABLE_NUMBER;
1402127dd473Swhl739 return (0);
1403127dd473Swhl739 } else if ((p != key) && (*p == '.')) {
1404127dd473Swhl739 /*
1405127dd473Swhl739 * Warn on IPv4 address strings
1406127dd473Swhl739 * which are "valid" for inet_aton() but not
1407127dd473Swhl739 * in inet_pton().
1408127dd473Swhl739 *
1409127dd473Swhl739 * Typical examples: '10.5' or '10.0.0.05'
1410127dd473Swhl739 */
1411127dd473Swhl739 return (1);
1412127dd473Swhl739 }
1413127dd473Swhl739 }
1414127dd473Swhl739 }
1415127dd473Swhl739
1416127dd473Swhl739 if (strchr(key, '.') == NULL) {
1417127dd473Swhl739 *ptype = IPFW_TABLE_INTERFACE;
1418127dd473Swhl739 return (0);
1419127dd473Swhl739 }
1420127dd473Swhl739
1421127dd473Swhl739 if (lookup_host(key, (struct in_addr *)&addr) != 0)
1422127dd473Swhl739 return (1);
1423127dd473Swhl739
1424127dd473Swhl739 *ptype = IPFW_TABLE_CIDR;
1425127dd473Swhl739 return (0);
1426127dd473Swhl739 }
1427127dd473Swhl739
1428127dd473Swhl739 static void
tentry_fill_key(ipfw_obj_header * oh,ipfw_obj_tentry * tent,char * key,int add,uint8_t * ptype,uint32_t * pvmask,ipfw_xtable_info * xi)1429127dd473Swhl739 tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
1430127dd473Swhl739 int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi)
1431127dd473Swhl739 {
1432127dd473Swhl739 uint8_t type, tflags;
1433127dd473Swhl739 uint32_t vmask;
1434127dd473Swhl739 int error;
1435127dd473Swhl739
1436127dd473Swhl739 type = 0;
1437127dd473Swhl739 tflags = 0;
1438127dd473Swhl739 vmask = 0;
1439127dd473Swhl739
1440127dd473Swhl739 if (xi->tablename[0] == '\0')
1441127dd473Swhl739 error = table_get_info(oh, xi);
1442127dd473Swhl739 else
1443127dd473Swhl739 error = 0;
1444127dd473Swhl739
1445127dd473Swhl739 if (error == 0) {
144622ce4affSfengbojiang if (g_co.test_only == 0) {
1447127dd473Swhl739 /* Table found */
1448127dd473Swhl739 type = xi->type;
1449127dd473Swhl739 tflags = xi->tflags;
1450127dd473Swhl739 vmask = xi->vmask;
1451127dd473Swhl739 } else {
1452127dd473Swhl739 /*
1453127dd473Swhl739 * We're running `ipfw -n`
1454127dd473Swhl739 * Compatibility layer: try to guess key type
1455127dd473Swhl739 * before failing.
1456127dd473Swhl739 */
1457127dd473Swhl739 if (guess_key_type(key, &type) != 0) {
1458127dd473Swhl739 /* Inknown key */
1459127dd473Swhl739 errx(EX_USAGE, "Cannot guess "
1460127dd473Swhl739 "key '%s' type", key);
1461127dd473Swhl739 }
1462127dd473Swhl739 vmask = IPFW_VTYPE_LEGACY;
1463127dd473Swhl739 }
1464127dd473Swhl739 } else {
1465127dd473Swhl739 if (error != ESRCH)
1466127dd473Swhl739 errx(EX_OSERR, "Error requesting table %s info",
1467127dd473Swhl739 oh->ntlv.name);
1468127dd473Swhl739 if (add == 0)
1469127dd473Swhl739 errx(EX_DATAERR, "Table %s does not exist",
1470127dd473Swhl739 oh->ntlv.name);
1471127dd473Swhl739 /*
1472127dd473Swhl739 * Table does not exist
1473127dd473Swhl739 * Compatibility layer: try to guess key type before failing.
1474127dd473Swhl739 */
1475127dd473Swhl739 if (guess_key_type(key, &type) != 0) {
1476127dd473Swhl739 /* Inknown key */
1477127dd473Swhl739 errx(EX_USAGE, "Table %s does not exist, cannot guess "
1478127dd473Swhl739 "key '%s' type", oh->ntlv.name, key);
1479127dd473Swhl739 }
1480127dd473Swhl739
1481127dd473Swhl739 vmask = IPFW_VTYPE_LEGACY;
1482127dd473Swhl739 }
1483127dd473Swhl739
1484127dd473Swhl739 tentry_fill_key_type(key, tent, type, tflags);
1485127dd473Swhl739
1486127dd473Swhl739 *ptype = type;
1487127dd473Swhl739 *pvmask = vmask;
1488127dd473Swhl739 }
1489127dd473Swhl739
1490127dd473Swhl739 static void
set_legacy_value(uint32_t val,ipfw_table_value * v)1491127dd473Swhl739 set_legacy_value(uint32_t val, ipfw_table_value *v)
1492127dd473Swhl739 {
1493127dd473Swhl739 v->tag = val;
1494127dd473Swhl739 v->pipe = val;
1495127dd473Swhl739 v->divert = val;
1496127dd473Swhl739 v->skipto = val;
1497127dd473Swhl739 v->netgraph = val;
1498127dd473Swhl739 v->fib = val;
1499127dd473Swhl739 v->nat = val;
1500127dd473Swhl739 v->nh4 = val;
1501127dd473Swhl739 v->dscp = (uint8_t)val;
1502127dd473Swhl739 v->limit = val;
1503127dd473Swhl739 }
1504127dd473Swhl739
1505127dd473Swhl739 static void
tentry_fill_value(ipfw_obj_header * oh __unused,ipfw_obj_tentry * tent,char * arg,uint8_t type __unused,uint32_t vmask)150622ce4affSfengbojiang tentry_fill_value(ipfw_obj_header *oh __unused, ipfw_obj_tentry *tent,
150722ce4affSfengbojiang char *arg, uint8_t type __unused, uint32_t vmask)
1508127dd473Swhl739 {
1509127dd473Swhl739 struct addrinfo hints, *res;
151022ce4affSfengbojiang struct in_addr ipaddr;
151122ce4affSfengbojiang const char *etype;
151222ce4affSfengbojiang char *comma, *e, *n, *p;
1513127dd473Swhl739 uint32_t a4, flag, val;
1514127dd473Swhl739 ipfw_table_value *v;
1515127dd473Swhl739 uint32_t i;
1516127dd473Swhl739 int dval;
1517127dd473Swhl739
1518127dd473Swhl739 v = &tent->v.value;
1519127dd473Swhl739
1520127dd473Swhl739 /* Compat layer: keep old behavior for legacy value types */
1521127dd473Swhl739 if (vmask == IPFW_VTYPE_LEGACY) {
1522127dd473Swhl739 /* Try to interpret as number first */
1523127dd473Swhl739 val = strtoul(arg, &p, 0);
1524127dd473Swhl739 if (*p == '\0') {
1525127dd473Swhl739 set_legacy_value(val, v);
1526127dd473Swhl739 return;
1527127dd473Swhl739 }
1528127dd473Swhl739 if (inet_pton(AF_INET, arg, &val) == 1) {
1529127dd473Swhl739 set_legacy_value(ntohl(val), v);
1530127dd473Swhl739 return;
1531127dd473Swhl739 }
1532127dd473Swhl739 /* Try hostname */
153322ce4affSfengbojiang if (lookup_host(arg, &ipaddr) == 0) {
153422ce4affSfengbojiang set_legacy_value(ntohl(ipaddr.s_addr), v);
1535127dd473Swhl739 return;
1536127dd473Swhl739 }
1537127dd473Swhl739 errx(EX_OSERR, "Unable to parse value %s", arg);
1538127dd473Swhl739 }
1539127dd473Swhl739
1540127dd473Swhl739 /*
1541127dd473Swhl739 * Shorthands: handle single value if vmask consists
1542127dd473Swhl739 * of numbers only. e.g.:
1543127dd473Swhl739 * vmask = "fib,skipto" -> treat input "1" as "1,1"
1544127dd473Swhl739 */
1545127dd473Swhl739
1546127dd473Swhl739 n = arg;
1547127dd473Swhl739 etype = NULL;
154822ce4affSfengbojiang for (i = 1; i < (1u << 31); i *= 2) {
1549127dd473Swhl739 if ((flag = (vmask & i)) == 0)
1550127dd473Swhl739 continue;
1551127dd473Swhl739 vmask &= ~flag;
1552127dd473Swhl739
1553127dd473Swhl739 if ((comma = strchr(n, ',')) != NULL)
1554127dd473Swhl739 *comma = '\0';
1555127dd473Swhl739
1556127dd473Swhl739 switch (flag) {
1557127dd473Swhl739 case IPFW_VTYPE_TAG:
1558127dd473Swhl739 v->tag = strtol(n, &e, 10);
1559127dd473Swhl739 if (*e != '\0')
1560127dd473Swhl739 etype = "tag";
1561127dd473Swhl739 break;
1562127dd473Swhl739 case IPFW_VTYPE_PIPE:
1563127dd473Swhl739 v->pipe = strtol(n, &e, 10);
1564127dd473Swhl739 if (*e != '\0')
1565127dd473Swhl739 etype = "pipe";
1566127dd473Swhl739 break;
1567127dd473Swhl739 case IPFW_VTYPE_DIVERT:
1568127dd473Swhl739 v->divert = strtol(n, &e, 10);
1569127dd473Swhl739 if (*e != '\0')
1570127dd473Swhl739 etype = "divert";
1571127dd473Swhl739 break;
1572127dd473Swhl739 case IPFW_VTYPE_SKIPTO:
1573127dd473Swhl739 v->skipto = strtol(n, &e, 10);
1574127dd473Swhl739 if (*e != '\0')
1575127dd473Swhl739 etype = "skipto";
1576127dd473Swhl739 break;
1577127dd473Swhl739 case IPFW_VTYPE_NETGRAPH:
1578127dd473Swhl739 v->netgraph = strtol(n, &e, 10);
1579127dd473Swhl739 if (*e != '\0')
1580127dd473Swhl739 etype = "netgraph";
1581127dd473Swhl739 break;
1582127dd473Swhl739 case IPFW_VTYPE_FIB:
1583127dd473Swhl739 v->fib = strtol(n, &e, 10);
1584127dd473Swhl739 if (*e != '\0')
1585127dd473Swhl739 etype = "fib";
1586127dd473Swhl739 break;
1587127dd473Swhl739 case IPFW_VTYPE_NAT:
1588127dd473Swhl739 v->nat = strtol(n, &e, 10);
1589127dd473Swhl739 if (*e != '\0')
1590127dd473Swhl739 etype = "nat";
1591127dd473Swhl739 break;
1592127dd473Swhl739 case IPFW_VTYPE_LIMIT:
1593127dd473Swhl739 v->limit = strtol(n, &e, 10);
1594127dd473Swhl739 if (*e != '\0')
1595127dd473Swhl739 etype = "limit";
1596127dd473Swhl739 break;
1597127dd473Swhl739 case IPFW_VTYPE_NH4:
1598127dd473Swhl739 if (strchr(n, '.') != NULL &&
1599127dd473Swhl739 inet_pton(AF_INET, n, &a4) == 1) {
1600127dd473Swhl739 v->nh4 = ntohl(a4);
1601127dd473Swhl739 break;
1602127dd473Swhl739 }
160322ce4affSfengbojiang if (lookup_host(n, &ipaddr) == 0) {
160422ce4affSfengbojiang v->nh4 = ntohl(ipaddr.s_addr);
1605127dd473Swhl739 break;
160622ce4affSfengbojiang }
1607127dd473Swhl739 etype = "ipv4";
1608127dd473Swhl739 break;
1609127dd473Swhl739 case IPFW_VTYPE_DSCP:
1610127dd473Swhl739 if (isalpha(*n)) {
1611127dd473Swhl739 if ((dval = match_token(f_ipdscp, n)) != -1) {
1612127dd473Swhl739 v->dscp = dval;
1613127dd473Swhl739 break;
1614127dd473Swhl739 } else
1615127dd473Swhl739 etype = "DSCP code";
1616127dd473Swhl739 } else {
1617127dd473Swhl739 v->dscp = strtol(n, &e, 10);
1618127dd473Swhl739 if (v->dscp > 63 || *e != '\0')
1619127dd473Swhl739 etype = "DSCP value";
1620127dd473Swhl739 }
1621127dd473Swhl739 break;
1622127dd473Swhl739 case IPFW_VTYPE_NH6:
1623127dd473Swhl739 if (strchr(n, ':') != NULL) {
1624127dd473Swhl739 memset(&hints, 0, sizeof(hints));
162522ce4affSfengbojiang hints.ai_family = AF_INET6;
1626127dd473Swhl739 hints.ai_flags = AI_NUMERICHOST;
1627*d4a07e70Sfengbojiang /* FIXME: getaddrinfo not support IPv6 */
1628127dd473Swhl739 if (getaddrinfo(n, NULL, &hints, &res) == 0) {
1629127dd473Swhl739 v->nh6 = ((struct sockaddr_in6 *)
1630127dd473Swhl739 res->ai_addr)->sin6_addr;
1631127dd473Swhl739 v->zoneid = ((struct sockaddr_in6 *)
1632127dd473Swhl739 res->ai_addr)->sin6_scope_id;
1633127dd473Swhl739 freeaddrinfo(res);
1634127dd473Swhl739 break;
1635127dd473Swhl739 }
1636127dd473Swhl739 }
1637127dd473Swhl739 etype = "ipv6";
1638127dd473Swhl739 break;
1639127dd473Swhl739 }
1640127dd473Swhl739
1641127dd473Swhl739 if (etype != NULL)
1642127dd473Swhl739 errx(EX_USAGE, "Unable to parse %s as %s", n, etype);
1643127dd473Swhl739
1644127dd473Swhl739 if (comma != NULL)
1645127dd473Swhl739 *comma++ = ',';
1646127dd473Swhl739
1647127dd473Swhl739 if ((n = comma) != NULL)
1648127dd473Swhl739 continue;
1649127dd473Swhl739
1650127dd473Swhl739 /* End of input. */
1651127dd473Swhl739 if (vmask != 0)
1652127dd473Swhl739 errx(EX_USAGE, "Not enough fields inside value");
1653127dd473Swhl739 }
1654127dd473Swhl739 }
1655127dd473Swhl739
1656127dd473Swhl739 /*
1657127dd473Swhl739 * Compare table names.
1658127dd473Swhl739 * Honor number comparison.
1659127dd473Swhl739 */
1660127dd473Swhl739 static int
tablename_cmp(const void * a,const void * b)1661127dd473Swhl739 tablename_cmp(const void *a, const void *b)
1662127dd473Swhl739 {
166322ce4affSfengbojiang const ipfw_xtable_info *ia, *ib;
1664127dd473Swhl739
166522ce4affSfengbojiang ia = (const ipfw_xtable_info *)a;
166622ce4affSfengbojiang ib = (const ipfw_xtable_info *)b;
1667127dd473Swhl739
1668127dd473Swhl739 return (stringnum_cmp(ia->tablename, ib->tablename));
1669127dd473Swhl739 }
1670127dd473Swhl739
1671127dd473Swhl739 /*
1672127dd473Swhl739 * Retrieves table list from kernel,
1673127dd473Swhl739 * optionally sorts it and calls requested function for each table.
1674127dd473Swhl739 * Returns 0 on success.
1675127dd473Swhl739 */
1676127dd473Swhl739 static int
tables_foreach(table_cb_t * f,void * arg,int sort)1677127dd473Swhl739 tables_foreach(table_cb_t *f, void *arg, int sort)
1678127dd473Swhl739 {
1679127dd473Swhl739 ipfw_obj_lheader *olh;
1680127dd473Swhl739 ipfw_xtable_info *info;
1681127dd473Swhl739 size_t sz;
168222ce4affSfengbojiang uint32_t i;
168322ce4affSfengbojiang int error;
1684127dd473Swhl739
1685127dd473Swhl739 /* Start with reasonable default */
1686127dd473Swhl739 sz = sizeof(*olh) + 16 * sizeof(ipfw_xtable_info);
1687127dd473Swhl739
1688127dd473Swhl739 for (;;) {
1689127dd473Swhl739 if ((olh = calloc(1, sz)) == NULL)
1690127dd473Swhl739 return (ENOMEM);
1691127dd473Swhl739
1692127dd473Swhl739 olh->size = sz;
1693127dd473Swhl739 if (do_get3(IP_FW_TABLES_XLIST, &olh->opheader, &sz) != 0) {
1694127dd473Swhl739 sz = olh->size;
1695127dd473Swhl739 free(olh);
1696127dd473Swhl739 if (errno != ENOMEM)
1697127dd473Swhl739 return (errno);
1698127dd473Swhl739 continue;
1699127dd473Swhl739 }
1700127dd473Swhl739
1701127dd473Swhl739 if (sort != 0)
170222ce4affSfengbojiang qsort(olh + 1, olh->count, olh->objsize,
170322ce4affSfengbojiang tablename_cmp);
1704127dd473Swhl739
1705127dd473Swhl739 info = (ipfw_xtable_info *)(olh + 1);
1706127dd473Swhl739 for (i = 0; i < olh->count; i++) {
170722ce4affSfengbojiang if (g_co.use_set == 0 || info->set == g_co.use_set - 1)
170822ce4affSfengbojiang error = f(info, arg);
170922ce4affSfengbojiang info = (ipfw_xtable_info *)((caddr_t)info +
171022ce4affSfengbojiang olh->objsize);
1711127dd473Swhl739 }
1712127dd473Swhl739 free(olh);
1713127dd473Swhl739 break;
1714127dd473Swhl739 }
1715127dd473Swhl739 return (0);
1716127dd473Swhl739 }
1717127dd473Swhl739
1718127dd473Swhl739
1719127dd473Swhl739 /*
1720127dd473Swhl739 * Retrieves all entries for given table @i in
1721127dd473Swhl739 * eXtended format. Allocate buffer large enough
1722127dd473Swhl739 * to store result. Called needs to free it later.
1723127dd473Swhl739 *
1724127dd473Swhl739 * Returns 0 on success.
1725127dd473Swhl739 */
1726127dd473Swhl739 static int
table_do_get_list(ipfw_xtable_info * i,ipfw_obj_header ** poh)1727127dd473Swhl739 table_do_get_list(ipfw_xtable_info *i, ipfw_obj_header **poh)
1728127dd473Swhl739 {
1729127dd473Swhl739 ipfw_obj_header *oh;
1730127dd473Swhl739 size_t sz;
1731127dd473Swhl739 int c;
1732127dd473Swhl739
1733127dd473Swhl739 sz = 0;
1734127dd473Swhl739 oh = NULL;
1735127dd473Swhl739 for (c = 0; c < 8; c++) {
1736127dd473Swhl739 if (sz < i->size)
1737127dd473Swhl739 sz = i->size + 44;
1738127dd473Swhl739 if (oh != NULL)
1739127dd473Swhl739 free(oh);
1740127dd473Swhl739 if ((oh = calloc(1, sz)) == NULL)
1741127dd473Swhl739 continue;
1742127dd473Swhl739 table_fill_objheader(oh, i);
1743127dd473Swhl739 oh->opheader.version = 1; /* Current version */
1744127dd473Swhl739 if (do_get3(IP_FW_TABLE_XLIST, &oh->opheader, &sz) == 0) {
1745127dd473Swhl739 *poh = oh;
1746127dd473Swhl739 return (0);
1747127dd473Swhl739 }
1748127dd473Swhl739
1749127dd473Swhl739 if (errno != ENOMEM)
1750127dd473Swhl739 break;
1751127dd473Swhl739 }
1752127dd473Swhl739 free(oh);
1753127dd473Swhl739
1754127dd473Swhl739 return (errno);
1755127dd473Swhl739 }
1756127dd473Swhl739
1757127dd473Swhl739 /*
1758127dd473Swhl739 * Shows all entries from @oh in human-readable format
1759127dd473Swhl739 */
1760127dd473Swhl739 static void
table_show_list(ipfw_obj_header * oh,int need_header)1761127dd473Swhl739 table_show_list(ipfw_obj_header *oh, int need_header)
1762127dd473Swhl739 {
1763127dd473Swhl739 ipfw_obj_tentry *tent;
1764127dd473Swhl739 uint32_t count;
1765127dd473Swhl739 ipfw_xtable_info *i;
1766127dd473Swhl739
1767127dd473Swhl739 i = (ipfw_xtable_info *)(oh + 1);
1768127dd473Swhl739 tent = (ipfw_obj_tentry *)(i + 1);
1769127dd473Swhl739
1770127dd473Swhl739 if (need_header)
1771127dd473Swhl739 printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
1772127dd473Swhl739
1773127dd473Swhl739 count = i->count;
1774127dd473Swhl739 while (count > 0) {
1775127dd473Swhl739 table_show_entry(i, tent);
1776127dd473Swhl739 tent = (ipfw_obj_tentry *)((caddr_t)tent + tent->head.length);
1777127dd473Swhl739 count--;
1778127dd473Swhl739 }
1779127dd473Swhl739 }
1780127dd473Swhl739
1781127dd473Swhl739 static void
table_show_value(char * buf,size_t bufsize,ipfw_table_value * v,uint32_t vmask,int print_ip)1782127dd473Swhl739 table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
1783127dd473Swhl739 uint32_t vmask, int print_ip)
1784127dd473Swhl739 {
1785127dd473Swhl739 char abuf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2];
1786127dd473Swhl739 struct sockaddr_in6 sa6;
1787127dd473Swhl739 uint32_t flag, i, l;
1788127dd473Swhl739 size_t sz;
1789127dd473Swhl739 struct in_addr a4;
1790127dd473Swhl739
1791127dd473Swhl739 sz = bufsize;
1792127dd473Swhl739
1793127dd473Swhl739 /*
1794127dd473Swhl739 * Some shorthands for printing values:
1795127dd473Swhl739 * legacy assumes all values are equal, so keep the first one.
1796127dd473Swhl739 */
1797127dd473Swhl739 if (vmask == IPFW_VTYPE_LEGACY) {
1798127dd473Swhl739 if (print_ip != 0) {
1799127dd473Swhl739 flag = htonl(v->tag);
1800127dd473Swhl739 inet_ntop(AF_INET, &flag, buf, sz);
1801127dd473Swhl739 } else
1802127dd473Swhl739 snprintf(buf, sz, "%u", v->tag);
1803127dd473Swhl739 return;
1804127dd473Swhl739 }
1805127dd473Swhl739
180622ce4affSfengbojiang for (i = 1; i < (1u << 31); i *= 2) {
1807127dd473Swhl739 if ((flag = (vmask & i)) == 0)
1808127dd473Swhl739 continue;
1809127dd473Swhl739 l = 0;
1810127dd473Swhl739
1811127dd473Swhl739 switch (flag) {
1812127dd473Swhl739 case IPFW_VTYPE_TAG:
1813127dd473Swhl739 l = snprintf(buf, sz, "%u,", v->tag);
1814127dd473Swhl739 break;
1815127dd473Swhl739 case IPFW_VTYPE_PIPE:
1816127dd473Swhl739 l = snprintf(buf, sz, "%u,", v->pipe);
1817127dd473Swhl739 break;
1818127dd473Swhl739 case IPFW_VTYPE_DIVERT:
1819127dd473Swhl739 l = snprintf(buf, sz, "%d,", v->divert);
1820127dd473Swhl739 break;
1821127dd473Swhl739 case IPFW_VTYPE_SKIPTO:
1822127dd473Swhl739 l = snprintf(buf, sz, "%d,", v->skipto);
1823127dd473Swhl739 break;
1824127dd473Swhl739 case IPFW_VTYPE_NETGRAPH:
1825127dd473Swhl739 l = snprintf(buf, sz, "%u,", v->netgraph);
1826127dd473Swhl739 break;
1827127dd473Swhl739 case IPFW_VTYPE_FIB:
1828127dd473Swhl739 l = snprintf(buf, sz, "%u,", v->fib);
1829127dd473Swhl739 break;
1830127dd473Swhl739 case IPFW_VTYPE_NAT:
1831127dd473Swhl739 l = snprintf(buf, sz, "%u,", v->nat);
1832127dd473Swhl739 break;
1833127dd473Swhl739 case IPFW_VTYPE_LIMIT:
1834127dd473Swhl739 l = snprintf(buf, sz, "%u,", v->limit);
1835127dd473Swhl739 break;
1836127dd473Swhl739 case IPFW_VTYPE_NH4:
1837127dd473Swhl739 a4.s_addr = htonl(v->nh4);
1838127dd473Swhl739 inet_ntop(AF_INET, &a4, abuf, sizeof(abuf));
1839127dd473Swhl739 l = snprintf(buf, sz, "%s,", abuf);
1840127dd473Swhl739 break;
1841127dd473Swhl739 case IPFW_VTYPE_DSCP:
1842127dd473Swhl739 l = snprintf(buf, sz, "%d,", v->dscp);
1843127dd473Swhl739 break;
1844127dd473Swhl739 case IPFW_VTYPE_NH6:
184522ce4affSfengbojiang sa6.sin6_family = AF_INET6;
1846127dd473Swhl739 sa6.sin6_len = sizeof(sa6);
1847127dd473Swhl739 sa6.sin6_addr = v->nh6;
1848127dd473Swhl739 sa6.sin6_port = 0;
1849127dd473Swhl739 sa6.sin6_scope_id = v->zoneid;
1850*d4a07e70Sfengbojiang #ifndef FSTACK
1851127dd473Swhl739 if (getnameinfo((const struct sockaddr *)&sa6,
1852127dd473Swhl739 sa6.sin6_len, abuf, sizeof(abuf), NULL, 0,
1853127dd473Swhl739 NI_NUMERICHOST) == 0)
1854*d4a07e70Sfengbojiang #else
1855*d4a07e70Sfengbojiang if (inet_ntop(AF_INET6_LINUX, &sa6.sin6_addr, abuf, sizeof(abuf)) != NULL)
1856*d4a07e70Sfengbojiang #endif
1857127dd473Swhl739 l = snprintf(buf, sz, "%s,", abuf);
1858127dd473Swhl739 break;
1859127dd473Swhl739 }
1860127dd473Swhl739
1861127dd473Swhl739 buf += l;
1862127dd473Swhl739 sz -= l;
1863127dd473Swhl739 }
1864127dd473Swhl739
1865127dd473Swhl739 if (sz != bufsize)
1866127dd473Swhl739 *(buf - 1) = '\0';
1867127dd473Swhl739 }
1868127dd473Swhl739
1869127dd473Swhl739 static void
table_show_entry(ipfw_xtable_info * i,ipfw_obj_tentry * tent)1870127dd473Swhl739 table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
1871127dd473Swhl739 {
187222ce4affSfengbojiang char tbuf[128], pval[128];
187322ce4affSfengbojiang const char *comma;
1874127dd473Swhl739 void *paddr;
1875127dd473Swhl739 struct tflow_entry *tfe;
1876127dd473Swhl739
1877127dd473Swhl739 table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask,
187822ce4affSfengbojiang g_co.do_value_as_ip);
1879127dd473Swhl739
1880127dd473Swhl739 switch (i->type) {
1881127dd473Swhl739 case IPFW_TABLE_ADDR:
1882127dd473Swhl739 /* IPv4 or IPv6 prefixes */
1883127dd473Swhl739 inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
1884127dd473Swhl739 printf("%s/%u %s\n", tbuf, tent->masklen, pval);
1885127dd473Swhl739 break;
1886127dd473Swhl739 case IPFW_TABLE_INTERFACE:
1887127dd473Swhl739 /* Interface names */
1888127dd473Swhl739 printf("%s %s\n", tent->k.iface, pval);
1889127dd473Swhl739 break;
1890127dd473Swhl739 case IPFW_TABLE_NUMBER:
1891127dd473Swhl739 /* numbers */
1892127dd473Swhl739 printf("%u %s\n", tent->k.key, pval);
1893127dd473Swhl739 break;
1894127dd473Swhl739 case IPFW_TABLE_FLOW:
1895127dd473Swhl739 /* flows */
1896127dd473Swhl739 tfe = &tent->k.flow;
1897127dd473Swhl739 comma = "";
1898127dd473Swhl739
1899127dd473Swhl739 if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) {
1900127dd473Swhl739 if (tfe->af == AF_INET)
1901127dd473Swhl739 paddr = &tfe->a.a4.sip;
1902127dd473Swhl739 else
1903127dd473Swhl739 paddr = &tfe->a.a6.sip6;
1904127dd473Swhl739
1905127dd473Swhl739 inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
1906127dd473Swhl739 printf("%s%s", comma, tbuf);
1907127dd473Swhl739 comma = ",";
1908127dd473Swhl739 }
1909127dd473Swhl739
1910127dd473Swhl739 if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) {
1911127dd473Swhl739 printf("%s%d", comma, tfe->proto);
1912127dd473Swhl739 comma = ",";
1913127dd473Swhl739 }
1914127dd473Swhl739
1915127dd473Swhl739 if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) {
1916127dd473Swhl739 printf("%s%d", comma, ntohs(tfe->sport));
1917127dd473Swhl739 comma = ",";
1918127dd473Swhl739 }
1919127dd473Swhl739 if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) {
1920127dd473Swhl739 if (tfe->af == AF_INET)
1921127dd473Swhl739 paddr = &tfe->a.a4.dip;
1922127dd473Swhl739 else
1923127dd473Swhl739 paddr = &tfe->a.a6.dip6;
1924127dd473Swhl739
1925127dd473Swhl739 inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
1926127dd473Swhl739 printf("%s%s", comma, tbuf);
1927127dd473Swhl739 comma = ",";
1928127dd473Swhl739 }
1929127dd473Swhl739
1930127dd473Swhl739 if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) {
1931127dd473Swhl739 printf("%s%d", comma, ntohs(tfe->dport));
1932127dd473Swhl739 comma = ",";
1933127dd473Swhl739 }
1934127dd473Swhl739
1935127dd473Swhl739 printf(" %s\n", pval);
1936127dd473Swhl739 }
1937127dd473Swhl739 }
1938127dd473Swhl739
1939127dd473Swhl739 static int
table_do_get_stdlist(uint16_t opcode,ipfw_obj_lheader ** polh)1940127dd473Swhl739 table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh)
1941127dd473Swhl739 {
1942127dd473Swhl739 ipfw_obj_lheader req, *olh;
1943127dd473Swhl739 size_t sz;
1944127dd473Swhl739
1945127dd473Swhl739 memset(&req, 0, sizeof(req));
1946127dd473Swhl739 sz = sizeof(req);
1947127dd473Swhl739
1948127dd473Swhl739 if (do_get3(opcode, &req.opheader, &sz) != 0)
1949127dd473Swhl739 if (errno != ENOMEM)
1950127dd473Swhl739 return (errno);
1951127dd473Swhl739
1952127dd473Swhl739 sz = req.size;
1953127dd473Swhl739 if ((olh = calloc(1, sz)) == NULL)
1954127dd473Swhl739 return (ENOMEM);
1955127dd473Swhl739
1956127dd473Swhl739 olh->size = sz;
1957127dd473Swhl739 if (do_get3(opcode, &olh->opheader, &sz) != 0) {
1958127dd473Swhl739 free(olh);
1959127dd473Swhl739 return (errno);
1960127dd473Swhl739 }
1961127dd473Swhl739
1962127dd473Swhl739 *polh = olh;
1963127dd473Swhl739 return (0);
1964127dd473Swhl739 }
1965127dd473Swhl739
1966127dd473Swhl739 static int
table_do_get_algolist(ipfw_obj_lheader ** polh)1967127dd473Swhl739 table_do_get_algolist(ipfw_obj_lheader **polh)
1968127dd473Swhl739 {
1969127dd473Swhl739
1970127dd473Swhl739 return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh));
1971127dd473Swhl739 }
1972127dd473Swhl739
1973127dd473Swhl739 static int
table_do_get_vlist(ipfw_obj_lheader ** polh)1974127dd473Swhl739 table_do_get_vlist(ipfw_obj_lheader **polh)
1975127dd473Swhl739 {
1976127dd473Swhl739
1977127dd473Swhl739 return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh));
1978127dd473Swhl739 }
1979127dd473Swhl739
1980127dd473Swhl739 void
ipfw_list_ta(int ac __unused,char * av[]__unused)198122ce4affSfengbojiang ipfw_list_ta(int ac __unused, char *av[] __unused)
1982127dd473Swhl739 {
1983127dd473Swhl739 ipfw_obj_lheader *olh;
1984127dd473Swhl739 ipfw_ta_info *info;
1985127dd473Swhl739 const char *atype;
198622ce4affSfengbojiang uint32_t i;
198722ce4affSfengbojiang int error;
1988127dd473Swhl739
1989127dd473Swhl739 error = table_do_get_algolist(&olh);
1990127dd473Swhl739 if (error != 0)
1991127dd473Swhl739 err(EX_OSERR, "Unable to request algorithm list");
1992127dd473Swhl739
1993127dd473Swhl739 info = (ipfw_ta_info *)(olh + 1);
1994127dd473Swhl739 for (i = 0; i < olh->count; i++) {
1995127dd473Swhl739 if ((atype = match_value(tabletypes, info->type)) == NULL)
1996127dd473Swhl739 atype = "unknown";
1997127dd473Swhl739 printf("--- %s ---\n", info->algoname);
1998127dd473Swhl739 printf(" type: %s\n refcount: %u\n", atype, info->refcnt);
1999127dd473Swhl739
2000127dd473Swhl739 info = (ipfw_ta_info *)((caddr_t)info + olh->objsize);
2001127dd473Swhl739 }
2002127dd473Swhl739
2003127dd473Swhl739 free(olh);
2004127dd473Swhl739 }
2005127dd473Swhl739
2006127dd473Swhl739
2007127dd473Swhl739 /* Copy of current kernel table_value structure */
2008127dd473Swhl739 struct _table_value {
2009127dd473Swhl739 uint32_t tag; /* O_TAG/O_TAGGED */
2010127dd473Swhl739 uint32_t pipe; /* O_PIPE/O_QUEUE */
2011127dd473Swhl739 uint16_t divert; /* O_DIVERT/O_TEE */
2012127dd473Swhl739 uint16_t skipto; /* skipto, CALLRET */
2013127dd473Swhl739 uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */
2014127dd473Swhl739 uint32_t fib; /* O_SETFIB */
2015127dd473Swhl739 uint32_t nat; /* O_NAT */
2016127dd473Swhl739 uint32_t nh4;
2017127dd473Swhl739 uint8_t dscp;
2018127dd473Swhl739 uint8_t spare0;
2019127dd473Swhl739 uint16_t spare1;
2020127dd473Swhl739 /* -- 32 bytes -- */
2021127dd473Swhl739 struct in6_addr nh6;
2022127dd473Swhl739 uint32_t limit; /* O_LIMIT */
2023127dd473Swhl739 uint32_t zoneid;
2024127dd473Swhl739 uint64_t refcnt; /* Number of references */
2025127dd473Swhl739 };
2026127dd473Swhl739
202722ce4affSfengbojiang static int
compare_values(const void * _a,const void * _b)2028127dd473Swhl739 compare_values(const void *_a, const void *_b)
2029127dd473Swhl739 {
203022ce4affSfengbojiang const struct _table_value *a, *b;
2031127dd473Swhl739
203222ce4affSfengbojiang a = (const struct _table_value *)_a;
203322ce4affSfengbojiang b = (const struct _table_value *)_b;
2034127dd473Swhl739
2035127dd473Swhl739 if (a->spare1 < b->spare1)
2036127dd473Swhl739 return (-1);
2037127dd473Swhl739 else if (a->spare1 > b->spare1)
2038127dd473Swhl739 return (1);
2039127dd473Swhl739
2040127dd473Swhl739 return (0);
2041127dd473Swhl739 }
2042127dd473Swhl739
2043127dd473Swhl739 void
ipfw_list_values(int ac __unused,char * av[]__unused)204422ce4affSfengbojiang ipfw_list_values(int ac __unused, char *av[] __unused)
2045127dd473Swhl739 {
204622ce4affSfengbojiang char buf[128];
2047127dd473Swhl739 ipfw_obj_lheader *olh;
2048127dd473Swhl739 struct _table_value *v;
204922ce4affSfengbojiang uint32_t i, vmask;
205022ce4affSfengbojiang int error;
2051127dd473Swhl739
2052127dd473Swhl739 error = table_do_get_vlist(&olh);
2053127dd473Swhl739 if (error != 0)
2054127dd473Swhl739 err(EX_OSERR, "Unable to request value list");
2055127dd473Swhl739
2056127dd473Swhl739 vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */
2057127dd473Swhl739
2058127dd473Swhl739 table_print_valheader(buf, sizeof(buf), vmask);
2059127dd473Swhl739 printf("HEADER: %s\n", buf);
2060127dd473Swhl739 v = (struct _table_value *)(olh + 1);
2061127dd473Swhl739 qsort(v, olh->count, olh->objsize, compare_values);
2062127dd473Swhl739 for (i = 0; i < olh->count; i++) {
2063127dd473Swhl739 table_show_value(buf, sizeof(buf), (ipfw_table_value *)v,
2064127dd473Swhl739 vmask, 0);
2065127dd473Swhl739 printf("[%u] refs=%lu %s\n", v->spare1, (u_long)v->refcnt, buf);
2066127dd473Swhl739 v = (struct _table_value *)((caddr_t)v + olh->objsize);
2067127dd473Swhl739 }
2068127dd473Swhl739
2069127dd473Swhl739 free(olh);
2070127dd473Swhl739 }
2071127dd473Swhl739
2072127dd473Swhl739 int
table_check_name(const char * tablename)2073127dd473Swhl739 table_check_name(const char *tablename)
2074127dd473Swhl739 {
2075127dd473Swhl739
2076127dd473Swhl739 if (ipfw_check_object_name(tablename) != 0)
2077127dd473Swhl739 return (EINVAL);
2078127dd473Swhl739 /* Restrict some 'special' names */
2079127dd473Swhl739 if (strcmp(tablename, "all") == 0)
2080127dd473Swhl739 return (EINVAL);
2081127dd473Swhl739 return (0);
2082127dd473Swhl739 }
2083127dd473Swhl739
2084