Exemple #1
0
/*
 * Print the ip address contained in a command.
 */
void
print_ip6(struct buf_pr *bp, ipfw_insn_ip6 *cmd, char const *s)
{
       struct hostent *he = NULL;
       int len = F_LEN((ipfw_insn *) cmd) - 1;
       struct in6_addr *a = &(cmd->addr6);
       char trad[255];

       bprintf(bp, "%s%s ", cmd->o.len & F_NOT ? " not": "", s);

       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
	       bprintf(bp, "me6");
	       return;
       }
       if (cmd->o.opcode == O_IP6) {
	       bprintf(bp, " ip6");
	       return;
       }

       /*
	* len == 4 indicates a single IP, whereas lists of 1 or more
	* addr/mask pairs have len = (2n+1). We convert len to n so we
	* use that to count the number of entries.
	*/

       for (len = len / 4; len > 0; len -= 2, a += 2) {
	   int mb =	/* mask length */
	       (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
	       128 : contigmask((uint8_t *)&(a[1]), 128);

	   if (mb == 128 && co.do_resolv)
	       he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
	   if (he != NULL)	     /* resolved to name */
	       bprintf(bp, "%s", he->h_name);
	   else if (mb == 0)	   /* any */
	       bprintf(bp, "any");
	   else {	  /* numeric IP followed by some kind of mask */
	       if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
		   bprintf(bp, "Error ntop in print_ip6\n");
	       bprintf(bp, "%s",  trad );
	       if (mb < 0) /* mask not contiguous */
		   bprintf(bp, "/%s",
		       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
	       else if (mb < 128)
		   bprintf(bp, "/%d", mb);
	   }
	   if (len > 2)
	       bprintf(bp, ",");
       }
}
Exemple #2
0
/*
 * We enter here when we have a rule with O_LOG.
 * XXX this function alone takes about 2Kbytes of code!
 */
void
ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
    struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
    struct ip *ip)
{
	char *action;
	int limit_reached = 0;
	char action2[92], proto[128], fragment[32];

	if (V_fw_verbose == 0) {
#ifndef WITHOUT_BPF
		LOGIF_RLOCK();
		if (log_if == NULL || log_if->if_bpf == NULL) {
			LOGIF_RUNLOCK();
			return;
		}

		if (args->eh) /* layer2, use orig hdr */
			BPF_MTAP2(log_if, args->eh, ETHER_HDR_LEN, m);
		else
			/* Add fake header. Later we will store
			 * more info in the header.
			 */
			BPF_MTAP2(log_if, "DDDDDDSSSSSS\x08\x00", ETHER_HDR_LEN, m);
		LOGIF_RUNLOCK();
#endif /* !WITHOUT_BPF */
		return;
	}
	/* the old 'log' function */
	fragment[0] = '\0';
	proto[0] = '\0';

