1/*
2 * ipmroute.c		"ip mroute".
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/ioctl.h>
19#include <sys/socket.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22#include <string.h>
23
24#include <linux/netdevice.h>
25#include <linux/if.h>
26#include <linux/if_arp.h>
27#include <linux/sockios.h>
28
29#include "utils.h"
30
31char filter_dev[16];
32int  filter_family;
33
34static void usage(void) __attribute__((noreturn));
35
36static void usage(void)
37{
38	fprintf(stderr, "Usage: ip mroute show [ PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n");
39#if 0
40	fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n");
41#endif
42	exit(-1);
43}
44
45static char *viftable[32];
46
47struct rtfilter
48{
49	inet_prefix mdst;
50	inet_prefix msrc;
51} filter;
52
53static void read_viftable(void)
54{
55	char buf[256];
56	FILE *fp = fopen("/proc/net/ip_mr_vif", "r");
57
58	if (!fp)
59		return;
60
61	if (!fgets(buf, sizeof(buf), fp)) {
62		fclose(fp);
63		return;
64	}
65	while (fgets(buf, sizeof(buf), fp)) {
66		int vifi;
67		char dev[256];
68
69		if (sscanf(buf, "%d%s", &vifi, dev) < 2)
70			continue;
71
72		if (vifi<0 || vifi>31)
73			continue;
74
75		viftable[vifi] = strdup(dev);
76	}
77	fclose(fp);
78}
79
80static void read_mroute_list(FILE *ofp)
81{
82	char buf[256];
83	FILE *fp = fopen("/proc/net/ip_mr_cache", "r");
84
85	if (!fp)
86		return;
87
88	if (!fgets(buf, sizeof(buf), fp)) {
89		fclose(fp);
90		return;
91	}
92
93	while (fgets(buf, sizeof(buf), fp)) {
94		inet_prefix maddr, msrc;
95		unsigned pkts, b, w;
96		int vifi;
97		char oiflist[256];
98		char sbuf[256];
99		char mbuf[256];
100		char obuf[256];
101
102		oiflist[0] = 0;
103		if (sscanf(buf, "%x%x%d%u%u%u %[^\n]",
104			   maddr.data, msrc.data, &vifi,
105			   &pkts, &b, &w, oiflist) < 6)
106			continue;
107
108		if (vifi!=-1 && (vifi < 0 || vifi>31))
109			continue;
110
111		if (filter_dev[0] && (vifi<0 || strcmp(filter_dev, viftable[vifi])))
112			continue;
113		if (filter.mdst.family && inet_addr_match(&maddr, &filter.mdst, filter.mdst.bitlen))
114			continue;
115		if (filter.msrc.family && inet_addr_match(&msrc, &filter.msrc, filter.msrc.bitlen))
116			continue;
117
118		snprintf(obuf, sizeof(obuf), "(%s, %s)",
119			 format_host(AF_INET, 4, &msrc.data[0], sbuf, sizeof(sbuf)),
120			 format_host(AF_INET, 4, &maddr.data[0], mbuf, sizeof(mbuf)));
121
122		fprintf(ofp, "%-32s Iif: ", obuf);
123
124		if (vifi == -1)
125			fprintf(ofp, "unresolved ");
126		else
127			fprintf(ofp, "%-10s ", viftable[vifi]);
128
129		if (oiflist[0]) {
130			char *next = NULL;
131			char *p = oiflist;
132			int ovifi, ottl;
133
134			fprintf(ofp, "Oifs: ");
135
136			while (p) {
137				next = strchr(p, ' ');
138				if (next) {
139					*next = 0;
140					next++;
141				}
142				if (sscanf(p, "%d:%d", &ovifi, &ottl)<2) {
143					p = next;
144					continue;
145				}
146				p = next;
147
148				fprintf(ofp, "%s", viftable[ovifi]);
149				if (ottl>1)
150					fprintf(ofp, "(ttl %d) ", ovifi);
151				else
152					fprintf(ofp, " ");
153			}
154		}
155
156		if (show_stats && b) {
157			fprintf(ofp, "%s  %u packets, %u bytes", _SL_, pkts, b);
158			if (w)
159				fprintf(ofp, ", %u arrived on wrong iif.", w);
160		}
161		fprintf(ofp, "\n");
162	}
163	fclose(fp);
164}
165
166
167static int mroute_list(int argc, char **argv)
168{
169	while (argc > 0) {
170		if (strcmp(*argv, "iif") == 0) {
171			NEXT_ARG();
172			strncpy(filter_dev, *argv, sizeof(filter_dev)-1);
173		} else if (matches(*argv, "from") == 0) {
174			NEXT_ARG();
175			get_prefix(&filter.msrc, *argv, AF_INET);
176		} else {
177			if (strcmp(*argv, "to") == 0) {
178				NEXT_ARG();
179			}
180			if (matches(*argv, "help") == 0)
181				usage();
182			get_prefix(&filter.mdst, *argv, AF_INET);
183		}
184		argv++; argc--;
185	}
186
187	read_viftable();
188	read_mroute_list(stdout);
189	return 0;
190}
191
192int do_multiroute(int argc, char **argv)
193{
194	if (argc < 1)
195		return mroute_list(0, NULL);
196#if 0
197	if (matches(*argv, "add") == 0)
198		return mroute_modify(RTM_NEWADDR, argc-1, argv+1);
199	if (matches(*argv, "delete") == 0)
200		return mroute_modify(RTM_DELADDR, argc-1, argv+1);
201	if (matches(*argv, "get") == 0)
202		return mroute_get(argc-1, argv+1);
203#endif
204	if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
205	    || matches(*argv, "lst") == 0)
206		return mroute_list(argc-1, argv+1);
207	if (matches(*argv, "help") == 0)
208		usage();
209	fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv);
210	exit(-1);
211}
212