summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>1997-06-02 05:02:37 +0000
committerjulian <julian@FreeBSD.org>1997-06-02 05:02:37 +0000
commit18750f03542b6c804f678f3b4fc1d4b4405fa79a (patch)
tree3a450132fdb541b607910a830c480304a9099681 /sbin
parentff62a150f76ce0a4c48dd9db8488b95c2fb31db5 (diff)
downloadFreeBSD-src-18750f03542b6c804f678f3b4fc1d4b4405fa79a.zip
FreeBSD-src-18750f03542b6c804f678f3b4fc1d4b4405fa79a.tar.gz
Submitted by: Whistle Communications (archie Cobbs)
these are quite extensive additions to the ipfw code. they include a change to the API because the old method was broken, but the user view is kept the same. The new code allows a particular match to skip forward to a particular line number, so that blocks of rules can be used without checking all the intervening rules. There are also many more ways of rejecting connections especially TCP related, and many many more ... see the man page for a complete description.
Diffstat (limited to 'sbin')
-rw-r--r--sbin/ipfw/Makefile2
-rw-r--r--sbin/ipfw/ipfw.8183
-rw-r--r--sbin/ipfw/ipfw.c622
3 files changed, 557 insertions, 250 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index 3effc23..1bc6fa4 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -1,5 +1,7 @@
PROG= ipfw
+COPTS+= -Wall
+
MAN8= ipfw.8
.include <bsd.prog.mk>
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 6bfa4cd..7198d04 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -19,10 +19,10 @@ flush
.Fl q
.Oc
zero
-.Op Ar number
+.Op Ar number ...
.Nm ipfw
delete
-.Ar number
+.Ar number ...
.Nm ipfw
.Op Fl aftN
list
@@ -122,41 +122,101 @@ Try to resolve addresses and service names in output.
.Bl -hang -offset flag -width 1234567890123456
.It Ar allow
Allow packets that match rule.
-The search terminates.
-.It Ar pass
-Same as allow.
-.It Ar accept
-Same as allow.
-.It Ar count
-Update counters for all packets that match rule.
-The search continues with the next rule.
+The search terminates. Aliases are
+.Ar pass ,
+.Ar permit ,
+and
+.Ar accept .
.It Ar deny
Discard packets that match this rule.
The search terminates.
+.Ar Drop
+is an alias for
+.Ar deny .
.It Ar reject
-Discard packets that match this rule, and try to send an ICMP notice.
+(Deprecated.) Discard packets that match this rule, and try to send an ICMP
+host unreachable notice.
The search terminates.
+.It Ar unreach code
+Discard packets that match this rule, and try to send an ICMP
+unreachable notice with code
+.Ar code ,
+where
+.Ar code
+is a number from zero to 255, or one of these aliases:
+.Ar net ,
+.Ar host ,
+.Ar protocol ,
+.Ar port ,
+.Ar needfrag ,
+.Ar srcfail ,
+.Ar net-unknown ,
+.Ar host-unknown ,
+.Ar isolated ,
+.Ar net-prohib ,
+.Ar host-prohib ,
+.Ar tosnet ,
+.Ar toshost ,
+.Ar filter-prohib ,
+.Ar host-precedence ,
+or
+.Ar precedence-cutoff .
+The search terminates.
+.It Ar reset
+TCP packets only. Discard packets that match this rule,
+and try to send a TCP reset (RST) notice.
+The search terminates.
+.It Ar count
+Update counters for all packets that match rule.
+The search continues with the next rule.
.It Ar divert port
-Divert packets that match this rule to the divert socket bound to port
+Divert packets that match this rule to the
+.Xr divert 4
+socket bound to port
.Ar port .
The search terminates.
+.It Ar tee port
+Send a copy of packets matching this rule to the
+.Xr divert 4
+socket bound to port
+.Ar port .
+The search continues with the next rule.
+.It Ar skipto number
+Skip all subsequent rules numbered less than
+.Ar number .
+The search continues with the first rule numbered
+.Ar number
+or higher.
.El
.Pp
-When a packet matches a rule with the ``log''
-keyword, a message will be printed on the console.
+If a packet matches more than one
+.Ar divert
+and/or
+.Ar tee
+rule, all but the last are ignored.
+.Pp
+If the kernel was compiled with
+.Dv IPFIREWALL_VERBOSE ,
+then when a packet matches a rule with the ``log''
+keyword a message will be printed on the console.
If the kernel was compiled with the
-.Dv IP_FIREWALL_VERBOSE_LIMIT
+.Dv IPFIREWALL_VERBOSE_LIMIT
option, then logging will cease after the number of packets
specified by the option are received for that particular
chain entry. Logging may then be re-enabled by clearing
the packet counter for that entry.
.Pp
+Console logging and the log limit are adjustable dynamically
+through the
+.Xr sysctl 8
+interface.
+.Pp
.Ar proto :
.Bl -hang -offset flag -width 1234567890123456
.It Ar ip
-All packets match.
-.It Ar all
-All packets match.
+All packets match. The alias
+.Ar all
+has the same effect.
.It Ar tcp
Only TCP packets match.
.It Ar udp
@@ -172,7 +232,6 @@ for a complete list).
.Ar src
and
.Ar dst :
-.Pp
.Bl -hang -offset flag
.It Ar <address/mask>
.Op Ar ports
@@ -217,20 +276,72 @@ and the port list is limited to
.Pa /usr/src/sys/netinet/ip_fw.h )
ports.
.Pp
-If ``via''
-.Ar name
-is specified, only packets received via or on their way out of an interface
-matching
-.Ar name
-will match this rule.
+Rules can apply to packets when they are incoming, or outgoing, or both.
+The
+.Ar in
+keyword indicates the rule should only match incoming packets.
+The
+.Ar out
+keyword indicates the rule should only match outgoing packets.
.Pp
-If ``via''
-.Ar ipno
-is specified, only packets received via or on their way out of an interface
-having the address
-.Ar ipno
-will match this rule.
+To match packets going through a certain interface, specify
+the interface using
+.Ar via :
+.Bl -hang -offset flag -width 1234567890123456
+.It Ar via ifX
+Packet must be going through interface
+.Ar ifX.
+.It Ar via if*
+Packet must be going through interface
+.Ar ifX ,
+where X is any unit number.
+.It Ar via any
+Packet must be going through
+.Em some
+interface.
+.It Ar via ipno
+Packet must be going through the interface having IP address
+.Ar ipno .
+.El
+.Pp
+The
+.Ar via
+keyword causes the interface to always be checked.
+If
+.Ar recv
+or
+.Ar xmit
+is used instead of
+.Ar via ,
+then the only receive or transmit interface (respectively) is checked.
+By specifying both, it is possible to match packets based on both receive
+and transmit interface, e.g.:
+.Pp
+.Dl "ipfw add 100 deny ip from any to any out recv ed0 xmit ed1"
.Pp
+The
+.Ar recv
+interface can be tested on either incoming or outgoing packets, while the
+.Ar xmit
+interface can only be tested on outgoing packets. So
+.Ar out
+is required (and
+.Ar in
+invalid) whenver
+.Ar xmit
+is used. Specifying
+.Ar via
+together with
+.Ar xmit
+or
+.Ar recv
+is invalid.
+.Pp
+A packet may not have a receive or transmit interface: packets originating
+from the local host have no receive interface. while packets destined for
+the local host have no transmit interface.
+.Pp
+Additional
.Ar options :
.Bl -hang -offset flag -width 1234567890123456
.It frag
@@ -350,11 +461,11 @@ This rule diverts all incoming packets from 192.168.2.0/24 to divert port 5000:
.Sh SEE ALSO
.Xr divert 4 ,
.Xr ip 4 ,
-.Xr ipfirewall 4 ,
.Xr protocols 5 ,
.Xr services 5 ,
.Xr reboot 8 ,
-.Xr syslogd 8
+.Xr syslogd 8 ,
+.Xr sysctl 8
.Sh BUGS
.Pp
.Em WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!
@@ -367,6 +478,12 @@ do anything you don't understand.
.Pp
When manipulating/adding chain entries, service and protocol names are
not accepted.
+.Pp
+Incoming packet fragments diverted by
+.Ar divert
+are reassembled before delivery to the socket, whereas fragments diverted via
+.Ar tee
+are not.
.Sh AUTHORS
Ugen J. S. Antsilevich,
Poul-Henning Kamp,
diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c
index 1254721..7a426ec 100644
--- a/sbin/ipfw/ipfw.c
+++ b/sbin/ipfw/ipfw.c
@@ -16,7 +16,7 @@
*
* NEW command line interface for IP firewall facility
*
- * $Id: ipfw.c,v 1.41 1997/03/05 12:08:44 bde Exp $
+ * $Id: ipfw.c,v 1.42 1997/03/29 03:32:28 imp Exp $
*
*/
@@ -32,18 +32,23 @@
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
#include <netinet/ip_fw.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
-int lineno = -1;
-char progname[BUFSIZ]; /* Program name for errors */
+extern char *__progname;
+int lineno = -1;
int s; /* main RAW socket */
int do_resolv=0; /* Would try to resolv all */
@@ -52,9 +57,33 @@ int do_time=0; /* Show time stamps */
int do_quiet=0; /* Be quiet in add and flush */
int do_force=0; /* Don't ask for confirmation */
-int
-mask_bits(m_ad)
- struct in_addr m_ad;
+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 int
+mask_bits(struct in_addr m_ad)
{
int h_fnd=0,h_num=0,i;
u_long mask;
@@ -101,15 +130,47 @@ print_port(prot, port, comma)
printf("%s%d",comma,port);
}
-void
-show_ipfw(chain)
- struct ip_fw *chain;
+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)
{
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/*stayopen*/);
@@ -138,17 +199,28 @@ show_ipfw(chain)
case IP_FW_F_ACCEPT:
printf("allow");
break;
- case IP_FW_F_DIVERT:
- printf("divert %u", chain->fw_divert_port);
+ case IP_FW_F_DENY:
+ printf("deny");
break;
case IP_FW_F_COUNT:
printf("count");
break;
- case IP_FW_F_DENY:
- if (chain->fw_flg & IP_FW_F_ICMPRPL)
- printf("reject");
- else
- printf("deny");
+ 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_REJECT:
+ if (chain->fw_reject_code == IP_FW_REJECT_RST)
+ printf("reset");
+ else {
+ printf("unreach ");
+ print_reject_code(chain->fw_reject_code);
+ }
break;
default:
errx(1, "impossible");
@@ -161,7 +233,7 @@ show_ipfw(chain)
if (pe)
printf(" %s", pe->p_name);
else
- printf("%u", chain->fw_prot);
+ printf(" %u", chain->fw_prot);
printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
@@ -192,9 +264,9 @@ show_ipfw(chain)
printf(inet_ntoa(chain->fw_src));
}
- if (chain->fw_prot != IPPROTO_IP) {
+ if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
comma = " ";
- for (i=0;i<chain->fw_nsp; i++ ) {
+ for (i = 0; i < nsp; i++) {
print_port(chain->fw_prot, chain->fw_pts[i], comma);
if (i==0 && (chain->fw_flg & IP_FW_F_SRNG))
comma = "-";
@@ -232,35 +304,36 @@ show_ipfw(chain)
printf(inet_ntoa(chain->fw_dst));
}
- comma = " ";
- for (i=0;i<chain->fw_ndp;i++) {
- print_port(chain->fw_prot, chain->fw_pts[chain->fw_nsp+i],
- comma);
- if (i==0 && (chain->fw_flg & IP_FW_F_DRNG))
- comma = "-";
- else
- comma = ",";
- }
+ 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_pts[nsp+i], comma);
+ if (i==0 && (chain->fw_flg & IP_FW_F_DRNG))
+ comma = "-";
+ else
+ comma = ",";
+ }
+ }
- if ((chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
- ;
- else if (chain->fw_flg & IP_FW_F_IN)
+ /* Direction */
+ if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
printf(" in");
- else if (chain->fw_flg & IP_FW_F_OUT)
+ if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
printf(" out");
- if (chain->fw_flg&IP_FW_F_IFNAME && chain->fw_via_name[0]) {
- char ifnb[FW_IFNLEN+1];
- printf(" via ");
- strncpy(ifnb,chain->fw_via_name,FW_IFNLEN);
- ifnb[FW_IFNLEN]='\0';
- if (chain->fw_flg & IP_FW_F_IFUWILD)
- printf("%s*",ifnb);
- else
- printf("%s%d",ifnb,chain->fw_via_unit);
- } else if (chain->fw_via_ip.s_addr) {
- printf(" via ");
- printf(inet_ntoa(chain->fw_via_ip));
+ /* 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)
@@ -342,44 +415,50 @@ list(ac, av)
show_ipfw(r);
}
-void
-show_usage(str)
- char *str;
+static void
+show_usage(const char *fmt, ...)
{
- if (str)
- fprintf(stderr,"%s: ERROR - %s\n",progname,str);
+ 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);
+ } else
+ warnx("error");
fprintf(stderr,
"Usage:\n"
-"\t%s [options]\n"
-"\t\tflush\n"
-"\t\tadd [number] rule\n"
-"\t\tdelete number\n"
-"\t\tlist [number]\n"
-"\t\tshow [number]\n"
-"\t\tzero [number]\n"
-"\trule:\taction proto src dst extras...\n"
-"\t\taction: {allow|deny|reject|count|divert port} [log]\n"
-"\t\tproto: {ip|tcp|udp|icmp|<number>}}\n"
-"\t\tsrc: from {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
-"\t\tdst: to {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
-"\textras:\n"
-"\t\tfragment\n"
-"\t\t{in|out|inout}\n"
-"\t\tvia {ifname|ip}\n"
-"\t\t{established|setup}\n"
-"\t\ttcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
-"\t\tipoptions [!]{ssrr|lsrr|rr|ts},...\n"
-"\t\ticmptypes {type},...\n"
-"\t\tproto {ipproto},...\n"
-, progname
-);
-
-
- fprintf(stderr,"See man %s(8) for proper usage.\n",progname);
- exit (1);
+" %s [options]\n"
+" flush\n"
+" add [number] rule\n"
+" delete number ...\n"
+" list [number]\n"
+" show [number]\n"
+" zero [number ...]\n"
+" rule: 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} [log]\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"
+" fragment\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"
+" icmptypes {type[,type]}...\n",
+__progname);
+
+ errx(1, "see man %s(8) for proper usage.", __progname);
}
-int
+static int
lookup_host (host, ipaddr)
char *host;
struct in_addr *ipaddr;
@@ -415,22 +494,25 @@ fill_ip(ipno, mask, acp, avp)
*p++ = '\0';
}
- if (lookup_host(*av,ipno) != 0)
- show_usage("ip number\n");
+ if (lookup_host(*av, ipno) != 0)
+ show_usage("hostname ``%s'' unknown", *av);
switch (md) {
case ':':
if (!inet_aton(p,mask))
- show_usage("ip number\n");
+ 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(0xffffffff << (32 - atoi(p)));
+ mask->s_addr =
+ htonl(~0 << (32 - atoi(p)));
}
break;
default:
- mask->s_addr = htonl(0xffffffff);
+ mask->s_addr = htonl(~0);
break;
}
ipno->s_addr &= mask->s_addr;
@@ -441,7 +523,27 @@ fill_ip(ipno, mask, acp, avp)
*avp = av;
}
-void
+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;
{
@@ -520,15 +622,13 @@ fill_tcpflag(set, reset, vp)
break;
}
if (i == sizeof(flags) / sizeof(flags[0]))
- show_usage("invalid tcp flag\n");
+ show_usage("invalid tcp flag ``%s''", p);
p = q;
}
}
-void
-fill_ipopt(set, reset, vp)
- u_char *set, *reset;
- char **vp;
+static void
+fill_ipopt(u_char *set, u_char *reset, char **vp)
{
char *p = *vp,*q;
u_char *d;
@@ -593,18 +693,16 @@ delete(ac,av)
av++; ac--;
/* Rule number */
- if (ac && isdigit(**av)) {
+ while (ac && isdigit(**av)) {
rule.fw_number = atoi(*av); av++; ac--;
+ i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
+ if (i)
+ warn("setsockopt(%s)", "IP_FW_DEL");
}
-
- i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
- if (i)
- err(1,"setsockopt(IP_FW_DEL)");
}
-int
-verify_interface(rule)
- struct ip_fw *rule;
+static void
+verify_interface(union ip_fw_if *ifu)
{
struct ifreq ifr;
@@ -613,16 +711,42 @@ verify_interface(rule)
* If a wildcard was specified, check for unit 0.
*/
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
- rule->fw_via_name,
- rule->fw_flg & IP_FW_F_IFUWILD ? 0 : rule->fw_via_unit);
+ ifu->fu_via_if.name,
+ ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit);
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
- return(-1); /* interface isn't recognized by the kernel */
+ warnx("warning: interface ``%s'' does not exist", ifr.ifr_name);
+}
- return(0); /* interface exists */
+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;
}
-void
+static void
add(ac,av)
int ac;
char **av;
@@ -631,6 +755,7 @@ add(ac,av)
int i;
u_char proto;
struct protoent *pe;
+ int saw_xmrc = 0, saw_via = 0;
memset(&rule, 0, sizeof rule);
@@ -642,26 +767,48 @@ add(ac,av)
}
/* Action */
- if (ac && (!strncmp(*av,"accept",strlen(*av))
+ 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)))) {
+ || !strncmp(*av,"permit",strlen(*av))) {
rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
- } else if (ac && !strncmp(*av,"count",strlen(*av))) {
+ } else if (!strncmp(*av,"count",strlen(*av))) {
rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
- } else if (ac && !strncmp(*av,"divert",strlen(*av))) {
+ } else if (!strncmp(*av,"divert",strlen(*av))) {
rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--;
if (!ac)
show_usage("missing divert port");
rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
if (rule.fw_divert_port == 0)
show_usage("illegal divert port");
- } else if (ac && (!strncmp(*av,"deny",strlen(*av)))) {
+ } else if (!strncmp(*av,"tee",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_TEE; av++; ac--;
+ if (!ac)
+ show_usage("missing divert port");
+ rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
+ if (rule.fw_divert_port == 0)
+ show_usage("illegal divert port");
+ } 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 (ac && !strncmp(*av,"reject",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_DENY|IP_FW_F_ICMPRPL; 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 {
- show_usage("missing/unrecognized action\n");
+ show_usage("invalid action ``%s''", *av);
}
/* [log] */
@@ -670,103 +817,142 @@ add(ac,av)
}
/* protocol */
- if (ac) {
- 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\n");
- }
- } else
- show_usage("missing protocol\n");
+ 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''\n");
+ 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\n");
+ if (!ac)
+ show_usage("missing arguments");
fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
if (ac && isdigit(**av)) {
- if (fill_port(&rule.fw_nsp, &rule.fw_pts, 0, *av, 0))
+ u_short nports = 0;
+
+ if (fill_port(&nports, rule.fw_pts, 0, *av))
rule.fw_flg |= IP_FW_F_SRNG;
+ IP_FW_SETNSRCP(&rule, nports);
av++; ac--;
}
/* to */
if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
- else show_usage("missing ``to''\n");
+ 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\n");
+ if (!ac)
+ show_usage("missing arguments");
fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
if (ac && isdigit(**av)) {
- if (fill_port(&rule.fw_ndp, &rule.fw_pts, rule.fw_nsp, *av, 0))
+ u_short nports = 0;
+
+ if (fill_port(&nports,
+ rule.fw_pts, IP_FW_GETNSRCP(&rule), *av))
rule.fw_flg |= IP_FW_F_DRNG;
+ IP_FW_SETNDSTP(&rule, nports);
av++; ac--;
}
- if ((rule.fw_prot != IPPROTO_TCP) &&
- (rule.fw_prot != IPPROTO_UDP) &&
- (rule.fw_nsp || rule.fw_ndp)) {
- show_usage("only TCP and UDP protocols are valid with port specifications");
+ 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 (ac && !strncmp(*av,"via",strlen(*av))) {
- if (rule.fw_via_ip.s_addr || (rule.fw_flg & IP_FW_F_IFNAME)) {
- show_usage("multiple 'via' options specified");
- }
-
- av++; ac--;
- if (!ac) {
- show_usage("'via' option specified with no interface.");
- }
- if (!isdigit(**av)) {
- char *q;
-
- strncpy(rule.fw_via_name, *av, sizeof(rule.fw_via_name));
- rule.fw_via_name[sizeof(rule.fw_via_name) - 1] = '\0';
- for (q = rule.fw_via_name; *q && !isdigit(*q) && *q != '*'; q++)
- continue;
- if (*q == '*')
- rule.fw_flg |= IP_FW_F_IFUWILD;
- else
- rule.fw_via_unit = atoi(q);
- *q = '\0';
- rule.fw_flg |= IP_FW_F_IFNAME;
- if (verify_interface(&rule) != 0)
- fprintf(stderr, "Warning: interface does not exist\n");
- } else if (inet_aton(*av,&rule.fw_via_ip) == INADDR_NONE) {
- show_usage("bad IP# after via\n");
+ if (!strncmp(*av,"in",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_IN;
+ 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--;
- continue;
+ 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 (!strncmp(*av,"fragment",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_FRAG; 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 (!strncmp(*av,"in",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_IN; 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,"out",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_OUT; av++; ac--; continue;
+ if (!strncmp(*av,"fragment",strlen(*av))) {
+ rule.fw_flg |= IP_FW_F_FRAG;
+ av++; ac--; continue;
}
- if (ac > 1 && !strncmp(*av,"ipoptions",strlen(*av))) {
+ if (!strncmp(*av,"ipoptions",strlen(*av))) {
av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``ipoptions''");
fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av);
av++; ac--; continue;
}
@@ -780,31 +966,50 @@ add(ac,av)
rule.fw_tcpnf |= IP_FW_TCPF_ACK;
av++; ac--; continue;
}
- if (ac > 1 && !strncmp(*av,"tcpflags",strlen(*av))) {
+ if (!strncmp(*av,"tcpflags",strlen(*av))) {
av++; ac--;
+ if (!ac)
+ show_usage("missing argument"
+ " for ``tcpflags''");
fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
av++; ac--; continue;
}
}
if (rule.fw_prot == IPPROTO_ICMP) {
- if (ac > 1 && !strncmp(*av,"icmptypes",strlen(*av))) {
+ if (!strncmp(*av,"icmptypes",strlen(*av))) {
av++; ac--;
- fill_icmptypes(rule.fw_icmptypes, av, &rule.fw_flg);
+ if (!ac)
+ show_usage("missing argument"
+ " for ``icmptypes''");
+ fill_icmptypes(rule.fw_icmptypes,
+ av, &rule.fw_flg);
av++; ac--; continue;
}
}
- printf("%d %s\n",ac,*av);
- show_usage("Unknown argument\n");
+ 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");
+
if (!do_quiet)
show_ipfw(&rule);
i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
if (i)
- err(1,"setsockopt(IP_FW_ADD)");
+ err(1, "setsockopt(%s)", "IP_FW_ADD");
}
-void
+static void
zero (ac, av)
int ac;
char **av;
@@ -813,28 +1018,26 @@ zero (ac, av)
if (!ac) {
/* clear all entries */
- if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0) {
- fprintf(stderr,"%s: setsockopt failed.\n",progname);
- exit(1);
- }
+ if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0)
+ err(1, "setsockopt(%s)", "IP_FW_ZERO");
if (!do_quiet)
printf("Accounting cleared.\n");
} else {
- /* clear a specific entry */
struct ip_fw rule;
memset(&rule, 0, sizeof rule);
-
- /* Rule number */
- if (isdigit(**av)) {
- rule.fw_number = atoi(*av); av++; ac--;
-
- if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, &rule, sizeof rule))
- err(1, "setsockopt(Zero)");
- printf("Entry %d cleared\n", rule.fw_number);
- }
- else {
- show_usage("expected number");
+ 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("setsockopt(%s)", "IP_FW_ZERO");
+ else
+ printf("Entry %d cleared\n",
+ rule.fw_number);
+ } else
+ show_usage("invalid rule number ``%s''", *av);
}
}
}
@@ -874,7 +1077,7 @@ ipfw_main(ac,av)
do_resolv=1;
break;
default:
- show_usage("Unrecognised switch");
+ show_usage("invalid flag ``-%c''", ch);
}
ac -= optind;
@@ -908,10 +1111,8 @@ ipfw_main(ac,av)
do_flush = 1;
}
if ( do_flush ) {
- if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0)<0) {
- fprintf(stderr,"%s: setsockopt failed.\n",progname);
- exit(1);
- }
+ if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0) < 0)
+ err(1, "setsockopt(%s)", "IP_FW_FLUSH");
if (!do_quiet)
printf("Flushed all rules.\n");
}
@@ -936,47 +1137,34 @@ main(ac, av)
char **av;
{
#define MAX_ARGS 32
+#define WHITESP " \t\f\v\n\r"
char buf[BUFSIZ];
- char *args[MAX_ARGS];
+ char *a, *args[MAX_ARGS];
char linename[10];
int i;
FILE *f;
- strncpy(progname,*av, sizeof(progname));
- progname[sizeof(progname) - 1] = '\0';
-
s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
- if ( s < 0 ) {
- fprintf(stderr,"%s: Can't open raw socket.\n"
- "Must be root to use this program.\n",progname);
- exit(1);
- }
+ if ( s < 0 )
+ err(1, "socket");
setbuf(stdout,0);
if (av[1] && !access(av[1], R_OK)) {
lineno = 0;
- f = fopen(av[1], "r");
+ if ((f = fopen(av[1], "r")) == NULL)
+ err(1, "fopen: %s", av[1]);
while (fgets(buf, BUFSIZ, f)) {
- if (buf[strlen(buf)-1]=='\n')
- buf[strlen(buf)-1] = 0;
lineno++;
sprintf(linename, "Line %d", lineno);
args[0] = linename;
- args[1] = buf;
- while(*args[1] == ' ')
- args[1]++;
- i = 2;
- while((args[i] = strchr(args[i-1],' '))) {
- *(args[i]++) = 0;
- while(*args[i] == ' ')
- args[i]++;
- i++;
- }
- if (*args[i-1] == 0)
- i--;
+ for (i = 1, a = strtok(buf, WHITESP);
+ a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
+ args[i] = a;
+ if (i == MAX_ARGS)
+ errx(1, "%s: too many arguments", linename);
args[i] = NULL;
ipfw_main(i, args);
OpenPOWER on IntegriCloud