/*
 * (c) copyright 2001 by wojtek kaniewski <wojtekka@irc.pl>
 *
 * unsupported stuff. if it doesn't work, DO NOT mail me. YMMV.
 */

#define __KERNEL__

#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <sys/syscall.h>

static inline _syscall1(int, get_kernel_syms, struct kernel_sym *, table);

struct kernel_sym *symbols;
int symbols_count;

int kmem_fd;

int get_ioctl_flags(char *name)
{
	int sock, i;
	struct ifconf ifc;
	struct ifreq *ifr;
	char buf[1024];

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

	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = buf;

	if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
		perror("ioctl");
		close(sock);
		return 0;
	}

	for (i = 0, ifr = ifc.ifc_req; i < (ifc.ifc_len / sizeof(ifc)); ifr++, i++) {
		if (ioctl(sock, SIOCGIFFLAGS, ifr) == -1)
			continue;
		if (!strcmp(ifr->ifr_name, name)) {
			close(sock);
			return ifr->ifr_flags;
		}
	}

	

	return 0;
}

int get_symbols()
{
	if ((symbols_count = get_kernel_syms(NULL)) == -1)
		return -1;
	if (!(symbols = (void*) malloc(symbols_count * sizeof(struct kernel_sym))))
		return -1;
	get_kernel_syms(symbols);

	return 0;
}

void free_symbols()
{
	free(symbols);
}

unsigned long symbol_offset(char *name)
{
	int i;

	for (i = 0; i < symbols_count; i++)
		if (!strcmp(symbols[i].name, name))
			return symbols[i].value;

	return -1;
}

int kmem_read(unsigned long offset, void *ptr, int size)
{
	lseek(kmem_fd, offset, 0);
	return read(kmem_fd, ptr, size);
}

char *kmem_string(unsigned long offset)
{
	static char buf[100];
	char ch = 1;
	int i = 0;

	while (ch) {
		lseek(kmem_fd, offset+i, 0);
		read(kmem_fd, &ch, 1);
		buf[i++] = ch;
	}
	
	return buf;
}

void inspect_promisc()
{
	struct device d;
	unsigned long foo;

	foo = symbol_offset("dev_base");
	kmem_read(foo, &foo, sizeof(foo));

	while (foo) {
		char *name;
		int fl;

		if ((kmem_read(foo, &d, sizeof(d))) < sizeof(d)) {
			perror("read");
			return;
		}

		name = kmem_string((unsigned long) d.name);
		fl = get_ioctl_flags(name);

		printf("device \"%s\":\n  kmem.promiscuity = %d (%s)\n  kmem.flags  = 0x%.4x (%s)\n  kmem.gflags = 0x%.4x (%s)\n  ioctl.flags = 0x%.4x (%s)\n", name, d.promiscuity, (d.promiscuity) ? "yes" : "no", d.flags, (d.flags & IFF_PROMISC) ? "yes" : "no", d.gflags, (d.gflags & IFF_PROMISC) ? "yes" : "no", fl, (fl & IFF_PROMISC) ? "yes" : "no");

		foo = (unsigned long) d.next;
	}
}

int main()
{
	if ((kmem_fd = open("/dev/kmem", 0)) == -1) {
		perror("open");
		return 1;
	}

	get_symbols();
	
	inspect_promisc();

	free_symbols();

	close(kmem_fd);
}

