/*
 * DF-0753 live trigger attempt.
 *
 * Injects an MPLS frame via bpf BIOCSFEEDBACK on vtnet0 to exercise
 * mpls_input -> mpls_forward -> mpls_output(PUSH) on a live kernel
 * with mpls.ko loaded.
 *
 * Per source analysis (sys/netproto/mpls/mpls_output.c:157 M_PREPEND +
 * sys/kern/uipc_mbuf.c:1500 m_prepend + mbuf.h:446 M_LEADINGSPACE):
 * ether_input always leaves ETHER_HDR_LEN (14) bytes of leading space;
 * MPLS_MAXLOPS=3 * 4 = 12 < 14, so PUSH never calls m_prepend on
 * standard ethernet-received frames.  This trigger confirms that
 * EMPIRICALLY (no panic on standard frames) — the deterministic proof
 * is in mpls_stale_harness.c.
 *
 * Usage: ./mpls_trigger <ifname>   (default vtnet0)
 * Requires root to open /dev/bpf.
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>

#ifndef ETHERTYPE_MPLS
#define ETHERTYPE_MPLS 0x8847
#endif

int
main(int argc, char **argv)
{
	const char *ifname = (argc > 1) ? argv[1] : "vtnet0";
	char dev[16];
	int fd, i, n;
	struct ifreq ifr;
	u_int dlt;
	u_int imm = 1;

	for (i = 0; i < 16; i++) {
		snprintf(dev, sizeof(dev), "/dev/bpf%d", i);
		fd = open(dev, O_RDWR);
		if (fd >= 0)
			break;
	}
	if (fd < 0)
		err(1, "open bpf");

	memset(&ifr, 0, sizeof(ifr));
	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(fd, BIOCSETIF, &ifr) < 0)
		err(1, "BIOCSETIF %s", ifname);
	if (ioctl(fd, BIOCIMMEDIATE, &imm) < 0)
		err(1, "BIOCIMMEDIATE");
	if (ioctl(fd, BIOCSFEEDBACK, &imm) < 0)
		err(1, "BIOCSFEEDBACK");
	imm = 1;
	if (ioctl(fd, BIOCSHDRCMPLT, &imm) < 0)
		err(1, "BIOCSHDRCMPLT");
	if (ioctl(fd, BIOCGDLT, &dlt) < 0)
		err(1, "BIOCGDLT");
	if (dlt != DLT_EN10MB)
		errx(1, "unexpected datalink type %u", dlt);

	/*
	 * Frame: dst=broadcast, src=vtnet0, ethertype=0x8847,
	 * then a single MPLS label (label=100, S=1, TTL=64) + 20 bytes
	 * payload.  This is a bottom-of-stack MPLS frame that mpls_input
	 * will try to forward via mpls_forward -> mpls_output.
	 */
	unsigned char frame[64];
	memset(frame, 0, sizeof(frame));
	memset(frame + 0, 0xff, 6);                /* dst = broadcast */
	frame[6] = 0x52; frame[7] = 0x54; frame[8] = 0x00;  /* src */
	frame[9] = 0x12; frame[10] = 0x34; frame[11] = 0x56;
	frame[12] = 0x88; frame[13] = 0x47;        /* ethertype MPLS */
	/* MPLS shim: label=100 (0x064), exp=0, S=1, TTL=64 */
	/* big-endian: (100<<12)|(1<<8)|64 = 0x000641 */
	frame[14] = 0x00; frame[15] = 0x06;
	frame[16] = 0x41; frame[17] = 0x40;

	printf("DF-0753: injecting 1 MPLS frame (%zu bytes) label=100 S=1 TTL=64 on %s\n",
	    sizeof(frame), ifname);
	fflush(stdout);

	n = write(fd, frame, sizeof(frame));
	if (n < 0)
		err(1, "write bpf");
	printf("DF-0753: write returned %d\n", n);
	printf("DF-0753: if mpls_forward + PUSH route is configured, mpls_output runs.\n");
	printf("DF-0753: with 14-byte ether headroom >= max 12-byte PUSH, NO realloc expected.\n");
	fflush(stdout);

	close(fd);
	return 0;
}