	if (f == NULL) {	/* bogus pkt */
		if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit)
			return;
		V_norule_counter++;
		if (V_norule_counter == V_verbose_limit)
			limit_reached = V_verbose_limit;
		action = "Refuse";
	} else {	/* O_LOG is the first action, find the real one */
		ipfw_insn *cmd = ACTION_PTR(f);
		ipfw_insn_log *l = (ipfw_insn_log *)cmd;

		if (l->max_log != 0 && l->log_left == 0)
			return;
		l->log_left--;
		if (l->log_left == 0)
			limit_reached = l->max_log;
		cmd += F_LEN(cmd);	/* point to first action */
		if (cmd->opcode == O_ALTQ) {
			ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd;

			snprintf(SNPARGS(action2, 0), "Altq %d",
				altq->qid);
			cmd += F_LEN(cmd);
		}
		if (cmd->opcode == O_PROB)
			cmd += F_LEN(cmd);

		if (cmd->opcode == O_TAG)
			cmd += F_LEN(cmd);

		action = action2;
		switch (cmd->opcode) {
		case O_DENY:
			action = "Deny";
			break;

		case O_REJECT:
			if (cmd->arg1==ICMP_REJECT_RST)
				action = "Reset";
			else if (cmd->arg1==ICMP_UNREACH_HOST)
				action = "Reject";
			else
				snprintf(SNPARGS(action2, 0), "Unreach %d",
					cmd->arg1);
			break;

		case O_UNREACH6:
			if (cmd->arg1==ICMP6_UNREACH_RST)
				action = "Reset";
			else
				snprintf(SNPARGS(action2, 0), "Unreach %d",
					cmd->arg1);
			break;

		case O_ACCEPT:
			action = "Accept";
			break;
		case O_COUNT:
			action = "Count";
			break;
		case O_DIVERT:
			snprintf(SNPARGS(action2, 0), "Divert %d",
				cmd->arg1);
			break;
		case O_TEE:
			snprintf(SNPARGS(action2, 0), "Tee %d",
				cmd->arg1);
			break;
		case O_SETFIB:
			snprintf(SNPARGS(action2, 0), "SetFib %d",
				IP_FW_ARG_TABLEARG(cmd->arg1));
			break;
		case O_SKIPTO:
			snprintf(SNPARGS(action2, 0), "SkipTo %d",
				IP_FW_ARG_TABLEARG(cmd->arg1));
			break;
		case O_PIPE:
			snprintf(SNPARGS(action2, 0), "Pipe %d",
				IP_FW_ARG_TABLEARG(cmd->arg1));
			break;
		case O_QUEUE:
			snprintf(SNPARGS(action2, 0), "Queue %d",
				IP_FW_ARG_TABLEARG(cmd->arg1));
			break;
		case O_FORWARD_IP: {
			ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;
			int len;
			struct in_addr dummyaddr;
			if (sa->sa.sin_addr.s_addr == INADDR_ANY)
				dummyaddr.s_addr = htonl(tablearg);
			else
				dummyaddr.s_addr = sa->sa.sin_addr.s_addr;

			len = snprintf(SNPARGS(action2, 0), "Forward to %s",
				inet_ntoa(dummyaddr));

			if (sa->sa.sin_port)
				snprintf(SNPARGS(action2, len), ":%d",
				    sa->sa.sin_port);
			}
			break;
#ifdef INET6
		case O_FORWARD_IP6: {
			char buf[INET6_ADDRSTRLEN];
			ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd;
			int len;

			len = snprintf(SNPARGS(action2, 0), "Forward to [%s]",
			    ip6_sprintf(buf, &sa->sa.sin6_addr));

			if (sa->sa.sin6_port)
				snprintf(SNPARGS(action2, len), ":%u",
				    sa->sa.sin6_port);
			}
			break;
#endif
		case O_NETGRAPH:
			snprintf(SNPARGS(action2, 0), "Netgraph %d",
				cmd->arg1);
			break;
		case O_NGTEE:
			snprintf(SNPARGS(action2, 0), "Ngtee %d",
				cmd->arg1);
			break;
		case O_NAT:
			action = "Nat";
 			break;
		case O_REASS:
			action = "Reass";
			break;
		case O_CALLRETURN:
			if (cmd->len & F_NOT)
				action = "Return";
			else
				snprintf(SNPARGS(action2, 0), "Call %d",
				    cmd->arg1);
			break;
		default:
			action = "UNKNOWN";
			break;
		}
	}

	if (hlen == 0) {	/* non-ip */
		snprintf(SNPARGS(proto, 0), "MAC");

	} else {
		int len;
#ifdef INET6
		char src[INET6_ADDRSTRLEN + 2], dst[INET6_ADDRSTRLEN + 2];
#else
		char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
#endif
		struct icmphdr *icmp;
		struct tcphdr *tcp;
		struct udphdr *udp;
#ifdef INET6
		struct ip6_hdr *ip6 = NULL;
		struct icmp6_hdr *icmp6;
		u_short ip6f_mf;
#endif
		src[0] = '\0';
		dst[0] = '\0';
#ifdef INET6
		ip6f_mf = offset & IP6F_MORE_FRAG;
		offset &= IP6F_OFF_MASK;

		if (IS_IP6_FLOW_ID(&(args->f_id))) {
			char ip6buf[INET6_ADDRSTRLEN];
			snprintf(src, sizeof(src), "[%s]",
			    ip6_sprintf(ip6buf, &args->f_id.src_ip6));
			snprintf(dst, sizeof(dst), "[%s]",
			    ip6_sprintf(ip6buf, &args->f_id.dst_ip6));

			ip6 = (struct ip6_hdr *)ip;
			tcp = (struct tcphdr *)(((char *)ip) + hlen);
			udp = (struct udphdr *)(((char *)ip) + hlen);
		} else
#endif
		{
			tcp = L3HDR(struct tcphdr, ip);
			udp = L3HDR(struct udphdr, ip);

			inet_ntop(AF_INET, &ip->ip_src, src, sizeof(src));
			inet_ntop(AF_INET, &ip->ip_dst, dst, sizeof(dst));
		}

		switch (args->f_id.proto) {
		case IPPROTO_TCP:
			len = snprintf(SNPARGS(proto, 0), "TCP %s", src);
			if (offset == 0)
				snprintf(SNPARGS(proto, len), ":%d %s:%d",
				    ntohs(tcp->th_sport),
				    dst,
				    ntohs(tcp->th_dport));
			else
				snprintf(SNPARGS(proto, len), " %s", dst);
			break;

		case IPPROTO_UDP:
			len = snprintf(SNPARGS(proto, 0), "UDP %s", src);
			if (offset == 0)
				snprintf(SNPARGS(proto, len), ":%d %s:%d",
				    ntohs(udp->uh_sport),
				    dst,
				    ntohs(udp->uh_dport));
			else
				snprintf(SNPARGS(proto, len), " %s", dst);
			break;

		case IPPROTO_ICMP:
			icmp = L3HDR(struct icmphdr, ip);
			if (offset == 0)
				len = snprintf(SNPARGS(proto, 0),
				    "ICMP:%u.%u ",
				    icmp->icmp_type, icmp->icmp_code);
			else
				len = snprintf(SNPARGS(proto, 0), "ICMP ");
			len += snprintf(SNPARGS(proto, len), "%s", src);
			snprintf(SNPARGS(proto, len), " %s", dst);
			break;
#ifdef INET6
		case IPPROTO_ICMPV6:
			icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen);
			if (offset == 0)
				len = snprintf(SNPARGS(proto, 0),
				    "ICMPv6:%u.%u ",
				    icmp6->icmp6_type, icmp6->icmp6_code);
			else
				len = snprintf(SNPARGS(proto, 0), "ICMPv6 ");
			len += snprintf(SNPARGS(proto, len), "%s", src);
			snprintf(SNPARGS(proto, len), " %s", dst);
			break;
#endif
		default:
			len = snprintf(SNPARGS(proto, 0), "P:%d %s",
			    args->f_id.proto, src);
			snprintf(SNPARGS(proto, len), " %s", dst);
			break;
		}

