/*
 * DF-3347 harness -- lagg_lacp_attach ignores lacp_port_create failure
 * (if_lagg.c:2081-2082):
 *
 *	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
 *		lacp_port_create(lp);		 <-- return value dropped
 *
 * A failing lacp_port_create (if_addmulti failure or its
 * kmalloc(M_NOWAIT)) leaves ports in sc_ports with lp_psc == NULL;
 * every later LACP operation that walks sc_ports and derefs
 * LACP_PORT(lp) NULL-derefs.  Fault-injection harness.
 */
#include "guard.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct lagg_port {
	const char	*ifname;
	void		*lp_psc;
};
struct lacp_port { int marker; };

static int create_fail;			/* fault knob */

/* verbatim lacp_port_create shape (lacp.c:530-565) */
static int
lacp_port_create(struct lagg_port *lp)
{
	void *p;
	if (create_fail)			/* if_addmulti/kmalloc failure */
		return 12;			/* ENOMEM */
	p = calloc(1, sizeof(struct lacp_port));
	((struct lacp_port *)p)->marker = 1;
	lp->lp_psc = p;
	return 0;
}

/* verbatim lagg_lacp_attach loop (if_lagg.c:2081-2082) */
static void
lagg_lacp_attach(struct lagg_port *ports, int n)
{
	int i;
	for (i = 0; i < n; i++)
		lacp_port_create(&ports[i]);	/* failure IGNORED */
}

/* later LACP operation walking sc_ports (e.g. lacp_linkstate / tick) */
static void
lacp_tick(struct lagg_port *ports, int n)
{
	int i;
	for (i = 0; i < n; i++)
		((struct lacp_port *)ports[i].lp_psc)->marker++;	/* deref */
}

	static struct lagg_port ports[2];

static struct lagg_port ports[2];

int
main(int argc, char **argv)
{
	int poison = (argc > 1 && argv[1][0] == 'p');

	printf("DF-3347 (lacp_port_create failure ignored, if_lagg.c:2081)\n");
	memset(ports, 0, sizeof(ports));
	ports[0].ifname = "tap0";
	ports[1].ifname = "tap1";
	create_fail = 0;
	lacp_port_create(&ports[0]);		/* port 1 ok */
	create_fail = poison;
	lacp_port_create(&ports[1]);		/* port 2 fails, IGNORED */
	printf("  port1 lp_psc=%p port2 lp_psc=%p (failure silently ignored)\n",
	    ports[0].lp_psc, ports[1].lp_psc);
	if (!poison) {
		create_fail = 0;
		lacp_port_create(&ports[1]);
		lacp_tick(ports, 2);
		printf("  control: both ports attached, tick ok\n");
		return 0;
	}
	printf("  later LACP op derefs NULL lp_psc:\n");
	fflush(stdout);
#ifdef FIXED
	/* DF-3347 fix: abort the attach loop on the first failure */
	{
		int bad = 0, k;
		for (k = 0; k < 2; k++)
			if (ports[k].lp_psc == NULL)
				bad++;
		if (bad) {
			printf("FIXED: %d port(s) unattached, LACP ops"
			    " suspended\n", bad);
			return 0;
		}
	}
#endif
	lacp_tick(ports, 2);			/* NULL deref -> SEGV */
	printf("  ran?!\n");
	return 0;
}
