freebsd-skq/sbin/ipfw/ipfw.c
luigi 8b2204b3d0 Fix a couple of problems which could cause panics at runtime:
+ setting a bandwidth too large for a pipe (above 2Gbit/s) could
   cause the internal representation (which is int) to wrap to a
   negative number, causing an infinite loop in the kernel;

 + (see PR bin/35628): when configuring RED parameters for a queue,
   the values are not passed to the kernel resulting in panics at
   runtime (part of the problem here is also that the kernel does
   not check for valid parameters being passed, but this will be
   fixed in a separate commit).

These are both critical fixes which need to be merged into 4.6-RELEASE.

MFC after: 1 day
2002-05-05 21:34:10 +00:00

2646 lines
64 KiB
C

/*
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
* Copyright (c) 1994 Ugen J.S.Antsilevich
*
* Idea and grammar partially left from:
* Copyright (c) 1993 Daniel Boulet
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*
* NEW command line interface for IP firewall facility
*
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_fw.h>
#include <net/route.h> /* def. of struct route */
#include <netinet/ip_dummynet.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
int s, /* main RAW socket */
do_resolv, /* Would try to resolve all */
do_acct, /* Show packet/byte count */
do_time, /* Show time stamps */
do_quiet, /* Be quiet in add and flush */
do_force, /* Don't ask for confirmation */
do_pipe, /* this cmd refers to a pipe */
do_sort, /* field to sort results (0 = no) */
do_dynamic, /* display dynamic rules */
do_expired, /* display expired dynamic rules */
verbose;
struct icmpcode {
int code;
char *str;
};
static struct icmpcode icmpcodes[] = {
{ ICMP_UNREACH_NET, "net" },
{ ICMP_UNREACH_HOST, "host" },
{ ICMP_UNREACH_PROTOCOL, "protocol" },
{ ICMP_UNREACH_PORT, "port" },
{ ICMP_UNREACH_NEEDFRAG, "needfrag" },
{ ICMP_UNREACH_SRCFAIL, "srcfail" },
{ ICMP_UNREACH_NET_UNKNOWN, "net-unknown" },
{ ICMP_UNREACH_HOST_UNKNOWN, "host-unknown" },
{ ICMP_UNREACH_ISOLATED, "isolated" },
{ ICMP_UNREACH_NET_PROHIB, "net-prohib" },
{ ICMP_UNREACH_HOST_PROHIB, "host-prohib" },
{ ICMP_UNREACH_TOSNET, "tosnet" },
{ ICMP_UNREACH_TOSHOST, "toshost" },
{ ICMP_UNREACH_FILTER_PROHIB, "filter-prohib" },
{ ICMP_UNREACH_HOST_PRECEDENCE, "host-precedence" },
{ ICMP_UNREACH_PRECEDENCE_CUTOFF, "precedence-cutoff" },
{ 0, NULL }
};
static void show_usage(void);
static int
mask_bits(struct in_addr m_ad)
{
int h_fnd = 0, h_num = 0, i;
u_long mask;
mask = ntohl(m_ad.s_addr);
for (i = 0; i < sizeof(u_long)*CHAR_BIT; i++) {
if (mask & 1L) {
h_fnd = 1;
h_num++;
} else {
if (h_fnd)
return -1;
}
mask = mask >> 1;
}
return h_num;
}
static void
print_port(u_char prot, u_short port, const char comma)
{
struct servent *se = NULL;
struct protoent *pe;
if (comma == ':') {
printf("%c0x%04x", comma, port);
return;
}
if (do_resolv) {
pe = getprotobynumber(prot);
se = getservbyport(htons(port), pe ? pe->p_name : NULL);
}
if (se)
printf("%c%s", comma, se->s_name);
else
printf("%c%d", comma, port);
}
static void
print_iface(char *key, union ip_fw_if *un, int byname)
{
char ifnb[FW_IFNLEN+1];
if (byname) {
strncpy(ifnb, un->fu_via_if.name, FW_IFNLEN);
ifnb[FW_IFNLEN] = '\0';
if (un->fu_via_if.unit == -1)
printf(" %s %s*", key, ifnb);
else
printf(" %s %s%d", key, ifnb, un->fu_via_if.unit);
} else if (un->fu_via_ip.s_addr != 0) {
printf(" %s %s", key, inet_ntoa(un->fu_via_ip));
} else
printf(" %s any", key);
}
static void
print_reject_code(int code)
{
struct icmpcode *ic;
for (ic = icmpcodes; ic->str; ic++)
if (ic->code == code) {
printf("%s", ic->str);
return;
}
printf("%u", code);
}
/**
* _s_x holds a string-int pair for various lookups.
* s=NULL terminates the struct.
*/
struct _s_x { char *s; int x; };
static struct _s_x limit_masks[] = {
{"src-addr", DYN_SRC_ADDR},
{"src-port", DYN_SRC_PORT},
{"dst-addr", DYN_DST_ADDR},
{"dst-port", DYN_DST_PORT},
{NULL, 0} };
static void
show_ipfw(struct ip_fw *chain)
{
char comma;
u_long adrt;
struct hostent *he;
struct protoent *pe;
int i, mb;
int nsp = IP_FW_GETNSRCP(chain);
int ndp = IP_FW_GETNDSTP(chain);
if (do_resolv)
setservent(1/*stay open*/);
printf("%05u ", chain->fw_number);
if (do_acct)
printf("%10qu %10qu ", chain->fw_pcnt, chain->fw_bcnt);
if (do_time) {
if (chain->timestamp) {
char timestr[30];
time_t t = _long_to_time(chain->timestamp);
strcpy(timestr, ctime(&t));
*strchr(timestr, '\n') = '\0';
printf("%s ", timestr);
} else {
printf(" ");
}
}
if (chain->fw_flg == IP_FW_F_CHECK_S) {
printf("check-state\n");
goto done;
}
if (chain->fw_flg & IP_FW_F_RND_MATCH) {
double d = 1.0 * chain->dont_match_prob;
d = 1 - (d / 0x7fffffff);
printf("prob %f ", d);
}
switch (chain->fw_flg & IP_FW_F_COMMAND) {
case IP_FW_F_ACCEPT:
printf("allow");
break;
case IP_FW_F_DENY:
printf("deny");
break;
case IP_FW_F_COUNT:
printf("count");
break;
case IP_FW_F_DIVERT:
printf("divert %u", chain->fw_divert_port);
break;
case IP_FW_F_TEE:
printf("tee %u", chain->fw_divert_port);
break;
case IP_FW_F_SKIPTO:
printf("skipto %u", chain->fw_skipto_rule);
break;
case IP_FW_F_PIPE:
printf("pipe %u", chain->fw_skipto_rule);
break;
case IP_FW_F_QUEUE:
printf("queue %u", chain->fw_skipto_rule);
break;
case IP_FW_F_REJECT:
if (chain->fw_reject_code == IP_FW_REJECT_RST)
printf("reset");
else {
printf("unreach ");
print_reject_code(chain->fw_reject_code);
}
break;
case IP_FW_F_FWD:
printf("fwd %s", inet_ntoa(chain->fw_fwd_ip.sin_addr));
if(chain->fw_fwd_ip.sin_port)
printf(",%d", chain->fw_fwd_ip.sin_port);
break;
default:
errx(EX_OSERR, "impossible");
}
if (chain->fw_flg & IP_FW_F_PRN) {
printf(" log");
if (chain->fw_logamount)
printf(" logamount %d", chain->fw_logamount);
}
pe = getprotobynumber(chain->fw_prot);
if (pe)
printf(" %s", pe->p_name);
else
printf(" %u", chain->fw_prot);
if (chain->fw_flg & IP_FW_F_SME) {
printf(" from me");
} else {
printf(" from %s",
chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
adrt = ntohl(chain->fw_smsk.s_addr);
if (adrt == ULONG_MAX && do_resolv) {
adrt = (chain->fw_src.s_addr);
he = gethostbyaddr((char *)&adrt,
sizeof(u_long), AF_INET);
if (he == NULL)
printf("%s", inet_ntoa(chain->fw_src));
else
printf("%s", he->h_name);
} else if (adrt != ULONG_MAX) {
mb = mask_bits(chain->fw_smsk);
if (mb == 0) {
printf("any");
} else if (mb > 0) {
printf("%s", inet_ntoa(chain->fw_src));
printf("/%d", mb);
} else {
printf("%s", inet_ntoa(chain->fw_src));
printf(":");
printf("%s", inet_ntoa(chain->fw_smsk));
}
} else {
printf("%s", inet_ntoa(chain->fw_src));
}
}
if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
comma = ' ';
for (i = 0; i < nsp; i++) {
print_port(chain->fw_prot,
chain->fw_uar.fw_pts[i], comma);
if (i == 0 && (chain->fw_flg & IP_FW_F_SRNG))
comma = '-';
else if (i == 0 && (chain->fw_flg & IP_FW_F_SMSK))
comma = ':';
else
comma = ',';
}
}
if (chain->fw_flg & IP_FW_F_DME) {
printf(" to me");
} else {
printf(" to %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : "");
adrt = ntohl(chain->fw_dmsk.s_addr);
if (adrt == ULONG_MAX && do_resolv) {
adrt = (chain->fw_dst.s_addr);
he = gethostbyaddr((char *)&adrt,
sizeof(u_long), AF_INET);
if (he == NULL)
printf("%s", inet_ntoa(chain->fw_dst));
else
printf("%s", he->h_name);
} else if (adrt != ULONG_MAX) {
mb = mask_bits(chain->fw_dmsk);
if (mb == 0) {
printf("any");
} else if (mb > 0) {
printf("%s", inet_ntoa(chain->fw_dst));
printf("/%d", mb);
} else {
printf("%s", inet_ntoa(chain->fw_dst));
printf(":");
printf("%s", inet_ntoa(chain->fw_dmsk));
}
} else {
printf("%s", inet_ntoa(chain->fw_dst));
}
}
if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
comma = ' ';
for (i = 0; i < ndp; i++) {
print_port(chain->fw_prot,
chain->fw_uar.fw_pts[nsp+i], comma);
if (i == 0 && (chain->fw_flg & IP_FW_F_DRNG))
comma = '-';
else if (i == 0 && (chain->fw_flg & IP_FW_F_DMSK))
comma = ':';
else
comma = ',';
}
}
if (chain->fw_flg & IP_FW_F_UID) {
struct passwd *pwd = getpwuid(chain->fw_uid);
if (pwd)
printf(" uid %s", pwd->pw_name);
else
printf(" uid %u", chain->fw_uid);
}
if (chain->fw_flg & IP_FW_F_GID) {
struct group *grp = getgrgid(chain->fw_gid);
if (grp)
printf(" gid %s", grp->gr_name);
else
printf(" gid %u", chain->fw_gid);
}
if (chain->fw_flg & IP_FW_F_KEEP_S) {
struct _s_x *p = limit_masks;
switch(chain->dyn_type) {
default:
printf(" *** unknown type ***");
break ;
case DYN_KEEP_STATE:
printf(" keep-state");
break;
case DYN_LIMIT:
printf(" limit");
for ( ; p->s != NULL ; p++)
if (chain->limit_mask & p->x)
printf(" %s", p->s);
printf(" %d", chain->conn_limit);
break ;
}
}
/* Direction */
if (chain->fw_flg & IP_FW_BRIDGED)
printf(" bridged");
if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
printf(" in");
if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
printf(" out");
/* Handle hack for "via" backwards compatibility */
if ((chain->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
print_iface("via",
&chain->fw_in_if, chain->fw_flg & IP_FW_F_IIFNAME);
} else {
/* Receive interface specified */
if (chain->fw_flg & IP_FW_F_IIFACE)
print_iface("recv", &chain->fw_in_if,
chain->fw_flg & IP_FW_F_IIFNAME);
/* Transmit interface specified */
if (chain->fw_flg & IP_FW_F_OIFACE)
print_iface("xmit", &chain->fw_out_if,
chain->fw_flg & IP_FW_F_OIFNAME);
}
if (chain->fw_flg & IP_FW_F_FRAG)
printf(" frag");
if (chain->fw_ipflg & IP_FW_IF_IPOPT) {
int _opt_printed = 0;
#define PRINTOPT(x) {if (_opt_printed) printf(",");\
printf(x); _opt_printed = 1;}
printf(" ipopt ");
if (chain->fw_ipopt & IP_FW_IPOPT_SSRR)
PRINTOPT("ssrr");
if (chain->fw_ipnopt & IP_FW_IPOPT_SSRR)
PRINTOPT("!ssrr");
if (chain->fw_ipopt & IP_FW_IPOPT_LSRR)
PRINTOPT("lsrr");
if (chain->fw_ipnopt & IP_FW_IPOPT_LSRR)
PRINTOPT("!lsrr");
if (chain->fw_ipopt & IP_FW_IPOPT_RR)
PRINTOPT("rr");
if (chain->fw_ipnopt & IP_FW_IPOPT_RR)
PRINTOPT("!rr");
if (chain->fw_ipopt & IP_FW_IPOPT_TS)
PRINTOPT("ts");
if (chain->fw_ipnopt & IP_FW_IPOPT_TS)
PRINTOPT("!ts");
}
if (chain->fw_ipflg & IP_FW_IF_IPLEN)
printf(" iplen %u", chain->fw_iplen);
if (chain->fw_ipflg & IP_FW_IF_IPID)
printf(" ipid %#x", chain->fw_ipid);
if (chain->fw_ipflg & IP_FW_IF_IPPRE)
printf(" ipprecedence %u", (chain->fw_iptos & 0xe0) >> 5);
if (chain->fw_ipflg & IP_FW_IF_IPTOS) {
int _opt_printed = 0;
printf(" iptos ");
if (chain->fw_iptos & IPTOS_LOWDELAY)
PRINTOPT("lowdelay");
if (chain->fw_ipntos & IPTOS_LOWDELAY)
PRINTOPT("!lowdelay");
if (chain->fw_iptos & IPTOS_THROUGHPUT)
PRINTOPT("throughput");
if (chain->fw_ipntos & IPTOS_THROUGHPUT)
PRINTOPT("!throughput");
if (chain->fw_iptos & IPTOS_RELIABILITY)
PRINTOPT("reliability");
if (chain->fw_ipntos & IPTOS_RELIABILITY)
PRINTOPT("!reliability");
if (chain->fw_iptos & IPTOS_MINCOST)
PRINTOPT("mincost");
if (chain->fw_ipntos & IPTOS_MINCOST)
PRINTOPT("!mincost");
if (chain->fw_iptos & IPTOS_CE)
PRINTOPT("congestion");
if (chain->fw_ipntos & IPTOS_CE)
PRINTOPT("!congestion");
}
if (chain->fw_ipflg & IP_FW_IF_IPTTL)
printf(" ipttl %u", chain->fw_ipttl);
if (chain->fw_ipflg & IP_FW_IF_IPVER)
printf(" ipversion %u", chain->fw_ipver);
if (chain->fw_ipflg & IP_FW_IF_TCPEST)
printf(" established");
else if (chain->fw_tcpf == IP_FW_TCPF_SYN &&
chain->fw_tcpnf == IP_FW_TCPF_ACK)
printf(" setup");
else if (chain->fw_ipflg & IP_FW_IF_TCPFLG) {
int _flg_printed = 0;
#define PRINTFLG(x) {if (_flg_printed) printf(",");\
printf(x); _flg_printed = 1;}
printf(" tcpflags ");
if (chain->fw_tcpf & IP_FW_TCPF_FIN)
PRINTFLG("fin");
if (chain->fw_tcpnf & IP_FW_TCPF_FIN)
PRINTFLG("!fin");
if (chain->fw_tcpf & IP_FW_TCPF_SYN)
PRINTFLG("syn");
if (chain->fw_tcpnf & IP_FW_TCPF_SYN)
PRINTFLG("!syn");
if (chain->fw_tcpf & IP_FW_TCPF_RST)
PRINTFLG("rst");
if (chain->fw_tcpnf & IP_FW_TCPF_RST)
PRINTFLG("!rst");
if (chain->fw_tcpf & IP_FW_TCPF_PSH)
PRINTFLG("psh");
if (chain->fw_tcpnf & IP_FW_TCPF_PSH)
PRINTFLG("!psh");
if (chain->fw_tcpf & IP_FW_TCPF_ACK)
PRINTFLG("ack");
if (chain->fw_tcpnf & IP_FW_TCPF_ACK)
PRINTFLG("!ack");
if (chain->fw_tcpf & IP_FW_TCPF_URG)
PRINTFLG("urg");
if (chain->fw_tcpnf & IP_FW_TCPF_URG)
PRINTFLG("!urg");
}
if (chain->fw_ipflg & IP_FW_IF_TCPOPT) {
int _opt_printed = 0;
#define PRINTTOPT(x) {if (_opt_printed) printf(",");\
printf(x); _opt_printed = 1;}
printf(" tcpoptions ");
if (chain->fw_tcpopt & IP_FW_TCPOPT_MSS)
PRINTTOPT("mss");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_MSS)
PRINTTOPT("!mss");
if (chain->fw_tcpopt & IP_FW_TCPOPT_WINDOW)
PRINTTOPT("window");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_WINDOW)
PRINTTOPT("!window");
if (chain->fw_tcpopt & IP_FW_TCPOPT_SACK)
PRINTTOPT("sack");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_SACK)
PRINTTOPT("!sack");
if (chain->fw_tcpopt & IP_FW_TCPOPT_TS)
PRINTTOPT("ts");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_TS)
PRINTTOPT("!ts");
if (chain->fw_tcpopt & IP_FW_TCPOPT_CC)
PRINTTOPT("cc");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_CC)
PRINTTOPT("!cc");
}
if (chain->fw_ipflg & IP_FW_IF_TCPSEQ)
printf(" tcpseq %lu", (u_long)ntohl(chain->fw_tcpseq));
if (chain->fw_ipflg & IP_FW_IF_TCPACK)
printf(" tcpack %lu", (u_long)ntohl(chain->fw_tcpack));
if (chain->fw_ipflg & IP_FW_IF_TCPWIN)
printf(" tcpwin %hu", ntohs(chain->fw_tcpwin));
if (chain->fw_flg & IP_FW_F_ICMPBIT) {
int i, first = 1;
unsigned j;
printf(" icmptype");
for (i = 0; i < IP_FW_ICMPTYPES_DIM; ++i)
for (j = 0; j < sizeof(unsigned) * 8; ++j)
if (chain->fw_uar.fw_icmptypes[i] & (1 << j)) {
printf("%c%d", first ? ' ' : ',',
i * sizeof(unsigned) * 8 + j);
first = 0;
}
}
printf("\n");
done:
if (do_resolv)
endservent();
}
static void
show_dyn_ipfw(struct ipfw_dyn_rule *d)
{
struct protoent *pe;
struct in_addr a;
if (!d->expire && !do_expired)
return;
printf("%05d %qu %qu (T %ds, slot %d)",
(int)(d->rule),
d->pcnt, d->bcnt,
d->expire,
d->bucket);
switch (d->dyn_type) {
case DYN_LIMIT_PARENT:
printf(" PARENT %d", d->count);
break;
case DYN_LIMIT:
printf(" LIMIT");
break;
case DYN_KEEP_STATE: /* bidir, no mask */
printf(" <->");
break;
}
if (do_resolv && (pe = getprotobynumber(d->id.proto)) != NULL)
printf(" %s,", pe->p_name);
else
printf(" %u,", d->id.proto);
a.s_addr = htonl(d->id.src_ip);
printf(" %s %d", inet_ntoa(a), d->id.src_port);
a.s_addr = htonl(d->id.dst_ip);
printf("<-> %s %d", inet_ntoa(a), d->id.dst_port);
printf("\n");
}
int
sort_q(const void *pa, const void *pb)
{
int rev = (do_sort < 0);
int field = rev ? -do_sort : do_sort;
long long res = 0;
const struct dn_flow_queue *a = pa;
const struct dn_flow_queue *b = pb;
switch (field) {
case 1: /* pkts */
res = a->len - b->len;
break;
case 2: /* bytes */
res = a->len_bytes - b->len_bytes;
break;
case 3: /* tot pkts */
res = a->tot_pkts - b->tot_pkts;
break;
case 4: /* tot bytes */
res = a->tot_bytes - b->tot_bytes;
break;
}
if (res < 0)
res = -1;
if (res > 0)
res = 1;
return (int)(rev ? res : -res);
}
static void
list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
{
int l;
printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
fs->flow_mask.proto,
fs->flow_mask.src_ip, fs->flow_mask.src_port,
fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
if (fs->rq_elements == 0)
return;
printf("BKT Prot ___Source IP/port____ "
"____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
if (do_sort != 0)
heapsort(q, fs->rq_elements, sizeof *q, sort_q);
for (l = 0; l < fs->rq_elements; l++) {
struct in_addr ina;
struct protoent *pe;
ina.s_addr = htonl(q[l].id.src_ip);
printf("%3d ", q[l].hash_slot);
pe = getprotobynumber(q[l].id.proto);
if (pe)
printf("%-4s ", pe->p_name);
else
printf("%4u ", q[l].id.proto);
printf("%15s/%-5d ",
inet_ntoa(ina), q[l].id.src_port);
ina.s_addr = htonl(q[l].id.dst_ip);
printf("%15s/%-5d ",
inet_ntoa(ina), q[l].id.dst_port);
printf("%4qu %8qu %2u %4u %3u\n",
q[l].tot_pkts, q[l].tot_bytes,
q[l].len, q[l].len_bytes, q[l].drops);
if (verbose)
printf(" S %20qd F %20qd\n",
q[l].S, q[l].F);
}
}
static void
print_flowset_parms(struct dn_flow_set *fs, char *prefix)
{
int l;
char qs[30];
char plr[30];
char red[90]; /* Display RED parameters */
l = fs->qsize;
if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
if (l >= 8192)
sprintf(qs, "%d KB", l / 1024);
else
sprintf(qs, "%d B", l);
} else
sprintf(qs, "%3d sl.", l);
if (fs->plr)
sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
else
plr[0] = '\0';
if (fs->flags_fs & DN_IS_RED) /* RED parameters */
sprintf(red,
"\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
(fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
1.0 * fs->w_q / (double)(1 << SCALE_RED),
SCALE_VAL(fs->min_th),
SCALE_VAL(fs->max_th),
1.0 * fs->max_p / (double)(1 << SCALE_RED));
else
sprintf(red, "droptail");
printf("%s %s%s %d queues (%d buckets) %s\n",
prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
}
static void
list(int ac, char *av[])
{
struct ip_fw *rules, *r;
struct ipfw_dyn_rule *dynrules, *d;
struct dn_pipe *pipes;
void *data = NULL;
int n, nbytes, nstat, ndyn;
int exitval = EX_OK;
int lac;
char **lav;
u_long rnum;
char *endptr;
int seen = 0;
/* get rules or pipes from kernel, resizing array as necessary */
{
const int unit = do_pipe ? sizeof(*pipes) : sizeof(*rules);
const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
int nalloc = unit;
nbytes = nalloc;
while (nbytes >= nalloc) {
nalloc = nalloc * 2 + 200;
nbytes = nalloc;
if ((data = realloc(data, nbytes)) == NULL)
err(EX_OSERR, "realloc");
if (getsockopt(s, IPPROTO_IP, ocmd, data, &nbytes) < 0)
err(EX_OSERR, "getsockopt(IP_%s_GET)",
do_pipe ? "DUMMYNET" : "FW");
}
}
/* display requested pipes */
if (do_pipe) {
u_long rulenum;
void *next = data;
struct dn_pipe *p = (struct dn_pipe *) data;
struct dn_flow_set *fs;
struct dn_flow_queue *q;
int l;
if (ac > 0)
rulenum = strtoul(*av++, NULL, 10);
else
rulenum = 0;
for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
double b = p->bandwidth;
char buf[30];
char prefix[80];
if (p->next != (struct dn_pipe *)DN_IS_PIPE)
break;
l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
next = (void *)p + l;
nbytes -= l;
q = (struct dn_flow_queue *)(p+1);
if (rulenum != 0 && rulenum != p->pipe_nr)
continue;
if (p->if_name[0] != '\0')
sprintf(buf, "%s", p->if_name);
else if (b == 0)
sprintf(buf, "unlimited");
else if (b >= 1000000)
sprintf(buf, "%7.3f Mbit/s", b/1000000);
else if (b >= 1000)
sprintf(buf, "%7.3f Kbit/s", b/1000);
else
sprintf(buf, "%7.3f bit/s ", b);
sprintf(prefix, "%05d: %s %4d ms ",
p->pipe_nr, buf, p->delay);
print_flowset_parms(&(p->fs), prefix);
if (verbose)
printf(" V %20qd\n", p->V >> MY_M);
list_queues(&(p->fs), q);
}
fs = (struct dn_flow_set *) next;
for (; nbytes >= sizeof *fs; fs = (struct dn_flow_set *)next) {
char prefix[80];
if (fs->next != (struct dn_flow_set *)DN_IS_QUEUE)
break;
l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
next = (void *)fs + l;
nbytes -= l;
q = (struct dn_flow_queue *)(fs+1);
sprintf(prefix, "q%05d: weight %d pipe %d ",
fs->fs_nr, fs->weight, fs->parent_nr);
print_flowset_parms(fs, prefix);
list_queues(fs, q);
}
free(data);
return;
}
rules = (struct ip_fw *)data;
for (nstat = 0; rules[nstat].fw_number < 65535; ++nstat)
/* nothing */ ;
nstat++; /* counting starts from 0 ... */
dynrules = (struct ipfw_dyn_rule *)&rules[nstat];
ndyn = (nbytes - (nstat * sizeof *rules)) / sizeof *dynrules;
/* if no rule numbers were specified, list all rules */
if (ac == 0) {
for (n = 0; n < nstat; n++)
show_ipfw(&rules[n]);
if (do_dynamic && ndyn) {
printf("## Dynamic rules:\n");
for (n = 0, d = dynrules; n < ndyn; n++, d++)
show_dyn_ipfw(d);
}
free(data);
return;
}
/* display specific rules requested on command line */
for (lac = ac, lav = av; lac != 0; lac--) {
/* convert command line rule # */
rnum = strtoul(*lav++, &endptr, 10);
if (*endptr) {
exitval = EX_USAGE;
warnx("invalid rule number: %s", *(lav - 1));
continue;
}
for (n = seen = 0, r = rules; n < nstat; n++, r++) {
if (r->fw_number > rnum)
break;
if (r->fw_number == rnum) {
show_ipfw(r);
seen = 1;
}
}
if (!seen) {
/* give precedence to other error(s) */
if (exitval == EX_OK)
exitval = EX_UNAVAILABLE;
warnx("rule %lu does not exist", rnum);
}
}
printf("## Dynamic rules:\n");
if (do_dynamic && ndyn) {
for (lac = ac, lav = av; lac != 0; lac--) {
rnum = strtoul(*lav++, &endptr, 10);
if (*endptr)
/* already warned */
continue;
for (n = 0, d = dynrules; n < ndyn; n++, d++) {
if ((int)(d->rule) > rnum)
break;
if ((int)(d->rule) == rnum)
show_dyn_ipfw(d);
}
}
}
ac = 0;
free(data);
if (exitval != EX_OK)
exit(exitval);
}
static void
show_usage(void)
{
fprintf(stderr, "usage: ipfw [options]\n"
" [pipe] flush\n"
" add [number] rule\n"
" [pipe] delete number ...\n"
" [pipe] list [number ...]\n"
" [pipe] show [number ...]\n"
" zero [number ...]\n"
" resetlog [number ...]\n"
" pipe number config [pipeconfig]\n"
" rule: [prob <match_probability>] action proto src dst extras...\n"
" action:\n"
" {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
" reset|count|skipto num|divert port|tee port|fwd ip|\n"
" pipe num} [log [logamount count]]\n"
" proto: {ip|tcp|udp|icmp|<number>}\n"
" src: from [not] {me|any|ip[{/bits|:mask}]} [{port[-port]}, [port], ...]\n"
" dst: to [not] {me|any|ip[{/bits|:mask}]} [{port[-port]}, [port], ...]\n"
" extras:\n"
" uid {user id}\n"
" gid {group id}\n"
" fragment (may not be used with ports or tcpflags)\n"
" in\n"
" out\n"
" {xmit|recv|via} {iface|ip|any}\n"
" {established|setup}\n"
" tcpflags [!]{syn|fin|rst|ack|psh|urg}, ...\n"
" ipoptions [!]{ssrr|lsrr|rr|ts}, ...\n"
" iplen {length}\n"
" ipid {identification number}\n"
" ipprecedence {precedence}\n"
" iptos [!]{lowdelay|throughput|reliability|mincost|congestion}, ...\n"
" ipttl {time to live}\n"
" ipversion {version number}\n"
" tcpoptions [!]{mss|window|sack|ts|cc}, ...\n"
" tcpseq {sequence number}\n"
" tcpack {acknowledgement number}\n"
" tcpwin {window size}\n"
" icmptypes {type[, type]}...\n"
" keep-state [method]\n"
" pipeconfig:\n"
" {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
" {bw|bandwidth} interface_name\n"
" delay <milliseconds>\n"
" queue <size>{packets|Bytes|KBytes}\n"
" plr <fraction>\n"
" mask {all| [dst-ip|src-ip|dst-port|src-port|proto] <number>}\n"
" buckets <number>}\n"
" {red|gred} <fraction>/<number>/<number>/<fraction>\n"
" droptail\n"
);
exit(EX_USAGE);
}
static int
lookup_host (char *host, struct in_addr *ipaddr)
{
struct hostent *he;
if (!inet_aton(host, ipaddr)) {
if ((he = gethostbyname(host)) == NULL)
return(-1);
*ipaddr = *(struct in_addr *)he->h_addr_list[0];
}
return(0);
}
static void
fill_ip(struct in_addr *ipno, struct in_addr *mask, int *acp, char ***avp)
{
int ac = *acp;
char **av = *avp;
char *p = 0, md = 0;
if (ac && !strncmp(*av, "any", strlen(*av))) {
ipno->s_addr = mask->s_addr = 0; av++; ac--;
} else {
p = strchr(*av, '/');
if (!p)
p = strchr(*av, ':');
if (p) {
md = *p;
*p++ = '\0';
}
if (lookup_host(*av, ipno) != 0)
errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
switch (md) {
case ':':
if (!inet_aton(p, mask))
errx(EX_DATAERR, "bad netmask ``%s''", p);
break;
case '/':
if (atoi(p) == 0) {
mask->s_addr = 0;
} else if (atoi(p) > 32) {
errx(EX_DATAERR, "bad width ``%s''", p);
} else {
mask->s_addr =
htonl(~0 << (32 - atoi(p)));
}
break;
default:
mask->s_addr = htonl(~0);
break;
}
ipno->s_addr &= mask->s_addr;
av++;
ac--;
}
*acp = ac;
*avp = av;
}
static void
fill_reject_code(u_short *codep, char *str)
{
struct icmpcode *ic;
u_long val;
char *s;
val = strtoul(str, &s, 0);
if (s != str && *s == '\0' && val < 0x100) {
*codep = val;
return;
}
for (ic = icmpcodes; ic->str; ic++)
if (!strcasecmp(str, ic->str)) {
*codep = ic->code;
return;
}
errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
}
static void
add_port(u_short *cnt, u_short *ptr, u_short off, u_short port)
{
if (off + *cnt >= IP_FW_MAX_PORTS)
errx(EX_USAGE, "too many ports (max is %d)", IP_FW_MAX_PORTS);
ptr[off+*cnt] = port;
(*cnt)++;
}
static int
lookup_port(const char *arg, int proto, int test, int nodash)
{
int val;
char *earg, buf[32];
struct servent *s;
char *p, *q;
snprintf(buf, sizeof(buf), "%s", arg);
for (p = q = buf; *p; *q++ = *p++) {
if (*p == '\\') {
if (*(p+1))
p++;
} else {
if (*p == ',' || (nodash && *p == '-'))
break;
}
}
*q = '\0';
val = (int) strtoul(buf, &earg, 0);
if (!*buf || *earg) {
char *protocol = NULL;
if (proto != 0) {
struct protoent *pe = getprotobynumber(proto);
if (pe)
protocol = pe->p_name;
}
setservent(1);
if ((s = getservbyname(buf, protocol))) {
val = htons(s->s_port);
} else {
if (!test)
errx(EX_DATAERR, "unknown port ``%s''", buf);
val = -1;
}
} else {
if (val < 0 || val > 0xffff) {
if (!test)
errx(EX_DATAERR,
"port ``%s'' out of range", buf);
val = -1;
}
}
return(val);
}
/*
* return: 0 normally, 1 if first pair is a range,
* 2 if first pair is a port+mask
*/
static int
fill_port(u_short *cnt, u_short *ptr, u_short off, char *arg, int proto)
{
char *s;
int initial_range = 0;
for (s = arg; *s && *s != ',' && *s != '-' && *s != ':'; s++) {
if (*s == '\\' && *(s+1))
s++;
}
if (*s == ':') {
*s++ = '\0';
if (strchr(arg, ','))
errx(EX_USAGE, "port/mask must be first in list");
add_port(cnt, ptr, off,
*arg ? lookup_port(arg, proto, 0, 0) : 0x0000);
arg = s;
s = strchr(arg, ',');
if (s)
*s++ = '\0';
add_port(cnt, ptr, off,
*arg ? lookup_port(arg, proto, 0, 0) : 0xffff);
arg = s;
initial_range = 2;
} else
if (*s == '-') {
*s++ = '\0';
if (strchr(arg, ','))
errx(EX_USAGE, "port range must be first in list");
add_port(cnt, ptr, off,
*arg ? lookup_port(arg, proto, 0, 0) : 0x0000);
arg = s;
s = strchr(arg, ',');
if (s)
*s++ = '\0';
add_port(cnt, ptr, off,
*arg ? lookup_port(arg, proto, 0, 0) : 0xffff);
arg = s;
initial_range = 1;
}
while (arg != NULL) {
s = strchr(arg, ',');
if (s)
*s++ = '\0';
add_port(cnt, ptr, off, lookup_port(arg, proto, 0, 0));
arg = s;
}
return initial_range;
}
static void
fill_tcpflag(u_char *set, u_char *reset, char **vp)
{
char *p = *vp, *q;
u_char *d;
while (p && *p) {
struct tpcflags {
char * name;
u_char value;
} flags[] = {
{ "syn", IP_FW_TCPF_SYN },
{ "fin", IP_FW_TCPF_FIN },
{ "ack", IP_FW_TCPF_ACK },
{ "psh", IP_FW_TCPF_PSH },
{ "rst", IP_FW_TCPF_RST },
{ "urg", IP_FW_TCPF_URG }
};
int i;
if (*p == '!') {
p++;
d = reset;
} else {
d = set;
}
q = strchr(p, ',');
if (q)
*q++ = '\0';
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
if (!strncmp(p, flags[i].name, strlen(p))) {
*d |= flags[i].value;
break;
}
if (i == sizeof(flags) / sizeof(flags[0]))
errx(EX_DATAERR, "invalid tcp flag ``%s''", p);
p = q;
}
}
static void
fill_tcpopts(u_char *set, u_char *reset, char **vp)
{
char *p = *vp, *q;
u_char *d;
while (p && *p) {
struct tpcopts {
char * name;
u_char value;
} opts[] = {
{ "mss", IP_FW_TCPOPT_MSS },
{ "window", IP_FW_TCPOPT_WINDOW },
{ "sack", IP_FW_TCPOPT_SACK },
{ "ts", IP_FW_TCPOPT_TS },
{ "cc", IP_FW_TCPOPT_CC },
};
int i;
if (*p == '!') {
p++;
d = reset;
} else {
d = set;
}
q = strchr(p, ',');
if (q)
*q++ = '\0';
for (i = 0; i < sizeof(opts) / sizeof(opts[0]); ++i)
if (!strncmp(p, opts[i].name, strlen(p))) {
*d |= opts[i].value;
break;
}
if (i == sizeof(opts) / sizeof(opts[0]))
errx(EX_DATAERR, "invalid tcp option ``%s''", p);
p = q;
}
}
static void
fill_ipopt(u_char *set, u_char *reset, char **vp)
{
char *p = *vp, *q;
u_char *d;
while (p && *p) {
if (*p == '!') {
p++;
d = reset;
} else {
d = set;
}
q = strchr(p, ',');
if (q)
*q++ = '\0';
if (!strncmp(p, "ssrr", strlen(p))) *d |= IP_FW_IPOPT_SSRR;
if (!strncmp(p, "lsrr", strlen(p))) *d |= IP_FW_IPOPT_LSRR;
if (!strncmp(p, "rr", strlen(p))) *d |= IP_FW_IPOPT_RR;
if (!strncmp(p, "ts", strlen(p))) *d |= IP_FW_IPOPT_TS;
p = q;
}
}
static void
fill_iptos(u_char *set, u_char *reset, char **vp)
{
char *p = *vp, *q;
u_char *d;
while (p && *p) {
if (*p == '!') {
p++;
d = reset;
} else {
d = set;
}
q = strchr(p, ',');
if (q)
*q++ = '\0';
if (!strncmp(p, "lowdelay", strlen(p)))
*d |= IPTOS_LOWDELAY;
if (!strncmp(p, "throughput", strlen(p)))
*d |= IPTOS_THROUGHPUT;
if (!strncmp(p, "reliability", strlen(p)))
*d |= IPTOS_RELIABILITY;
if (!strncmp(p, "mincost", strlen(p)))
*d |= IPTOS_MINCOST;
if (!strncmp(p, "congestion", strlen(p)))
*d |= IPTOS_CE;
#if 0 /* conflicting! */
if (!strncmp(p, "ecntransport", strlen(p)))
*d |= IPTOS_ECT;
#endif
p = q;
}
}
static void
fill_icmptypes(unsigned *types, char **vp, u_int *fw_flg)
{
unsigned long icmptype;
char *c = *vp;
while (*c) {
if (*c == ',')
++c;
icmptype = strtoul(c, &c, 0);
if (*c != ',' && *c != '\0')
errx(EX_DATAERR, "invalid ICMP type");
if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
errx(EX_DATAERR, "ICMP type out of range");
types[icmptype / (sizeof(unsigned) * 8)] |=
1 << (icmptype % (sizeof(unsigned) * 8));
*fw_flg |= IP_FW_F_ICMPBIT;
}
}
static void
delete(int ac, char *av[])
{
struct ip_fw rule;
struct dn_pipe pipe;
int i;
int exitval = EX_OK;
memset(&rule, 0, sizeof rule);
memset(&pipe, 0, sizeof pipe);
av++; ac--;
/* Rule number */
while (ac && isdigit(**av)) {
i = atoi(*av); av++; ac--;
if (do_pipe) {
if (do_pipe == 1)
pipe.pipe_nr = i;
else
pipe.fs.fs_nr = i;
i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL,
&pipe, sizeof pipe);
if (i) {
exitval = 1;
warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
do_pipe == 1 ? pipe.pipe_nr :
pipe.fs.fs_nr);
}
} else {
rule.fw_number = i;
i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule,
sizeof rule);
if (i) {
exitval = EX_UNAVAILABLE;
warn("rule %u: setsockopt(IP_FW_DEL)",
rule.fw_number);
}
}
}
if (exitval != EX_OK)
exit(exitval);
}
static void
verify_interface(union ip_fw_if *ifu)
{
struct ifreq ifr;
/*
* If a unit was specified, check for that exact interface.
* If a wildcard was specified, check for unit 0.
*/
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
ifu->fu_via_if.name,
ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit);
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
warnx("warning: interface ``%s'' does not exist",
ifr.ifr_name);
}
static void
fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg)
{
if (!ac)
errx(EX_USAGE, "missing argument for ``%s''", which);
/* Parse the interface or address */
if (!strcmp(arg, "any")) {
ifu->fu_via_ip.s_addr = 0;
*byname = 0;
} else if (!isdigit(*arg)) {
char *q;
*byname = 1;
strncpy(ifu->fu_via_if.name, arg,
sizeof(ifu->fu_via_if.name));
ifu->fu_via_if.name[sizeof(ifu->fu_via_if.name) - 1] = '\0';
for (q = ifu->fu_via_if.name;
*q && !isdigit(*q) && *q != '*'; q++)
continue;
ifu->fu_via_if.unit = (*q == '*') ? -1 : atoi(q);
*q = '\0';
verify_interface(ifu);
} else if (!inet_aton(arg, &ifu->fu_via_ip)) {
errx(EX_DATAERR, "bad ip address ``%s''", arg);
} else
*byname = 0;
}
static void
config_pipe(int ac, char **av)
{
struct dn_pipe pipe;
int i;
char *end;
memset(&pipe, 0, sizeof pipe);
av++; ac--;
/* Pipe number */
if (ac && isdigit(**av)) {
i = atoi(*av); av++; ac--;
if (do_pipe == 1)
pipe.pipe_nr = i;
else
pipe.fs.fs_nr = i;
}
while (ac > 1) {
if (!strncmp(*av, "plr", strlen(*av))) {
double d = strtod(av[1], NULL);
if (d > 1)
d = 1;
else if (d < 0)
d = 0;
pipe.fs.plr = (int)(d*0x7fffffff);
av += 2;
ac -= 2;
} else if (!strncmp(*av, "queue", strlen(*av))) {
end = NULL;
pipe.fs.qsize = strtoul(av[1], &end, 0);
if (*end == 'K' || *end == 'k') {
pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES;
pipe.fs.qsize *= 1024;
} else if (*end == 'B' || !strncmp(end, "by", 2)) {
pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES;
}
av += 2;
ac -= 2;
} else if (!strncmp(*av, "buckets", strlen(*av))) {
pipe.fs.rq_size = strtoul(av[1], NULL, 0);
av += 2;
ac -= 2;
} else if (!strncmp(*av, "mask", strlen(*av))) {
/* per-flow queue, mask is dst_ip, dst_port,
* src_ip, src_port, proto measured in bits
*/
u_int32_t a;
void *par = NULL;
pipe.fs.flow_mask.dst_ip = 0;
pipe.fs.flow_mask.src_ip = 0;
pipe.fs.flow_mask.dst_port = 0;
pipe.fs.flow_mask.src_port = 0;
pipe.fs.flow_mask.proto = 0;
end = NULL;
av++; ac--;
if (ac >= 1 && !strncmp(*av, "all", strlen(*av))) {
/* special case -- all bits are significant */
pipe.fs.flow_mask.dst_ip = ~0;
pipe.fs.flow_mask.src_ip = ~0;
pipe.fs.flow_mask.dst_port = ~0;
pipe.fs.flow_mask.src_port = ~0;
pipe.fs.flow_mask.proto = ~0;
pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
av++;
ac--;
continue;
}
while (ac >= 1) {
int len = strlen(*av);
if (!strncmp(*av, "dst-ip", len))
par = &pipe.fs.flow_mask.dst_ip;
else if (!strncmp(*av, "src-ip", len))
par = &pipe.fs.flow_mask.src_ip;
else if (!strncmp(*av, "dst-port", len))
par = &pipe.fs.flow_mask.dst_port;
else if (!strncmp(*av, "src-port", len))
par = &pipe.fs.flow_mask.src_port;
else if (!strncmp(*av, "proto", len))
par = &pipe.fs.flow_mask.proto;
else
break;
if (ac < 2)
errx(EX_USAGE, "mask: %s value"
" missing", *av);
if (*av[1] == '/') {
a = strtoul(av[1]+1, &end, 0);
if (a == 32) /* special case... */
a = ~0;
else
a = (1 << a) - 1;
} else {
a = strtoul(av[1], &end, 0);
}
if (par == &pipe.fs.flow_mask.src_port
|| par == &pipe.fs.flow_mask.dst_port) {
if (a >= (1 << 16))
errx(EX_DATAERR, "mask: %s"
" must be 16 bit, not"
" 0x%08x", *av, a);
*((u_int16_t *)par) = (u_int16_t)a;
} else if (par == &pipe.fs.flow_mask.proto) {
if (a >= (1 << 8))
errx(EX_DATAERR, "mask: %s"
" must be"
" 8 bit, not 0x%08x",
*av, a);
*((u_int8_t *)par) = (u_int8_t)a;
} else
*((u_int32_t *)par) = a;
if (a != 0)
pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
av += 2;
ac -= 2;
} /* end for */
} else if (!strncmp(*av, "red", strlen(*av))
|| !strncmp(*av, "gred", strlen(*av))) {
/* RED enabled */
pipe.fs.flags_fs |= DN_IS_RED;
if (*av[0] == 'g')
pipe.fs.flags_fs |= DN_IS_GENTLE_RED;
if ((end = strsep(&av[1], "/"))) {
double w_q = strtod(end, NULL);
if (w_q > 1 || w_q <= 0)
errx(EX_DATAERR, "w_q %f must be "
"0 < x <= 1", w_q);
pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED));
}
if ((end = strsep(&av[1], "/"))) {
pipe.fs.min_th = strtoul(end, &end, 0);
if (*end == 'K' || *end == 'k')
pipe.fs.min_th *= 1024;
}
if ((end = strsep(&av[1], "/"))) {
pipe.fs.max_th = strtoul(end, &end, 0);
if (*end == 'K' || *end == 'k')
pipe.fs.max_th *= 1024;
}
if ((end = strsep(&av[1], "/"))) {
double max_p = strtod(end, NULL);
if (max_p > 1 || max_p <= 0)
errx(EX_DATAERR, "max_p %f must be "
"0 < x <= 1", max_p);
pipe.fs.max_p =
(int)(max_p * (1 << SCALE_RED));
}
av += 2;
ac -= 2;
} else if (!strncmp(*av, "droptail", strlen(*av))) {
/* DROPTAIL */
pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
av += 1;
ac -= 1;
} else {
int len = strlen(*av);
if (do_pipe == 1) {
/* some commands are only good for pipes. */
if (!strncmp(*av, "bw", len)
|| !strncmp(*av, "bandwidth", len)) {
if (av[1][0] >= 'a'
&& av[1][0] <= 'z') {
int l = sizeof(pipe.if_name)-1;
/* interface name */
strncpy(pipe.if_name, av[1], l);
pipe.if_name[l] = '\0';
pipe.bandwidth = 0;
} else {
pipe.if_name[0] = '\0';
pipe.bandwidth =
strtoul(av[1], &end, 0);
if (*end == 'K'
|| *end == 'k') {
end++;
pipe.bandwidth *=
1000;
} else if (*end == 'M') {
end++;
pipe.bandwidth *=
1000000;
}
if (*end == 'B'
|| !strncmp(end, "by", 2))
pipe.bandwidth *= 8;
}
if (pipe.bandwidth < 0)
errx(EX_DATAERR,
"bandwidth too large");
av += 2;
ac -= 2;
} else if (!strncmp(*av, "delay", len)) {
pipe.delay = strtoul(av[1], NULL, 0);
av += 2;
ac -= 2;
} else {
errx(EX_DATAERR, "unrecognised pipe"
" option ``%s''", *av);
}
} else { /* this refers to a queue */
if (!strncmp(*av, "weight", len)) {
pipe.fs.weight =
strtoul(av[1], &end, 0);
av += 2;
ac -= 2;
} else if (!strncmp(*av, "pipe", len)) {
pipe.fs.parent_nr =
strtoul(av[1], &end, 0);
av += 2;
ac -= 2;
} else {
errx(EX_DATAERR, "unrecognised option "
"``%s''", *av);
}
}
}
}
if (do_pipe == 1) {
if (pipe.pipe_nr == 0)
errx(EX_DATAERR, "pipe_nr %d must be > 0",
pipe.pipe_nr);
if (pipe.delay > 10000)
errx(EX_DATAERR, "delay %d must be < 10000",
pipe.delay);
} else { /* do_pipe == 2, queue */
if (pipe.fs.parent_nr == 0)
errx(EX_DATAERR, "pipe %d must be > 0",
pipe.fs.parent_nr);
if (pipe.fs.weight >100)
errx(EX_DATAERR, "weight %d must be <= 100",
pipe.fs.weight);
}
if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) {
if (pipe.fs.qsize > 1024*1024)
errx(EX_DATAERR, "queue size %d, must be < 1MB",
pipe.fs.qsize);
} else {
if (pipe.fs.qsize > 100)
errx(EX_DATAERR, "queue size %d, must be"
" 2 <= x <= 100", pipe.fs.qsize);
}
if (pipe.fs.flags_fs & DN_IS_RED) {
size_t len;
int lookup_depth, avg_pkt_size;
double s, idle, weight, w_q;
struct clockinfo clock;
int t;
if (pipe.fs.min_th >= pipe.fs.max_th)
errx(EX_DATAERR, "min_th %d must be < than max_th %d",
pipe.fs.min_th, pipe.fs.max_th);
if (pipe.fs.max_th == 0)
errx(EX_DATAERR, "max_th must be > 0");
len = sizeof(int);
if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
&lookup_depth, &len, NULL, 0) == -1)
errx(1, "sysctlbyname(\"%s\")",
"net.inet.ip.dummynet.red_lookup_depth");
if (lookup_depth == 0)
errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
" must be greater than zero");
len = sizeof(int);
if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
&avg_pkt_size, &len, NULL, 0) == -1)
errx(1, "sysctlbyname(\"%s\")",
"net.inet.ip.dummynet.red_avg_pkt_size");
if (avg_pkt_size == 0)
errx(EX_DATAERR,
"net.inet.ip.dummynet.red_avg_pkt_size must"
" be greater than zero");
len = sizeof(struct clockinfo);
if (sysctlbyname("kern.clockrate", &clock, &len, NULL, 0) == -1)
errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
/*
* Ticks needed for sending a medium-sized packet.
* Unfortunately, when we are configuring a WF2Q+ queue, we
* do not have bandwidth information, because that is stored
* in the parent pipe, and also we have multiple queues
* competing for it. So we set s=0, which is not very
* correct. But on the other hand, why do we want RED with
* WF2Q+ ?
*/
if (pipe.bandwidth==0) /* this is a WF2Q+ queue */
s = 0;
else
s = clock.hz * avg_pkt_size * 8 / pipe.bandwidth;
/*
* max idle time (in ticks) before avg queue size becomes 0.
* NOTA: (3/w_q) is approx the value x so that
* (1-w_q)^x < 10^-3.
*/
w_q = ((double)pipe.fs.w_q) / (1 << SCALE_RED);
idle = s * 3. / w_q;
pipe.fs.lookup_step = (int)idle / lookup_depth;
if (!pipe.fs.lookup_step)
pipe.fs.lookup_step = 1;
weight = 1 - w_q;
for (t = pipe.fs.lookup_step; t > 0; --t)
weight *= weight;
pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
}
i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,
sizeof pipe);
if (i)
err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
}
static void
add(int ac, char *av[])
{
struct ip_fw rule;
int i;
u_char proto;
struct protoent *pe;
int saw_xmrc = 0, saw_via = 0;
memset(&rule, 0, sizeof rule);
av++; ac--;
/* Rule number */
if (ac && isdigit(**av)) {
rule.fw_number = atoi(*av); av++; ac--;
}
/* Action */
if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) {
double d = strtod(av[1], NULL);
if (d <= 0 || d > 1)
errx(EX_DATAERR, "illegal match prob. %s", av[1]);
if (d != 1) { /* 1 means always match */
rule.fw_flg |= IP_FW_F_RND_MATCH;
rule.dont_match_prob = (long)((1 - d) * 0x7fffffff);
}
av += 2; ac -= 2;
}
if (ac == 0)
errx(EX_USAGE, "missing action");
if (!strncmp(*av, "accept", strlen(*av))
|| !strncmp(*av, "pass", strlen(*av))
|| !strncmp(*av, "allow", strlen(*av))
|| !strncmp(*av, "permit", strlen(*av))) {
rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
} else if (!strncmp(*av, "count", strlen(*av))) {
rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
} else if (!strncmp(*av, "pipe", strlen(*av))) {
rule.fw_flg |= IP_FW_F_PIPE; av++; ac--;
if (!ac)
errx(EX_USAGE, "missing pipe number");
rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
} else if (!strncmp(*av, "queue", strlen(*av))) {
rule.fw_flg |= IP_FW_F_QUEUE; av++; ac--;
if (!ac)
errx(EX_USAGE, "missing queue number");
rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
} else if (!strncmp(*av, "divert", strlen(*av))) {
rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--;
if (!ac)
errx(EX_USAGE, "missing %s port", "divert");
rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
if (rule.fw_divert_port == 0) {
struct servent *s;
setservent(1);
s = getservbyname(av[-1], "divert");
if (s != NULL)
rule.fw_divert_port = ntohs(s->s_port);
else
errx(EX_DATAERR, "illegal %s port", "divert");
}
} else if (!strncmp(*av, "tee", strlen(*av))) {
rule.fw_flg |= IP_FW_F_TEE; av++; ac--;
if (!ac)
errx(EX_USAGE, "missing %s port", "tee divert");
rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
if (rule.fw_divert_port == 0) {
struct servent *s;
setservent(1);
s = getservbyname(av[-1], "divert");
if (s != NULL)
rule.fw_divert_port = ntohs(s->s_port);
else
errx(EX_DATAERR, "illegal %s port",
"tee divert");
}
} else if (!strncmp(*av, "fwd", strlen(*av))
|| !strncmp(*av, "forward", strlen(*av))) {
struct in_addr dummyip;
char *pp;
rule.fw_flg |= IP_FW_F_FWD; av++; ac--;
if (!ac)
errx(EX_USAGE, "missing forwarding IP address");
rule.fw_fwd_ip.sin_len = sizeof(struct sockaddr_in);
rule.fw_fwd_ip.sin_family = AF_INET;
rule.fw_fwd_ip.sin_port = 0;
pp = strchr(*av, ':');
if(pp == NULL)
pp = strchr(*av, ',');
if(pp != NULL)
{
*(pp++) = '\0';
i = lookup_port(pp, 0, 1, 0);
if (i == -1)
errx(EX_DATAERR, "illegal forwarding"
" port ``%s''", pp);
else
rule.fw_fwd_ip.sin_port = (u_short)i;
}
fill_ip(&(rule.fw_fwd_ip.sin_addr), &dummyip, &ac, &av);
if (rule.fw_fwd_ip.sin_addr.s_addr == 0)
errx(EX_DATAERR, "illegal forwarding IP address");
} else if (!strncmp(*av, "skipto", strlen(*av))) {
rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
if (!ac)
errx(EX_USAGE, "missing skipto rule number");
rule.fw_skipto_rule = strtoul(*av, NULL, 10); av++; ac--;
} else if ((!strncmp(*av, "deny", strlen(*av))
|| !strncmp(*av, "drop", strlen(*av)))) {
rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
} else if (!strncmp(*av, "reject", strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
rule.fw_reject_code = ICMP_UNREACH_HOST;
} else if (!strncmp(*av, "reset", strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */
} else if (!strncmp(*av, "unreach", strlen(*av))) {
rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
} else if (!strncmp(*av, "check-state", strlen(*av))) {
rule.fw_flg |= IP_FW_F_CHECK_S; av++; ac--;
goto done;
} else {
errx(EX_DATAERR, "invalid action ``%s''", *av);
}
/* [log] */
if (ac && !strncmp(*av, "log", strlen(*av))) {
rule.fw_flg |= IP_FW_F_PRN; av++; ac--;
}
if (ac && !strncmp(*av, "logamount", strlen(*av))) {
if (!(rule.fw_flg & IP_FW_F_PRN))
errx(EX_USAGE, "``logamount'' not valid without"
" ``log''");
ac--; av++;
if (!ac)
errx(EX_USAGE, "``logamount'' requires argument");
rule.fw_logamount = atoi(*av);
if (rule.fw_logamount < 0)
errx(EX_DATAERR, "``logamount'' argument must be"
" positive");
if (rule.fw_logamount == 0)
rule.fw_logamount = -1;
ac--; av++;
}
/* protocol */
if (ac == 0)
errx(EX_USAGE, "missing protocol");
if ((proto = atoi(*av)) > 0) {
rule.fw_prot = proto; av++; ac--;
} else if (!strncmp(*av, "all", strlen(*av))) {
rule.fw_prot = IPPROTO_IP; av++; ac--;
} else if ((pe = getprotobyname(*av)) != NULL) {
rule.fw_prot = pe->p_proto; av++; ac--;
} else {
errx(EX_DATAERR, "invalid protocol ``%s''", *av);
}
if (rule.fw_prot != IPPROTO_TCP
&& (rule.fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
&& rule.fw_reject_code == IP_FW_REJECT_RST)
errx(EX_DATAERR, "``reset'' is only valid for tcp packets");
/* from */
if (ac && !strncmp(*av, "from", strlen(*av))) { av++; ac--; }
else
errx(EX_USAGE, "missing ``from''");
if (ac && !strncmp(*av, "not", strlen(*av))) {
rule.fw_flg |= IP_FW_F_INVSRC;
av++; ac--;
}
if (!ac)
errx(EX_USAGE, "missing arguments");
if (ac && !strncmp(*av, "me", strlen(*av))) {
rule.fw_flg |= IP_FW_F_SME;
av++; ac--;
} else {
fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
}
if (ac && (isdigit(**av)
|| lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) {
u_short nports = 0;
int retval;
retval = fill_port(&nports, rule.fw_uar.fw_pts,
0, *av, rule.fw_prot);
if (retval == 1)
rule.fw_flg |= IP_FW_F_SRNG;
else if (retval == 2)
rule.fw_flg |= IP_FW_F_SMSK;
IP_FW_SETNSRCP(&rule, nports);
av++; ac--;
}
/* to */
if (ac && !strncmp(*av, "to", strlen(*av))) { av++; ac--; }
else
errx(EX_USAGE, "missing ``to''");
if (ac && !strncmp(*av, "not", strlen(*av))) {
rule.fw_flg |= IP_FW_F_INVDST;
av++; ac--;
}
if (!ac)
errx(EX_USAGE, "missing arguments");
if (ac && !strncmp(*av, "me", strlen(*av))) {
rule.fw_flg |= IP_FW_F_DME;
av++; ac--;
} else {
fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
}
if (ac && (isdigit(**av)
|| lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) {
u_short nports = 0;
int retval;
retval = fill_port(&nports, rule.fw_uar.fw_pts,
IP_FW_GETNSRCP(&rule), *av, rule.fw_prot);
if (retval == 1)
rule.fw_flg |= IP_FW_F_DRNG;
else if (retval == 2)
rule.fw_flg |= IP_FW_F_DMSK;
IP_FW_SETNDSTP(&rule, nports);
av++; ac--;
}
if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
&& (IP_FW_GETNSRCP(&rule) || IP_FW_GETNDSTP(&rule))) {
errx(EX_USAGE, "only TCP and UDP protocols are valid"
" with port specifications");
}
while (ac) {
if (!strncmp(*av, "uid", strlen(*av))) {
struct passwd *pwd;
char *end;
uid_t uid;
rule.fw_flg |= IP_FW_F_UID;
ac--; av++;
if (!ac)
errx(EX_USAGE, "``uid'' requires argument");
uid = strtoul(*av, &end, 0);
if (*end == '\0')
pwd = getpwuid(uid);
else
pwd = getpwnam(*av);
if (pwd == NULL)
errx(EX_DATAERR, "uid \"%s\" is"
" nonexistent", *av);
rule.fw_uid = pwd->pw_uid;
ac--; av++;
} else if (!strncmp(*av, "gid", strlen(*av))) {
struct group *grp;
char *end;
gid_t gid;
rule.fw_flg |= IP_FW_F_GID;
ac--; av++;
if (!ac)
errx(EX_USAGE, "``gid'' requires argument");
gid = strtoul(*av, &end, 0);
if (*end == '\0')
grp = getgrgid(gid);
else
grp = getgrnam(*av);
if (grp == NULL)
errx(EX_DATAERR, "gid \"%s\" is"
" nonexistent", *av);
rule.fw_gid = grp->gr_gid;
ac--; av++;
} else if (!strncmp(*av, "in", strlen(*av))) {
rule.fw_flg |= IP_FW_F_IN;
av++; ac--;
} else if (!strncmp(*av,"limit",strlen(*av))) {
/* dyn. rule used to limit number of connections. */
rule.fw_flg |= IP_FW_F_KEEP_S;
rule.dyn_type = DYN_LIMIT ;
rule.limit_mask = 0 ;
av++; ac--;
for (; ac >1 ;) {
struct _s_x *p = limit_masks;
for ( ; p->s != NULL ; p++)
if (!strncmp(*av, p->s, strlen(*av))) {
rule.limit_mask |= p->x ;
av++; ac-- ;
break ;
}
if (p->s == NULL)
break ;
}
if (ac < 1)
errx(EX_USAGE,
"limit needs mask and # of connections");
rule.conn_limit = atoi(*av);
if (rule.conn_limit == 0)
errx(EX_USAGE, "limit: limit must be >0");
if (rule.limit_mask == 0)
errx(EX_USAGE, "missing limit mask");
av++; ac--;
} else if (!strncmp(*av, "keep-state", strlen(*av))) {
u_long type;
rule.fw_flg |= IP_FW_F_KEEP_S;
av++; ac--;
if (ac > 0 && (type = atoi(*av)) != 0) {
rule.dyn_type = type;
av++; ac--;
}
} else if (!strncmp(*av, "bridged", strlen(*av))) {
rule.fw_flg |= IP_FW_BRIDGED;
av++; ac--;
} else if (!strncmp(*av, "out", strlen(*av))) {
rule.fw_flg |= IP_FW_F_OUT;
av++; ac--;
} else if (ac && !strncmp(*av, "xmit", strlen(*av))) {
union ip_fw_if ifu;
int byname;
if (saw_via) {
badviacombo:
errx(EX_USAGE, "``via'' is incompatible"
" with ``xmit'' and ``recv''");
}
saw_xmrc = 1;
av++; ac--;
fill_iface("xmit", &ifu, &byname, ac, *av);
rule.fw_out_if = ifu;
rule.fw_flg |= IP_FW_F_OIFACE;
if (byname)
rule.fw_flg |= IP_FW_F_OIFNAME;
av++; ac--;
} else if (ac && !strncmp(*av, "recv", strlen(*av))) {
union ip_fw_if ifu;
int byname;
if (saw_via)
goto badviacombo;
saw_xmrc = 1;
av++; ac--;
fill_iface("recv", &ifu, &byname, ac, *av);
rule.fw_in_if = ifu;
rule.fw_flg |= IP_FW_F_IIFACE;
if (byname)
rule.fw_flg |= IP_FW_F_IIFNAME;
av++; ac--;
} else if (ac && !strncmp(*av, "via", strlen(*av))) {
union ip_fw_if ifu;
int byname = 0;
if (saw_xmrc)
goto badviacombo;
saw_via = 1;
av++; ac--;
fill_iface("via", &ifu, &byname, ac, *av);
rule.fw_out_if = rule.fw_in_if = ifu;
if (byname)
rule.fw_flg |=
(IP_FW_F_IIFNAME | IP_FW_F_OIFNAME);
av++; ac--;
} else if (!strncmp(*av, "fragment", strlen(*av))) {
rule.fw_flg |= IP_FW_F_FRAG;
av++; ac--;
} else if (!strncmp(*av, "ipoptions", strlen(*av))
|| !strncmp(*av, "ipopts", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``ipoptions''");
rule.fw_ipflg |= IP_FW_IF_IPOPT;
fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av);
av++; ac--;
} else if (!strncmp(*av, "iplen", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``iplen''");
rule.fw_ipflg |= IP_FW_IF_IPLEN;
rule.fw_iplen = (u_short)strtoul(*av, NULL, 0);
av++; ac--;
} else if (!strncmp(*av, "ipid", strlen(*av))) {
unsigned long ipid;
char *c;
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``ipid''");
ipid = strtoul(*av, &c, 0);
if (*c != '\0')
errx(EX_DATAERR, "argument to ipid must"
" be numeric");
if (ipid > 65535)
errx(EX_DATAERR, "argument to ipid out"
" of range");
rule.fw_ipflg |= IP_FW_IF_IPID;
rule.fw_ipid = (u_short)ipid;
av++; ac--;
} else if (!strncmp(*av, "ipprecedence", strlen(*av))) {
u_long ippre;
char *c;
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``ipprecedence''");
ippre = strtoul(*av, &c, 0);
if (*c != '\0')
errx(EX_DATAERR, "argument to ipprecedence"
" must be numeric");
if (ippre > 7)
errx(EX_DATAERR, "argument to ipprecedence"
" out of range");
rule.fw_ipflg |= IP_FW_IF_IPPRE;
rule.fw_iptos |= (u_short)(ippre << 5);
av++; ac--;
} else if (!strncmp(*av, "iptos", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``iptos''");
rule.fw_ipflg |= IP_FW_IF_IPTOS;
fill_iptos(&rule.fw_iptos, &rule.fw_ipntos, av);
av++; ac--;
} else if (!strncmp(*av, "ipttl", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``ipttl''");
rule.fw_ipflg |= IP_FW_IF_IPTTL;
rule.fw_ipttl = (u_short)strtoul(*av, NULL, 0);
av++; ac--;
} else if (!strncmp(*av, "ipversion", strlen(*av))
|| !strncmp(*av, "ipver", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``ipversion''");
rule.fw_ipflg |= IP_FW_IF_IPVER;
rule.fw_ipver = (u_short)strtoul(*av, NULL, 0);
av++; ac--;
} else if (rule.fw_prot == IPPROTO_TCP) {
if (!strncmp(*av, "established", strlen(*av))) {
rule.fw_ipflg |= IP_FW_IF_TCPEST;
av++; ac--;
} else if (!strncmp(*av, "setup", strlen(*av))) {
rule.fw_tcpf |= IP_FW_TCPF_SYN;
rule.fw_tcpnf |= IP_FW_TCPF_ACK;
rule.fw_ipflg |= IP_FW_IF_TCPFLG;
av++; ac--;
} else if (!strncmp(*av, "tcpflags", strlen(*av))
|| !strncmp(*av, "tcpflgs", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``tcpflags''");
rule.fw_ipflg |= IP_FW_IF_TCPFLG;
fill_tcpflag(&rule.fw_tcpf,
&rule.fw_tcpnf, av);
av++; ac--;
} else if (!strncmp(*av, "tcpoptions", strlen(*av))
|| !strncmp(*av, "tcpopts", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``tcpoptions''");
rule.fw_ipflg |= IP_FW_IF_TCPOPT;
fill_tcpopts(&rule.fw_tcpopt,
&rule.fw_tcpnopt, av);
av++; ac--;
} else if (!strncmp(*av, "tcpseq", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``tcpseq''");
rule.fw_ipflg |= IP_FW_IF_TCPSEQ;
rule.fw_tcpseq =
htonl(strtoul(*av, NULL, 0));
av++; ac--;
} else if (!strncmp(*av, "tcpack", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``tcpack''");
rule.fw_ipflg |= IP_FW_IF_TCPACK;
rule.fw_tcpack =
htonl(strtoul(*av, NULL, 0));
av++; ac--;
} else if (!strncmp(*av, "tcpwin", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``tcpwin''");
rule.fw_ipflg |= IP_FW_IF_TCPWIN;
rule.fw_tcpwin =
htons((u_short)strtoul(*av, NULL, 0));
av++; ac--;
} else
errx(EX_USAGE, "unknown or out of order"
" argument ``%s''", *av);
} else if (rule.fw_prot == IPPROTO_ICMP) {
if (!strncmp(*av, "icmptypes", strlen(*av))) {
av++; ac--;
if (!ac)
errx(EX_USAGE, "missing argument"
" for ``icmptypes''");
fill_icmptypes(rule.fw_uar.fw_icmptypes,
av, &rule.fw_flg);
av++; ac--;
} else
errx(EX_USAGE, "unknown or out of"
" order argument ``%s''", *av);
} else
errx(EX_USAGE, "unknown argument ``%s''", *av);
}
/* No direction specified -> do both directions */
if (!(rule.fw_flg & (IP_FW_F_OUT|IP_FW_F_IN)))
rule.fw_flg |= (IP_FW_F_OUT|IP_FW_F_IN);
/* Sanity check interface check, but handle "via" case separately */
if (saw_via) {
if (rule.fw_flg & IP_FW_F_IN)
rule.fw_flg |= IP_FW_F_IIFACE;
if (rule.fw_flg & IP_FW_F_OUT)
rule.fw_flg |= IP_FW_F_OIFACE;
} else if ((rule.fw_flg & IP_FW_F_OIFACE)
&& (rule.fw_flg & IP_FW_F_IN)) {
errx(EX_DATAERR, "can't check xmit interface of incoming"
" packets");
}
/* frag may not be used in conjunction with ports or TCP flags */
if (rule.fw_flg & IP_FW_F_FRAG) {
if (rule.fw_tcpf || rule.fw_tcpnf)
errx(EX_DATAERR, "can't mix 'frag' and tcpflags");
if (rule.fw_nports)
errx(EX_DATAERR, "can't mix 'frag' and port"
" specifications");
}
if (rule.fw_flg & IP_FW_F_PRN) {
if (!rule.fw_logamount) {
size_t len = sizeof(int);
if (sysctlbyname("net.inet.ip.fw.verbose_limit",
&rule.fw_logamount, &len, NULL, 0) == -1)
errx(1, "sysctlbyname(\"%s\")",
"net.inet.ip.fw.verbose_limit");
} else if (rule.fw_logamount == -1)
rule.fw_logamount = 0;
rule.fw_loghighest = rule.fw_logamount;
}
done:
i = sizeof(rule);
if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, &i) == -1)
err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
if (!do_quiet)
show_ipfw(&rule);
}
static void
zero (int ac, char *av[])
{
struct ip_fw rule;
int failed = EX_OK;
av++; ac--;
if (!ac) {
/* clear all entries */
if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, NULL, 0) < 0)
err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO");
if (!do_quiet)
printf("Accounting cleared.\n");
return;
}
memset(&rule, 0, sizeof rule);
while (ac) {
/* Rule number */
if (isdigit(**av)) {
rule.fw_number = atoi(*av); av++; ac--;
if (setsockopt(s, IPPROTO_IP,
IP_FW_ZERO, &rule, sizeof rule)) {
warn("rule %u: setsockopt(IP_FW_ZERO)",
rule.fw_number);
failed = EX_UNAVAILABLE;
} else if (!do_quiet)
printf("Entry %d cleared\n",
rule.fw_number);
} else {
errx(EX_USAGE, "invalid rule number ``%s''", *av);
}
}
if (failed != EX_OK)
exit(failed);
}
static void
resetlog (int ac, char *av[])
{
struct ip_fw rule;
int failed = EX_OK;
av++; ac--;
if (!ac) {
/* clear all entries */
if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, NULL, 0) < 0)
err(EX_UNAVAILABLE, "setsockopt(IP_FW_RESETLOG)");
if (!do_quiet)
printf("Logging counts reset.\n");
return;
}
memset(&rule, 0, sizeof rule);
while (ac) {
/* Rule number */
if (isdigit(**av)) {
rule.fw_number = atoi(*av); av++; ac--;
if (setsockopt(s, IPPROTO_IP,
IP_FW_RESETLOG, &rule, sizeof rule)) {
warn("rule %u: setsockopt(IP_FW_RESETLOG)",
rule.fw_number);
failed = EX_UNAVAILABLE;
} else if (!do_quiet)
printf("Entry %d logging count reset\n",
rule.fw_number);
} else {
errx(EX_DATAERR, "invalid rule number ``%s''", *av);
}
}
if (failed != EX_OK)
exit(failed);
}
static int
ipfw_main(int ac, char **av)
{
int ch;
if (ac == 1)
show_usage();
/* Set the force flag for non-interactive processes */
do_force = !isatty(STDIN_FILENO);
optind = optreset = 1;
while ((ch = getopt(ac, av, "s:adefNqtv")) != -1)
switch (ch) {
case 's': /* sort */
do_sort = atoi(optarg);
break;
case 'a':
do_acct = 1;
break;
case 'd':
do_dynamic = 1;
break;
case 'e':
do_expired = 1;
break;
case 'f':
do_force = 1;
break;
case 'N':
do_resolv = 1;
break;
case 'q':
do_quiet = 1;
break;
case 't':
do_time = 1;
break;
case 'v': /* verbose */
verbose++;
break;
default:
show_usage();
}
ac -= optind;
if (*(av += optind) == NULL)
errx(EX_USAGE, "bad arguments, for usage summary ``ipfw''");
if (!strncmp(*av, "pipe", strlen(*av))) {
do_pipe = 1;
ac--;
av++;
} else if (!strncmp(*av, "queue", strlen(*av))) {
do_pipe = 2;
ac--;
av++;
}
if (!ac)
errx(EX_USAGE, "pipe requires arguments");
/* allow argument swapping */
if (ac > 1 && *av[0] >= '0' && *av[0] <= '9') {
char *p = av[0];
av[0] = av[1];
av[1] = p;
}
if (!strncmp(*av, "add", strlen(*av))) {
add(ac, av);
} else if (do_pipe && !strncmp(*av, "config", strlen(*av))) {
config_pipe(ac, av);
} else if (!strncmp(*av, "delete", strlen(*av))) {
delete(ac, av);
} else if (!strncmp(*av, "flush", strlen(*av))) {
int do_flush = 0;
if (do_force || do_quiet)
do_flush = 1;
else {
int c;
/* Ask the user */
printf("Are you sure? [yn] ");
fflush(stdout);
do {
c = toupper(getc(stdin));
while (c != '\n' && getc(stdin) != '\n')
if (feof(stdin))
return (0);
} while (c != 'Y' && c != 'N');
printf("\n");
if (c == 'Y')
do_flush = 1;
}
if (do_flush) {
if (setsockopt(s, IPPROTO_IP,
do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH,
NULL, 0) < 0)
err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
do_pipe ? "DUMMYNET" : "FW");
if (!do_quiet)
printf("Flushed all %s.\n",
do_pipe ? "pipes" : "rules");
}
} else if (!strncmp(*av, "zero", strlen(*av))) {
zero(ac, av);
} else if (!strncmp(*av, "resetlog", strlen(*av))) {
resetlog(ac, av);
} else if (!strncmp(*av, "print", strlen(*av))) {
list(--ac, ++av);
} else if (!strncmp(*av, "list", strlen(*av))) {
list(--ac, ++av);
} else if (!strncmp(*av, "show", strlen(*av))) {
do_acct++;
list(--ac, ++av);
} else {
errx(EX_USAGE, "bad arguments, for usage summary ``ipfw''");
}
return 0;
}
int
main(int ac, char *av[])
{
#define MAX_ARGS 32
#define WHITESP " \t\f\v\n\r"
char buf[BUFSIZ];
char *a, *p, *args[MAX_ARGS], *cmd = NULL;
char linename[10];
int i, c, lineno, qflag, pflag, status;
FILE *f = NULL;
pid_t preproc = 0;
s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (s < 0)
err(EX_UNAVAILABLE, "socket");
setbuf(stdout, 0);
/*
* Only interpret the last command line argument as a file to
* be preprocessed if it is specified as an absolute pathname.
*/
if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) {
qflag = pflag = i = 0;
lineno = 0;
while ((c = getopt(ac, av, "D:U:p:q")) != -1)
switch(c) {
case 'D':
if (!pflag)
errx(EX_USAGE, "-D requires -p");
if (i > MAX_ARGS - 2)
errx(EX_USAGE,
"too many -D or -U options");
args[i++] = "-D";
args[i++] = optarg;
break;
case 'U':
if (!pflag)
errx(EX_USAGE, "-U requires -p");
if (i > MAX_ARGS - 2)
errx(EX_USAGE,
"too many -D or -U options");
args[i++] = "-U";
args[i++] = optarg;
break;
case 'p':
pflag = 1;
cmd = optarg;
args[0] = cmd;
i = 1;
break;
case 'q':
qflag = 1;
break;
default:
errx(EX_USAGE, "bad arguments, for usage"
" summary ``ipfw''");
}
av += optind;
ac -= optind;
if (ac != 1)
errx(EX_USAGE, "extraneous filename arguments");
if ((f = fopen(av[0], "r")) == NULL)
err(EX_UNAVAILABLE, "fopen: %s", av[0]);
if (pflag) {
/* pipe through preprocessor (cpp or m4) */
int pipedes[2];
args[i] = 0;
if (pipe(pipedes) == -1)
err(EX_OSERR, "cannot create pipe");
switch((preproc = fork())) {
case -1:
err(EX_OSERR, "cannot fork");
case 0:
/* child */
if (dup2(fileno(f), 0) == -1
|| dup2(pipedes[1], 1) == -1)
err(EX_OSERR, "dup2()");
fclose(f);
close(pipedes[1]);
close(pipedes[0]);
execvp(cmd, args);
err(EX_OSERR, "execvp(%s) failed", cmd);
default:
/* parent */
fclose(f);
close(pipedes[1]);
if ((f = fdopen(pipedes[0], "r")) == NULL) {
int savederrno = errno;
(void)kill(preproc, SIGTERM);
errno = savederrno;
err(EX_OSERR, "fdopen()");
}
}
}
while (fgets(buf, BUFSIZ, f)) {
lineno++;
sprintf(linename, "Line %d", lineno);
args[0] = linename;
if (*buf == '#')
continue;
if ((p = strchr(buf, '#')) != NULL)
*p = '\0';
i = 1;
if (qflag)
args[i++] = "-q";
for (a = strtok(buf, WHITESP);
a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
args[i] = a;
if (i == (qflag? 2: 1))
continue;
if (i == MAX_ARGS)
errx(EX_USAGE, "%s: too many arguments",
linename);
args[i] = NULL;
ipfw_main(i, args);
}
fclose(f);
if (pflag) {
if (waitpid(preproc, &status, 0) == -1)
errx(EX_OSERR, "waitpid()");
if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
errx(EX_UNAVAILABLE,
"preprocessor exited with status %d",
WEXITSTATUS(status));
else if (WIFSIGNALED(status))
errx(EX_UNAVAILABLE,
"preprocessor exited with signal %d",
WTERMSIG(status));
}
} else {
ipfw_main(ac, av);
}
return EX_OK;
}