diff options
author | alex <alex@FreeBSD.org> | 1996-06-09 23:46:21 +0000 |
---|---|---|
committer | alex <alex@FreeBSD.org> | 1996-06-09 23:46:21 +0000 |
commit | e4c90a2ba34f0c92523e482a3bb12b8f17c34624 (patch) | |
tree | f6990716abfdf665c7708e26fdf6a39089f5ba0f /sys/netinet | |
parent | 59a999900b5402f50d38388a3a41241553eb18ae (diff) | |
download | FreeBSD-src-e4c90a2ba34f0c92523e482a3bb12b8f17c34624.zip FreeBSD-src-e4c90a2ba34f0c92523e482a3bb12b8f17c34624.tar.gz |
Big sweep over ipfw, picking up where Poul left off:
- Log ICMP type during verbose output.
- Added IPFIREWALL_VERBOSE_LIMIT option to prevent denial of service
attacks via syslog flooding.
- Filter based on ICMP type.
- Timestamp chain entries when they are matched.
- Interfaces can now be matched with a wildcard specification (i.e.
will match any interface unit for a given name).
- Prevent the firewall chain from being manipulated when securelevel
is greater than 2.
- Fixed bug that allowed the default policy to be deleted.
- Ability to zero individual accounting entries.
- Remove definitions of old_chk_ptr and old_ctl_ptr when compiling
ipfw as a lkm.
- Remove some redundant code shared between ip_fw_init and ipfw_load.
Closes PRs: 1192, 1219, and 1267.
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/ip_fw.c | 175 | ||||
-rw-r--r-- | sys/netinet/ip_fw.h | 41 |
2 files changed, 133 insertions, 83 deletions
diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c index 2dee52a..089b4bb 100644 --- a/sys/netinet/ip_fw.c +++ b/sys/netinet/ip_fw.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 1996 Alex Nash * Copyright (c) 1993 Daniel Boulet * Copyright (c) 1994 Ugen J.S.Antsilevich * @@ -11,7 +12,7 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id: ip_fw.c,v 1.35 1996/05/06 20:31:04 phk Exp $ + * $Id: ip_fw.c,v 1.36 1996/05/08 04:28:57 gpalmer Exp $ */ /* @@ -22,10 +23,9 @@ #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/queue.h> -#if 0 /* XXX -current, but not -stable */ #include <sys/kernel.h> +#include <sys/time.h> #include <sys/sysctl.h> -#endif #include <net/if.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -41,7 +41,11 @@ static int fw_verbose = 1; #else static int fw_verbose = 0; #endif - +#ifdef IPFIREWALL_VERBOSE_LIMIT +static int fw_verbose_limit = IPFIREWALL_VERBOSE_LIMIT; +#else +static int fw_verbose_limit = 0; +#endif LIST_HEAD (ip_fw_head, ip_fw_chain) ip_fw_chain; @@ -49,6 +53,7 @@ LIST_HEAD (ip_fw_head, ip_fw_chain) ip_fw_chain; SYSCTL_NODE(net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); SYSCTL_INT(net_inet_ip_fw, OID_AUTO, debug, CTLFLAG_RW, &fw_debug, 0, ""); SYSCTL_INT(net_inet_ip_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw_verbose, 0, ""); +SYSCTL_INT(net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw_verbose_limit, 0, ""); #endif #define dprintf(a) if (!fw_debug); else printf a @@ -62,16 +67,20 @@ SYSCTL_INT(net_inet_ip_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw_verbose, 0, ""); static int add_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl)); static int del_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl)); +static int zero_entry __P((struct mbuf *m)); static struct ip_fw * check_ipfw_struct __P(( struct mbuf *m)); static int ipopts_match __P((struct ip *ip, struct ip_fw *f)); static int port_match __P((u_short *portptr, int nports, u_short port, int range_flag)); static int tcpflg_match __P((struct tcphdr *tcp, struct ip_fw *f)); -static void ipfw_report __P((char *txt, int rule, struct ip *ip)); +static int icmptype_match __P((struct icmp * icmp, struct ip_fw * f)); +static void ipfw_report __P((char *txt, int rule, struct ip *ip, int counter)); +#ifdef ACTUALLY_LKM_NOT_KERNEL static ip_fw_chk_t *old_chk_ptr; static ip_fw_ctl_t *old_ctl_ptr; +#endif static int ip_fw_chk __P((struct ip **pip, int hlen, struct ifnet *rif, int dir, struct mbuf **m)); @@ -82,11 +91,7 @@ static int ip_fw_ctl __P((int stage, struct mbuf **mm)); * Returns 1 if the port is matched by the vector, 0 otherwise */ static inline int -port_match(portptr, nports, port, range_flag) - u_short *portptr; - int nports; - u_short port; - int range_flag; +port_match(u_short *portptr, int nports, u_short port, int range_flag) { if (!nports) return 1; @@ -106,9 +111,7 @@ port_match(portptr, nports, port, range_flag) } static int -tcpflg_match(tcp, f) - struct tcphdr *tcp; - struct ip_fw *f; +tcpflg_match(struct tcphdr *tcp, struct ip_fw *f) { u_char flg_set, flg_clr; @@ -127,11 +130,26 @@ tcpflg_match(tcp, f) return 1; } +static int +icmptype_match(struct icmp *icmp, struct ip_fw *f) +{ + int type; + + if (!(f->fw_flg & IP_FW_F_ICMPBIT)) + return(1); + + type = icmp->icmp_type; + + /* check for matching type in the bitmap */ + if (f->fw_icmptypes[type / (sizeof(unsigned) * 8)] & + (1U << (type % (8 * sizeof(unsigned))))) + return(1); + + return(0); /* no match */ +} static int -ipopts_match(ip, f) - struct ip *ip; - struct ip_fw *f; +ipopts_match(struct ip *ip, struct ip_fw *f) { register u_char *cp; int opt, optlen, cnt; @@ -188,13 +206,15 @@ ipopts_match(ip, f) } static void -ipfw_report(char *txt, int rule, struct ip *ip) +ipfw_report(char *txt, int rule, struct ip *ip, int counter) { struct tcphdr *tcp = (struct tcphdr *) ((u_long *) ip + ip->ip_hl); struct udphdr *udp = (struct udphdr *) ((u_long *) ip + ip->ip_hl); struct icmp *icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); if (!fw_verbose) return; + if (fw_verbose_limit != 0 && counter > fw_verbose_limit) + return; printf("ipfw: %d %s ",rule, txt); switch (ip->ip_p) { case IPPROTO_TCP: @@ -212,7 +232,7 @@ ipfw_report(char *txt, int rule, struct ip *ip) printf(":%d", ntohs(udp->uh_dport)); break; case IPPROTO_ICMP: - printf("ICMP:%u ", icmp->icmp_type); + printf("ICMP:%u.%u ", icmp->icmp_type, icmp->icmp_code); print_ip(ip->ip_src); printf(" "); print_ip(ip->ip_dst); @@ -234,11 +254,7 @@ ipfw_report(char *txt, int rule, struct ip *ip) */ static int -ip_fw_chk(pip, hlen, rif, dir, m) - struct ip **pip; - struct ifnet *rif; - int hlen, dir; - struct mbuf **m; +ip_fw_chk(struct ip **pip, int hlen, struct ifnet *rif, int dir, struct mbuf **m) { struct ip_fw_chain *chain; register struct ip_fw *f = NULL; @@ -256,7 +272,10 @@ ip_fw_chk(pip, hlen, rif, dir, m) * we're not going to pass it... */ if ((ip->ip_off & IP_OFFMASK) == 1) { - ipfw_report("Refuse", -1, ip); + static int frag_counter = 0; + + ++frag_counter; + ipfw_report("Refuse", -1, ip, frag_counter); m_freem(*m); return 0; } @@ -332,7 +351,7 @@ ip_fw_chk(pip, hlen, rif, dir, m) if ((f->fw_flg & IP_FW_F_IFNAME) && f->fw_via_name[0]) { /* Not same unit, don't match */ - if (rif->if_unit != f->fw_via_unit) + if (!(f->fw_flg & IP_FW_F_IFUWILD) && rif->if_unit != f->fw_via_unit) continue; /* Not same name */ @@ -382,8 +401,12 @@ ip_fw_chk(pip, hlen, rif, dir, m) continue; /* ICMP, done */ - if (prt == IP_FW_F_ICMP) + if (prt == IP_FW_F_ICMP) { + if (!icmptype_match(icmp, f)) + continue; + goto got_match; + } /* Fragments can't match past this point */ if (ip->ip_off & IP_OFFMASK) @@ -406,13 +429,14 @@ ip_fw_chk(pip, hlen, rif, dir, m) got_match: f->fw_pcnt++; f->fw_bcnt+=ip->ip_len; + f->timestamp = time.tv_sec; if (f->fw_flg & IP_FW_F_PRN) { if (f->fw_flg & IP_FW_F_ACCEPT) - ipfw_report("Accept", f->fw_number, ip); + ipfw_report("Allow", f->fw_number, ip, f->fw_pcnt); else if (f->fw_flg & IP_FW_F_COUNT) - ipfw_report("Count", f->fw_number, ip); + ipfw_report("Count", f->fw_number, ip, f->fw_pcnt); else - ipfw_report("Deny", f->fw_number, ip); + ipfw_report("Deny", f->fw_number, ip, f->fw_pcnt); } if (f->fw_flg & IP_FW_F_ACCEPT) return 1; @@ -445,9 +469,7 @@ got_match: } static int -add_entry(chainptr, frwl) - struct ip_fw_head *chainptr; - struct ip_fw *frwl; +add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) { struct ip_fw *ftmp = 0; struct ip_fw_chain *fwc = 0, *fcp, *fcpl = 0; @@ -502,9 +524,7 @@ add_entry(chainptr, frwl) } static int -del_entry(chainptr, frwl) - struct ip_fw_head *chainptr; - struct ip_fw *frwl; +del_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) { struct ip_fw_chain *fcp; int s; @@ -512,7 +532,7 @@ del_entry(chainptr, frwl) s = splnet(); fcp = chainptr->lh_first; - if (fcp->rule->fw_number != (u_short)-1) { + if (frwl->fw_number != (u_short)-1) { for (; fcp; fcp = fcp->chain.le_next) { if (fcp->rule->fw_number == frwl->fw_number) { LIST_REMOVE(fcp, chain); @@ -528,9 +548,40 @@ del_entry(chainptr, frwl) return (EINVAL); } +static int +zero_entry(struct mbuf *m) +{ + struct ip_fw *frwl; + struct ip_fw_chain *fcp; + int s; + + if (m) { + frwl = check_ipfw_struct(m); + + if (!frwl) + return(EINVAL); + } + else + frwl = NULL; + + /* + * It's possible to insert multiple chain entries with the + * same number, so we don't stop after finding the first + * match if zeroing a specific entry. + */ + s = splnet(); + for (fcp = ip_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next) + if (!frwl || frwl->fw_number == fcp->rule->fw_number) { + fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0; + fcp->rule->timestamp = 0; + } + splx(s); + + return(0); +} + static struct ip_fw * -check_ipfw_struct(m) - struct mbuf *m; +check_ipfw_struct(struct mbuf *m) { struct ip_fw *frwl; @@ -566,19 +617,11 @@ check_ipfw_struct(m) frwl->fw_nsp, frwl->fw_ndp)); return (NULL); } -#if 0 - if ((frwl->fw_flg & IP_FW_F_KIND) == IP_FW_F_ICMP) { - dprintf(("ip_fw_ctl: request for unsupported ICMP frwling\n")); - return (NULL); - } -#endif return frwl; } static int -ip_fw_ctl(stage, mm) - int stage; - struct mbuf **mm; +ip_fw_ctl(int stage, struct mbuf **mm) { int error; struct mbuf *m; @@ -596,6 +639,11 @@ ip_fw_ctl(stage, mm) return (0); } m = *mm; + /* only allow get calls if secure mode > 2 */ + if (securelevel > 2) { + if (m) (void)m_free(m); + return(EPERM); + } if (stage == IP_FW_FLUSH) { while (ip_fw_chain.lh_first != NULL && ip_fw_chain.lh_first->rule->fw_number != (u_short)-1) { @@ -610,13 +658,9 @@ ip_fw_ctl(stage, mm) return (0); } if (stage == IP_FW_ZERO) { - int s = splnet(); - struct ip_fw_chain *fcp; - for (fcp = ip_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next) - fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0; - splx(s); + error = zero_entry(m); if (m) (void)m_free(m); - return (0); + return (error); } if (m == NULL) { printf("ip_fw_ctl: NULL mbuf ptr\n"); @@ -657,7 +701,15 @@ ip_fw_init(void) deny.fw_number = (u_short)-1; add_entry(&ip_fw_chain, &deny); - printf("IP firewall initialized\n"); + printf("IP firewall initialized, "); +#ifndef IPFIREWALL_VERBOSE + printf("logging disabled\n"); +#else + if (fw_verbose_limit == 0) + printf("unlimited logging\n"); + else + printf("logging limited to %d packets/entry\n", fw_verbose_limit); +#endif } #ifdef ACTUALLY_LKM_NOT_KERNEL @@ -672,21 +724,12 @@ static int ipfw_load(struct lkm_table *lkmtp, int cmd) { int s=splnet(); - struct ip_fw deny; - old_chk_ptr = ip_fw_chk_ptr; - old_ctl_ptr = ip_fw_ctl_ptr; - ip_fw_chk_ptr = ip_fw_chk; - ip_fw_ctl_ptr = ip_fw_ctl; - - LIST_INIT(&ip_fw_chain); - bzero(&deny, sizeof deny); - deny.fw_flg = IP_FW_F_ALL; - deny.fw_number = (u_short)-1; - add_entry(&ip_fw_chain, &deny); + old_chk_ptr = ip_fw_chk_ptr; + old_ctl_ptr = ip_fw_ctl_ptr; + ip_fw_init(); splx(s); - printf("IP firewall loaded\n"); return 0; } diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 5c5ee75..a8b11a8 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -11,7 +11,7 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id: ip_fw.h,v 1.18 1996/04/03 13:52:15 phk Exp $ + * $Id: ip_fw.h,v 1.19 1996/06/02 00:14:50 gpalmer Exp $ */ /* @@ -49,6 +49,9 @@ struct ip_fw { u_short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */ u_char fw_ipopt,fw_ipnopt; /* IP options set/unset */ u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ +#define IP_FW_ICMPTYPES_DIM (256 / (sizeof(unsigned) * 8)) + unsigned fw_icmptypes[IP_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */ + long timestamp; /* timestamp (tv_sec) of last match */ }; struct ip_fw_chain { @@ -60,33 +63,37 @@ struct ip_fw_chain { * Values for "flags" field . */ -#define IP_FW_F_ALL 0x000 /* This is a universal packet rule */ -#define IP_FW_F_TCP 0x001 /* This is a TCP packet rule */ -#define IP_FW_F_UDP 0x002 /* This is a UDP packet rule */ -#define IP_FW_F_ICMP 0x003 /* This is a ICMP packet rule */ -#define IP_FW_F_KIND 0x003 /* Mask to isolate rule kind */ +#define IP_FW_F_ALL 0x0000 /* This is a universal packet rule */ +#define IP_FW_F_TCP 0x0001 /* This is a TCP packet rule */ +#define IP_FW_F_UDP 0x0002 /* This is a UDP packet rule */ +#define IP_FW_F_ICMP 0x0003 /* This is a ICMP packet rule */ +#define IP_FW_F_KIND 0x0003 /* Mask to isolate rule kind */ -#define IP_FW_F_IN 0x004 /* Inbound */ -#define IP_FW_F_OUT 0x008 /* Outboun */ +#define IP_FW_F_IN 0x0004 /* Inbound */ +#define IP_FW_F_OUT 0x0008 /* Outbound */ -#define IP_FW_F_ACCEPT 0x010 /* This is an accept rule */ -#define IP_FW_F_COUNT 0x020 /* This is an accept rule */ -#define IP_FW_F_PRN 0x040 /* Print if this rule matches */ -#define IP_FW_F_ICMPRPL 0x080 /* Send back icmp unreachable packet */ +#define IP_FW_F_ACCEPT 0x0010 /* This is an accept rule */ +#define IP_FW_F_COUNT 0x0020 /* This is an accept rule */ +#define IP_FW_F_PRN 0x0040 /* Print if this rule matches */ +#define IP_FW_F_ICMPRPL 0x0080 /* Send back icmp unreachable packet */ -#define IP_FW_F_SRNG 0x100 /* The first two src ports are a min * +#define IP_FW_F_SRNG 0x0100 /* The first two src ports are a min * * and max range (stored in host byte * * order). */ -#define IP_FW_F_DRNG 0x200 /* The first two dst ports are a min * +#define IP_FW_F_DRNG 0x0200 /* The first two dst ports are a min * * and max range (stored in host byte * * order). */ -#define IP_FW_F_IFNAME 0x400 /* Use interface name/unit (not IP) */ +#define IP_FW_F_IFNAME 0x0400 /* Use interface name/unit (not IP) */ -#define IP_FW_F_FRAG 0x800 /* Fragment */ +#define IP_FW_F_FRAG 0x0800 /* Fragment */ -#define IP_FW_F_MASK 0xFFF /* All possible flag bits mask */ +#define IP_FW_F_ICMPBIT 0x1000 /* ICMP type bitmap is valid */ + +#define IP_FW_F_IFUWILD 0x2000 /* Match all interface units */ + +#define IP_FW_F_MASK 0x3FFF /* All possible flag bits mask */ /* * Definitions for IP option names. |