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