#ifdef INET6
		if (IS_IP6_FLOW_ID(&(args->f_id))) {
			if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
				snprintf(SNPARGS(fragment, 0),
				    " (frag %08x:%d@%d%s)",
				    args->f_id.extra,
				    ntohs(ip6->ip6_plen) - hlen,
				    ntohs(offset) << 3, ip6f_mf ? "+" : "");
		} else
#endif
		{
			int ipoff, iplen;
			ipoff = ntohs(ip->ip_off);
			iplen = ntohs(ip->ip_len);
			if (ipoff & (IP_MF | IP_OFFMASK))
				snprintf(SNPARGS(fragment, 0),
				    " (frag %d:%d@%d%s)",
				    ntohs(ip->ip_id), iplen - (ip->ip_hl << 2),
				    offset << 3,
				    (ipoff & IP_MF) ? "+" : "");
		}
	}
#ifdef __FreeBSD__
	if (oif || m->m_pkthdr.rcvif)
		log(LOG_SECURITY | LOG_INFO,
		    "ipfw: %d %s %s %s via %s%s\n",
		    f ? f->rulenum : -1,
		    action, proto, oif ? "out" : "in",
		    oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname,
		    fragment);
	else
#endif
		log(LOG_SECURITY | LOG_INFO,
		    "ipfw: %d %s %s [no if info]%s\n",
		    f ? f->rulenum : -1,
		    action, proto, fragment);
	if (limit_reached)
		log(LOG_SECURITY | LOG_NOTICE,
		    "ipfw: limit %d reached on entry %d\n",
		    limit_reached, f ? f->rulenum : -1);
}
Exemple #3
0
SYSEND

#endif /* SYSCTL_NODE */


/*
 * Some macros used in the various matching options.
 * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T
 * Other macros just cast void * into the appropriate type
 */
#define	L3HDR(T, ip)	((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
#define	TCP(p)		((struct tcphdr *)(p))
#define	SCTP(p)		((struct sctphdr *)(p))
#define	UDP(p)		((struct udphdr *)(p))
#define	ICMP(p)		((struct icmphdr *)(p))
#define	ICMP6(p)	((struct icmp6_hdr *)(p))

