1/*
2 * rtmon.c		RTnetlink listener.
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <sys/socket.h>
19#include <sys/time.h>
20#include <netinet/in.h>
21#include <string.h>
22
23#include "SNAPSHOT.h"
24
25#include "utils.h"
26#include "libnetlink.h"
27
28int resolve_hosts = 0;
29static int init_phase = 1;
30
31static void write_stamp(FILE *fp)
32{
33	char buf[128];
34	struct nlmsghdr *n1 = (void*)buf;
35	struct timeval tv;
36
37	n1->nlmsg_type = 15;
38	n1->nlmsg_flags = 0;
39	n1->nlmsg_seq = 0;
40	n1->nlmsg_pid = 0;
41	n1->nlmsg_len = NLMSG_LENGTH(4*2);
42	gettimeofday(&tv, NULL);
43	((__u32*)NLMSG_DATA(n1))[0] = tv.tv_sec;
44	((__u32*)NLMSG_DATA(n1))[1] = tv.tv_usec;
45	fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp);
46}
47
48static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
49		    void *arg)
50{
51	FILE *fp = (FILE*)arg;
52	if (!init_phase)
53		write_stamp(fp);
54	fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp);
55	fflush(fp);
56	return 0;
57}
58
59void usage(void)
60{
61	fprintf(stderr, "Usage: rtmon file FILE [ all | LISTofOBJECTS]\n");
62	fprintf(stderr, "LISTofOBJECTS := [ link ] [ address ] [ route ]\n");
63	exit(-1);
64}
65
66int
67main(int argc, char **argv)
68{
69	FILE *fp;
70	struct rtnl_handle rth;
71	int family = AF_UNSPEC;
72	unsigned groups = ~0U;
73	int llink = 0;
74	int laddr = 0;
75	int lroute = 0;
76	char *file = NULL;
77
78	while (argc > 1) {
79		if (matches(argv[1], "-family") == 0) {
80			argc--;
81			argv++;
82			if (argc <= 1)
83				usage();
84			if (strcmp(argv[1], "inet") == 0)
85				family = AF_INET;
86			else if (strcmp(argv[1], "inet6") == 0)
87				family = AF_INET6;
88			else if (strcmp(argv[1], "link") == 0)
89				family = AF_INET6;
90			else if (strcmp(argv[1], "help") == 0)
91				usage();
92			else {
93				fprintf(stderr, "Protocol ID \"%s\" is unknown, try \"rtmon help\".\n", argv[1]);
94				exit(-1);
95			}
96		} else if (strcmp(argv[1], "-4") == 0) {
97			family = AF_INET;
98		} else if (strcmp(argv[1], "-6") == 0) {
99			family = AF_INET6;
100		} else if (strcmp(argv[1], "-0") == 0) {
101			family = AF_PACKET;
102		} else if (matches(argv[1], "-Version") == 0) {
103			printf("rtmon utility, iproute2-ss%s\n", SNAPSHOT);
104			exit(0);
105		} else if (matches(argv[1], "file") == 0) {
106			argc--;
107			argv++;
108			if (argc <= 1)
109				usage();
110			file = argv[1];
111		} else if (matches(argv[1], "link") == 0) {
112			llink=1;
113			groups = 0;
114		} else if (matches(argv[1], "address") == 0) {
115			laddr=1;
116			groups = 0;
117		} else if (matches(argv[1], "route") == 0) {
118			lroute=1;
119			groups = 0;
120		} else if (strcmp(argv[1], "all") == 0) {
121			groups = ~0U;
122		} else if (matches(argv[1], "help") == 0) {
123			usage();
124		} else {
125			fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]);
126			exit(-1);
127		}
128		argc--;	argv++;
129	}
130
131	if (file == NULL) {
132		fprintf(stderr, "Not enough information: argument \"file\" is required\n");
133		exit(-1);
134	}
135	if (llink)
136		groups |= nl_mgrp(RTNLGRP_LINK);
137	if (laddr) {
138		if (!family || family == AF_INET)
139			groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR);
140		if (!family || family == AF_INET6)
141			groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR);
142	}
143	if (lroute) {
144		if (!family || family == AF_INET)
145			groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE);
146		if (!family || family == AF_INET6)
147			groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE);
148	}
149
150	fp = fopen(file, "w");
151	if (fp == NULL) {
152		perror("Cannot fopen");
153		exit(-1);
154	}
155
156	if (rtnl_open(&rth, groups) < 0)
157		exit(1);
158
159	if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) {
160		perror("Cannot send dump request");
161		exit(1);
162	}
163
164	write_stamp(fp);
165
166	if (rtnl_dump_filter(&rth, dump_msg, fp) < 0) {
167		fprintf(stderr, "Dump terminated\n");
168		return 1;
169	}
170
171	init_phase = 0;
172
173	if (rtnl_listen(&rth, dump_msg, (void*)fp) < 0)
174		exit(2);
175
176	exit(0);
177}
178