/*
 * DF-3511 diag: dump what the kernel actually sends back for MKPEER
 * messages with loaded vs unloaded type names, plus kldstat deltas.
 */
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netgraph/ng_message.h>
#include <netgraph/socket/ng_socket.h>

#ifndef NG7_VERSION
#define NG7_VERSION	8
#endif
#ifndef NGF_RESP
#define NGF_RESP	0x00000002	/* message is a response */
#endif

struct sa_ng_big {
	struct sockaddr_ng	sg;
	char			pad[64];
};

static void
mkpeer_msg(char *buf, size_t bufsz, const char *type, u_int32_t token)
{
	struct ng_mesg *m = (struct ng_mesg *)buf;
	struct ngm_mkpeer *mkp;

	memset(buf, 0, bufsz);
	m->header.version = NG7_VERSION;
	m->header.typecookie = NGM_GENERIC_COOKIE;
	m->header.cmd = NGM_MKPEER;
	m->header.arglen = sizeof(struct ngm_mkpeer);
	m->header.token = token;
	mkp = (struct ngm_mkpeer *)m->data;
	strlcpy(mkp->type, type, sizeof(mkp->type));
	strlcpy(mkp->ourhook, "o", sizeof(mkp->ourhook));
	strlcpy(mkp->peerhook, "p", sizeof(mkp->peerhook));
}

static void
dump_responses(int cs, const char *tag)
{
	char rb[65536];
	struct sockaddr_ng from;
	socklen_t fromlen;
	int n, shown = 0;

	for (;;) {
		fromlen = sizeof(from);
		n = recvfrom(cs, rb, sizeof(rb), MSG_DONTWAIT,
		    (struct sockaddr *)&from, &fromlen);
		if (n <= 0)
			break;
		if (shown < 3) {
			struct ng_mesg *m = (struct ng_mesg *)rb;
			if (n >= (int)sizeof(struct ng_mesg)) {
				int dlen = n - sizeof(struct ng_mesg);
				char data[64];
				memset(data, 0, sizeof(data));
				if (dlen > 0) {
					memcpy(data, m->data,
					    (size_t)dlen > 63 ? 63 : (size_t)dlen);
				}
				printf("[%s] resp n=%d ver=%u cookie=%u cmd=%u "
				    "flags=%u token=%x arglen=%u data='%.40s'\n",
				    tag, n, m->header.version,
				    m->header.typecookie, m->header.cmd,
				    m->header.flags, m->header.token,
				    m->header.arglen, data);
			} else {
				printf("[%s] resp n=%d (short)\n", tag, n);
			}
			shown++;
		}
	}
}

int
main(void)
{
	char buf[sizeof(struct ng_mesg) + sizeof(struct ngm_mkpeer)];
	struct sa_ng_big sb;
	int cs, i;

	if (geteuid() != 0) { fprintf(stderr, "root only\n"); return (1); }
	cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL);
	if (cs < 0) { perror("socket"); return (1); }

	/* try a sequence of type names, one send each, dump responses */
	{
		const char *types[] = { "socket", "echo", "df3511x",
					"cisco", "huey", NULL };
		for (i = 0; types[i]; i++) {
			mkpeer_msg(buf, sizeof(buf), types[i], 0x1000 + i);
			memset(&sb, 0, sizeof(sb));
			sb.sg.sg_len = 4;
			sb.sg.sg_family = AF_NETGRAPH;
			sb.sg.sg_data[0] = '.';
			sb.sg.sg_data[1] = '\0';
			errno = 0;
			if (sendto(cs, buf, sizeof(buf), 0,
			    (struct sockaddr *)&sb.sg, 4) < 0)
				printf("type=%-9s sendto FAILED errno=%d(%s)\n",
				    types[i], errno, strerror(errno));
			else
				printf("type=%-9s sendto ok\n", types[i]);
			usleep(50000);	/* give cpu0 a moment */
			dump_responses(cs, types[i]);
		}
	}
	close(cs);
	return (0);
}