/*
 * The main check routine for the firewall.
 *
 * All arguments are in args so we can modify them and return them
 * back to the caller.
 *
 * Parameters:
 *
 *	args->m	(in/out) The packet; we set to NULL when/if we nuke it.
 *		Starts with the IP header.
 *	args->eh (in)	Mac header if present, NULL for layer3 packet.
 *	args->L3offset	Number of bytes bypassed if we came from L2.
 *			e.g. often sizeof(eh)  ** NOTYET **
 *	args->oif	Outgoing interface, NULL if packet is incoming.
 *		The incoming interface is in the mbuf. (in)
 *	args->divert_rule (in/out)
 *		Skip up to the first rule past this rule number;
 *		upon return, non-zero port number for divert or tee.
 *
 *	args->rule	Pointer to the last matching rule (in/out)
 *	args->next_hop	Socket we are forwarding to (out).
 *	args->next_hop6	IPv6 next hop we are forwarding to (out).
 *	args->f_id	Addresses grabbed from the packet (out)
 * 	args->rule.info	a cookie depending on rule action
 *
 * Return value:
 *
 *	IP_FW_PASS	the packet must be accepted
 *	IP_FW_DENY	the packet must be dropped
 *	IP_FW_DIVERT	divert packet, port in m_tag
 *	IP_FW_TEE	tee packet, port in m_tag
 *	IP_FW_DUMMYNET	to dummynet, pipe in args->cookie
 *	IP_FW_NETGRAPH	into netgraph, cookie args->cookie
 *		args->rule contains the matching rule,
 *		args->rule.info has additional information.
 *
 */
int
ipfw_chk(struct ip_fw_args *args)
{
	struct ip_fw_chain *chain = &V_layer3_chain;

	/* Read comment below about this variable. */
	struct mbuf *m = args->m;

	if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready))
		return (IP_FW_PASS);	/* accept */

	args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */

	/*
	 * Local variables holding state while processing a packet:
	 *
	 * IMPORTANT NOTE: to speed up the processing of rules, there
	 * are some assumption on the values of the variables, which
	 * are documented here. Should you change them, please check
	 * the implementation of the various instructions to make sure
	 * that they still work.
	 *
	 * args->eh	The MAC header. It is non-null for a layer2
	 *	packet, it is NULL for a layer-3 packet.
	 * **notyet**
	 * args->L3offset Offset in the packet to the L3 (IP or equiv.) header.
	 *
	 * m | args->m	Pointer to the mbuf, as received from the caller.
	 *	It may change if ipfw_chk() does an m_pullup, or if it
	 *	consumes the packet because it calls send_reject().
	 *	XXX This has to change, so that ipfw_chk() never modifies
	 *	or consumes the buffer.
	 * ip	is the beginning of the ip(4 or 6) header.
	 *	Calculated by adding the L3offset to the start of data.
	 *	(Until we start using L3offset, the packet is
	 *	supposed to start with the ip header).
	 */
	struct ip *ip = mtod(m, struct ip *);

	/*
	 * For rules which contain uid/gid or jail constraints, cache
	 * a copy of the users credentials after the pcb lookup has been
	 * executed. This will speed up the processing of rules with
	 * these types of constraints, as well as decrease contention
	 * on pcb related locks.
	 */
#ifndef __FreeBSD__
	struct bsd_ucred ucred_cache;
#else
	struct ucred *ucred_cache = NULL;
