#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <linux/netdevice.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/route.h>
#include <linux/netlink.h>

void print_route(struct in_rtmsg *nrt)
{
	fprintf(stderr, "%08x/%d via %08x/%s tos %02x class %d metric %d flags %08x win %d mtu %d rtt %d\n",
		ntohl(nrt->rtmsg_prefix.s_addr),
		nrt->rtmsg_prefixlen,
		ntohl(nrt->rtmsg_gateway.s_addr),
		nrt->rtmsg_device,
		nrt->rtmsg_tos,
		nrt->rtmsg_class,
		nrt->rtmsg_metric,
		nrt->rtmsg_flags,
		nrt->rtmsg_window, nrt->rtmsg_mtu, nrt->rtmsg_rtt);
}

void print_dev(struct in_ifmsg *nrt)
{
	fprintf(stderr, "%08x/%d brd %08x flags %08x mtu %d type %d\n",
		ntohl(nrt->ifmsg_prefix.s_addr),
		nrt->ifmsg_prefixlen,
		ntohl(nrt->ifmsg_brd.s_addr),
		nrt->ifmsg_flags,
		nrt->ifmsg_mtu, nrt->ifmsg_lladdr.sa_family);
}


int main(int argc, char **argv)
{
	int status;
	int fd;
	char *buf[4096];
	struct nlmsghdr *h;
	struct in_rtmsg *nrt;
	struct in_ifmsg *nif;
	int		*p;
	int	tim;
	int i;

	openlog ("rtmon", LOG_PID | LOG_CONS, LOG_DAEMON);

	fd = open("/dev/route", O_RDONLY);
	if (fd < 0) {
		syslog(LOG_CRIT, "cannot open /dev/route: %m\n");
		exit(-1);
	}

	while (1) {
		status = read(fd, buf, sizeof(buf));
		if (status < 0) {
			if (errno == EINTR)
				continue;
			syslog(LOG_CRIT, "cannot read /dev/route: %m\n");
			exit(-1);
		}
		if (status == 0) {
			syslog(LOG_CRIT, "EOF /dev/route\n");
			exit(-1);
		}
		time(&tim);
		fprintf(stderr, "==== Message %s", ctime(&tim));

		for (h = (struct nlmsghdr*)buf, i=1;
		     status >= sizeof(*h);
		     i++) {
			unsigned long type= h->nlmsg_type;
			unsigned long pid=h->nlmsg_pid;
			int	      l=    h->nlmsg_len-sizeof(*h);
			unsigned long seq=  h->nlmsg_seq;

			if (l<0) {
				syslog(LOG_CRIT, "malformed message: len=%d\n", l);
				exit(-1);
			}

			nrt=(struct in_rtmsg*)h->nlmsg_data;
			nif=(struct in_ifmsg*)h->nlmsg_data;
			p=(int*)h->nlmsg_data;

			status -= NLMSG_ALIGN(h->nlmsg_len);
			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(h->nlmsg_len));

			fprintf(stderr, "%d(%d.%d):\t", i, pid, seq);

			switch (type) {
			case RTMSG_NEWDEVICE:
				if (l < sizeof(*nif))
					fprintf(stderr, "UP truncated\n");
				else {
					fprintf(stderr, "UP %s\n", nif->ifmsg_device);
					print_dev(nif);
				}
				break;
			case RTMSG_DELDEVICE:
				if (l < sizeof(*nif))
					fprintf(stderr, "DOWN truncated\n");
				else {
					fprintf(stderr, "DOWN %s\n", nif->ifmsg_device);
					print_dev(nif);
				}
				break;
			case RTMSG_NEWROUTE:
				if (l < sizeof(*nrt))
					fprintf(stderr, "NEW ROUTE truncated\n");
				else
				{
					fprintf(stderr, "NEW ROUTE\n");
					print_route(nrt);
				}
				break;
			case RTMSG_DELROUTE:
				if (l < sizeof(*nrt))
					fprintf(stderr, "DEL ROUTE truncated\n");
				else
				{
					fprintf(stderr, "DEL ROUTE\n");
					print_route(nrt);
				}
				break;
			case RTMSG_ACK:
				if (l < sizeof(int))
				    fprintf(stderr, "ERROR truncated\n");
				else
				    fprintf(stderr, "ERROR %d\n", p[0]);
				break;
			case RTMSG_OVERRUN:
				if (l < 2 * sizeof(int))
				    fprintf(stderr, "OVERRUN truncated\n");
				else
				    fprintf(stderr, "OVERRUN %d %d\n", p[0], p[1]);
				break;
			default:
				fprintf (stderr, "INVALID %08x\n", type);
				break;
			}
		}
		fprintf(stderr, "\n");
	}

	return 0;
}
