1/*
2 *   $Id: process.c,v 1.17 2009/06/19 07:34:07 psavola Exp $
3 *
4 *   Authors:
5 *    Pedro Roque		<roque@di.fc.ul.pt>
6 *    Lars Fenneberg		<lf@elemental.net>
7 *
8 *   This software is Copyright 1996,1997 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
21static void process_rs(int, struct Interface *, unsigned char *msg,
22		       int len, struct sockaddr_in6 *);
23static void process_ra(struct Interface *, unsigned char *msg, int len,
24	struct sockaddr_in6 *);
25static int  addr_match(struct in6_addr *a1, struct in6_addr *a2,
26	int prefixlen);
27
28void
29process(int sock, struct Interface *ifacel, unsigned char *msg, int len,
30	struct sockaddr_in6 *addr, struct in6_pktinfo *pkt_info, int hoplimit)
31{
32	struct Interface *iface;
33	struct icmp6_hdr *icmph;
34	char addr_str[INET6_ADDRSTRLEN];
35
36	if ( ! pkt_info )
37	{
38		flog(LOG_WARNING, "received packet with no pkt_info!" );
39		return;
40	}
41
42	/*
43	 * can this happen?
44	 */
45
46	if (len < sizeof(struct icmp6_hdr))
47	{
48		flog(LOG_WARNING, "received icmpv6 packet with invalid length: %d",
49			len);
50		return;
51	}
52
53	icmph = (struct icmp6_hdr *) msg;
54
55	if (icmph->icmp6_type != ND_ROUTER_SOLICIT &&
56	    icmph->icmp6_type != ND_ROUTER_ADVERT)
57	{
58		/*
59		 *	We just want to listen to RSs and RAs
60		 */
61
62		flog(LOG_ERR, "icmpv6 filter failed");
63		return;
64	}
65
66	if (icmph->icmp6_type == ND_ROUTER_ADVERT)
67	{
68		if (len < sizeof(struct nd_router_advert)) {
69			flog(LOG_WARNING, "received icmpv6 RA packet with invalid length: %d",
70				len);
71			return;
72		}
73
74		if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
75			flog(LOG_WARNING, "received icmpv6 RA packet with non-linklocal source address");
76			return;
77		}
78	}
79
80	if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
81	{
82		if (len < sizeof(struct nd_router_solicit)) {
83			flog(LOG_WARNING, "received icmpv6 RS packet with invalid length: %d",
84				len);
85			return;
86		}
87	}
88
89	if (icmph->icmp6_code != 0)
90	{
91		flog(LOG_WARNING, "received icmpv6 RS/RA packet with invalid code: %d",
92			icmph->icmp6_code);
93		return;
94	}
95
96	dlog(LOG_DEBUG, 4, "if_index %u", pkt_info->ipi6_ifindex);
97
98	/* get iface by received if_index */
99
100	for (iface = ifacel; iface; iface=iface->next)
101	{
102		if (iface->if_index == pkt_info->ipi6_ifindex)
103		{
104			break;
105		}
106	}
107
108	if (iface == NULL)
109	{
110		dlog(LOG_DEBUG, 2, "received packet from unknown interface: %d",
111			pkt_info->ipi6_ifindex);
112		return;
113	}
114
115	if (hoplimit != 255)
116	{
117		print_addr(&addr->sin6_addr, addr_str);
118		flog(LOG_WARNING, "received RS or RA with invalid hoplimit %d from %s",
119			hoplimit, addr_str);
120		return;
121	}
122
123	if (!iface->AdvSendAdvert)
124	{
125		dlog(LOG_DEBUG, 2, "AdvSendAdvert is off for %s", iface->Name);
126		return;
127	}
128
129	dlog(LOG_DEBUG, 4, "found Interface: %s", iface->Name);
130
131	if (icmph->icmp6_type == ND_ROUTER_SOLICIT)
132	{
133		process_rs(sock, iface, msg, len, addr);
134	}
135	else if (icmph->icmp6_type == ND_ROUTER_ADVERT)
136	{
137		process_ra(iface, msg, len, addr);
138	}
139}
140
141static void
142process_rs(int sock, struct Interface *iface, unsigned char *msg, int len,
143	struct sockaddr_in6 *addr)
144{
145	double delay;
146	double next;
147	struct timeval tv;
148	uint8_t *opt_str;
149
150	/* validation */
151	len -= sizeof(struct nd_router_solicit);
152
153	opt_str = (uint8_t *)(msg + sizeof(struct nd_router_solicit));
154
155	while (len > 0)
156	{
157		int optlen;
158
159		if (len < 2)
160		{
161			flog(LOG_WARNING, "trailing garbage in RS");
162			return;
163		}
164
165		optlen = (opt_str[1] << 3);
166
167		if (optlen == 0)
168		{
169			flog(LOG_WARNING, "zero length option in RS");
170			return;
171		}
172		else if (optlen > len)
173		{
174			flog(LOG_WARNING, "option length greater than total length in RS");
175			return;
176		}
177
178		if (*opt_str == ND_OPT_SOURCE_LINKADDR &&
179		    IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr)) {
180			flog(LOG_WARNING, "received icmpv6 RS packet with unspecified source address and there is a lladdr option");
181			return;
182		}
183
184		len -= optlen;
185		opt_str += optlen;
186	}
187
188	gettimeofday(&tv, NULL);
189
190	delay = MAX_RA_DELAY_TIME*rand()/(RAND_MAX+1.0);
191	dlog(LOG_DEBUG, 3, "random mdelay for %s: %.2f", iface->Name, delay);
192
193	if (iface->UnicastOnly) {
194		mdelay(delay);
195		send_ra_forall(sock, iface, &addr->sin6_addr);
196	}
197	else if ((tv.tv_sec + tv.tv_usec / 1000000.0) - (iface->last_multicast_sec +
198	          iface->last_multicast_usec / 1000000.0) < iface->MinDelayBetweenRAs) {
199		/* last RA was sent only a few moments ago, don't send another immediately */
200		clear_timer(&iface->tm);
201		next = iface->MinDelayBetweenRAs - (tv.tv_sec + tv.tv_usec / 1000000.0) +
202		       (iface->last_multicast_sec + iface->last_multicast_usec / 1000000.0) + delay/1000.0;
203		set_timer(&iface->tm, next);
204	}
205	else {
206		/* no RA sent in a while, send an immediate multicast reply */
207		clear_timer(&iface->tm);
208		send_ra_forall(sock, iface, &addr->sin6_addr);
209
210		next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
211		set_timer(&iface->tm, next);
212	}
213}
214
215/*
216 * check router advertisements according to RFC 4861, 6.2.7
217 */
218static void
219process_ra(struct Interface *iface, unsigned char *msg, int len,
220	struct sockaddr_in6 *addr)
221{
222	struct nd_router_advert *radvert;
223	char addr_str[INET6_ADDRSTRLEN];
224	uint8_t *opt_str;
225
226	print_addr(&addr->sin6_addr, addr_str);
227
228	radvert = (struct nd_router_advert *) msg;
229
230	if ((radvert->nd_ra_curhoplimit && iface->AdvCurHopLimit) &&
231	   (radvert->nd_ra_curhoplimit != iface->AdvCurHopLimit))
232	{
233		flog(LOG_WARNING, "our AdvCurHopLimit on %s doesn't agree with %s",
234			iface->Name, addr_str);
235	}
236
237	if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) && !iface->AdvManagedFlag)
238	{
239		flog(LOG_WARNING, "our AdvManagedFlag on %s doesn't agree with %s",
240			iface->Name, addr_str);
241	}
242
243	if ((radvert->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) && !iface->AdvOtherConfigFlag)
244	{
245		flog(LOG_WARNING, "our AdvOtherConfigFlag on %s doesn't agree with %s",
246			iface->Name, addr_str);
247	}
248
249	/* note: we don't check the default router preference here, because they're likely different */
250
251	if ((radvert->nd_ra_reachable && iface->AdvReachableTime) &&
252	   (ntohl(radvert->nd_ra_reachable) != iface->AdvReachableTime))
253	{
254		flog(LOG_WARNING, "our AdvReachableTime on %s doesn't agree with %s",
255			iface->Name, addr_str);
256	}
257
258	if ((radvert->nd_ra_retransmit && iface->AdvRetransTimer) &&
259	   (ntohl(radvert->nd_ra_retransmit) != iface->AdvRetransTimer))
260	{
261		flog(LOG_WARNING, "our AdvRetransTimer on %s doesn't agree with %s",
262			iface->Name, addr_str);
263	}
264
265	len -= sizeof(struct nd_router_advert);
266
267	if (len == 0)
268		return;
269
270	opt_str = (uint8_t *)(msg + sizeof(struct nd_router_advert));
271
272	while (len > 0)
273	{
274		int optlen;
275		struct nd_opt_prefix_info *pinfo;
276		struct nd_opt_rdnss_info_local *rdnssinfo;
277		struct nd_opt_mtu *mtu;
278		struct AdvPrefix *prefix;
279		struct AdvRDNSS *rdnss;
280		char prefix_str[INET6_ADDRSTRLEN];
281		char rdnss_str[INET6_ADDRSTRLEN];
282		uint32_t preferred, valid, count;
283
284		if (len < 2)
285		{
286			flog(LOG_ERR, "trailing garbage in RA on %s from %s",
287				iface->Name, addr_str);
288			break;
289		}
290
291		optlen = (opt_str[1] << 3);
292
293		if (optlen == 0)
294		{
295			flog(LOG_ERR, "zero length option in RA on %s from %s",
296				iface->Name, addr_str);
297			break;
298		}
299		else if (optlen > len)
300		{
301			flog(LOG_ERR, "option length greater than total"
302				" length in RA on %s from %s",
303				iface->Name, addr_str);
304			break;
305		}
306
307		switch (*opt_str)
308		{
309		case ND_OPT_MTU:
310			mtu = (struct nd_opt_mtu *)opt_str;
311
312			if (iface->AdvLinkMTU && (ntohl(mtu->nd_opt_mtu_mtu) != iface->AdvLinkMTU))
313			{
314				flog(LOG_WARNING, "our AdvLinkMTU on %s doesn't agree with %s",
315					iface->Name, addr_str);
316			}
317			break;
318		case ND_OPT_PREFIX_INFORMATION:
319			pinfo = (struct nd_opt_prefix_info *) opt_str;
320			preferred = ntohl(pinfo->nd_opt_pi_preferred_time);
321			valid = ntohl(pinfo->nd_opt_pi_valid_time);
322
323			prefix = iface->AdvPrefixList;
324			while (prefix)
325			{
326				if (prefix->enabled &&
327				    (prefix->PrefixLen == pinfo->nd_opt_pi_prefix_len) &&
328				    addr_match(&prefix->Prefix, &pinfo->nd_opt_pi_prefix,
329				    	 prefix->PrefixLen))
330				{
331					print_addr(&prefix->Prefix, prefix_str);
332
333					if (valid != prefix->AdvValidLifetime)
334					{
335						flog(LOG_WARNING, "our AdvValidLifetime on"
336						 " %s for %s doesn't agree with %s",
337						 iface->Name,
338						 prefix_str,
339						 addr_str
340						 );
341					}
342					if (preferred != prefix->AdvPreferredLifetime)
343					{
344						flog(LOG_WARNING, "our AdvPreferredLifetime on"
345						 " %s for %s doesn't agree with %s",
346						 iface->Name,
347						 prefix_str,
348						 addr_str
349						 );
350					}
351				}
352
353				prefix = prefix->next;
354			}
355			break;
356		case ND_OPT_ROUTE_INFORMATION:
357			/* not checked: these will very likely vary a lot */
358			break;
359		case ND_OPT_SOURCE_LINKADDR:
360			/* not checked */
361			break;
362		case ND_OPT_TARGET_LINKADDR:
363		case ND_OPT_REDIRECTED_HEADER:
364			flog(LOG_ERR, "invalid option %d in RA on %s from %s",
365				(int)*opt_str, iface->Name, addr_str);
366			break;
367		/* Mobile IPv6 extensions */
368		case ND_OPT_RTR_ADV_INTERVAL:
369		case ND_OPT_HOME_AGENT_INFO:
370			/* not checked */
371			break;
372		case ND_OPT_RDNSS_INFORMATION:
373			rdnssinfo = (struct nd_opt_rdnss_info_local *) opt_str;
374			count = rdnssinfo->nd_opt_rdnssi_len;
375
376			/* Check the RNDSS addresses received */
377			switch (count) {
378				case 7:
379					rdnss = iface->AdvRDNSSList;
380					if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr3 )) {
381						/* no match found in iface->AdvRDNSSList */
382						print_addr(&rdnssinfo->nd_opt_rdnssi_addr3, rdnss_str);
383						flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
384							rdnss_str, iface->Name, addr_str);
385					}
386					/* FALLTHROUGH */
387				case 5:
388					rdnss = iface->AdvRDNSSList;
389					if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr2 )) {
390						/* no match found in iface->AdvRDNSSList */
391						print_addr(&rdnssinfo->nd_opt_rdnssi_addr2, rdnss_str);
392						flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
393							rdnss_str, iface->Name, addr_str);
394					}
395					/* FALLTHROUGH */
396				case 3:
397					rdnss = iface->AdvRDNSSList;
398					if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr1 )) {
399						/* no match found in iface->AdvRDNSSList */
400						print_addr(&rdnssinfo->nd_opt_rdnssi_addr1, rdnss_str);
401						flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
402							rdnss_str, iface->Name, addr_str);
403					}
404
405					break;
406				default:
407					flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s",
408							count, iface->Name, addr_str);
409			}
410
411			break;
412		default:
413			dlog(LOG_DEBUG, 1, "unknown option %d in RA on %s from %s",
414				(int)*opt_str, iface->Name, addr_str);
415			break;
416		}
417
418		len -= optlen;
419		opt_str += optlen;
420	}
421}
422
423static int
424addr_match(struct in6_addr *a1, struct in6_addr *a2, int prefixlen)
425{
426	unsigned int pdw;
427	unsigned int pbi;
428
429	pdw = prefixlen >> 0x05;  /* num of whole uint32_t in prefix */
430	pbi = prefixlen &  0x1f;  /* num of bits in incomplete uint32_t in prefix */
431
432	if (pdw)
433	{
434		if (memcmp(a1, a2, pdw << 2))
435			return 0;
436	}
437
438	if (pbi)
439	{
440		uint32_t w1, w2;
441		uint32_t mask;
442
443		w1 = *((uint32_t *)a1 + pdw);
444		w2 = *((uint32_t *)a2 + pdw);
445
446		mask = htonl(((uint32_t) 0xffffffff) << (0x20 - pbi));
447
448		if ((w1 ^ w2) & mask)
449			return 0;
450	}
451
452	return 1;
453}
454
455