#endif
	int ucred_lookup = 0;

	/*
	 * oif | args->oif	If NULL, ipfw_chk has been called on the
	 *	inbound path (ether_input, ip_input).
	 *	If non-NULL, ipfw_chk has been called on the outbound path
	 *	(ether_output, ip_output).
	 */
	struct ifnet *oif = args->oif;

	int f_pos = 0;		/* index of current rule in the array */
	int retval = 0;

	/*
	 * hlen	The length of the IP header.
	 */
	u_int hlen = 0;		/* hlen >0 means we have an IP pkt */

	/*
	 * offset	The offset of a fragment. offset != 0 means that
	 *	we have a fragment at this offset of an IPv4 packet.
	 *	offset == 0 means that (if this is an IPv4 packet)
	 *	this is the first or only fragment.
	 *	For IPv6 offset|ip6f_mf == 0 means there is no Fragment Header
	 *	or there is a single packet fragement (fragement header added
	 *	without needed).  We will treat a single packet fragment as if
	 *	there was no fragment header (or log/block depending on the
	 *	V_fw_permit_single_frag6 sysctl setting).
	 */
	u_short offset = 0;
	u_short ip6f_mf = 0;

	/*
	 * Local copies of addresses. They are only valid if we have
	 * an IP packet.
	 *
	 * proto	The protocol. Set to 0 for non-ip packets,
	 *	or to the protocol read from the packet otherwise.
	 *	proto != 0 means that we have an IPv4 packet.
	 *
	 * src_port, dst_port	port numbers, in HOST format. Only
	 *	valid for TCP and UDP packets.
	 *
	 * src_ip, dst_ip	ip addresses, in NETWORK format.
	 *	Only valid for IPv4 packets.
	 */
	uint8_t proto;
	uint16_t src_port = 0, dst_port = 0;	/* NOTE: host format	*/
	struct in_addr src_ip, dst_ip;		/* NOTE: network format	*/
	uint16_t iplen=0;
	int pktlen;
	uint16_t	etype = 0;	/* Host order stored ether type */

	/*
	 * dyn_dir = MATCH_UNKNOWN when rules unchecked,
	 * 	MATCH_NONE when checked and not matched (q = NULL),
	 *	MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL)
	 */
	int dyn_dir = MATCH_UNKNOWN;
	ipfw_dyn_rule *q = NULL;

	/*
	 * We store in ulp a pointer to the upper layer protocol header.
	 * In the ipv4 case this is easy to determine from the header,
	 * but for ipv6 we might have some additional headers in the middle.
	 * ulp is NULL if not found.
	 */
	void *ulp = NULL;		/* upper layer protocol pointer. */

	/* XXX ipv6 variables */
	int is_ipv6 = 0;
	uint8_t	icmp6_type = 0;
	uint16_t ext_hd = 0;	/* bits vector for extension header filtering */
	/* end of ipv6 variables */

	int is_ipv4 = 0;

	int done = 0;		/* flag to exit the outer loop */

	dst_ip.s_addr = 0;		/* make sure it is initialized */
	src_ip.s_addr = 0;		/* make sure it is initialized */
	pktlen = m->m_pkthdr.len;
	proto = args->f_id.proto = 0;	/* mark f_id invalid */
		/* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */

	// Fill in some variables.
	inspect_pkt(args, ip, m, &src_ip, &dst_ip, &src_port, &dst_port, &etype, &ext_hd, &iplen, &pktlen, &is_ipv4, &is_ipv6, &hlen, &proto, &icmp6_type, &ip6f_mf, &offset, &ulp);

	IPFW_PF_RLOCK(chain);
	if (! V_ipfw_vnet_ready) { /* shutting down, leave NOW. */
		IPFW_PF_RUNLOCK(chain);
		return (IP_FW_PASS);	/* accept */
	}
	if (args->rule.slot) {
		/*
		 * Packet has already been tagged as a result of a previous
		 * match on rule args->rule aka args->rule_id (PIPE, QUEUE,
		 * REASS, NETGRAPH, DIVERT/TEE...)
		 * Validate the slot and continue from the next one
		 * if still present, otherwise do a lookup.
		 */
		f_pos = (args->rule.chain_id == chain->id) ?
		    args->rule.slot :
		    ipfw_find_rule(chain, args->rule.rulenum,
			args->rule.rule_id);
	} else {
		f_pos = 0;
	}

	/*
	 * Now scan the rules, and parse microinstructions for each rule.
	 * We have two nested loops and an inner switch. Sometimes we
	 * need to break out of one or both loops, or re-enter one of
	 * the loops with updated variables. Loop variables are:
	 *
	 *	f_pos (outer loop) points to the current rule.
	 *		On output it points to the matching rule.
	 *	done (outer loop) is used as a flag to break the loop.
	 *	l (inner loop)	residual length of current rule.
	 *		cmd points to the current microinstruction.
	 *
	 * We break the inner loop by setting l=0 and possibly
	 * cmdlen=0 if we don't want to advance cmd.
	 * We break the outer loop by setting done=1
	 * We can restart the inner loop by setting l>0 and f_pos, f, cmd
	 * as needed.
	 */
	for (; f_pos < chain->n_rules; f_pos++) {
		ipfw_insn *cmd;
		uint32_t tablearg = 0;
		int l, cmdlen, skip_or; /* skip rest of OR block */
		struct ip_fw *f;

		f = chain->map[f_pos];
		if (V_set_disable & (1 << f->set) )
			continue;

		skip_or = 0;
		for (l = f->cmd_len, cmd = f->cmd ; l > 0 ;
		    l -= cmdlen, cmd += cmdlen) {
			int match;

			/*
			 * check_body is a jump target used when we find a
			 * CHECK_STATE, and need to jump to the body of
			 * the target rule.
			 */

/* check_body: */
			cmdlen = F_LEN(cmd);
			/*
			 * An OR block (insn_1 || .. || insn_n) has the
			 * F_OR bit set in all but the last instruction.
			 * The first match will set "skip_or", and cause
			 * the following instructions to be skipped until
			 * past the one with the F_OR bit clear.
			 */
			if (skip_or) {		/* skip this instruction */
				if ((cmd->len & F_OR) == 0)
					skip_or = 0;	/* next one is good */
				continue;
			}
			match = 0; /* set to 1 if we succeed */

			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:
				rule_nop(&match);
				break;

			case O_FORWARD_MAC:
				rule_forward_mac(cmd->opcode);
				break;

			case O_GID:
			case O_UID:
			case O_JAIL:
				rule_jail(&match, offset, proto, cmd, args, ucred_lookup, ucred_cache);
				break;

			case O_RECV:
				rule_recv(&match, cmd, m, chain, &tablearg);
				break;

			case O_XMIT:
				rule_xmit(&match, oif, cmd, chain, &tablearg);
				break;

			case O_VIA:
				rule_via(&match, oif, m, cmd, chain, &tablearg);
				break;

			case O_MACADDR2:
				rule_macaddr2(&match, args, cmd);
				break;

			case O_MAC_TYPE:
				rule_mac_type(&match, args, cmd, cmdlen, etype);
				break;

			case O_FRAG:
				rule_frag(&match, offset);
				break;

			case O_IN:
				rule_in(&match, oif);
				break;

			case O_LAYER2:
				rule_layer2(&match, args);
				break;

			case O_DIVERTED:
				rule_diverted(&match, args, cmd);
				break;

			case O_PROTO:
				rule_proto(&match, proto, cmd);
				break;

			case O_IP_SRC:
				rule_ip_src(&match, is_ipv4, cmd, &src_ip);
				break;

			case O_IP_SRC_LOOKUP:
			case O_IP_DST_LOOKUP:
				rule_ip_dst_lookup(&match, cmd, cmdlen, args, &tablearg, is_ipv4, is_ipv6, ip, &dst_ip, &src_ip, dst_port, src_port, offset, proto, ucred_lookup, ucred_cache, chain);
				break;

			case O_IP_SRC_MASK:
			case O_IP_DST_MASK:
				rule_ip_dst_mask(&match, is_ipv4, cmd, cmdlen, &dst_ip, &src_ip);
				break;

			case O_IP_SRC_ME:
				rule_ip_src_me(&match, is_ipv4, is_ipv6, &src_ip, args);
#ifdef INET6
				/* FALLTHROUGH */
			case O_IP6_SRC_ME:
				rule_ip6_src_me(&match, is_ipv6, args);
#endif
				break;

			case O_IP_DST_SET:
			case O_IP_SRC_SET:
				rule_ip_src_set(&match, is_ipv4, cmd, args);
				break;

			case O_IP_DST:
				rule_ip_dst(&match, is_ipv4, cmd, &dst_ip);
				break;

			case O_IP_DST_ME:
				rule_ip_dst_me(&match, args, is_ipv4, is_ipv6, &dst_ip);
				
#ifdef INET6
				/* FALLTHROUGH */
			case O_IP6_DST_ME:
				rule_ip6_dst_me(&match, args, is_ipv6);
#endif
				break;


			case O_IP_SRCPORT:
			case O_IP_DSTPORT:
				rule_ip_dstport(&match, proto, offset, cmd, cmdlen, dst_port, src_port);
				break;

			case O_ICMPTYPE:
				rule_icmptype(&match, offset, proto, ulp, cmd);
				break;

#ifdef INET6
			case O_ICMP6TYPE:
				rule_icmp6type(&match, offset, is_ipv6, proto, ulp, cmd);
				break;
#endif /* INET6 */

			case O_IPOPT:
				rule_ipopt(&match, is_ipv4, ip, cmd);
				break;

			case O_IPVER:
				rule_ipver(&match, is_ipv4, cmd, ip);
				break;

			case O_IPID:
			case O_IPLEN:
			case O_IPTTL:
				rule_ipttl(&match, is_ipv4, cmd, cmdlen, ip, iplen);
				break;

			case O_IPPRECEDENCE:
				rule_ipprecedence(&match, is_ipv4, cmd, ip);
				break;

			case O_IPTOS:
				rule_iptos(&match, is_ipv4, cmd, ip);
				break;

			case O_DSCP:
				rule_dscp(&match, is_ipv4, is_ipv6, cmd, ip);
				break;

			case O_TCPDATALEN:
				rule_tcpdatalen(&match, proto, offset, ulp, iplen, cmdlen, cmd, ip);
				break;

			case O_TCPFLAGS:
				rule_tcpflags(&match, proto, offset, cmd, ulp);
				break;

			case O_TCPOPTS:
				if (rule_tcpopts(&match, hlen, ulp, proto, offset, cmd, m, args))
					goto pullup_failed;
				break;

			case O_TCPSEQ:
				rule_tcpseq(&match, proto, offset, cmd, ulp);
				break;

			case O_TCPACK:
				rule_tcpack(&match, proto, offset, cmd, ulp);
				break;

			case O_TCPWIN:
				rule_tcpwin(&match, proto, offset, cmd, cmdlen, ulp);
				break;

			case O_ESTAB:
				rule_estab(&match, proto, offset, ulp);
				break;

			case O_ALTQ:
				rule_altq(&match, cmd, m, ip);
				break;

			case O_LOG:
				rule_log(&match, f, hlen, args, m, oif, offset, ip6f_mf, tablearg, ip);
				break;

			case O_PROB:
				rule_prob(&match, cmd);
				break;

			case O_VERREVPATH:
				rule_verrevpath(&match, oif, m, is_ipv6, args, &src_ip);
				break;

			case O_VERSRCREACH:
				rule_versrcreach(&match, hlen, oif, is_ipv6, args, &src_ip);
				break;

			case O_ANTISPOOF:
				rule_antispoof(&match, oif, hlen, is_ipv4, is_ipv6, &src_ip, args, m);
				break;

			case O_IPSEC:
#ifdef IPSEC
				rule_ipsec(&match, m);
#endif
				/* otherwise no match */
				break;

#ifdef INET6
			case O_IP6_SRC:
				rule_ip6_src(&match, is_ipv6, args, cmd);
				break;

			case O_IP6_DST:
				rule_ip6_dst(&match, is_ipv6, args, cmd);
				break;

			case O_IP6_SRC_MASK:
			case O_IP6_DST_MASK:
				rule_ip6_dst_mask(&match, args, cmd, cmdlen, is_ipv6);
				break;

			case O_FLOW6ID:
				rule_flow6id(&match, is_ipv6, args, cmd);
				break;

			case O_EXT_HDR:
				rule_ext_hdr(&match, is_ipv6, ext_hd, cmd);
				break;

			case O_IP6:
				rule_ip6(&match, is_ipv6);
				break;
#endif

			case O_IP4:
				rule_ip4(&match, is_ipv4);
				break;

			case O_TAG: 
				rule_tag(&match, cmd, m, tablearg);
				break;

			case O_FIB: /* try match the specified fib */
				rule_fib(&match, args, cmd);
				break;

			case O_SOCKARG:
				rule_sockarg(&match, is_ipv6, proto, &dst_ip, &src_ip, dst_port, src_port, args, &tablearg);
				break;

			case O_TAGGED:
				rule_tagged(&match, cmd, cmdlen, m, tablearg);
				break;
				
			/*
			 * 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 set l=0, done=1)
			 *
			 * Exceptions:
			 * O_COUNT and O_SKIPTO actions:
			 *   instead of terminating, we jump to the next rule
			 *   (setting l=0), or to the SKIPTO target (setting
			 *   f/f_len, cmd and l as needed), respectively.
			 *
			 * O_TAG, O_LOG and O_ALTQ action parameters:
			 *   perform some action and set match = 1;
			 *
			 * 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 (set retval,
			 *   break loops with l=0, done=1)
			 *
			 * 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
			 *   if an entry is found, or
			 *   (CHECK_STATE only) a jump to the next rule if
			 *   the entry is not found.
			 *   The result of the lookup is cached so that
			 *   further instances of these opcodes become NOPs.
			 *   The jump to the next rule is done by setting
			 *   l=0, cmdlen=0.
			 */
			case O_LIMIT:
			case O_KEEP_STATE:
				rule_keep_state(&match, f, cmd, args, tablearg, &retval, &l, &done);
				break;

			case O_PROBE_STATE:
			case O_CHECK_STATE:
				rule_check_state(&match, &dyn_dir, q, args, proto, ulp, pktlen, f, &f_pos, chain, cmd, &cmdlen, &l);
				break;

			case O_ACCEPT:
				rule_accept(&retval, &l, &done);
				break;

			case O_PIPE:
			case O_QUEUE:
				rule_queue(args, f_pos, chain, cmd, tablearg, &retval, &l, &done);
				break;

			case O_DIVERT:
			case O_TEE:
				rule_tee(&l, &done, &retval, cmd, args, f_pos, tablearg, chain);
				break;

			case O_COUNT:
				rule_count(&l, f, pktlen);
				break;

			case O_SKIPTO:
				rule_skipto(&match, &l, cmd, &cmdlen, &skip_or, &f_pos, f, pktlen, chain, tablearg);
			    continue;
			    break;	/* NOTREACHED */

			case O_CALLRETURN:
				rule_callreturn(cmd, m, f, chain, tablearg, pktlen, &skip_or, &cmdlen, &f_pos, &l);
				continue;
				break;	/* NOTREACHED */

			case O_REJECT:
				rule_reject(hlen, is_ipv4, offset, proto, ulp, m, &dst_ip, args, cmd, iplen, ip);
				/* FALLTHROUGH */
#ifdef INET6
			case O_UNREACH6:
				rule_unreach6(hlen, is_ipv6, offset, proto, icmp6_type, m, args, cmd, ip);
				/* FALLTHROUGH */
#endif
			case O_DENY:
				rule_deny(&l, &done, &retval);
				break;

			case O_FORWARD_IP:
				rule_forward_ip(args, q, f, dyn_dir, cmd, tablearg, &retval, &l, &done);
				break;

#ifdef INET6
			case O_FORWARD_IP6:
				rule_forward_ip6(args, q, f, dyn_dir, cmd, &retval, &l, &done);
				break;
#endif

			case O_NETGRAPH:
			case O_NGTEE:
				rule_ngtee(args, f_pos, chain, cmd, tablearg, &retval, &l, &done);
				break;

			case O_SETFIB:
				rule_setfib(f, pktlen, tablearg, cmd, m, args, &l);
				break;

			case O_SETDSCP:
				rule_setdscp(cmd, ip, is_ipv4, is_ipv6, tablearg, f, pktlen, &l);
				break;

			case O_NAT:
				rule_nat(args, f_pos, chain, cmd, m, tablearg, &retval, &done, &l);
				break;

			case O_REASS:
				rule_reass(f, f_pos, chain, pktlen, ip, args, m, &retval, &done, &l);
				break;

			default:
				panic("-- unknown opcode %d\n", cmd->opcode);
			} /* end of switch() on opcodes */
			/*
			 * if we get here with l=0, then match is irrelevant.
			 */

			if (cmd->len & F_NOT)
				match = !match;

			if (match) {
				if (cmd->len & F_OR)
					skip_or = 1;
			} else {
				if (!(cmd->len & F_OR)) /* not an OR block, */
					break;		/* try next rule    */
			}

		}	/* end of inner loop, scan opcodes */

		if (done)
			break;

/* next_rule:; */	/* try next rule		*/

	}		/* end of outer for, scan rules */

	if (done) {
		struct ip_fw *rule = chain->map[f_pos];
		/* Update statistics */
		IPFW_INC_RULE_COUNTER(rule, pktlen);
	} else {
		retval = IP_FW_DENY;
		printf("ipfw: ouch!, skip past end of rules, denying packet\n");
	}
	IPFW_PF_RUNLOCK(chain);
#if defined(__FreeBSD__) && !defined(USERSPACE)
	if (ucred_cache != NULL)
		crfree(ucred_cache);
#endif
	return (retval);

pullup_failed:
	if (V_fw_verbose)
		printf("ipfw: pullup failed\n");
	return (IP_FW_DENY);
}