8b2204b3d0
+ 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
2646 lines
64 KiB
C
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;
|
|
}
|