/*
 * DF-3410 -- TUNSIFMODE raw-value flags injection.
 *
 * tunifioctl() case TUNSIFMODE (sys/net/tun/if_tun.c:731-738):
 *
 *	switch (*(int *)data & ~IFF_MULTICAST) {	 <-- validates MASKED value
 *	case IFF_POINTOPOINT:
 *	case IFF_BROADCAST:
 *		ifp->if_flags &= ~(IFF_BROADCAST | IFF_POINTOPOINT);
 *		ifp->if_flags |= *(int *)data;		 <-- injects RAW value
 *		break;
 *	default:
 *		return (EINVAL);
 *	}
 *
 * The masked switch only clears IFF_MULTICAST, so the switch equality
 * admits exactly {IFF_BROADCAST, IFF_BROADCAST|IFF_MULTICAST} (and the
 * PTP twins).  The injected raw word can therefore carry the
 * IFF_MULTICAST bit into if_flags even though the validated mode is
 * plain IFF_BROADCAST.
 *
 * Honest impact note (verified live): IFF_MULTICAST is in
 * IFF_CANTCHANGE (if.h:119-124) so SIOCSIFFLAGS cannot clear it, and
 * tun(4) is created with IFF_MULTICAST already set (if_tun.c:232) --
 * so on stock the injected bit re-asserts an already-set flag and no
 * *configuration* delta is observable.  The raw-word OR itself is
 * proven by the acceptance of the extra-bit value (a masked-value
 * implementation would behave identically) plus the source line.
 *
 * root only (needs an open /dev/tun fd).  Build: cc -O -o tunsifmode tunsifmode.c
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <sys/ioccom.h>
#include <net/if.h>
#include <sys/sockio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define	TUNSIFMODE	_IOW('t', 94, int)

static void
showflags(const char *tag, int flags)
{
	printf("%-32s 0x%04x  [%s%s%s%s%s]\n", tag, flags & 0xffff,
	    (flags & IFF_UP) ? "UP " : "",
	    (flags & IFF_BROADCAST) ? "BCAST " : "",
	    (flags & IFF_POINTOPOINT) ? "PTP " : "",
	    (flags & IFF_MULTICAST) ? "MCAST " : "",
	    (flags & IFF_RUNNING) ? "RUN " : "");
}

int
main(void)
{
	struct ifreq ifr;
	int fd, s, val, rc = 0;

	if (geteuid() != 0) {
		fprintf(stderr, "must run as root\n");
		return (1);
	}
	fd = open("/dev/tun", O_RDWR);	/* autoclone: creates tun0 */
	if (fd < 0) { perror("open /dev/tun"); return (1); }
	s = socket(AF_INET, SOCK_DGRAM, 0);
	memset(&ifr, 0, sizeof(ifr));
	strcpy(ifr.ifr_name, "tun0");
	if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { perror("SIOCGIFFLAGS"); return (1); }
	showflags("flags at open:", ifr.ifr_flags);

	/* A. masked/validated value: plain IFF_BROADCAST -> must succeed */
	val = IFF_BROADCAST;
	if (ioctl(fd, TUNSIFMODE, &val) == 0) {
		ioctl(s, SIOCGIFFLAGS, &ifr);
		showflags("after TUNSIFMODE(BCAST):", ifr.ifr_flags);
	} else { printf("TUNSIFMODE(BCAST): %s\n", strerror(errno)); rc = 1; }

	/* B. raw word carrying the extra IFF_MULTICAST bit:
	 *    masked switch still sees IFF_BROADCAST -> accepted, and the
	 *    RAW word (with the multicast bit) is ORed into if_flags. */
	val = (int)(IFF_BROADCAST | IFF_MULTICAST);
	if (ioctl(fd, TUNSIFMODE, &val) == 0) {
		ioctl(s, SIOCGIFFLAGS, &ifr);
		showflags("after TUNSIFMODE(BCAST|MCAST):", ifr.ifr_flags);
		printf("\nraw word with extraneous bit ACCEPTED "
		    "(a masked-value inject would be indistinguishable here;\n"
		    "the raw OR is at if_tun.c:737 ifp->if_flags |= *(int *)data)\n");
		printf("VERDICT: REPRODUCED (mechanism; injectable delta is "
		    "limited to the IFF_MULTICAST bit, which tun(4) always "
		    "has set -- no stock config delta)\n");
	} else {
		printf("TUNSIFMODE(BCAST|MCAST): %s\n", strerror(errno));
		rc = 1;
	}

	/* C. sanity: anything beyond the multicast bit still EINVALs */
	val = (int)(IFF_BROADCAST | 0x100);
	printf("TUNSIFMODE(BCAST|0x100) -> %s (switch equality holds)\n",
	    ioctl(fd, TUNSIFMODE, &val) == 0 ? "ACCEPTED" : strerror(errno));
	return (rc);
}
