2 * Copyright (c) 2014 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Bill Yuan <bycn82@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 #include <netinet/in.h>
53 #include <arpa/inet.h>
55 #include <net/route.h>
58 #include "../../../sys/net/ipfw3/ip_fw3.h"
59 #include "../../../sbin/ipfw3/ipfw.h"
60 #include "ipfw3_basic.h"
63 #define IP_MASK_ALL 0xffffffff
65 * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
66 * This is only used in this code.
68 #define IPPROTO_ETHERTYPE 0x1000
71 struct char_int_map limit_types[] = {
79 static struct char_int_map ether_types[] = {
88 { "pppoe_disc", 0x8863 },
89 { "pppoe_sess", 0x8864 },
90 { "ipx_8022", 0x00E0 },
91 { "ipx_8023", 0x0000 },
93 { "ipx_snap", 0x8137 },
100 * match_token takes a table and a string, returns the value associated
101 * with the string (0 meaning an error in most cases)
104 match_token(struct char_int_map *table, char *string)
107 if (strcmp(table->key, string) == 0)
116 match_token2(struct char_int_map *table, int val)
119 if (table->val == val)
128 fill_iface(ipfw_insn_if *cmd, char *arg)
131 cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
133 /* Parse the interface or address */
134 if (!strcmp(arg, "any")){
136 } else if (!isdigit(*arg)) {
137 strlcpy(cmd->name, arg, sizeof(cmd->name));
138 cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
139 } else if (!inet_aton(arg, &cmd->p.ip))
140 errx(EX_DATAERR, "bad ip address ``%s''", arg);
144 lookup_host (char *host, struct in_addr *ipaddr)
148 if (!inet_aton(host, ipaddr)) {
149 if ((he = gethostbyname(host)) == NULL)
151 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
157 * Like strtol, but also translates service names into port numbers
158 * for some protocols.
160 * proto == -1 disables the protocol check;
161 * proto == IPPROTO_ETHERTYPE looks up an internal table
162 * proto == <some value in /etc/protocols> matches the values there.
163 * Returns *end == s in case the parameter is not found.
166 strtoport(char *s, char **end, int base, int proto)
172 *end = s; /* default - not found */
174 return 0; /* not found */
177 return strtol(s, end, base);
180 * find separator. '\\' escapes the next char.
182 for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) {
183 if (*s1 == '\\' && s1[1] != '\0')
187 buf = malloc(s1 - s + 1);
192 * copy into a buffer skipping backslashes
194 for (p = s, i = 0; p != s1 ; p++)
199 if (proto == IPPROTO_ETHERTYPE) {
200 i = match_token(ether_types, buf);
202 if (i != -1) { /* found */
207 struct protoent *pe = NULL;
211 pe = getprotobynumber(proto);
213 se = getservbyname(buf, pe ? pe->p_name : NULL);
217 return ntohs(se->s_port);
220 return 0; /* not found */
224 contigmask(u_char *p, int len)
227 for (i=0; i<len ; i++) {
228 if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
231 for (n=i+1; n < len; n++) {
232 if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
233 return -1; /* mask not contiguous */
239 fill_ip(ipfw_insn_ip *cmd, char *av)
241 char *p = NULL, md = 0;
244 cmd->o.len &= ~F_LEN_MASK; /* zero len */
246 if (!strncmp(av, "any", strlen(av)))
249 if (!strncmp(av, "me", strlen(av))) {
250 cmd->o.len |= F_INSN_SIZE(ipfw_insn);
263 if (lookup_host(av, &cmd->addr) != 0)
264 errx(EX_NOHOST, "hostname ``%s'' unknown", av);
267 if (!inet_aton(p, &cmd->mask))
268 errx(EX_DATAERR, "bad netmask ``%s''", p);
273 cmd->mask.s_addr = htonl(0);
275 errx(EX_DATAERR, "bad width ``%s''", p);
277 cmd->mask.s_addr = htonl(~0 << (32 - i));
280 cmd->mask.s_addr = htonl(~0);
283 cmd->addr.s_addr &= cmd->mask.s_addr;
290 int i = contigmask((u_char *)&(cmd->mask), 32);
292 if (i < 24 || i > 31) {
293 fprintf(stderr, "invalid set with mask %d\n", i);
296 cmd->o.arg1 = 1<<(32-i);
297 cmd->addr.s_addr = ntohl(cmd->addr.s_addr);
298 d = (u_int32_t *)&cmd->mask;
299 cmd->o.opcode = O_BASIC_IP_DST_SET; /* default */
300 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
301 for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
302 d[i] = 0; /* clear masks */
305 low = cmd->addr.s_addr & 0xff;
306 high = low + cmd->o.arg1 - 1;
307 while (isdigit(*av)) {
309 u_int16_t a = strtol(av, &s, 0);
311 if (s == av) /* no parameter */
313 if (a < low || a > high) {
315 "addr %d out of range [%d-%d]\n",
320 d[ a/32] |= 1<<(a & 31);
328 if (cmd->mask.s_addr == 0) {
329 if (cmd->o.len & F_NOT)
330 errx(EX_DATAERR, "not any never matches");
331 else /* useless, nuke it */
333 } else if (cmd->mask.s_addr == IP_MASK_ALL) {
334 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
335 } else { /* addr/mask */
336 cmd->o.len |= F_INSN_SIZE(ipfw_insn_ip);
337 printf("cmd->o.len=%d\n", cmd->o.len);
342 static ipfw_insn *add_srcip(ipfw_insn *cmd, char *av)
344 fill_ip((ipfw_insn_ip *)cmd, av);
345 if ((int)cmd->opcode == O_BASIC_IP_DST_SET) /* set */
346 cmd->opcode = O_BASIC_IP_SRC_SET;
347 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
348 cmd->opcode = O_BASIC_IP_SRC_ME;
349 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
350 cmd->opcode = O_BASIC_IP_SRC;
351 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
352 cmd->opcode = O_BASIC_IP_SRC_MASK;
357 static ipfw_insn *add_dstip(ipfw_insn *cmd, char *av)
359 fill_ip((ipfw_insn_ip *)cmd, av);
360 if ((int)cmd->opcode == O_BASIC_IP_DST_SET)
361 cmd->opcode = O_BASIC_IP_DST_SET;
362 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))
363 cmd->opcode = O_BASIC_IP_DST_ME;
364 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
365 cmd->opcode = O_BASIC_IP_DST;
366 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip))
367 cmd->opcode = O_BASIC_IP_DST_MASK;
372 static ipfw_insn *add_proto(ipfw_insn *cmd, char *av)
376 if (!strncmp(av, "all", strlen(av))) {
378 } else if ((proto = atoi(av)) > 0) {
380 } else if ((pe = getprotobyname(av)) != NULL) {
383 errx(EX_USAGE, "protocol `%s' not recognizable\n", av);
385 if (proto != IPPROTO_IP) {
386 cmd->opcode = O_BASIC_PROTO;
387 cmd->module = MODULE_BASIC_ID;
388 cmd->len = cmd->len|LEN_OF_IPFWINSN;
395 parse_count(ipfw_insn **cmd, int *ac, char **av[])
397 (*cmd)->opcode = O_BASIC_COUNT;
398 (*cmd)->module = MODULE_BASIC_ID;
399 (*cmd)->len = LEN_OF_IPFWINSN;
404 parse_skipto(ipfw_insn **cmd, int *ac, char **av[])
407 (*cmd)->opcode = O_BASIC_SKIPTO;
408 (*cmd)->module = MODULE_BASIC_ID;
409 (*cmd)->len = LEN_OF_IPFWINSN;
410 (*cmd)->arg1 = strtoul(**av, NULL, 10);
415 * cmd->arg3 is count of the destination
416 * cmd->arg1 is the type, random 0, round-robin 1, sticky 2
419 parse_forward(ipfw_insn **cmd, int *ac, char **av[])
421 ipfw_insn_sa *p = (ipfw_insn_sa *)(*cmd);
422 struct sockaddr_in *sa;
423 char *tok, *end = '\0';
427 (*cmd)->opcode = O_BASIC_FORWARD;
430 * multiple forward destinations are seperated by colon
431 * ip address and port are seperated by comma
432 * e.g. 192.168.1.1:80,192.168.1.2:8080
433 * 192.168.1.1,192.168.1.2 or keep the port the same
435 tok = strtok(**av, ",");
438 while (tok != NULL) {
439 sa->sin_len = sizeof(struct sockaddr_in);
440 sa->sin_family = AF_INET;
442 str = strchr(tok,':');
445 port = strtoport(str, &end, 0, 0);
446 sa->sin_port = (u_short)port;
448 lookup_host(tok, &(sa->sin_addr));
449 tok = strtok (NULL, ",");
453 (*cmd)->arg3 = count;
455 errx(EX_DATAERR, "forward `%s' not recognizable", **av);
459 if (strcmp(**av, "round-robin") == 0) {
462 } else if (strcmp(**av, "sticky") == 0) {
470 (*cmd)->len = LEN_OF_IPFWINSN + count * sizeof(struct sockaddr_in);
474 parse_in(ipfw_insn **cmd, int *ac, char **av[])
476 (*cmd)->opcode = O_BASIC_IN;
477 (*cmd)->module = MODULE_BASIC_ID;
478 (*cmd)->len = LEN_OF_IPFWINSN;
484 parse_out(ipfw_insn **cmd, int *ac, char **av[])
486 (*cmd)->opcode = O_BASIC_OUT;
487 (*cmd)->module = MODULE_BASIC_ID;
488 (*cmd)->len = LEN_OF_IPFWINSN;
495 parse_via(ipfw_insn **cmd, int *ac, char **av[])
497 (*cmd)->module = MODULE_BASIC_ID;
498 (*cmd)->len = LEN_OF_IPFWINSN;
499 if (strcmp(*av[0], "via")==0) {
500 (*cmd)->opcode = O_BASIC_VIA;
501 } else if (strcmp(*av[0], "xmit")==0) {
502 (*cmd)->opcode = O_BASIC_XMIT;
503 } else if (strcmp(*av[0], "recv")==0) {
504 (*cmd)->opcode = O_BASIC_RECV;
507 fill_iface((ipfw_insn_if *)(*cmd), *av[0]);
512 parse_from(ipfw_insn **cmd, int *ac, char **av[])
514 (*cmd)->module = MODULE_BASIC_ID;
516 add_srcip(*cmd, **av);
521 parse_to(ipfw_insn **cmd, int *ac, char **av[])
523 (*cmd)->module = MODULE_BASIC_ID;
525 add_dstip(*cmd, **av);
530 parse_proto(ipfw_insn **cmd, int *ac, char **av[])
532 add_proto(*cmd, **av);
537 parse_prob(ipfw_insn **cmd, int *ac, char **av[])
540 (*cmd)->opcode = O_BASIC_PROB;
541 (*cmd)->module = MODULE_BASIC_ID;
542 (*cmd)->len = LEN_OF_IPFWINSN;
543 (*cmd)->arg1 = strtoul(**av, NULL, 10);
548 parse_keep_state(ipfw_insn **cmd, int *ac, char **av[])
551 (*cmd)->opcode = O_BASIC_KEEP_STATE;
552 (*cmd)->module = MODULE_BASIC_ID;
553 (*cmd)->len = LEN_OF_IPFWINSN;
554 if (strcmp(**av, "limit") == 0) {
556 (*cmd)->arg3 = match_token(limit_types, **av);
557 if ((*cmd)->arg3 == 0)
558 errx(EX_DATAERR, "limit `%s' not recognizable", **av);
561 (*cmd)->arg1 = strtoul(**av, NULL, 10);
562 if ((*cmd)->arg1 == 0)
563 errx(EX_DATAERR, "bad limit `%s'", **av);
567 if (strcmp(**av, "live") == 0) {
569 (*cmd)->arg2 = strtoul(**av, NULL, 10);
575 parse_check_state(ipfw_insn **cmd, int *ac, char **av[])
578 (*cmd)->opcode = O_BASIC_CHECK_STATE;
579 (*cmd)->module = MODULE_BASIC_ID;
580 (*cmd)->len = LEN_OF_IPFWINSN;
584 parse_tagged(ipfw_insn **cmd, int *ac, char **av[])
587 (*cmd)->opcode = O_BASIC_TAGGED;
588 (*cmd)->module = MODULE_BASIC_ID;
589 (*cmd)->len = LEN_OF_IPFWINSN;
590 (*cmd)->arg1 = strtoul(**av, NULL, 10);
595 parse_comment(ipfw_insn **cmd, int *ac, char **av[])
598 char *p = (char *)((*cmd) + 1);
601 (*cmd)->opcode = O_BASIC_COMMENT;
602 (*cmd)->module = MODULE_BASIC_ID;
605 l += strlen(**av) + 1;
607 errx(EX_DATAERR, "comment too long (max 80 chars)");
620 parse_tag(ipfw_insn **cmd, int *ac, char **av[])
623 (*cmd)->opcode = O_BASIC_TAG;
624 (*cmd)->module = MODULE_BASIC_ID;
625 (*cmd)->len = LEN_OF_IPFWINSN;
626 (*cmd)->arg1 = strtoul(**av, NULL, 10);
631 parse_untag(ipfw_insn **cmd, int *ac, char **av[])
634 (*cmd)->opcode = O_BASIC_UNTAG;
635 (*cmd)->module = MODULE_BASIC_ID;
636 (*cmd)->len = LEN_OF_IPFWINSN;
637 (*cmd)->arg1 = strtoul(**av, NULL, 10);
642 show_count(ipfw_insn *cmd)
648 show_skipto(ipfw_insn *cmd)
650 printf(" skipto %u", cmd->arg1);
654 show_forward(ipfw_insn *cmd)
656 struct sockaddr_in *sa;
659 ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
662 for (i = 0; i < cmd->arg3; i++){
668 printf("%s", inet_ntoa(sa->sin_addr));
669 if (sa->sin_port != 0)
670 printf(":%d", sa->sin_port);
675 printf(" round-robin");
676 else if (cmd->arg1 == 2)
682 show_in(ipfw_insn *cmd)
688 show_out(ipfw_insn *cmd)
694 show_via(ipfw_insn *cmd)
697 ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
699 if ((int)cmd->opcode == O_BASIC_XMIT)
701 else if ((int)cmd->opcode == O_BASIC_RECV)
703 else if ((int)cmd->opcode == O_BASIC_VIA)
708 if (cmdif->name[0] == '\0')
709 printf(" %s %s", s, inet_ntoa(cmdif->p.ip));
711 printf(" %s %s", s, cmdif->name);
715 show_from(ipfw_insn *cmd)
717 printf(" from %s", inet_ntoa(((ipfw_insn_ip *)cmd)->addr));
721 show_to(ipfw_insn *cmd)
723 printf(" to %s", inet_ntoa(((ipfw_insn_ip *)cmd)->addr));
727 show_proto(ipfw_insn *cmd)
732 pe = getprotobynumber(cmd->arg1);
733 printf(" %s", pe->p_name);
737 show_prob(ipfw_insn *cmd)
739 printf(" prob %d%%", cmd->arg1);
743 show_keep_state(ipfw_insn *cmd)
745 printf(" keep-state");
746 if (cmd->arg1 != 0) {
747 char *type=match_token2(limit_types, cmd->arg3);
748 printf(" limit %s %d", type, cmd->arg1);
750 if (cmd->arg2 != 0) {
751 printf(" live %d", cmd->arg2);
756 show_check_state(ipfw_insn *cmd)
758 printf(" check-state");
762 show_tagged(ipfw_insn *cmd)
764 printf(" tagged %d", cmd->arg1);
768 show_comment(ipfw_insn *cmd)
770 printf(" // %s", (char *)(cmd + 1));
774 show_tag(ipfw_insn *cmd)
776 printf(" tag %d", cmd->arg1);
780 show_untag(ipfw_insn *cmd)
782 printf(" untag %d", cmd->arg1);
786 load_module(register_func function, register_keyword keyword)
788 keyword(MODULE_BASIC_ID, O_BASIC_COUNT, "count",
789 IPFW_KEYWORD_TYPE_ACTION);
790 function(MODULE_BASIC_ID, O_BASIC_COUNT,
791 (parser_func)parse_count, (shower_func)show_count);
793 keyword(MODULE_BASIC_ID, O_BASIC_SKIPTO, "skipto",
794 IPFW_KEYWORD_TYPE_ACTION);
795 function(MODULE_BASIC_ID, O_BASIC_SKIPTO,
796 (parser_func)parse_skipto, (shower_func)show_skipto);
798 keyword(MODULE_BASIC_ID, O_BASIC_FORWARD, "forward",
799 IPFW_KEYWORD_TYPE_ACTION);
800 function(MODULE_BASIC_ID, O_BASIC_FORWARD,
801 (parser_func)parse_forward, (shower_func)show_forward);
803 keyword(MODULE_BASIC_ID, O_BASIC_IN, "in", IPFW_KEYWORD_TYPE_FILTER);
804 function(MODULE_BASIC_ID, O_BASIC_IN,
805 (parser_func)parse_in, (shower_func)show_in);
807 keyword(MODULE_BASIC_ID, O_BASIC_OUT, "out", IPFW_KEYWORD_TYPE_FILTER);
808 function(MODULE_BASIC_ID, O_BASIC_OUT,
809 (parser_func)parse_out, (shower_func)show_out);
811 keyword(MODULE_BASIC_ID, O_BASIC_VIA, "via", IPFW_KEYWORD_TYPE_FILTER);
812 function(MODULE_BASIC_ID, O_BASIC_VIA,
813 (parser_func)parse_via, (shower_func)show_via);
815 keyword(MODULE_BASIC_ID, O_BASIC_XMIT, "xmit",
816 IPFW_KEYWORD_TYPE_FILTER);
817 function(MODULE_BASIC_ID, O_BASIC_XMIT,
818 (parser_func)parse_via, (shower_func)show_via);
820 keyword(MODULE_BASIC_ID, O_BASIC_RECV, "recv",
821 IPFW_KEYWORD_TYPE_FILTER);
822 function(MODULE_BASIC_ID, O_BASIC_RECV,
823 (parser_func)parse_via, (shower_func)show_via);
825 keyword(MODULE_BASIC_ID, O_BASIC_IP_SRC, "from",
826 IPFW_KEYWORD_TYPE_FILTER);
827 function(MODULE_BASIC_ID, O_BASIC_IP_SRC,
828 (parser_func)parse_from, (shower_func)show_from);
830 keyword(MODULE_BASIC_ID, O_BASIC_IP_DST, "to",
831 IPFW_KEYWORD_TYPE_FILTER);
832 function(MODULE_BASIC_ID, O_BASIC_IP_DST,
833 (parser_func)parse_to, (shower_func)show_to);
835 keyword(MODULE_BASIC_ID, O_BASIC_PROTO, "proto",
836 IPFW_KEYWORD_TYPE_FILTER);
837 function(MODULE_BASIC_ID, O_BASIC_PROTO,
838 (parser_func)parse_proto, (shower_func)show_proto);
840 keyword(MODULE_BASIC_ID, O_BASIC_PROB, "prob",
841 IPFW_KEYWORD_TYPE_FILTER);
842 function(MODULE_BASIC_ID, O_BASIC_PROB,
843 (parser_func)parse_prob, (shower_func)show_prob);
845 keyword(MODULE_BASIC_ID, O_BASIC_KEEP_STATE, "keep-state",
846 IPFW_KEYWORD_TYPE_FILTER);
847 function(MODULE_BASIC_ID, O_BASIC_KEEP_STATE,
848 (parser_func)parse_keep_state,
849 (shower_func)show_keep_state);
851 keyword(MODULE_BASIC_ID, O_BASIC_CHECK_STATE, "check-state",
852 IPFW_KEYWORD_TYPE_OTHERS);
853 function(MODULE_BASIC_ID, O_BASIC_CHECK_STATE,
854 (parser_func)parse_check_state,
855 (shower_func)show_check_state);
857 keyword(MODULE_BASIC_ID, O_BASIC_TAG, "tag", IPFW_KEYWORD_TYPE_OTHERS);
858 function(MODULE_BASIC_ID, O_BASIC_TAG,
859 (parser_func)parse_tag, (shower_func)show_tag);
861 keyword(MODULE_BASIC_ID, O_BASIC_UNTAG, "untag",
862 IPFW_KEYWORD_TYPE_OTHERS);
863 function(MODULE_BASIC_ID, O_BASIC_UNTAG,
864 (parser_func)parse_untag, (shower_func)show_untag);
866 keyword(MODULE_BASIC_ID, O_BASIC_TAGGED, "tagged",
867 IPFW_KEYWORD_TYPE_FILTER);
868 function(MODULE_BASIC_ID, O_BASIC_TAGGED,
869 (parser_func)parse_tagged, (shower_func)show_tagged);
871 keyword(MODULE_BASIC_ID, O_BASIC_COMMENT, "//",
872 IPFW_KEYWORD_TYPE_FILTER);
873 function(MODULE_BASIC_ID, O_BASIC_COMMENT,
874 (parser_func)parse_comment, (shower_func)show_comment);