ipt_do_table(struct sk_buff *skb,
	     unsigned int hook,
	     const struct net_device *in,
	     const struct net_device *out,
	     struct xt_table *table)
{
	static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
	const struct iphdr *ip;
	/* Initializing verdict to NF_DROP keeps gcc happy. */
	unsigned int verdict = NF_DROP;
	const char *indev, *outdev;
	const void *table_base;
	struct ipt_entry *e, **jumpstack;
	unsigned int *stackptr, origptr, cpu;
	const struct xt_table_info *private;
	struct xt_action_param acpar;

	/* Initialization */
	ip = ip_hdr(skb);
	indev = in ? in->name : nulldevname;
	outdev = out ? out->name : nulldevname;
	/* We handle fragments by dealing with the first fragment as
	 * if it was a normal packet.  All other fragments are treated
	 * normally, except that they will NEVER match rules that ask
	 * things we don't know, ie. tcp syn flag or ports).  If the
	 * rule is also a fragment-specific rule, non-fragments won't
	 * match it. */
	acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
	acpar.thoff   = ip_hdrlen(skb);
	acpar.hotdrop = false;
	acpar.in      = in;
	acpar.out     = out;
	acpar.family  = NFPROTO_IPV4;
	acpar.hooknum = hook;

	IP_NF_ASSERT(table->valid_hooks & (1 << hook));
	xt_info_rdlock_bh();
	private = table->private;
	cpu        = smp_processor_id();
	table_base = private->entries[cpu];
	jumpstack  = (struct ipt_entry **)private->jumpstack[cpu];
	stackptr   = per_cpu_ptr(private->stackptr, cpu);
	origptr    = *stackptr;

	e = get_entry(table_base, private->hook_entry[hook]);

	pr_debug("Entering %s(hook %u); sp at %u (UF %p)\n",
		 table->name, hook, origptr,
		 get_entry(table_base, private->underflow[hook]));

	do {
		const struct xt_entry_target *t;
		const struct xt_entry_match *ematch;

		IP_NF_ASSERT(e);
		if (!ip_packet_match(ip, indev, outdev,
		    &e->ip, acpar.fragoff)) {
 no_match:
			e = ipt_next_entry(e);
			continue;
		}

		xt_ematch_foreach(ematch, e) {
			acpar.match     = ematch->u.kernel.match;
			acpar.matchinfo = ematch->data;
			if (!acpar.match->match(skb, &acpar))
				goto no_match;
		}

		ADD_COUNTER(e->counters, skb->len, 1);

		t = ipt_get_target(e);
		IP_NF_ASSERT(t->u.kernel.target);

#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
		/* The packet is traced: log it */
		if (unlikely(skb->nf_trace))
			trace_packet(skb, hook, in, out,
				     table->name, private, e);
#endif
		/* Standard target? */
		if (!t->u.kernel.target->target) {
			int v;

			v = ((struct xt_standard_target *)t)->verdict;
			if (v < 0) {
				/* Pop from stack? */
				if (v != XT_RETURN) {
					verdict = (unsigned)(-v) - 1;
					break;
				}
				if (*stackptr == 0) {
					e = get_entry(table_base,
					    private->underflow[hook]);
					pr_debug("Underflow (this is normal) "
						 "to %p\n", e);
				} else {
					e = jumpstack[--*stackptr];
					pr_debug("Pulled %p out from pos %u\n",
						 e, *stackptr);
					e = ipt_next_entry(e);
				}
				continue;
			}
			if (table_base + v != ipt_next_entry(e) &&
			    !(e->ip.flags & IPT_F_GOTO)) {
				if (*stackptr >= private->stacksize) {
					verdict = NF_DROP;
					break;
				}
				jumpstack[(*stackptr)++] = e;
				pr_debug("Pushed %p into pos %u\n",
					 e, *stackptr - 1);
			}

			e = get_entry(table_base, v);
			continue;
		}

		acpar.target   = t->u.kernel.target;
		acpar.targinfo = t->data;

		verdict = t->u.kernel.target->target(skb, &acpar);
		/* Target might have changed stuff. */
		ip = ip_hdr(skb);
		if (verdict == XT_CONTINUE)
			e = ipt_next_entry(e);
		else
			/* Verdict */
			break;
	} while (!acpar.hotdrop);
	xt_info_rdunlock_bh();
	pr_debug("Exiting %s; resetting sp from %u to %u\n",
		 __func__, *stackptr, origptr);
	*stackptr = origptr;
#ifdef DEBUG_ALLOW_ALL
	return NF_ACCEPT;
#else
	if (acpar.hotdrop)
		return NF_DROP;
	else return verdict;
#endif
}