1/*
2 *   $Id: radvdump.c,v 1.18 2007/06/25 11:42:10 psavola Exp $
3 *
4 *   Authors:
5 *    Lars Fenneberg		<lf@elemental.net>
6 *    Marko Myllynen		<myllynen@lut.fi>
7 *
8 *   This software is Copyright 1996-2000 by the above mentioned author(s),
9 *   All Rights Reserved.
10 *
11 *   The license which is distributed with this software in the file COPYRIGHT
12 *   applies to this software. If your distribution is missing this file, you
13 *   may request it from <pekkas@netcore.fi>.
14 *
15 */
16
17#include <config.h>
18#include <includes.h>
19#include <radvd.h>
20
21char usage_str[] = "[-vhfe] [-d level]";
22
23#ifdef HAVE_GETOPT_LONG
24struct option prog_opt[] = {
25	{"debug", 1, 0, 'd'},
26	{"file-format", 0, 0, 'f'},
27	{"exclude-defaults", 0, 0, 'e'},
28	{"version", 0, 0, 'v'},
29	{"help", 0, 0, 'h'},
30	{NULL, 0, 0, 0}
31};
32#endif
33
34char *pname;
35int sock = -1;
36
37void version(void);
38void usage(void);
39void print_ff(unsigned char *, int, struct sockaddr_in6 *, int, unsigned int, int);
40void print_preferences(int);
41
42int
43main(int argc, char *argv[])
44{
45	unsigned char msg[MSG_SIZE];
46	int c, len, hoplimit;
47	int edefs = 0;
48	struct sockaddr_in6 rcv_addr;
49        struct in6_pktinfo *pkt_info = NULL;
50#ifdef HAVE_GETOPT_LONG
51	int opt_idx;
52#endif
53
54	pname = ((pname=strrchr(argv[0],'/')) != NULL)?pname+1:argv[0];
55
56	/* parse args */
57#ifdef HAVE_GETOPT_LONG
58	while ((c = getopt_long(argc, argv, "d:fehv", prog_opt, &opt_idx)) > 0)
59#else
60	while ((c = getopt(argc, argv, "d:fehv")) > 0)
61#endif
62	{
63		switch (c) {
64		case 'd':
65			set_debuglevel(atoi(optarg));
66			break;
67		case 'f':
68			break;
69		case 'e':
70			edefs = 1;
71			break;
72		case 'v':
73			version();
74			break;
75		case 'h':
76			usage();
77#ifdef HAVE_GETOPT_LONG
78		case ':':
79			fprintf(stderr, "%s: option %s: parameter expected\n", pname,
80				prog_opt[opt_idx].name);
81			exit(1);
82#endif
83		case '?':
84			exit(1);
85		}
86	}
87
88	if (log_open(L_STDERR, pname, NULL, 0) < 0)
89		exit(1);
90
91	/* get a raw socket for sending and receiving ICMPv6 messages */
92	sock = open_icmpv6_socket();
93	if (sock < 0)
94		exit(1);
95
96	for(;;)
97	{
98	        len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit);
99   	     	if (len > 0)
100       	 	{
101			struct icmp6_hdr *icmph;
102
103			/*
104			 * can this happen?
105			 */
106
107			if (len < sizeof(struct icmp6_hdr))
108			{
109				flog(LOG_WARNING, "received icmpv6 packet with invalid length: %d",
110					len);
111				exit(1);
112			}
113
114			icmph = (struct icmp6_hdr *) msg;
115
116			if (icmph->icmp6_type != ND_ROUTER_SOLICIT &&
117			    icmph->icmp6_type != ND_ROUTER_ADVERT)
118			{
119				/*
120				 *	We just want to listen to RSs and RAs
121				 */
122
123				flog(LOG_ERR, "icmpv6 filter failed");
124				exit(1);
125			}
126
127			dlog(LOG_DEBUG, 4, "receiver if_index: %u", pkt_info->ipi6_ifindex);
128
129			if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
130			{
131				/* not yet */
132			}
133			else if (icmph->icmp6_type == ND_ROUTER_ADVERT)
134				print_ff(msg, len, &rcv_addr, hoplimit, (unsigned int)pkt_info->ipi6_ifindex, edefs);
135        	}
136		else if (len == 0)
137       	 	{
138       	 		flog(LOG_ERR, "received zero lenght packet");
139       	 		exit(1);
140        	}
141        	else
142        	{
143			flog(LOG_ERR, "recv_rs_ra: %s", strerror(errno));
144			exit(1);
145        	}
146        }
147
148	exit(0);
149}
150
151void
152print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int hoplimit, unsigned int if_index, int edefs)
153{
154	/* XXX: hoplimit not being used for anything here.. */
155	struct nd_router_advert *radvert;
156	char addr_str[INET6_ADDRSTRLEN];
157	uint8_t *opt_str;
158	int orig_len = len;
159	char if_name[IFNAMSIZ] = "";
160
161	print_addr(&addr->sin6_addr, addr_str);
162	printf("#\n# radvd configuration generated by radvdump %s\n", VERSION);
163	printf("# based on Router Advertisement from %s\n", addr_str);
164	if_indextoname(if_index, if_name);
165	printf("# received by interface %s\n", if_name);
166	printf("#\n\ninterface %s\n{\n\tAdvSendAdvert on;\n", if_name);
167
168	printf("\t# Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump\n");
169
170	radvert = (struct nd_router_advert *) msg;
171
172	if (!edefs || DFLT_AdvManagedFlag != (ND_RA_FLAG_MANAGED == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)))
173	printf("\tAdvManagedFlag %s;\n",
174		(radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)?"on":"off");
175
176	if (!edefs || DFLT_AdvOtherConfigFlag != (ND_RA_FLAG_OTHER == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)))
177	printf("\tAdvOtherConfigFlag %s;\n",
178		(radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)?"on":"off");
179
180	if (!edefs || DFLT_AdvReachableTime != ntohl(radvert->nd_ra_reachable))
181	printf("\tAdvReachableTime %u;\n", ntohl(radvert->nd_ra_reachable));
182
183	if (!edefs || DFLT_AdvRetransTimer != ntohl(radvert->nd_ra_retransmit))
184	printf("\tAdvRetransTimer %u;\n", ntohl(radvert->nd_ra_retransmit));
185
186	if (!edefs || DFLT_AdvCurHopLimit != radvert->nd_ra_curhoplimit)
187	printf("\tAdvCurHopLimit %u;\n", radvert->nd_ra_curhoplimit);
188
189	if (!edefs || (3*DFLT_MaxRtrAdvInterval) != ntohs(radvert->nd_ra_router_lifetime))
190	printf("\tAdvDefaultLifetime %hu;\n", ntohs(radvert->nd_ra_router_lifetime));
191
192	/* Mobile IPv6 ext */
193	if (!edefs || DFLT_AdvHomeAgentFlag != (ND_RA_FLAG_HOME_AGENT == (radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT)))
194	printf("\tAdvHomeAgentFlag %s;\n",
195		(radvert->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT)?"on":"off");
196
197        /* Route Preferences and more specific routes */
198        /* XXX two middlemost bits from 8 bit field */
199	if (!edefs || (((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff) != DFLT_AdvDefaultPreference) {
200	        printf("\tAdvDefaultPreference ");
201	        print_preferences(((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff);
202	        printf(";\n");
203	}
204
205	len -= sizeof(struct nd_router_advert);
206
207	if (len == 0)
208		return;
209
210	opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert));
211
212	while (len > 0)
213	{
214		int optlen;
215		struct nd_opt_mtu *mtu;
216		struct HomeAgentInfo *ha_info;
217
218		if (len < 2)
219		{
220			flog(LOG_ERR, "trailing garbage in RA from %s",
221				addr_str);
222			break;
223		}
224
225		optlen = (opt_str[1] << 3);
226
227		if (optlen == 0)
228		{
229			flog(LOG_ERR, "zero length option in RA");
230			break;
231		}
232		else if (optlen > len)
233		{
234			flog(LOG_ERR, "option length greater than total"
235				" length in RA (type %d, optlen %d, len %d)",
236				(int)*opt_str, optlen, len);
237			break;
238		}
239
240		switch (*opt_str)
241		{
242		case ND_OPT_MTU:
243			mtu = (struct nd_opt_mtu *)opt_str;
244
245			if (!edefs || DFLT_AdvLinkMTU != ntohl(mtu->nd_opt_mtu_mtu))
246			printf("\tAdvLinkMTU %u;\n", ntohl(mtu->nd_opt_mtu_mtu));
247			break;
248		case ND_OPT_SOURCE_LINKADDR:
249			/* XXX: !DFLT depends on current DFLT_ value */
250			if (!edefs || !DFLT_AdvSourceLLAddress)
251			printf("\tAdvSourceLLAddress on;\n");
252			break;
253		/* Mobile IPv6 ext */
254		case ND_OPT_RTR_ADV_INTERVAL:
255			/* XXX: !DFLT depends on current DFLT_ value */
256			if (!edefs || !DFLT_AdvIntervalOpt)
257			printf("\tAdvIntervalOpt on;\n");
258			break;
259		/* Mobile IPv6 ext */
260		case ND_OPT_HOME_AGENT_INFO:
261			ha_info = (struct HomeAgentInfo *)opt_str;
262
263			/* XXX: we check DFLT_HomeAgentInfo by interface, and it's outside
264			   of context here, so we always need to print it out.. */
265			printf("\tAdvHomeAgentInfo on;\n");
266
267			/* NEMO ext */
268			if (!edefs || DFLT_AdvMobRtrSupportFlag != (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR))
269				printf("\tAdvMobRtrSupportFlag %s;\n", (ha_info->flags_reserved & ND_OPT_HAI_FLAG_SUPPORT_MR)?"on":"off");
270
271			if (!edefs || DFLT_HomeAgentPreference != ntohs(ha_info->preference))
272			printf("\tHomeAgentPreference %hu;\n", ntohs(ha_info->preference));
273			/* Hum.. */
274			if (!edefs || (3*DFLT_MaxRtrAdvInterval) != ntohs(ha_info->lifetime))
275			printf("\tHomeAgentLifetime %hu;\n", ntohs(ha_info->lifetime));
276			break;
277		case ND_OPT_TARGET_LINKADDR:
278		case ND_OPT_REDIRECTED_HEADER:
279			flog(LOG_ERR, "invalid option %d in RA", (int)*opt_str);
280			break;
281		case ND_OPT_PREFIX_INFORMATION:
282			break;
283		case ND_OPT_ROUTE_INFORMATION:
284			break;
285		case ND_OPT_RDNSS_INFORMATION:
286			break;
287		default:
288			dlog(LOG_DEBUG, 1, "unknown option %d in RA",
289				(int)*opt_str);
290			break;
291		}
292
293		len -= optlen;
294		opt_str += optlen;
295	}
296
297	orig_len -= sizeof(struct nd_router_advert);
298
299	if (orig_len == 0)
300		return;
301
302	opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert));
303
304	while (orig_len > 0)
305	{
306		int optlen;
307		struct nd_opt_prefix_info *pinfo;
308		struct nd_opt_route_info_local *rinfo;
309		struct nd_opt_rdnss_info_local *rdnss_info;
310		char prefix_str[INET6_ADDRSTRLEN];
311
312		if (orig_len < 2)
313		{
314			flog(LOG_ERR, "trailing garbage in RA from %s",
315				addr_str);
316			break;
317		}
318
319		optlen = (opt_str[1] << 3);
320
321		if (optlen == 0)
322		{
323			flog(LOG_ERR, "zero length option in RA");
324			break;
325		}
326		else if (optlen > orig_len)
327		{
328			flog(LOG_ERR, "option length greater than total"
329				" length in RA (type %d, optlen %d, len %d)",
330				(int)*opt_str, optlen, orig_len);
331			break;
332		}
333
334		switch (*opt_str)
335		{
336		case ND_OPT_PREFIX_INFORMATION:
337			pinfo = (struct nd_opt_prefix_info *) opt_str;
338
339			print_addr(&pinfo->nd_opt_pi_prefix, prefix_str);
340
341			printf("\n\tprefix %s/%d\n\t{\n", prefix_str, pinfo->nd_opt_pi_prefix_len);
342
343			if (ntohl(pinfo->nd_opt_pi_valid_time) == 0xffffffff)
344			{
345				if (!edefs || DFLT_AdvValidLifetime != 0xffffffff)
346				printf("\t\tAdvValidLifetime infinity; # (0xffffffff)\n");
347			}
348			else
349			{
350				if (!edefs || DFLT_AdvValidLifetime != ntohl(pinfo->nd_opt_pi_valid_time))
351				printf("\t\tAdvValidLifetime %u;\n", ntohl(pinfo->nd_opt_pi_valid_time));
352			}
353			if (ntohl(pinfo->nd_opt_pi_preferred_time) == 0xffffffff)
354			{
355				if (!edefs || DFLT_AdvPreferredLifetime != 0xffffffff)
356				printf("\t\tAdvPreferredLifetime infinity; # (0xffffffff)\n");
357			}
358			else
359			{
360				if (!edefs || DFLT_AdvPreferredLifetime != ntohl(pinfo->nd_opt_pi_preferred_time))
361				printf("\t\tAdvPreferredLifetime %u;\n", ntohl(pinfo->nd_opt_pi_preferred_time));
362			}
363
364			if (!edefs || DFLT_AdvOnLinkFlag != (ND_OPT_PI_FLAG_ONLINK == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)))
365			printf("\t\tAdvOnLink %s;\n",
366				(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)?"on":"off");
367
368			if (!edefs || DFLT_AdvAutonomousFlag != (ND_OPT_PI_FLAG_AUTO == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)))
369			printf("\t\tAdvAutonomous %s;\n",
370				(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO)?"on":"off");
371
372			/* Mobile IPv6 ext */
373			if (!edefs || DFLT_AdvRouterAddr != (ND_OPT_PI_FLAG_RADDR == (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR)))
374			printf("\t\tAdvRouterAddr %s;\n",
375				(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR)?"on":"off");
376
377			printf("\t}; # End of prefix definition\n\n");
378			break;
379		case ND_OPT_ROUTE_INFORMATION:
380			rinfo = (struct nd_opt_route_info_local *) opt_str;
381
382			print_addr(&rinfo->nd_opt_ri_prefix, prefix_str);
383
384			printf("\n\troute %s/%d\n\t{\n", prefix_str, rinfo->nd_opt_ri_prefix_len);
385
386			if (!edefs || (((radvert->nd_ra_flags_reserved & 0x18) >> 3) & 0xff) != DFLT_AdvRoutePreference) {
387				printf("\t\tAdvRoutePreference ");
388				print_preferences(((rinfo->nd_opt_ri_flags_reserved & 0x18) >> 3) & 0xff);
389				printf(";\n");
390			}
391
392			/* XXX: we check DFLT_AdvRouteLifetime by interface, and it's outside of context here */
393			if (ntohl(rinfo->nd_opt_ri_lifetime) == 0xffffffff)
394				printf("\t\tAdvRouteLifetime infinity; # (0xffffffff)\n");
395			else
396				printf("\t\tAdvRouteLifetime %u;\n", ntohl(rinfo->nd_opt_ri_lifetime));
397
398			printf("\t}; # End of route definition\n\n");
399			break;
400		case ND_OPT_RDNSS_INFORMATION:
401			rdnss_info = (struct nd_opt_rdnss_info_local *) opt_str;
402
403			printf("\n\tRDNSS");
404
405			print_addr(&rdnss_info->nd_opt_rdnssi_addr1, prefix_str);
406			printf(" %s", prefix_str);
407
408			if (rdnss_info->nd_opt_rdnssi_len >= 5) {
409				print_addr(&rdnss_info->nd_opt_rdnssi_addr2, prefix_str);
410				printf(" %s", prefix_str);
411			}
412			if (rdnss_info->nd_opt_rdnssi_len >= 7) {
413				print_addr(&rdnss_info->nd_opt_rdnssi_addr3, prefix_str);
414				printf(" %s", prefix_str);
415			}
416
417			printf("\n\t{\n");
418			if (!edefs
419			    || ((rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_PREF_MASK) >> ND_OPT_RDNSSI_PREF_SHIFT) != DFLT_AdvRDNSSPreference)
420				printf("\t\tAdvRDNSSPreference %d;\n",
421				  (rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_PREF_MASK) >> ND_OPT_RDNSSI_PREF_SHIFT);
422
423			if (!edefs
424			    || ((rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_FLAG_S) == 0 ) == DFLT_AdvRDNSSOpenFlag)
425				printf("\t\tAdvRDNSSOpen %s;\n", rdnss_info->nd_opt_rdnssi_pref_flag_reserved & ND_OPT_RDNSSI_FLAG_S ? "on" : "off");
426
427			/* as AdvRDNSSLifetime may depend on MaxRtrAdvInterval, it could change */
428			if (ntohl(rdnss_info->nd_opt_rdnssi_lifetime) == 0xffffffff)
429				printf("\t\tAdvRDNSSLifetime infinity; # (0xffffffff)\n");
430			else
431				printf("\t\tAdvRDNSSLifetime %u;\n", ntohl(rdnss_info->nd_opt_rdnssi_lifetime));
432
433			printf("\t}; # End of RDNSS definition\n\n");
434			break;
435		default:
436			break;
437		}
438		orig_len -= optlen;
439		opt_str += optlen;
440	}
441
442	printf("}; # End of interface definition\n");
443
444	fflush(stdout);
445}
446
447void
448print_preferences(int p)
449{
450	switch (p) {
451		case 0:
452			printf("medium");
453			break;
454		case 1:
455			printf("high");
456			break;
457		case 2:
458			/* reserved, ignore */
459			break;
460		case 3:
461			printf("low");
462			break;
463	}
464}
465
466void
467version(void)
468{
469	fprintf(stderr,"Version: %s\n\n", VERSION);
470	fprintf(stderr,"Please send bug reports and suggestions to %s\n",
471		CONTACT_EMAIL);
472	exit(1);
473}
474
475void
476usage(void)
477{
478	fprintf(stderr,"usage: %s %s\n", pname, usage_str);
479	exit(1);
480}
481