No functional changes, but:
Following Darren's suggestion, make Dijkstra happy and rewrite the ipfw_chk() main loop removing a lot of goto's and using instead a variable to store match status. Add a lot of comments to explain what instructions are supposed to do and how -- this should ease auditing of the code and make people more confident with it. In terms of code size: the entire file takes about 12700 bytes of text, about 3K of which are for the main function, ipfw_chk(), and 2K (ouch!) for ipfw_log().
This commit is contained in:
parent
7d4d3e9051
commit
d63b346ab1
@ -29,7 +29,7 @@
|
|||||||
#define DDB(x) x
|
#define DDB(x) x
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implement IP packet firewall
|
* Implement IP packet firewall (new version)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if !defined(KLD_MODULE)
|
#if !defined(KLD_MODULE)
|
||||||
@ -42,6 +42,8 @@
|
|||||||
#endif /* INET */
|
#endif /* INET */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define IPFW2 1
|
||||||
|
#if IPFW2
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
#include <sys/malloc.h>
|
#include <sys/malloc.h>
|
||||||
@ -75,8 +77,8 @@
|
|||||||
|
|
||||||
#include <machine/in_cksum.h> /* XXX for in_cksum */
|
#include <machine/in_cksum.h> /* XXX for in_cksum */
|
||||||
|
|
||||||
static int fw_verbose = 0;
|
static int fw_verbose;
|
||||||
static int verbose_limit = 0;
|
static int verbose_limit;
|
||||||
|
|
||||||
#define IPFW_DEFAULT_RULE 65535
|
#define IPFW_DEFAULT_RULE 65535
|
||||||
|
|
||||||
@ -164,9 +166,9 @@ static u_int32_t dyn_short_lifetime = 5;
|
|||||||
*/
|
*/
|
||||||
static u_int32_t dyn_grace_time = 10;
|
static u_int32_t dyn_grace_time = 10;
|
||||||
|
|
||||||
static u_int32_t static_count = 0; /* # of static rules */
|
static u_int32_t static_count; /* # of static rules */
|
||||||
static u_int32_t static_len = 0; /* size in bytes of static rules */
|
static u_int32_t static_len; /* size in bytes of static rules */
|
||||||
static u_int32_t dyn_count = 0; /* # of dynamic rules */
|
static u_int32_t dyn_count; /* # of dynamic rules */
|
||||||
static u_int32_t dyn_max = 1000; /* max # of dynamic rules */
|
static u_int32_t dyn_max = 1000; /* max # of dynamic rules */
|
||||||
|
|
||||||
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, CTLFLAG_RW,
|
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, CTLFLAG_RW,
|
||||||
@ -206,7 +208,7 @@ ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; /* hook into dummynet */
|
|||||||
*/
|
*/
|
||||||
#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
|
#define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
|
||||||
|
|
||||||
static int
|
static __inline int
|
||||||
icmptype_match(struct ip *ip, ipfw_insn_u32 *cmd)
|
icmptype_match(struct ip *ip, ipfw_insn_u32 *cmd)
|
||||||
{
|
{
|
||||||
int type = L3HDR(struct icmp,ip)->icmp_type;
|
int type = L3HDR(struct icmp,ip)->icmp_type;
|
||||||
@ -379,16 +381,18 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd)
|
|||||||
static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */
|
static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */
|
||||||
|
|
||||||
#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
|
#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
|
||||||
|
#define SNP(buf) buf, sizeof(buf)
|
||||||
/*
|
/*
|
||||||
* We enter here when we have a rule with O_LOG.
|
* We enter here when we have a rule with O_LOG.
|
||||||
|
* XXX this function alone takes about 2Kbytes of code!
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
|
ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
|
||||||
struct mbuf *m, struct ifnet *oif)
|
struct mbuf *m, struct ifnet *oif)
|
||||||
{
|
{
|
||||||
char *action;
|
char *action;
|
||||||
char action2[32], proto[47], fragment[27];
|
|
||||||
int limit_reached = 0;
|
int limit_reached = 0;
|
||||||
|
char action2[40], proto[48], fragment[28];
|
||||||
|
|
||||||
fragment[0] = '\0';
|
fragment[0] = '\0';
|
||||||
proto[0] = '\0';
|
proto[0] = '\0';
|
||||||
@ -1367,6 +1371,7 @@ again:
|
|||||||
skip_or = 0;
|
skip_or = 0;
|
||||||
for (l = f->cmd_len, cmd = f->cmd ; l > 0 ;
|
for (l = f->cmd_len, cmd = f->cmd ; l > 0 ;
|
||||||
l -= cmdlen, cmd += cmdlen) {
|
l -= cmdlen, cmd += cmdlen) {
|
||||||
|
int match;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check_body is a jump target used when we find a
|
* check_body is a jump target used when we find a
|
||||||
@ -1388,15 +1393,24 @@ check_body:
|
|||||||
skip_or = 0; /* next one is good */
|
skip_or = 0; /* next one is good */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (cmd->opcode) {
|
match = 0; /* set to 1 if we succeed */
|
||||||
case O_NOP:
|
|
||||||
goto cmd_match; /* That's easy */
|
switch (cmd->opcode) {
|
||||||
|
/*
|
||||||
|
* The first set of opcodes compares the packet's
|
||||||
|
* fields with some pattern, setting 'match' if a
|
||||||
|
* match is found. At the end of the loop there is
|
||||||
|
* logic to deal with F_NOT and F_OR flags associated
|
||||||
|
* with the opcode.
|
||||||
|
*/
|
||||||
|
case O_NOP:
|
||||||
|
match = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case O_IPPRE:
|
|
||||||
case O_FORWARD_MAC:
|
case O_FORWARD_MAC:
|
||||||
printf("ipfw: opcode %d unimplemented\n",
|
printf("ipfw: opcode %d unimplemented\n",
|
||||||
cmd->opcode);
|
cmd->opcode);
|
||||||
goto cmd_fail;
|
break;
|
||||||
|
|
||||||
case O_GID:
|
case O_GID:
|
||||||
case O_UID:
|
case O_UID:
|
||||||
@ -1406,7 +1420,7 @@ check_body:
|
|||||||
* packet with the ports info.
|
* packet with the ports info.
|
||||||
*/
|
*/
|
||||||
if (offset!=0)
|
if (offset!=0)
|
||||||
goto cmd_fail;
|
break;
|
||||||
{
|
{
|
||||||
struct inpcbinfo *pi;
|
struct inpcbinfo *pi;
|
||||||
int wildcard;
|
int wildcard;
|
||||||
@ -1419,7 +1433,7 @@ check_body:
|
|||||||
wildcard = 1;
|
wildcard = 1;
|
||||||
pi = &udbinfo;
|
pi = &udbinfo;
|
||||||
} else
|
} else
|
||||||
goto cmd_fail;
|
break;
|
||||||
|
|
||||||
pcb = (oif) ?
|
pcb = (oif) ?
|
||||||
in_pcblookup_hash(pi,
|
in_pcblookup_hash(pi,
|
||||||
@ -1432,42 +1446,35 @@ check_body:
|
|||||||
wildcard, NULL);
|
wildcard, NULL);
|
||||||
|
|
||||||
if (pcb == NULL || pcb->inp_socket == NULL)
|
if (pcb == NULL || pcb->inp_socket == NULL)
|
||||||
goto cmd_fail;
|
break;
|
||||||
if (cmd->opcode == O_UID) {
|
#if __FreeBSD_version < 500034
|
||||||
#if __FreeBSD_version >= 500034
|
#define socheckuid(a,b) ((a)->so_cred->cr_uid == (b))
|
||||||
if (socheckuid(pcb->inp_socket,
|
|
||||||
(uid_t)((ipfw_insn_u32 *)cmd)->d[0]
|
|
||||||
))
|
|
||||||
#else
|
|
||||||
if (pcb->inp_socket->so_cred->cr_uid !=
|
|
||||||
(uid_t)((ipfw_insn_u32 *)cmd)->d[0])
|
|
||||||
#endif
|
#endif
|
||||||
goto cmd_match;
|
if (cmd->opcode == O_UID) {
|
||||||
|
match =
|
||||||
|
socheckuid(pcb->inp_socket,
|
||||||
|
(uid_t)((ipfw_insn_u32 *)cmd)->d[0]);
|
||||||
} else {
|
} else {
|
||||||
if (groupmember(
|
match = groupmember(
|
||||||
(uid_t)((ipfw_insn_u32 *)cmd)->d[0],
|
(uid_t)((ipfw_insn_u32 *)cmd)->d[0],
|
||||||
pcb->inp_socket->so_cred))
|
pcb->inp_socket->so_cred);
|
||||||
goto cmd_match;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
goto cmd_fail;
|
break;
|
||||||
|
|
||||||
case O_RECV:
|
case O_RECV:
|
||||||
if (iface_match(m->m_pkthdr.rcvif,
|
match = iface_match(m->m_pkthdr.rcvif,
|
||||||
(ipfw_insn_if *)cmd))
|
(ipfw_insn_if *)cmd);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_XMIT:
|
case O_XMIT:
|
||||||
if (iface_match(oif, (ipfw_insn_if *)cmd))
|
match = iface_match(oif, (ipfw_insn_if *)cmd);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_VIA:
|
case O_VIA:
|
||||||
if (iface_match(oif ? oif : m->m_pkthdr.rcvif,
|
match = iface_match(oif ? oif :
|
||||||
(ipfw_insn_if *)cmd))
|
m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_MACADDR2:
|
case O_MACADDR2:
|
||||||
if (args->eh != NULL) { /* have MAC header */
|
if (args->eh != NULL) { /* have MAC header */
|
||||||
@ -1477,128 +1484,108 @@ check_body:
|
|||||||
((ipfw_insn_mac *)cmd)->mask;
|
((ipfw_insn_mac *)cmd)->mask;
|
||||||
u_int32_t *hdr = (u_int32_t *)args->eh;
|
u_int32_t *hdr = (u_int32_t *)args->eh;
|
||||||
|
|
||||||
if ( want[0] == (hdr[0] & mask[0]) &&
|
match =
|
||||||
want[1] == (hdr[1] & mask[1]) &&
|
( want[0] == (hdr[0] & mask[0]) &&
|
||||||
want[2] == (hdr[2] & mask[2]) )
|
want[1] == (hdr[1] & mask[1]) &&
|
||||||
goto cmd_match;
|
want[2] == (hdr[2] & mask[2]) );
|
||||||
}
|
}
|
||||||
goto cmd_fail;
|
break;
|
||||||
|
|
||||||
case O_MAC_TYPE:
|
case O_MAC_TYPE:
|
||||||
if (args->eh != NULL) {
|
if (args->eh != NULL) {
|
||||||
u_int16_t type =
|
u_int16_t t =
|
||||||
ntohs(args->eh->ether_type);
|
ntohs(args->eh->ether_type);
|
||||||
u_int16_t *p =
|
u_int16_t *p =
|
||||||
((ipfw_insn_u16 *)cmd)->ports;
|
((ipfw_insn_u16 *)cmd)->ports;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = cmdlen - 1; i>0; i--)
|
for (i = cmdlen - 1; !match && i>0;
|
||||||
if (type>=p[0] && type<=p[1])
|
i--, p += 2)
|
||||||
goto cmd_match;
|
match = (t>=p[0] && t<=p[1]);
|
||||||
else
|
|
||||||
p += 2;
|
|
||||||
}
|
}
|
||||||
goto cmd_fail;
|
break;
|
||||||
|
|
||||||
case O_FRAG:
|
case O_FRAG:
|
||||||
/* XXX check this -- MF bit ? */
|
match = (hlen > 0 && offset != 0);
|
||||||
if (hlen == 0 || offset != 0)
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IN: /* "out" is "not in" */
|
case O_IN: /* "out" is "not in" */
|
||||||
if (oif != NULL)
|
match = (oif == NULL);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_LAYER2:
|
case O_LAYER2:
|
||||||
if (args->eh == NULL)
|
match = (args->eh != NULL);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_PROTO:
|
case O_PROTO:
|
||||||
/*
|
/*
|
||||||
* We do not allow an arg of 0 so the
|
* We do not allow an arg of 0 so the
|
||||||
* check of "proto" only suffices.
|
* check of "proto" only suffices.
|
||||||
*/
|
*/
|
||||||
if (proto == cmd->arg1)
|
match = (proto == cmd->arg1);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_SRC:
|
case O_IP_SRC:
|
||||||
if (hlen > 0 &&
|
match = (hlen > 0 &&
|
||||||
((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
||||||
src_ip.s_addr)
|
src_ip.s_addr);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_SRC_MASK:
|
case O_IP_SRC_MASK:
|
||||||
if (hlen > 0 &&
|
match = (hlen > 0 &&
|
||||||
((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
||||||
(src_ip.s_addr &
|
(src_ip.s_addr &
|
||||||
((ipfw_insn_ip *)cmd)->mask.s_addr))
|
((ipfw_insn_ip *)cmd)->mask.s_addr));
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_SRC_ME:
|
case O_IP_SRC_ME:
|
||||||
if (hlen == 0)
|
if (hlen > 0) {
|
||||||
goto cmd_fail;
|
struct ifnet *tif;
|
||||||
{
|
|
||||||
struct ifnet *tif;
|
|
||||||
|
|
||||||
|
INADDR_TO_IFP(src_ip, tif);
|
||||||
INADDR_TO_IFP(src_ip, tif);
|
match = (tif != NULL);
|
||||||
if (tif != NULL)
|
}
|
||||||
goto cmd_match;
|
break;
|
||||||
}
|
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_DST_SET:
|
case O_IP_DST_SET:
|
||||||
case O_IP_SRC_SET:
|
case O_IP_SRC_SET:
|
||||||
if (hlen == 0)
|
if (hlen > 0) {
|
||||||
goto cmd_fail;
|
u_int32_t *d = (u_int32_t *)(cmd+1);
|
||||||
{
|
u_int32_t addr =
|
||||||
u_int32_t *d = (u_int32_t *)(cmd+1);
|
cmd->opcode == O_IP_DST_SET ?
|
||||||
u_int32_t a =
|
args->f_id.src_ip :
|
||||||
cmd->opcode == O_IP_DST_SET ?
|
args->f_id.dst_ip;
|
||||||
args->f_id.src_ip : args->f_id.dst_ip;
|
|
||||||
|
|
||||||
if (a < d[0])
|
if (addr < d[0])
|
||||||
goto cmd_fail;
|
break;
|
||||||
a -= d[0];
|
addr -= d[0]; /* subtract base */
|
||||||
if (a >= cmd->arg1)
|
match = (addr < cmd->arg1) &&
|
||||||
goto cmd_fail;
|
( d[ 1 + (addr>>5)] &
|
||||||
if (d[ 1 + (a>>5)] & (1<<(a & 0x1f)) )
|
(1<<(addr & 0x1f)) );
|
||||||
goto cmd_match;
|
}
|
||||||
}
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_DST:
|
case O_IP_DST:
|
||||||
if (hlen > 0 &&
|
match = (hlen > 0 &&
|
||||||
((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
||||||
dst_ip.s_addr)
|
dst_ip.s_addr);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_DST_MASK:
|
case O_IP_DST_MASK:
|
||||||
if (hlen == 0)
|
match = (hlen > 0) &&
|
||||||
goto cmd_fail;
|
(((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
||||||
if (((ipfw_insn_ip *)cmd)->addr.s_addr ==
|
(dst_ip.s_addr &
|
||||||
(dst_ip.s_addr &
|
((ipfw_insn_ip *)cmd)->mask.s_addr));
|
||||||
((ipfw_insn_ip *)cmd)->mask.s_addr))
|
break;
|
||||||
goto cmd_match;
|
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_DST_ME:
|
case O_IP_DST_ME:
|
||||||
if (hlen == 0)
|
if (hlen > 0) {
|
||||||
goto cmd_fail;
|
struct ifnet *tif;
|
||||||
{
|
|
||||||
struct ifnet *tif;
|
INADDR_TO_IFP(dst_ip, tif);
|
||||||
INADDR_TO_IFP(dst_ip, tif);
|
match = (tif != NULL);
|
||||||
if (tif != NULL)
|
}
|
||||||
goto cmd_match;
|
break;
|
||||||
}
|
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
case O_IP_SRCPORT:
|
case O_IP_SRCPORT:
|
||||||
case O_IP_DSTPORT:
|
case O_IP_DSTPORT:
|
||||||
@ -1607,180 +1594,206 @@ check_body:
|
|||||||
* to guarantee that we have an IPv4
|
* to guarantee that we have an IPv4
|
||||||
* packet with port info.
|
* packet with port info.
|
||||||
*/
|
*/
|
||||||
if (offset != 0)
|
if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP)
|
||||||
goto cmd_fail;
|
&& offset == 0) {
|
||||||
if (proto==IPPROTO_UDP ||
|
u_int16_t x =
|
||||||
proto==IPPROTO_TCP) {
|
|
||||||
u_int16_t port =
|
|
||||||
(cmd->opcode == O_IP_SRCPORT) ?
|
(cmd->opcode == O_IP_SRCPORT) ?
|
||||||
src_port : dst_port ;
|
src_port : dst_port ;
|
||||||
u_int16_t *p =
|
u_int16_t *p =
|
||||||
((ipfw_insn_u16 *)cmd)->ports;
|
((ipfw_insn_u16 *)cmd)->ports;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = cmdlen - 1; i>0; i--)
|
for (i = cmdlen - 1; !match && i>0;
|
||||||
if (port>=p[0] && port<=p[1])
|
i--, p += 2)
|
||||||
goto cmd_match;
|
match = (x>=p[0] && x<=p[1]);
|
||||||
else
|
|
||||||
p += 2;
|
|
||||||
}
|
}
|
||||||
goto cmd_fail;
|
break;
|
||||||
|
|
||||||
case O_ICMPTYPE:
|
case O_ICMPTYPE:
|
||||||
if (offset > 0 ||
|
match = (offset == 0 && proto==IPPROTO_ICMP &&
|
||||||
proto != IPPROTO_ICMP ||
|
icmptype_match(ip, (ipfw_insn_u32 *)cmd) );
|
||||||
!icmptype_match(ip, (ipfw_insn_u32 *)cmd) )
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IPOPT:
|
case O_IPOPT:
|
||||||
if (hlen == 0 ||
|
match = (hlen > 0 && ipopts_match(ip, cmd) );
|
||||||
!ipopts_match(ip, cmd) )
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IPVER:
|
case O_IPVER:
|
||||||
if (hlen == 0 || cmd->arg1 != ip->ip_v)
|
match = (hlen > 0 && cmd->arg1 == ip->ip_v);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IPTTL:
|
case O_IPTTL:
|
||||||
if (hlen == 0 || cmd->arg1 != ip->ip_ttl)
|
match = (hlen > 0 && cmd->arg1 == ip->ip_ttl);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IPID:
|
case O_IPID:
|
||||||
if (hlen == 0 || cmd->arg1 != ntohs(ip->ip_id))
|
match = (hlen > 0 &&
|
||||||
goto cmd_fail;
|
cmd->arg1 == ntohs(ip->ip_id));
|
||||||
goto cmd_match;
|
break;
|
||||||
|
|
||||||
case O_IPLEN:
|
case O_IPLEN:
|
||||||
if (hlen == 0 || cmd->arg1 != ip_len)
|
match = (hlen > 0 && cmd->arg1 == ip_len);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IPPRECEDENCE:
|
case O_IPPRECEDENCE:
|
||||||
if (hlen == 0 ||
|
match = (hlen > 0 &&
|
||||||
(cmd->arg1 != (ip->ip_tos & 0xe0)) )
|
(cmd->arg1 == (ip->ip_tos & 0xe0)) );
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_IPTOS:
|
case O_IPTOS:
|
||||||
if (hlen == 0 ||
|
match = (hlen > 0 &&
|
||||||
!flags_match(cmd, ip->ip_tos))
|
flags_match(cmd, ip->ip_tos));
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_TCPFLAGS:
|
case O_TCPFLAGS:
|
||||||
if (proto != IPPROTO_TCP ||
|
match = (proto == IPPROTO_TCP && offset == 0 &&
|
||||||
offset > 0 ||
|
flags_match(cmd,
|
||||||
!flags_match(cmd,
|
L3HDR(struct tcphdr,ip)->th_flags));
|
||||||
L3HDR(struct tcphdr,ip)->th_flags))
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_TCPOPTS:
|
case O_TCPOPTS:
|
||||||
if (proto != IPPROTO_TCP ||
|
match = (proto == IPPROTO_TCP && offset == 0 &&
|
||||||
offset > 0 ||
|
tcpopts_match(ip, cmd));
|
||||||
!tcpopts_match(ip, cmd))
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_TCPSEQ:
|
case O_TCPSEQ:
|
||||||
if (proto != IPPROTO_TCP || offset > 0 ||
|
match = (proto == IPPROTO_TCP && offset == 0 &&
|
||||||
((ipfw_insn_u32 *)cmd)->d[0] !=
|
((ipfw_insn_u32 *)cmd)->d[0] ==
|
||||||
L3HDR(struct tcphdr,ip)->th_seq)
|
L3HDR(struct tcphdr,ip)->th_seq);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_TCPACK:
|
case O_TCPACK:
|
||||||
if (proto != IPPROTO_TCP || offset > 0 ||
|
match = (proto == IPPROTO_TCP && offset == 0 &&
|
||||||
((ipfw_insn_u32 *)cmd)->d[0] !=
|
((ipfw_insn_u32 *)cmd)->d[0] ==
|
||||||
L3HDR(struct tcphdr,ip)->th_ack)
|
L3HDR(struct tcphdr,ip)->th_ack);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_TCPWIN:
|
case O_TCPWIN:
|
||||||
if (proto != IPPROTO_TCP || offset > 0 ||
|
match = (proto == IPPROTO_TCP && offset == 0 &&
|
||||||
cmd->arg1 !=
|
cmd->arg1 ==
|
||||||
L3HDR(struct tcphdr,ip)->th_win)
|
L3HDR(struct tcphdr,ip)->th_win);
|
||||||
goto cmd_fail;
|
break;
|
||||||
goto cmd_match;
|
|
||||||
|
|
||||||
case O_ESTAB:
|
case O_ESTAB:
|
||||||
if (proto != IPPROTO_TCP || offset > 0)
|
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
/* reject packets which have SYN only */
|
/* reject packets which have SYN only */
|
||||||
if ((L3HDR(struct tcphdr,ip)->th_flags &
|
/* XXX should i also check for TH_ACK ? */
|
||||||
(TH_RST | TH_ACK | TH_SYN)) == TH_SYN)
|
match = (proto == IPPROTO_TCP && offset == 0 &&
|
||||||
goto cmd_fail;
|
(L3HDR(struct tcphdr,ip)->th_flags &
|
||||||
goto cmd_match;
|
(TH_RST | TH_ACK | TH_SYN)) != TH_SYN);
|
||||||
|
break;
|
||||||
|
|
||||||
case O_LOG:
|
case O_LOG:
|
||||||
ipfw_log(f, hlen, args->eh, m, oif);
|
ipfw_log(f, hlen, args->eh, m, oif);
|
||||||
goto cmd_match;
|
match = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case O_PROB:
|
case O_PROB:
|
||||||
if (random() < ((ipfw_insn_u32 *)cmd)->d[0] )
|
match = (random()<((ipfw_insn_u32 *)cmd)->d[0]);
|
||||||
goto cmd_match;
|
break;
|
||||||
goto cmd_fail;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The second set of opcodes represents 'actions',
|
||||||
|
* i.e. the terminal part of a rule once the packet
|
||||||
|
* matches all previous patterns.
|
||||||
|
* Typically there is only one action for each rule,
|
||||||
|
* and the opcode is stored at the end of the rule
|
||||||
|
* (but there are exceptions -- see below).
|
||||||
|
*
|
||||||
|
* In general, here we set retval and terminate the
|
||||||
|
* outer loop (would be a 'break 3' in some language,
|
||||||
|
* but we need to do a 'goto done').
|
||||||
|
*
|
||||||
|
* Exceptions:
|
||||||
|
* O_COUNT and O_SKIPTO actions:
|
||||||
|
* instead of terminating, we jump to the next rule
|
||||||
|
* ('goto next_rule', equivalent to a 'break 2'),
|
||||||
|
* or to the SKIPTO target ('goto again' after
|
||||||
|
* having set f, cmd and l), respectively.
|
||||||
|
*
|
||||||
|
* O_LIMIT and O_KEEP_STATE: these opcodes are
|
||||||
|
* not real 'actions', and are stored right
|
||||||
|
* before the 'action' part of the rule.
|
||||||
|
* These opcodes try to install an entry in the
|
||||||
|
* state tables; if successful, we continue with
|
||||||
|
* the next opcode (match=1; break;), otherwise
|
||||||
|
* the packet * must be dropped
|
||||||
|
* ('goto done' after setting retval);
|
||||||
|
*
|
||||||
|
* O_PROBE_STATE and O_CHECK_STATE: these opcodes
|
||||||
|
* cause a lookup of the state table, and a jump
|
||||||
|
* to the 'action' part of the parent rule
|
||||||
|
* ('goto check_body') if an entry is found, or
|
||||||
|
* (CHECK_STATE only) a jump to the next rule if
|
||||||
|
* the entry is not found ('goto next_rule').
|
||||||
|
* The result of the lookup is cached to make
|
||||||
|
* further instances of these opcodes are
|
||||||
|
* effectively NOPs.
|
||||||
|
*/
|
||||||
case O_LIMIT:
|
case O_LIMIT:
|
||||||
case O_KEEP_STATE:
|
case O_KEEP_STATE:
|
||||||
if (install_state(f,
|
if (install_state(f,
|
||||||
(ipfw_insn_limit *)cmd, args))
|
(ipfw_insn_limit *)cmd, args)) {
|
||||||
goto deny; /* error/limit violation */
|
retval = IP_FW_PORT_DENY_FLAG;
|
||||||
goto cmd_match;
|
goto done; /* error/limit violation */
|
||||||
|
}
|
||||||
|
match = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case O_PROBE_STATE:
|
case O_PROBE_STATE:
|
||||||
case O_CHECK_STATE:
|
case O_CHECK_STATE:
|
||||||
/*
|
/*
|
||||||
* dynamic rules are checked at the first
|
* dynamic rules are checked at the first
|
||||||
* keep-state or check-state occurrence.
|
* keep-state or check-state occurrence,
|
||||||
* The compiler introduces a probe-state
|
* with the result being stored in dyn_dir.
|
||||||
|
* The compiler introduces a PROBE_STATE
|
||||||
* instruction for us when we have a
|
* instruction for us when we have a
|
||||||
* keep-state (because probe-state needs
|
* KEEP_STATE (because PROBE_STATE needs
|
||||||
* to be run first).
|
* to be run first).
|
||||||
*/
|
*/
|
||||||
if (dyn_dir == MATCH_UNKNOWN) {
|
if (dyn_dir == MATCH_UNKNOWN &&
|
||||||
q = lookup_dyn_rule(&args->f_id,
|
(q = lookup_dyn_rule(&args->f_id,
|
||||||
&dyn_dir);
|
&dyn_dir)) != NULL) {
|
||||||
if (q != NULL) {
|
/*
|
||||||
f = q->rule;
|
* Found dynamic entry, update stats
|
||||||
q->pcnt++;
|
* and jump to the 'action' part of
|
||||||
q->bcnt += ip_len;
|
* the parent rule.
|
||||||
/* go to ACTION */
|
*/
|
||||||
cmd = ACTION_PTR(f);
|
q->pcnt++;
|
||||||
l = f->cmd_len - f->act_ofs;
|
q->bcnt += ip_len;
|
||||||
goto check_body;
|
f = q->rule;
|
||||||
}
|
cmd = ACTION_PTR(f);
|
||||||
|
l = f->cmd_len - f->act_ofs;
|
||||||
|
goto check_body;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Dynamic entry not found. If CHECK_STATE,
|
||||||
|
* skip to next rule, if PROBE_STATE just
|
||||||
|
* ignore and continue with next opcode.
|
||||||
|
*/
|
||||||
if (cmd->opcode == O_CHECK_STATE)
|
if (cmd->opcode == O_CHECK_STATE)
|
||||||
goto next_rule;
|
goto next_rule;
|
||||||
else
|
match = 1;
|
||||||
goto cmd_match;
|
break;
|
||||||
|
|
||||||
case O_ACCEPT:
|
case O_ACCEPT:
|
||||||
retval = 0; /* accept */
|
retval = 0; /* accept */
|
||||||
goto accept;
|
goto done;
|
||||||
|
|
||||||
case O_PIPE:
|
case O_PIPE:
|
||||||
case O_QUEUE:
|
case O_QUEUE:
|
||||||
args->rule = f; /* report matching rule */
|
args->rule = f; /* report matching rule */
|
||||||
retval = cmd->arg1 | IP_FW_PORT_DYNT_FLAG;
|
retval = cmd->arg1 | IP_FW_PORT_DYNT_FLAG;
|
||||||
goto accept;
|
goto done;
|
||||||
|
|
||||||
case O_DIVERT:
|
case O_DIVERT:
|
||||||
case O_TEE:
|
case O_TEE:
|
||||||
if (args->eh) /* not on layer 2 */
|
if (args->eh) /* not on layer 2 */
|
||||||
goto cmd_fail;
|
break;
|
||||||
args->divert_rule = f->rulenum;
|
args->divert_rule = f->rulenum;
|
||||||
if (cmd->opcode == O_DIVERT)
|
retval = (cmd->opcode == O_DIVERT) ?
|
||||||
retval = cmd->arg1;
|
cmd->arg1 :
|
||||||
else
|
cmd->arg1 | IP_FW_PORT_TEE_FLAG;
|
||||||
retval = cmd->arg1|IP_FW_PORT_TEE_FLAG;
|
goto done;
|
||||||
goto accept;
|
|
||||||
|
|
||||||
case O_COUNT:
|
case O_COUNT:
|
||||||
case O_SKIPTO:
|
case O_SKIPTO:
|
||||||
@ -1810,67 +1823,42 @@ check_body:
|
|||||||
offset,ip_len);
|
offset,ip_len);
|
||||||
m = args->m;
|
m = args->m;
|
||||||
}
|
}
|
||||||
goto deny;
|
/* FALLTHROUGH */
|
||||||
|
case O_DENY:
|
||||||
|
retval = IP_FW_PORT_DENY_FLAG;
|
||||||
|
goto done;
|
||||||
|
|
||||||
case O_FORWARD_IP:
|
case O_FORWARD_IP:
|
||||||
if (args->eh) /* not valid on layer2 pkts */
|
if (args->eh) /* not valid on layer2 pkts */
|
||||||
goto cmd_fail;
|
break;
|
||||||
if (!q || dyn_dir == MATCH_FORWARD)
|
if (!q || dyn_dir == MATCH_FORWARD)
|
||||||
args->next_hop =
|
args->next_hop =
|
||||||
&((ipfw_insn_sa *)cmd)->sa;
|
&((ipfw_insn_sa *)cmd)->sa;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto accept;
|
goto done;
|
||||||
|
|
||||||
case O_DENY:
|
|
||||||
goto deny;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("-- unknown opcode %d\n", cmd->opcode);
|
panic("-- unknown opcode %d\n", cmd->opcode);
|
||||||
}
|
} /* end of switch() on opcodes */
|
||||||
panic("ipfw_chk: end of inner loop");
|
|
||||||
|
|
||||||
/*
|
if (cmd->len & F_NOT)
|
||||||
* This code is a bit spaghetti, but we have
|
match = !match;
|
||||||
* 4 cases to handle:
|
|
||||||
* INSN FAIL, no F_NOT --> insn_fail
|
|
||||||
* INSN FAIL, but we have F_NOT --> cmd_success
|
|
||||||
* INSN MATCH, no F_NOT --> cmd_success
|
|
||||||
* INSN MATCH, but we have F_NOT --> insn_fail
|
|
||||||
*
|
|
||||||
* after this:
|
|
||||||
* cmd_success, F_OR --> set skip_or
|
|
||||||
* cmd_success, not F_OR --> try next insn
|
|
||||||
* insn_fail, F_OR --> try next insn
|
|
||||||
* insn_fail, not F_OR --> rule does not match
|
|
||||||
*/
|
|
||||||
cmd_fail:
|
|
||||||
if (cmd->len & F_NOT) /* NOT fail is a success */
|
|
||||||
goto cmd_success;
|
|
||||||
else
|
|
||||||
goto insn_fail;
|
|
||||||
|
|
||||||
cmd_match:
|
if (match) {
|
||||||
if (cmd->len & F_NOT) { /* NOT match is a failure. */
|
if (cmd->len & F_OR)
|
||||||
insn_fail:
|
skip_or = 1;
|
||||||
if (cmd->len & F_OR) /* If an or block */
|
} else {
|
||||||
continue; /* try next insn */
|
if (!(cmd->len & F_OR)) /* not an OR block, */
|
||||||
else
|
break; /* try next rule */
|
||||||
break; /* otherwise next rule */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_success:
|
|
||||||
if (cmd->len & F_OR)
|
|
||||||
skip_or = 1;
|
|
||||||
} /* end of inner for, scan opcodes */
|
} /* end of inner for, scan opcodes */
|
||||||
|
|
||||||
next_rule:; /* try next rule */
|
next_rule:; /* try next rule */
|
||||||
|
|
||||||
} /* end of outer for, scan rules */
|
} /* end of outer for, scan rules */
|
||||||
|
|
||||||
deny:
|
done:
|
||||||
retval = IP_FW_PORT_DENY_FLAG;
|
|
||||||
|
|
||||||
accept:
|
|
||||||
/* Update statistics */
|
/* Update statistics */
|
||||||
f->pcnt++;
|
f->pcnt++;
|
||||||
f->bcnt += ip_len;
|
f->bcnt += ip_len;
|
||||||
@ -2155,13 +2143,13 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
ipfw_insn *cmd;
|
ipfw_insn *cmd;
|
||||||
|
|
||||||
if (size < sizeof(*rule)) {
|
if (size < sizeof(*rule)) {
|
||||||
printf("kipfw: rule too short\n");
|
printf("ipfw: rule too short\n");
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
/* first, check for valid size */
|
/* first, check for valid size */
|
||||||
l = RULESIZE(rule);
|
l = RULESIZE(rule);
|
||||||
if (l != size) {
|
if (l != size) {
|
||||||
printf("kipfw: size mismatch (have %d want %d)\n", size, l);
|
printf("ipfw: size mismatch (have %d want %d)\n", size, l);
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@ -2172,11 +2160,11 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||||
cmdlen = F_LEN(cmd);
|
cmdlen = F_LEN(cmd);
|
||||||
if (cmdlen > l) {
|
if (cmdlen > l) {
|
||||||
printf("kipfw: opcode %d size truncated\n",
|
printf("ipfw: opcode %d size truncated\n",
|
||||||
cmd->opcode);
|
cmd->opcode);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
DEB(printf("kipfw: opcode %d\n", cmd->opcode);)
|
DEB(printf("ipfw: opcode %d\n", cmd->opcode);)
|
||||||
switch (cmd->opcode) {
|
switch (cmd->opcode) {
|
||||||
case O_NOP:
|
case O_NOP:
|
||||||
case O_PROBE_STATE:
|
case O_PROBE_STATE:
|
||||||
@ -2190,7 +2178,6 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
case O_IPOPT:
|
case O_IPOPT:
|
||||||
case O_IPLEN:
|
case O_IPLEN:
|
||||||
case O_IPID:
|
case O_IPID:
|
||||||
case O_IPPRE:
|
|
||||||
case O_IPTOS:
|
case O_IPTOS:
|
||||||
case O_IPPRECEDENCE:
|
case O_IPPRECEDENCE:
|
||||||
case O_IPTTL:
|
case O_IPTTL:
|
||||||
@ -2234,7 +2221,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
if (cmdlen != F_INSN_SIZE(ipfw_insn_ip))
|
if (cmdlen != F_INSN_SIZE(ipfw_insn_ip))
|
||||||
goto bad_size;
|
goto bad_size;
|
||||||
if (((ipfw_insn_ip *)cmd)->mask.s_addr == 0) {
|
if (((ipfw_insn_ip *)cmd)->mask.s_addr == 0) {
|
||||||
printf("kipfw: opcode %d, useless rule\n",
|
printf("ipfw: opcode %d, useless rule\n",
|
||||||
cmd->opcode);
|
cmd->opcode);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
@ -2243,7 +2230,7 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
case O_IP_SRC_SET:
|
case O_IP_SRC_SET:
|
||||||
case O_IP_DST_SET:
|
case O_IP_DST_SET:
|
||||||
if (cmd->arg1 == 0 || cmd->arg1 > 256) {
|
if (cmd->arg1 == 0 || cmd->arg1 > 256) {
|
||||||
printf("kipfw: invalid set size %d\n",
|
printf("ipfw: invalid set size %d\n",
|
||||||
cmd->arg1);
|
cmd->arg1);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
@ -2295,33 +2282,33 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
goto bad_size;
|
goto bad_size;
|
||||||
check_action:
|
check_action:
|
||||||
if (have_action) {
|
if (have_action) {
|
||||||
printf("kipfw: opcode %d, multiple actions"
|
printf("ipfw: opcode %d, multiple actions"
|
||||||
" not allowed\n",
|
" not allowed\n",
|
||||||
cmd->opcode);
|
cmd->opcode);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
have_action = 1;
|
have_action = 1;
|
||||||
if (l != cmdlen) {
|
if (l != cmdlen) {
|
||||||
printf("kipfw: opcode %d, action must be"
|
printf("ipfw: opcode %d, action must be"
|
||||||
" last opcode\n",
|
" last opcode\n",
|
||||||
cmd->opcode);
|
cmd->opcode);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("kipfw: opcode %d, unknown opcode\n",
|
printf("ipfw: opcode %d, unknown opcode\n",
|
||||||
cmd->opcode);
|
cmd->opcode);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (have_action == 0) {
|
if (have_action == 0) {
|
||||||
printf("kipfw: missing action\n");
|
printf("ipfw: missing action\n");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bad_size:
|
bad_size:
|
||||||
printf("kipfw: opcode %d size %d wrong\n",
|
printf("ipfw: opcode %d size %d wrong\n",
|
||||||
cmd->opcode, cmdlen);
|
cmd->opcode, cmdlen);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
@ -2591,3 +2578,4 @@ static moduledata_t ipfwmod = {
|
|||||||
};
|
};
|
||||||
DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
|
DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
|
||||||
MODULE_VERSION(ipfw, 1);
|
MODULE_VERSION(ipfw, 1);
|
||||||
|
#endif /* IPFW2 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user