diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile --- a/sbin/ipfw/Makefile +++ b/sbin/ipfw/Makefile @@ -4,6 +4,10 @@ PACKAGE=ipfw PROG= ipfw + +LINKS= ${BINDIR}/ipfw ${BINDIR}/dnctl +MLINKS= ipfw.8 dnctl.8 + SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c SRCS+= nat64clat.c nat64lsn.c nat64stl.c nptv6.c diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,11 +1,11 @@ .\" .\" $FreeBSD$ .\" -.Dd June 4, 2021 +.Dd June 14, 2021 .Dt IPFW 8 .Os .Sh NAME -.Nm ipfw +.Nm ipfw , dnctl .Nd User interface for firewall, traffic shaper, packet scheduler, in-kernel NAT. .Sh SYNOPSIS @@ -88,12 +88,12 @@ .Brq Ar name | all .Cm flush .Ss DUMMYNET CONFIGURATION (TRAFFIC SHAPER AND PACKET SCHEDULER) -.Nm +.Nm dnctl .Brq Cm pipe | queue | sched .Ar number .Cm config .Ar config-options -.Nm +.Nm dnctl .Op Fl s Op Ar field .Brq Cm pipe | queue | sched .Brq Cm delete | list | show @@ -440,7 +440,7 @@ frequently required arguments like IP addresses. .Ss TRAFFIC SHAPER CONFIGURATION The -.Nm +.Nm dnctl .Cm pipe , queue and .Cm sched @@ -2650,11 +2650,11 @@ A value of 0 (default) means unlimited bandwidth. The unit must immediately follow the number, as in .Pp -.Dl "ipfw pipe 1 config bw 300Kbit/s" +.Dl "dnctl pipe 1 config bw 300Kbit/s" .Pp If a device name is specified instead of a numeric value, as in .Pp -.Dl "ipfw pipe 1 config bw tun0" +.Dl "dnctl pipe 1 config bw tun0" .Pp then the transmit clock is supplied by the specified device. At the moment only the @@ -2731,7 +2731,7 @@ a separator and '#' indicating the beginning a comment: .Bl -tag -width indent .It Cm name Ar identifier -optional name (listed by "ipfw pipe show") +optional name (listed by "dnctl pipe show") to identify the delay distribution; .It Cm bw Ar value the bandwidth used for the pipe. @@ -4356,15 +4356,15 @@ .Nm dummynet pipes: .Pp -.Dl "ipfw add pipe 10 ip from any to any" -.Dl "ipfw pipe 10 config plr 0.05" +.Dl "dnctl add pipe 10 ip from any to any" +.Dl "dnctl pipe 10 config plr 0.05" .Pp We can use pipes to artificially limit bandwidth, e.g.\& on a machine acting as a router, if we want to limit traffic from local clients on 192.168.2.0/24 we do: .Pp .Dl "ipfw add pipe 1 ip from 192.168.2.0/24 to any out" -.Dl "ipfw pipe 1 config bw 300Kbit/s queue 50KBytes" +.Dl "dnctl pipe 1 config bw 300Kbit/s queue 50KBytes" .Pp note that we use the .Cm out @@ -4378,8 +4378,8 @@ .Pp .Dl "ipfw add pipe 1 ip from any to any out" .Dl "ipfw add pipe 2 ip from any to any in" -.Dl "ipfw pipe 1 config bw 64Kbit/s queue 10Kbytes" -.Dl "ipfw pipe 2 config bw 64Kbit/s queue 10Kbytes" +.Dl "dnctl pipe 1 config bw 64Kbit/s queue 10Kbytes" +.Dl "dnctl pipe 2 config bw 64Kbit/s queue 10Kbytes" .Pp The above can be very useful, e.g.\& if you want to see how your fancy Web page will look for a residential user who @@ -4394,7 +4394,7 @@ management algorithm: .Pp .Dl "ipfw add pipe 1 ip from any to any" -.Dl "ipfw pipe 1 config bw 500Kbit/s queue 100 red 0.002/30/80/0.1" +.Dl "dnctl pipe 1 config bw 500Kbit/s queue 100 red 0.002/30/80/0.1" .Pp Another typical application of the traffic shaper is to introduce some delay in the communication. @@ -4405,8 +4405,8 @@ .Pp .Dl "ipfw add pipe 1 ip from any to any out" .Dl "ipfw add pipe 2 ip from any to any in" -.Dl "ipfw pipe 1 config delay 250ms bw 1Mbit/s" -.Dl "ipfw pipe 2 config delay 250ms bw 1Mbit/s" +.Dl "dnctl pipe 1 config delay 250ms bw 1Mbit/s" +.Dl "dnctl pipe 2 config delay 250ms bw 1Mbit/s" .Pp Per-flow queueing can be useful for a variety of purposes. A very simple one is counting traffic: @@ -4414,7 +4414,7 @@ .Dl "ipfw add pipe 1 tcp from any to any" .Dl "ipfw add pipe 1 udp from any to any" .Dl "ipfw add pipe 1 ip from any to any" -.Dl "ipfw pipe 1 config mask all" +.Dl "dnctl pipe 1 config mask all" .Pp The above set of rules will create queues (and collect statistics) for all traffic. @@ -4432,8 +4432,8 @@ .Pp .Dl "ipfw add pipe 1 ip from 192.168.2.0/24 to any out" .Dl "ipfw add pipe 2 ip from any to 192.168.2.0/24 in" -.Dl "ipfw pipe 1 config mask src-ip 0x000000ff bw 200Kbit/s queue 20Kbytes" -.Dl "ipfw pipe 2 config mask dst-ip 0x000000ff bw 200Kbit/s queue 20Kbytes" +.Dl "dnctl pipe 1 config mask src-ip 0x000000ff bw 200Kbit/s queue 20Kbytes" +.Dl "dnctl pipe 2 config mask dst-ip 0x000000ff bw 200Kbit/s queue 20Kbytes" .Ss LOOKUP TABLES In the following example, we need to create several traffic bandwidth classes and we need different hosts/networks to fall into different classes. @@ -4443,8 +4443,8 @@ that it should use. Then we classify traffic using a single rule: .Pp -.Dl "ipfw pipe 1 config bw 1000Kbyte/s" -.Dl "ipfw pipe 4 config bw 4000Kbyte/s" +.Dl "dnctl pipe 1 config bw 1000Kbyte/s" +.Dl "dnctl pipe 4 config bw 4000Kbyte/s" .Dl "..." .Dl "ipfw table T1 create type addr" .Dl "ipfw table T1 add 192.168.2.0/24 1" @@ -4626,7 +4626,7 @@ AQM using default configuration for traffic from 192.168.0.0/24 and 1Mbits/s rate limit, we do: .Pp -.Dl "ipfw pipe 1 config bw 1mbits/s codel" +.Dl "dnctl pipe 1 config bw 1mbits/s codel" .Dl "ipfw add 100 pipe 1 ip from 192.168.0.0/24 to any" .Pp To configure a @@ -4636,8 +4636,8 @@ AQM using different configurations parameters for traffic from 192.168.0.0/24 and 1Mbits/s rate limit, we do: .Pp -.Dl "ipfw pipe 1 config bw 1mbits/s" -.Dl "ipfw queue 1 config pipe 1 codel target 8ms interval 160ms ecn" +.Dl "dnctl pipe 1 config bw 1mbits/s" +.Dl "dnctl queue 1 config pipe 1 codel target 8ms interval 160ms ecn" .Dl "ipfw add 100 queue 1 ip from 192.168.0.0/24 to any" .Pp To configure a @@ -4647,7 +4647,7 @@ AQM using default configuration for traffic from 192.168.0.0/24 and 1Mbits/s rate limit, we do: .Pp -.Dl "ipfw pipe 1 config bw 1mbits/s pie" +.Dl "dnctl pipe 1 config bw 1mbits/s pie" .Dl "ipfw add 100 pipe 1 ip from 192.168.0.0/24 to any" .Pp To configure a @@ -4657,8 +4657,8 @@ AQM using different configuration parameters for traffic from 192.168.0.0/24 and 1Mbits/s rate limit, we do: .Pp -.Dl "ipfw pipe 1 config bw 1mbits/s" -.Dl "ipfw queue 1 config pipe 1 pie target 20ms tupdate 30ms ecn" +.Dl "dnctl pipe 1 config bw 1mbits/s" +.Dl "dnctl queue 1 config pipe 1 pie target 20ms tupdate 30ms ecn" .Dl "ipfw add 100 queue 1 ip from 192.168.0.0/24 to any" .Pp .Cm fq_codel @@ -4673,9 +4673,9 @@ scheduler using different configurations parameters for traffic from 192.168.0.0/24 and 1Mbits/s rate limit, we do: .Pp -.Dl "ipfw pipe 1 config bw 1mbits/s" -.Dl "ipfw sched 1 config pipe 1 type fq_codel" -.Dl "ipfw queue 1 config sched 1" +.Dl "dnctl pipe 1 config bw 1mbits/s" +.Dl "dnctl sched 1 config pipe 1 type fq_codel" +.Dl "dnctl queue 1 config sched 1" .Dl "ipfw add 100 queue 1 ip from 192.168.0.0/24 to any" .Pp To change @@ -4686,7 +4686,7 @@ .Ar target to 10ms, we do: .Pp -.Dl "ipfw sched 1 config pipe 1 type fq_codel target 10ms noecn" +.Dl "dnctl sched 1 config pipe 1 type fq_codel target 10ms noecn" .Pp Similar to .Cm fq_codel , @@ -4695,9 +4695,9 @@ scheduler using different configurations parameters for traffic from 192.168.0.0/24 and 1Mbits/s rate limit, we do: .Pp -.Dl "ipfw pipe 1 config bw 1mbits/s" -.Dl "ipfw sched 1 config pipe 1 type fq_pie" -.Dl "ipfw queue 1 config sched 1" +.Dl "dnctl pipe 1 config bw 1mbits/s" +.Dl "dnctl sched 1 config pipe 1 type fq_pie" +.Dl "dnctl queue 1 config sched 1" .Dl "ipfw add 100 queue 1 ip from 192.168.0.0/24 to any" .Pp The configurations of diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -20,6 +20,11 @@ * $FreeBSD$ */ +enum cmdline_prog { + cmdline_prog_ipfw, + cmdline_prog_dnctl +}; + /* * Options that can be set on the command line. * When reading commands from a file, a subset of the options can also @@ -54,8 +59,11 @@ uint32_t use_set; /* work with specified set number */ /* 0 means all sets, otherwise apply to set use_set - 1 */ + enum cmdline_prog prog; /* Are we ipfw or dnctl? */ }; +int is_ipfw(void); + enum { TIMESTAMP_NONE = 0, TIMESTAMP_STRING, diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -411,6 +411,12 @@ static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type); +int +is_ipfw(void) +{ + return (g_co.prog == cmdline_prog_ipfw); +} + /* * Simple string buffer API. * Used to simplify buffer passing between function and for diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c --- a/sbin/ipfw/main.c +++ b/sbin/ipfw/main.c @@ -36,7 +36,8 @@ static void help(void) { - fprintf(stderr, + if (is_ipfw()) { + fprintf(stderr, "ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" "\tipfw [-abcdefhnNqStTv] \n\n" "where is one of the following:\n\n" @@ -76,6 +77,16 @@ " setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" " tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" ); + } else { + fprintf(stderr, +"dnctl syntax summary (but please do read the dnctl(8) manpage):\n\n" +"\tdnctl [-hnsv] \n\n" +"where is one of the following:\n\n" +"[pipe|queue|sched] N config PIPE-BODY\n" +"[pipe|queue|sched] {delete|list|show} [N{,N}]\n" +"\n" +); + } exit(0); } @@ -231,7 +242,8 @@ g_co.do_force = !isatty(STDIN_FILENO); #ifdef EMULATE_SYSCTL /* sysctl emulation */ - if ( ac >= 2 && !strcmp(av[1], "sysctl")) { + if (is_ipfw() && ac >= 2 && + !strcmp(av[1], "sysctl")) { char *s; int i; @@ -263,87 +275,115 @@ save_av = av; optind = optreset = 1; /* restart getopt() */ - while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtv")) != -1) - switch (ch) { - case 'a': - do_acct = 1; - break; + if (is_ipfw()) { + while ((ch = getopt(ac, av, "abcdDefhinNp:qs:STtv")) != -1) + switch (ch) { + case 'a': + do_acct = 1; + break; - case 'b': - g_co.comment_only = 1; - g_co.do_compact = 1; - break; + case 'b': + g_co.comment_only = 1; + g_co.do_compact = 1; + break; - case 'c': - g_co.do_compact = 1; - break; + case 'c': + g_co.do_compact = 1; + break; - case 'd': - g_co.do_dynamic = 1; - break; + case 'd': + g_co.do_dynamic = 1; + break; - case 'D': - g_co.do_dynamic = 2; - break; + case 'D': + g_co.do_dynamic = 2; + break; - case 'e': - /* nop for compatibility */ - break; + case 'e': + /* nop for compatibility */ + break; - case 'f': - g_co.do_force = 1; - break; + case 'f': + g_co.do_force = 1; + break; - case 'h': /* help */ - free(save_av); - help(); - break; /* NOTREACHED */ + case 'h': /* help */ + free(save_av); + help(); + break; /* NOTREACHED */ - case 'i': - g_co.do_value_as_ip = 1; - break; + case 'i': + g_co.do_value_as_ip = 1; + break; - case 'n': - g_co.test_only = 1; - break; + case 'n': + g_co.test_only = 1; + break; - case 'N': - g_co.do_resolv = 1; - break; + case 'N': + g_co.do_resolv = 1; + break; - case 'p': - errx(EX_USAGE, "An absolute pathname must be used " - "with -p option."); - /* NOTREACHED */ + case 'p': + errx(EX_USAGE, "An absolute pathname must be used " + "with -p option."); + /* NOTREACHED */ - case 'q': - g_co.do_quiet = 1; - break; + case 'q': + g_co.do_quiet = 1; + break; - case 's': /* sort */ - g_co.do_sort = atoi(optarg); - break; + case 's': /* sort */ + g_co.do_sort = atoi(optarg); + break; - case 'S': - g_co.show_sets = 1; - break; + case 'S': + g_co.show_sets = 1; + break; - case 't': - g_co.do_time = TIMESTAMP_STRING; - break; + case 't': + g_co.do_time = TIMESTAMP_STRING; + break; - case 'T': - g_co.do_time = TIMESTAMP_NUMERIC; - break; + case 'T': + g_co.do_time = TIMESTAMP_NUMERIC; + break; - case 'v': /* verbose */ - g_co.verbose = 1; - break; + case 'v': /* verbose */ + g_co.verbose = 1; + break; - default: - free(save_av); - return 1; - } + default: + free(save_av); + return 1; + } + } else { + while ((ch = getopt(ac, av, "hns:v")) != -1) + switch (ch) { + + case 'h': /* help */ + free(save_av); + help(); + break; /* NOTREACHED */ + + case 'n': + g_co.test_only = 1; + break; + + case 's': /* sort */ + g_co.do_sort = atoi(optarg); + break; + + case 'v': /* verbose */ + g_co.verbose = 1; + break; + + default: + free(save_av); + return 1; + } + + } ac -= optind; av += optind; @@ -367,7 +407,7 @@ g_co.do_nat = 0; g_co.do_pipe = 0; g_co.use_set = 0; - if (!strncmp(*av, "nat", strlen(*av))) + if (is_ipfw() && !strncmp(*av, "nat", strlen(*av))) g_co.do_nat = 1; else if (!strncmp(*av, "pipe", strlen(*av))) g_co.do_pipe = 1; @@ -377,7 +417,7 @@ g_co.do_pipe = 2; else if (_substrcmp(*av, "sched") == 0) g_co.do_pipe = 3; - else if (!strncmp(*av, "set", strlen(*av))) { + else if (is_ipfw() && !strncmp(*av, "set", strlen(*av))) { if (ac > 1 && isdigit(av[1][0])) { g_co.use_set = strtonum(av[1], 0, resvd_set_number, &errstr); @@ -406,8 +446,12 @@ av[1] = p; } + if (! is_ipfw() && g_co.do_pipe == 0) { + help(); + } + if (g_co.use_set == 0) { - if (_substrcmp(*av, "add") == 0) + if (is_ipfw() && _substrcmp(*av, "add") == 0) ipfw_add(av); else if (g_co.do_nat && _substrcmp(*av, "show") == 0) ipfw_show_nat(ac, av); @@ -415,13 +459,13 @@ ipfw_config_pipe(ac, av); else if (g_co.do_nat && _substrcmp(*av, "config") == 0) ipfw_config_nat(ac, av); - else if (_substrcmp(*av, "set") == 0) + else if (is_ipfw() && _substrcmp(*av, "set") == 0) ipfw_sets_handler(av); - else if (_substrcmp(*av, "table") == 0) + else if (is_ipfw() && _substrcmp(*av, "table") == 0) ipfw_table_handler(ac, av); - else if (_substrcmp(*av, "enable") == 0) + else if (is_ipfw() && _substrcmp(*av, "enable") == 0) ipfw_sysctl_handler(av, 1); - else if (_substrcmp(*av, "disable") == 0) + else if (is_ipfw() && _substrcmp(*av, "disable") == 0) ipfw_sysctl_handler(av, 0); else try_next = 1; @@ -430,28 +474,28 @@ if (g_co.use_set || try_next) { if (_substrcmp(*av, "delete") == 0) ipfw_delete(av); - else if (!strncmp(*av, "nat64clat", strlen(*av))) + else if (is_ipfw() && !strncmp(*av, "nat64clat", strlen(*av))) ipfw_nat64clat_handler(ac, av); - else if (!strncmp(*av, "nat64stl", strlen(*av))) + else if (is_ipfw() && !strncmp(*av, "nat64stl", strlen(*av))) ipfw_nat64stl_handler(ac, av); - else if (!strncmp(*av, "nat64lsn", strlen(*av))) + else if (is_ipfw() && !strncmp(*av, "nat64lsn", strlen(*av))) ipfw_nat64lsn_handler(ac, av); - else if (!strncmp(*av, "nptv6", strlen(*av))) + else if (is_ipfw() && !strncmp(*av, "nptv6", strlen(*av))) ipfw_nptv6_handler(ac, av); else if (_substrcmp(*av, "flush") == 0) ipfw_flush(g_co.do_force); - else if (_substrcmp(*av, "zero") == 0) + else if (is_ipfw() && _substrcmp(*av, "zero") == 0) ipfw_zero(ac, av, 0 /* IP_FW_ZERO */); - else if (_substrcmp(*av, "resetlog") == 0) + else if (is_ipfw() && _substrcmp(*av, "resetlog") == 0) ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */); else if (_substrcmp(*av, "print") == 0 || _substrcmp(*av, "list") == 0) ipfw_list(ac, av, do_acct); else if (_substrcmp(*av, "show") == 0) ipfw_list(ac, av, 1 /* show counters */); - else if (_substrcmp(*av, "table") == 0) + else if (is_ipfw() && _substrcmp(*av, "table") == 0) ipfw_table_handler(ac, av); - else if (_substrcmp(*av, "internal") == 0) + else if (is_ipfw() && _substrcmp(*av, "internal") == 0) ipfw_internal_handler(ac, av); else errx(EX_USAGE, "bad command `%s'", *av); @@ -620,12 +664,22 @@ } } #endif + + if (strcmp(av[0], "dnctl") == 0) + g_co.prog = cmdline_prog_dnctl; + else + g_co.prog = cmdline_prog_ipfw; + /* * If the last argument is an absolute pathname, interpret it * as a file to be preprocessed. */ if (ac > 1 && av[ac - 1][0] == '/') { + if (! is_ipfw()) + errx(EX_USAGE, "usage: dnctl [options]\n" + "do \"dnctl -h\" for details"); + if (access(av[ac - 1], R_OK) == 0) ipfw_readfile(ac, av); else @@ -633,8 +687,9 @@ } else { if (ipfw_main(ac, av)) { errx(EX_USAGE, - "usage: ipfw [options]\n" - "do \"ipfw -h\" or \"man ipfw\" for details"); + "usage: %s [options]\n" + "do \"%s -h\" or \"man %s\" for details", av[0], + av[0], av[0]); } } return EX_OK;