freebsd-skq/sbin/ipfw/ipfw.c
Robert Watson 65450f2f77 o IPFW incorrectly handled filtering in the presence of previously
reserved and now allocated TCP flags in incoming packets.  This patch
  stops overloading those bits in the IP firewall rules, and moves
  colliding flags to a seperate field, ipflg.  The IPFW userland
  management tool, ipfw(8), is updated to reflect this change.  New TCP
  flags related to ECN are now included in tcp.h for reference, although
  we don't currently implement TCP+ECN.

o To use this fix without completely rebuilding, it is sufficient to copy
  ip_fw.h and tcp.h into your appropriate include directory, then rebuild
  the ipfw kernel module, and ipfw tool, and install both.  Note that a
  mismatch between module and userland tool will result in incorrect
  installation of firewall rules that may have unexpected effects.  This
  is an MFC candidate, following shakedown.  This bug does not appear
  to affect ipfilter.

Reviewed by:	security-officer, billf
Reported by:	Aragon Gouveia <aragon@phat.za.net>
2001-01-09 03:10:30 +00:00

2496 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) */
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(const char *fmt, ...);
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(prot, port, comma)
u_char prot;
u_short port;
const char *comma;
{
struct servent *se;
struct protoent *pe;
const char *protocol;
int printed = 0;
if (!strcmp(comma,":")) {
printf("%s0x%04x", comma, port);
return ;
}
if (do_resolv) {
pe = getprotobynumber(prot);
if (pe)
protocol = pe->p_name;
else
protocol = NULL;
se = getservbyport(htons(port), protocol);
if (se) {
printf("%s%s", comma, se->s_name);
printed = 1;
}
}
if (!printed)
printf("%s%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);
}
static void
show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
{
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("%*qu %*qu ",pcwidth,chain->fw_pcnt,bcwidth,chain->fw_bcnt);
if (do_time)
{
if (chain->timestamp)
{
char timestr[30];
strcpy(timestr, ctime((time_t *)&chain->timestamp));
*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 * (int)(chain->pipe_ptr) ;
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);
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(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(inet_ntoa(chain->fw_src));
printf("/%d",mb);
} else {
printf(inet_ntoa(chain->fw_src));
printf(":");
printf(inet_ntoa(chain->fw_smsk));
}
}
} else
printf(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 = ",";
}
}
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(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(inet_ntoa(chain->fw_dst));
printf("/%d",mb);
} else {
printf(inet_ntoa(chain->fw_dst));
printf(":");
printf(inet_ntoa(chain->fw_dmsk));
}
}
} else
printf(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) {
if (chain->next_rule_ptr)
printf(" keep-state %d", (int)chain->next_rule_ptr);
else
printf(" keep-state");
}
/* 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_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", ntohl(chain->fw_tcpseq));
if (chain->fw_ipflg & IP_FW_IF_TCPACK)
printf(" tcpack %lu", 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 type_index;
int first = 1;
printf(" icmptype");
for (type_index = 0; type_index < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
if (chain->fw_uar.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] &
(1U << (type_index % (sizeof(unsigned) * 8)))) {
printf("%c%d", first == 1 ? ' ' : ',', type_index);
first = 0;
}
}
printf("\n");
done:
if (do_resolv)
endservent();
}
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 %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(ac, av)
int ac;
char **av;
{
struct ip_fw *rules;
struct dn_pipe *pipes;
void *data = NULL;
int pcwidth = 0;
int bcwidth = 0;
int n, num = 0;
int nbytes;
/* 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;
/* determine num more accurately */
num = 0;
while (rules[num].fw_number < 65535)
num++ ;
num++ ; /* counting starts from 0 ... */
/* if showing stats, figure out column widths ahead of time */
if (do_acct) {
for (n = 0; n < num; n++) {
struct ip_fw *const r = &rules[n];
char temp[32];
int width;
/* packet counter */
width = sprintf(temp, "%qu", r->fw_pcnt);
if (width > pcwidth)
pcwidth = width;
/* byte counter */
width = sprintf(temp, "%qu", r->fw_bcnt);
if (width > bcwidth)
bcwidth = width;
}
}
if (ac == 0) {
/* display all rules */
for (n = 0; n < num; n++) {
struct ip_fw *const r = &rules[n];
show_ipfw(r, pcwidth, bcwidth);
}
} else {
/* display specific rules requested on command line */
int exitval = EX_OK;
while (ac--) {
u_long rnum;
char *endptr;
int seen;
/* convert command line rule # */
rnum = strtoul(*av++, &endptr, 10);
if (*endptr) {
exitval = EX_USAGE;
warnx("invalid rule number: %s", *(av - 1));
continue;
}
for (seen = n = 0; n < num; n++) {
struct ip_fw *const r = &rules[n];
if (r->fw_number > rnum)
break;
if (r->fw_number == rnum) {
show_ipfw(r, pcwidth, bcwidth);
seen = 1;
}
}
if (!seen) {
/* give precedence to other error(s) */
if (exitval == EX_OK)
exitval = EX_UNAVAILABLE;
warnx("rule %lu does not exist", rnum);
}
}
if (exitval != EX_OK)
exit(exitval);
}
/*
* show dynamic rules
*/
if (num * sizeof (rules[0]) != nbytes ) {
struct ipfw_dyn_rule *d =
(struct ipfw_dyn_rule *)&rules[num] ;
struct in_addr a ;
struct protoent *pe;
printf("## Dynamic rules:\n");
for (;; d++) {
printf("%05d %qu %qu (T %d, # %d) ty %d",
(int)(d->chain),
d->pcnt, d->bcnt,
d->expire,
d->bucket,
d->type);
pe = getprotobynumber(d->id.proto);
if (pe)
printf(" %s,", pe->p_name);
else
printf(" %u,", d->id.proto);
a.s_addr = htonl(d->id.src_ip);
printf(" %s", inet_ntoa(a));
printf(" %d", d->id.src_port);
switch (d->type) {
default: /* bidir, no mask */
printf(" <->");
break ;
}
a.s_addr = htonl(d->id.dst_ip);
printf(" %s", inet_ntoa(a));
printf(" %d", d->id.dst_port);
printf("\n");
if (d->next == NULL)
break ;
}
}
free(data);
}
static void
show_usage(const char *fmt, ...)
{
if (fmt) {
char buf[100];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
warnx("error: %s", buf);
}
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] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
" dst: to [not] {any|ip[{/bits|:mask}]} [{port|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"
" 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"
" 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 (host, ipaddr)
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(ipno, mask, acp, avp)
struct in_addr *ipno, *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)
show_usage("hostname ``%s'' unknown", *av);
switch (md) {
case ':':
if (!inet_aton(p,mask))
show_usage("bad netmask ``%s''", p);
break;
case '/':
if (atoi(p) == 0) {
mask->s_addr = 0;
} else if (atoi(p) > 32) {
show_usage("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;
}
show_usage("unknown ICMP unreachable code ``%s''", str);
}
static void
add_port(cnt, ptr, off, port)
u_short *cnt, *ptr, off, 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]))
show_usage("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]))
show_usage("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(types, vp, fw_flg)
u_long *types;
char **vp;
u_int *fw_flg;
{
char *c = *vp;
while (*c)
{
unsigned long icmptype;
if ( *c == ',' )
++c;
icmptype = strtoul(c, &c, 0);
if ( *c != ',' && *c != '\0' )
show_usage("invalid ICMP type");
if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
show_usage("ICMP type out of range");
types[icmptype / (sizeof(unsigned) * 8)] |=
1 << (icmptype % (sizeof(unsigned) * 8));
*fw_flg |= IP_FW_F_ICMPBIT;
}
}
static void
delete(ac,av)
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(%s)",
do_pipe==1 ? pipe.pipe_nr: pipe.fs.fs_nr,
"IP_DUMMYNET_DEL");
}
} 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(%s)", rule.fw_number, "IP_FW_DEL");
}
}
}
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)
show_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)) {
show_usage("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 ;
u_int32_t *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-- ;
} else {
for (;;) {
if (ac < 1)
break ;
if (!strncmp(*av,"dst-ip", strlen(*av)))
par = &(pipe.fs.flow_mask.dst_ip) ;
else if (!strncmp(*av,"src-ip", strlen(*av)))
par = &(pipe.fs.flow_mask.src_ip) ;
else if (!strncmp(*av,"dst-port", strlen(*av)))
(u_int16_t *)par = &(pipe.fs.flow_mask.dst_port) ;
else if (!strncmp(*av,"src-port", strlen(*av)))
(u_int16_t *)par = &(pipe.fs.flow_mask.src_port) ;
else if (!strncmp(*av,"proto", strlen(*av)))
(u_int8_t *)par = &(pipe.fs.flow_mask.proto) ;
else
break ;
if (ac < 2)
show_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 ;
fprintf(stderr, " mask is 0x%08x\n", a);
} else
a = strtoul(av[1], &end, 0);
if ( (u_int16_t *)par == &(pipe.fs.flow_mask.src_port) ||
(u_int16_t *)par == &(pipe.fs.flow_mask.dst_port) ) {
if (a >= (1<<16) )
show_usage("mask: %s must be 16 bit, not 0x%08x",
*av, a);
*((u_int16_t *)par) = (u_int16_t) a;
} else if ( (u_int8_t *)par == &(pipe.fs.flow_mask.proto) ) {
if (a >= (1<<8) )
show_usage("mask: %s must be 8 bit, not 0x%08x",
*av, a);
*((u_int8_t *)par) = (u_int8_t) a;
} else
*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)
show_usage("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)
show_usage("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 {
if (do_pipe == 1) {
/* some commands are only good for pipes. */
if (!strncmp(*av,"bw",strlen(*av)) ||
! strncmp(*av,"bandwidth",strlen(*av))) {
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 ;
}
av+=2; ac-=2;
} else if (!strncmp(*av,"delay",strlen(*av)) ) {
pipe.delay = strtoul(av[1], NULL, 0);
av+=2; ac-=2;
} else
show_usage("unrecognised pipe option ``%s''", *av);
} else { /* this refers to a queue */
if (!strncmp(*av,"weight",strlen(*av)) ) {
pipe.fs.weight = strtoul(av[1], &end, 0) ;
av += 2;
ac -= 2;
} else if (!strncmp(*av,"pipe",strlen(*av)) ) {
pipe.fs.parent_nr = strtoul(av[1], &end, 0) ;
av += 2;
ac -= 2;
} else
show_usage("unrecognised option ``%s''", *av);
}
}
}
if (do_pipe == 1) {
if (pipe.pipe_nr == 0 )
show_usage("pipe_nr %d must be > 0", pipe.pipe_nr);
if (pipe.delay > 10000 )
show_usage("delay %d must be < 10000", pipe.delay);
} else { /* do_pipe == 2, queue */
if (pipe.fs.parent_nr == 0)
show_usage("pipe %d must be > 0", pipe.fs.parent_nr);
if (pipe.fs.weight >100)
show_usage("weight %d must be <= 100", pipe.fs.weight);
}
if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES ) {
if (pipe.fs.qsize > 1024*1024 )
show_usage("queue size %d, must be < 1MB",
pipe.fs.qsize);
} else {
if (pipe.fs.qsize > 100 )
show_usage("queue size %d, must be 2 <= x <= 100",
pipe.fs.qsize);
}
if (pipe.fs.flags_fs & DN_IS_RED) {
if ( pipe.fs.min_th >= pipe.fs.max_th )
show_usage("min_th %d must be < than max_th %d",
pipe.fs.min_th, pipe.fs.max_th) ;
if ( pipe.fs.max_th == 0 )
show_usage("max_th must be > 0") ;
if ( pipe.bandwidth ) {
size_t len ;
int lookup_depth, avg_pkt_size ;
double s, idle, weight, w_q ;
struct clockinfo clock ;
int t ;
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)
show_usage("net.inet.ip.dummynet.red_lookup_depth must"
"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)
show_usage("net.inet.ip.dummynet.red_avg_pkt_size must"
"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 */
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)) ;
}
}
#if 0
printf("configuring pipe %d bw %d delay %d size %d\n",
pipe.pipe_nr, pipe.bandwidth, pipe.delay, pipe.queue_size);
#endif
i = setsockopt(s,IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,sizeof pipe);
if (i)
err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
}
static void
add(ac,av)
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)
show_usage("illegal match prob. %s", av[1]);
if (d != 1) { /* 1 means always match */
rule.fw_flg |= IP_FW_F_RND_MATCH ;
/* we really store dont_match probability */
(long)rule.pipe_ptr = (long)((1 - d) * 0x7fffffff) ;
}
av += 2 ; ac -= 2 ;
}
if (ac == 0)
show_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)
show_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)
show_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)
show_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
show_usage("illegal %s port", "divert");
}
} else if (!strncmp(*av,"tee",strlen(*av))) {
rule.fw_flg |= IP_FW_F_TEE; av++; ac--;
if (!ac)
show_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
show_usage("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)
show_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)
show_usage("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)
show_usage("illegal forwarding IP address");
} else if (!strncmp(*av,"skipto",strlen(*av))) {
rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
if (!ac)
show_usage("missing skipto rule number");
rule.fw_skipto_rule = strtoul(*av, NULL, 0); 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 {
show_usage("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))
show_usage("``logamount'' not valid without ``log''");
ac--; av++;
if (!ac)
show_usage("``logamount'' requires argument");
rule.fw_logamount = atoi(*av);
if (rule.fw_logamount < 0)
show_usage("``logamount'' argument must be positive");
if (rule.fw_logamount == 0)
rule.fw_logamount = -1;
ac--; av++;
}
/* protocol */
if (ac == 0)
show_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 {
show_usage("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)
show_usage("``reset'' is only valid for tcp packets");
/* from */
if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
else
show_usage("missing ``from''");
if (ac && !strncmp(*av,"not",strlen(*av))) {
rule.fw_flg |= IP_FW_F_INVSRC;
av++; ac--;
}
if (!ac)
show_usage("missing arguments");
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
show_usage("missing ``to''");
if (ac && !strncmp(*av,"not",strlen(*av))) {
rule.fw_flg |= IP_FW_F_INVDST;
av++; ac--;
}
if (!ac)
show_usage("missing arguments");
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))) {
show_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)
show_usage("``uid'' requires argument");
uid = strtoul(*av, &end, 0);
if (*end == '\0')
pwd = getpwuid(uid);
else
pwd = getpwnam(*av);
if (pwd == NULL)
show_usage("uid \"%s\" is nonexistant", *av);
rule.fw_uid = pwd->pw_uid;
ac--; av++;
continue;
}
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)
show_usage("``gid'' requires argument");
gid = strtoul(*av, &end, 0);
if (*end == '\0')
grp = getgrgid(gid);
else
grp = getgrnam(*av);
if (grp == NULL)
show_usage("gid \"%s\" is nonexistant", *av);
rule.fw_gid = grp->gr_gid;
ac--; av++;
continue;
}
if (!strncmp(*av,"in",strlen(*av))) {
rule.fw_flg |= IP_FW_F_IN;
av++; ac--; continue;
}
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) {
(int)rule.next_rule_ptr = type ;
av++; ac--;
}
continue;
}
if (!strncmp(*av,"bridged",strlen(*av))) {
rule.fw_flg |= IP_FW_BRIDGED;
av++; ac--; continue;
}
if (!strncmp(*av,"out",strlen(*av))) {
rule.fw_flg |= IP_FW_F_OUT;
av++; ac--; continue;
}
if (ac && !strncmp(*av,"xmit",strlen(*av))) {
union ip_fw_if ifu;
int byname;
if (saw_via) {
badviacombo:
show_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--; continue;
}
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--; continue;
}
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--; continue;
}
if (!strncmp(*av,"fragment",strlen(*av))) {
rule.fw_flg |= IP_FW_F_FRAG;
av++; ac--; continue;
}
if (!strncmp(*av,"ipoptions",strlen(*av)) ||
!strncmp(*av,"ipopts",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``ipoptions''");
rule.fw_ipflg |= IP_FW_IF_IPOPT;
fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av);
av++; ac--; continue;
}
if (!strncmp(*av,"iplen",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``iplen''");
rule.fw_ipflg |= IP_FW_IF_IPLEN;
rule.fw_iplen = (u_short)strtoul(*av, NULL, 0);
av++; ac--; continue;
}
if (!strncmp(*av,"ipid",strlen(*av))) {
unsigned long ipid;
char *c;
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``ipid''");
ipid = strtoul(*av, &c, 0);
if (*c != '\0')
show_usage("argument to ipid must be numeric");
if (ipid > 65535)
show_usage("argument to ipid out of range");
rule.fw_ipflg |= IP_FW_IF_IPID;
rule.fw_ipid = (u_short)ipid;
av++; ac--; continue;
}
if (!strncmp(*av,"iptos",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``iptos''");
rule.fw_ipflg |= IP_FW_IF_IPTOS;
fill_iptos(&rule.fw_iptos, &rule.fw_ipntos, av);
av++; ac--; continue;
}
if (!strncmp(*av,"ipttl",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``ipttl''");
rule.fw_ipflg |= IP_FW_IF_IPTTL;
rule.fw_ipttl = (u_short)strtoul(*av, NULL, 0);
av++; ac--; continue;
}
if (!strncmp(*av,"ipversion",strlen(*av)) ||
!strncmp(*av,"ipver",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``ipversion''");
rule.fw_ipflg |= IP_FW_IF_IPVER;
rule.fw_ipver = (u_short)strtoul(*av, NULL, 0);
av++; ac--; continue;
}
if (rule.fw_prot == IPPROTO_TCP) {
if (!strncmp(*av,"established",strlen(*av))) {
rule.fw_ipflg |= IP_FW_IF_TCPEST;
av++; ac--; continue;
}
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--; continue;
}
if (!strncmp(*av,"tcpflags",strlen(*av)) ||
!strncmp(*av,"tcpflgs",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``tcpflags''");
rule.fw_ipflg |= IP_FW_IF_TCPFLG;
fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
av++; ac--; continue;
}
if (!strncmp(*av,"tcpoptions",strlen(*av)) ||
!strncmp(*av, "tcpopts",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``tcpoptions''");
rule.fw_ipflg |= IP_FW_IF_TCPOPT;
fill_tcpopts(&rule.fw_tcpopt, &rule.fw_tcpnopt, av);
av++; ac--; continue;
}
if (!strncmp(*av,"tcpseq",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``tcpseq''");
rule.fw_ipflg |= IP_FW_IF_TCPSEQ;
rule.fw_tcpseq = htonl((u_int32_t)strtoul(*av, NULL, 0));
av++; ac--; continue;
}
if (!strncmp(*av,"tcpack",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``tcpack''");
rule.fw_ipflg |= IP_FW_IF_TCPACK;
rule.fw_tcpack = htonl((u_int32_t)strtoul(*av, NULL, 0));
av++; ac--; continue;
}
if (!strncmp(*av,"tcpwin",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``tcpwin''");
rule.fw_ipflg |= IP_FW_IF_TCPWIN;
rule.fw_tcpwin = htons((u_short)strtoul(*av, NULL, 0));
av++; ac--; continue;
}
}
if (rule.fw_prot == IPPROTO_ICMP) {
if (!strncmp(*av,"icmptypes",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``icmptypes''");
fill_icmptypes(rule.fw_uar.fw_icmptypes,
av, &rule.fw_flg);
av++; ac--; continue;
}
}
show_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))
show_usage("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)
show_usage("can't mix 'frag' and tcpflags");
if (rule.fw_nports)
show_usage("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, 10, 10);
}
static void
zero (ac, av)
int ac;
char **av;
{
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");
} else {
struct ip_fw rule;
int failed = EX_OK;
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(%s)", rule.fw_number,
"IP_FW_ZERO");
failed = EX_UNAVAILABLE;
}
else if (!do_quiet)
printf("Entry %d cleared\n",
rule.fw_number);
} else
show_usage("invalid rule number ``%s''", *av);
}
if (failed != EX_OK)
exit(failed);
}
}
static void
resetlog (ac, av)
int ac;
char **av;
{
av++; ac--;
if (!ac) {
/* clear all entries */
if (setsockopt(s,IPPROTO_IP,IP_FW_RESETLOG,NULL,0)<0)
err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_RESETLOG");
if (!do_quiet)
printf("Logging counts reset.\n");
} else {
struct ip_fw rule;
int failed = EX_OK;
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(%s)", rule.fw_number,
"IP_FW_RESETLOG");
failed = EX_UNAVAILABLE;
}
else if (!do_quiet)
printf("Entry %d logging count reset\n",
rule.fw_number);
} else
show_usage("invalid rule number ``%s''", *av);
}
if (failed != EX_OK)
exit(failed);
}
}
static int
ipfw_main(ac,av)
int ac;
char **av;
{
int ch;
if ( ac == 1 ) {
show_usage(NULL);
}
/* Initialize globals. */
do_resolv = do_acct = do_time = do_quiet =
do_pipe = do_sort = verbose = 0;
/* Set the force flag for non-interactive processes */
do_force = !isatty(STDIN_FILENO);
optind = optreset = 1;
while ((ch = getopt(ac, av, "s:afqtvN")) != -1)
switch(ch) {
case 's': /* sort */
do_sort= atoi(optarg);
break;
case 'a':
do_acct=1;
break;
case 'f':
do_force=1;
break;
case 'q':
do_quiet=1;
break;
case 't':
do_time=1;
break;
case 'v': /* verbose */
verbose++ ;
break;
case 'N':
do_resolv=1;
break;
default:
show_usage(NULL);
}
ac -= optind;
if (*(av+=optind)==NULL) {
show_usage("bad arguments");
}
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) {
show_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] ");
do {
fflush(stdout);
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 {
show_usage("bad arguments");
}
return 0;
}
int
main(ac, av)
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);
/*
* this is a nasty check on the last argument!!!
* If there happens to be a filename matching a keyword in the current
* directory, things will fail miserably.
*/
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:
show_usage(NULL);
}
av += optind;
ac -= optind;
if (ac != 1)
show_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) {
if (WIFEXITED(status)) {
if (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;
}