⬢ DragonFlyBSD Kernel Audit
DF-3545 / sampledemo.c
← back to finding ↓ download raw
/*
 * DF-3545 demo: ng7 ng_sample downstream_hook.dlci is never initialized
 * to -1 (constructor M_ZERO leaves it 0), so data received on the
 * "downstream" hook takes the DLCI branch (dlci != -1) and is reflected
 * back out the same hook (sys/netgraph7/ng_sample.c:339-350).  The
 * decode branch (:351-376) is unreachable.
 *
 * Graph: control node C --(C.sd <-> sample:downstream)-- sample node S.
 * Write "PING" on the data socket via hook "sd"; if the bug is live the
 * frame comes straight back and is readable on the same socket, and
 * GET_STATUS shows packets_out (not packets_in) incremented.
 *
 * Uses the netgraph7 ng_mesg layout (arglen u32 @4, cmd @8, flags @12,
 * token @16, typecookie @20, cmdstr[32] @24, data @56).
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#ifndef AF_NETGRAPH
#define AF_NETGRAPH 32
#endif
#define NG_AF AF_NETGRAPH
#define NG_PROTO_CONTROL 2
#define NG_PROTO_DATA 1

#define NG_VERSION 8
#define NG_GENERIC_COOKIE 1137070366u
#define NGM_MKPEER 2
#define NGM_NAME 4

#define NGM_XXX_COOKIE 915491374u
#define NGM_XXX_GET_STATUS 2

struct ng7_mesg {
	unsigned char version;
	unsigned char spare;
	unsigned short spare2;
	unsigned int arglen;
	unsigned int cmd;
	unsigned int flags;
	unsigned int token;
	unsigned int typecookie;
	unsigned char cmdstr[32];
	char data[2048];
};
#define NG7_HDRSZ 56
struct sockaddr_ng_t {
	unsigned char sg_len;
	unsigned char sg_family;
	char sg_data[64];
};
struct ngm_mkpeer_t { char type[32]; char ourhook[32]; char peerhook[32]; };
struct ngm_name_t { char name[32]; };

static void
mkaddr(struct sockaddr_ng_t *sa, const char *path)
{
	size_t n = strlen(path) + 1;
	memset(sa, 0, sizeof(*sa));
	sa->sg_len = 2 + n;
	sa->sg_family = NG_AF;
	memcpy(sa->sg_data, path, n);
}

static int
send_ctrl(int cs, const char *path, unsigned int cookie, unsigned int cmd,
    const void *arg, size_t arglen)
{
	struct sockaddr_ng_t sa;
	struct ng7_mesg msg;
	ssize_t n;

	memset(&msg, 0, sizeof(msg));
	msg.version = NG_VERSION;
	msg.arglen = arglen;
	msg.typecookie = cookie;
	msg.cmd = cmd;
	msg.token = 0x666;
	if (arglen)
		memcpy(msg.data, arg, arglen);
	mkaddr(&sa, path);
	n = sendto(cs, &msg, NG7_HDRSZ + arglen, 0,
	    (struct sockaddr *)&sa, sa.sg_len);
	return (n < 0) ? errno : 0;
}

int
main(void)
{
	struct ngm_name_t nm;
	struct ngm_mkpeer_t mk;
	struct sockaddr_ng_t sa;
	struct ng7_mesg msg;
	char namebuf[32], abspath[64], buf[256];
	int cs, ds, i, n, e, got = 0;
	unsigned int pin = 0, pout = 0;

	snprintf(namebuf, sizeof(namebuf), "q%d", getpid());
	snprintf(abspath, sizeof(abspath), "%s:", namebuf);

	cs = socket(NG_AF, SOCK_DGRAM, NG_PROTO_CONTROL);
	if (cs < 0) { perror("control socket"); exit(1); }
	ds = socket(NG_AF, SOCK_DGRAM, NG_PROTO_DATA);
	if (ds < 0) { perror("data socket"); exit(1); }

	memset(&nm, 0, sizeof(nm));
	strcpy(nm.name, namebuf);
	e = send_ctrl(cs, ".", NG_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm));
	if (e) { fprintf(stderr, "NGM_NAME: %s\n", strerror(e)); exit(1); }
	usleep(300000);

	mkaddr(&sa, abspath);
	if (connect(ds, (struct sockaddr *)&sa, sa.sg_len) < 0) {
		perror("data connect");
		exit(1);
	}

	memset(&mk, 0, sizeof(mk));
	strcpy(mk.type, "sample");
	strcpy(mk.ourhook, "sd");
	strcpy(mk.peerhook, "downstream");
	e = send_ctrl(cs, ".", NG_GENERIC_COOKIE, NGM_MKPEER, &mk, sizeof(mk));
	if (e) { fprintf(stderr, "mkpeer: %s\n", strerror(e)); exit(1); }
	usleep(300000);

	/* push data into the sample node's downstream hook */
	mkaddr(&sa, "sd");
	strcpy(buf, "PING\n");
	for (i = 0; i < 20; i++) {
		n = sendto(ds, buf, 5, 0, (struct sockaddr *)&sa, sa.sg_len);
		if (n >= 0)
			break;
		usleep(100000);
	}
	if (n < 0) {
		fprintf(stderr, "sendto failed: %s\n", strerror(errno));
		exit(1);
	}

	/* see if the frame comes back */
	memset(buf, 0, sizeof(buf));
	for (i = 0; i < 40 && !got; i++) {
		n = recv(ds, buf, sizeof(buf) - 1, MSG_DONTWAIT);
		if (n > 0)
			got = 1;
		else
			usleep(100000);
	}

	/* query GET_STATUS (no args) addressed to the sample node */
	memset(&msg, 0, sizeof(msg));
	msg.version = NG_VERSION;
	msg.arglen = 0;
	msg.typecookie = NGM_XXX_COOKIE;
	msg.cmd = NGM_XXX_GET_STATUS;
	msg.token = 0x777;
	mkaddr(&sa, "sd");
	sendto(cs, &msg, NG7_HDRSZ, 0, (struct sockaddr *)&sa, sa.sg_len);
	usleep(300000);
	n = recv(cs, &msg, sizeof(msg), MSG_DONTWAIT);
	if (n >= NG7_HDRSZ + 8) {
		memcpy(&pin, msg.data, 4);
		memcpy(&pout, msg.data + 4, 4);
	}

	if (got)
		printf("RESULT: REFLECTED - received %d bytes back: '%s'\n",
		    n, buf);
	else
		printf("RESULT: no data reflected back (decode branch taken)\n");
	printf("GET_STATUS: packets_in=%u packets_out=%u "
	    "(expect in=0,out>=1 if misrouted as DLCI traffic)\n", pin, pout);
	return (got ? 0 : 2);
}