/*
 * DF-3284 harness -- ieee80211_output raw-injection path admits frames
 * of sizeof(struct ieee80211_frame_ack) = 10 bytes and then runs
 * ieee80211_classify() which parses the mbuf as an 802.3 frame
 * ("XXX assumes an 802.3 frame", output.c:570-577/703-705).
 *
 * VERBATIM ieee80211_output.c raw path:
 *	:715  if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
 *	          senderr(EIO);                  (10 bytes ARE admitted)
 *	:723-736  dir TODS/DSTODS only gets sizeof(ieee80211_frame) (24)
 *	:757  if (ieee80211_classify(ni, m))   <-- 802.3 parse of a 10-byte
 *	                                           802.11 raw frame
 * VERBATIM ieee80211_classify (output.c:1180+):
 *	const struct ether_header *eh = mtod(m, struct ether_header *);
 *	if (eh->ether_type == htons(ETHERTYPE_PAE)) ...   reads bytes 12..13
 *	if (eh->ether_type == htons(ETHERTYPE_IP))
 *		m_copydata(m, 14 + offsetof(struct ip, ip_tos), 1, &tos);
 *
 * and m_copydata() (uipc_mbuf.c) KASSERTs the chain is long enough:
 *	KASSERT(m != NULL, ("m_copydata: length > size of mbuf chain"))
 * -> INVARIANTS kernel panics; without INVARIANTS bcopy reads garbage.
 * Both demonstrated here with the verbatim control flow and a faithful
 * m_copydata over a guard-placed "mbuf".
 */
#include "guard.h"

struct eh { uint8_t d[6], s[6]; uint16_t type; } __packed;

/* faithful m_copydata for a single-mbuf chain */
static void
m_copydata(const unsigned char *mdata, int mlen, int off, int len, void *cp)
{
	printf("    m_copydata(off=%d, len=%d) on chain of %d bytes\n",
	    off, len, mlen);
	if (off >= mlen) {
		printf("    KASSERT \"m_copydata: offset > size of mbuf chain\""
		    " FIRES (INVARIANTS kernel: panic)\n");
		/* kernel would stop here with a panic; emulate the read the
		 * non-INVARIANTS kernel would perform: */
		memcpy(cp, mdata + off, len);	/* reads past the chain */
		return;
	}
	memcpy(cp, mdata + off, len);
}

static int	/* returns 1 = would have matched IP */
classify(const unsigned char *mdata, int mlen)
{
	const struct eh *eh = (const struct eh *)mdata;

	printf("    classify: reading eh->ether_type at bytes 12..13 of a"
	    " %d-byte frame\n", mlen);
	if (eh->type == 0x0806)		/* any non-PAE/IP value; the READ is the bug */
		return 1;
	if (eh->type == 0x0800) {
		unsigned char tos;
		m_copydata(mdata, mlen, 14 + 1, 1, &tos);
		return 1;
	}
	return 0;
}

	static unsigned char *m;
	static int mlen, which;

static unsigned char *m;
static int mlen, which;

int
main(int argc, char **argv)
{
	which = (argc > 1) ? atoi(argv[1]) : 0;

	printf("DF-3284 (raw 10-byte frames parsed as 802.3 by classify)\n");
	if (which == 2) {
		mlen = 14;
		m = guard_buf(mlen);
		memset(m, 0, mlen);
		m[12] = 0x00; m[13] = 0x08;	/* LE read of 0x0800 */
		printf("  14-byte frame, ether_type==IP: m_copydata(off=15,1)\n");
	} else if (which == 1) {
		mlen = 10;
		m = guard_buf(mlen);
		memset(m, 0, mlen);
		printf("  10-byte frame: classify reads eh->ether_type (12..13)\n");
	} else {
		mlen = 40;
		m = guard_buf(mlen);
		memset(m, 0, mlen);
		printf("  control: 40-byte frame (well-formed 802.3)\n");
	}
	fflush(stdout);
#ifdef FIXED
	/* DF-3284 fix: raw 802.3 classification needs an 802.3 header */
	if (mlen < sizeof(struct eh) + 1) {	/* IP frames need TOS@15 */
		printf("FIXED: raw frame %d bytes < 14 (802.3 header),"
		    " rejected before classify\n", mlen);
		return 0;
	}
#endif
	{
		int r = classify(m, mlen);
		printf("  ran (in bounds), r=%d\n", r);
	}
	return 0;
}
