1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35#pragma ident	"%Z%%M%	%I%	%E% SMI"
36
37/*
38 * Routing Table Management Daemon
39 */
40#include "defs.h"
41
42static char	buf1[INET6_ADDRSTRLEN];
43static char	buf2[INET6_ADDRSTRLEN];
44
45static void	rip_input(struct sockaddr_in6 *from, int size, uint_t hopcount,
46    struct interface *ifp);
47
48/*
49 * Return a pointer to the specified option buffer.
50 * If not found return NULL.
51 */
52static void *
53find_ancillary(struct msghdr *rmsg, int cmsg_type)
54{
55	struct cmsghdr *cmsg;
56
57	for (cmsg = CMSG_FIRSTHDR(rmsg); cmsg != NULL;
58	    cmsg = CMSG_NXTHDR(rmsg, cmsg)) {
59		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
60		    cmsg->cmsg_type == cmsg_type) {
61			return (CMSG_DATA(cmsg));
62		}
63	}
64	return (NULL);
65}
66
67/*
68 * Read a packet and passes it to rip_input() for processing.
69 */
70void
71in_data(struct interface *ifp)
72{
73	struct sockaddr_in6 from;
74	int len;
75	struct msghdr rmsg;
76	struct iovec iov;
77	uchar_t *hopcntopt;
78
79	iov.iov_base = packet;
80	iov.iov_len = IPV6_MAX_PACKET;
81	rmsg.msg_name = &from;
82	rmsg.msg_namelen = (socklen_t)sizeof (from);
83	rmsg.msg_iov = &iov;
84	rmsg.msg_iovlen = 1;
85	rmsg.msg_control = control;
86	rmsg.msg_controllen = IPV6_MAX_PACKET;
87
88	if ((len = recvmsg(ifp->int_sock, &rmsg, 0)) < 0) {
89		/*
90		 * Only syslog if a true error occurred.
91		 */
92		if (errno != EINTR)
93			syslog(LOG_ERR, "in_data: recvmsg: %m");
94		return;
95	}
96	if (len == 0)
97		return;
98
99	if (tracing & INPUT_BIT) {
100		(void) inet_ntop(from.sin6_family, &from.sin6_addr, buf1,
101		    sizeof (buf1));
102	}
103
104	/* Ignore packets > 64k or control buffers that don't fit */
105	if (rmsg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
106		if (tracing & INPUT_BIT) {
107			(void) fprintf(stderr,
108			    "Truncated message: msg_flags 0x%x from %s\n",
109			    rmsg.msg_flags, buf1);
110		}
111		return;
112	}
113
114	if ((hopcntopt = find_ancillary(&rmsg, IPV6_HOPLIMIT)) == NULL) {
115		if (tracing & INPUT_BIT) {
116			(void) fprintf(stderr, "Unknown hop limit from %s\n",
117			    buf1);
118		}
119		return;
120	}
121	rip_input(&from, len, *(uint_t *)hopcntopt, ifp);
122}
123
124/*
125 * Process a newly received packet.
126 */
127static void
128rip_input(struct sockaddr_in6 *from, int size, uint_t hopcount,
129    struct interface *ifp)
130{
131	struct rt_entry *rt;
132	struct netinfo6 *n;
133	int newsize;
134	boolean_t changes = _B_FALSE;
135	int answer = supplier;
136	struct in6_addr prefix;
137	struct in6_addr nexthop;
138	struct in6_addr *gate;
139	boolean_t foundnexthop = _B_FALSE;
140	struct sioc_addrreq sa;
141	struct sockaddr_in6 *sin6;
142
143	TRACE_INPUT(ifp, from, size);
144	if (tracing & INPUT_BIT) {
145		(void) inet_ntop(from->sin6_family, (void *)&from->sin6_addr,
146		    buf1, sizeof (buf1));
147	}
148
149	/*
150	 * If the packet is recevied on an interface with IFF_NORTEXCH flag set,
151	 * we ignore the packet.
152	 */
153	if (ifp->int_flags & RIP6_IFF_NORTEXCH) {
154		if (tracing & INPUT_BIT) {
155			(void) fprintf(ftrace,
156			    "Ignore received RIPng packet on %s "
157			    "(no route exchange on interface)\n",
158			    ifp->int_name);
159			(void) fflush(ftrace);
160		}
161		return;
162	}
163	if (msg->rip6_vers != RIPVERSION6) {
164		if (tracing & INPUT_BIT) {
165			(void) fprintf(ftrace,
166			    "Bad version number %d in packet from %s\n",
167			    msg->rip6_vers, buf1);
168			(void) fflush(ftrace);
169		}
170		return;
171	}
172	if (ntohs(msg->rip6_res1) != 0) {
173		if (tracing & INPUT_BIT) {
174			(void) fprintf(ftrace,
175			    "Non-zero reserved octets found in packet from "
176			    "%s\n",
177			    buf1);
178			(void) fflush(ftrace);
179		}
180	}
181
182	switch (msg->rip6_cmd) {
183
184	case RIPCMD6_REQUEST:		/* multicasted request */
185		ifp->int_ipackets++;
186		newsize = 0;
187
188		/*
189		 * Adjust size by the length of the command, version and
190		 * reserved fields (which are in total 32-bit aligned).
191		 */
192		size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) +
193		    sizeof (msg->rip6_res1);
194
195		/*
196		 * From section 2.4.1 of RFC 2080:
197		 *
198		 *	If there is exactly one entry in the request with a
199		 *	destination prefix of zero, a prefix length of zero and
200		 *	an infinite metric, then supply the entire routing
201		 *	table.
202		 */
203		n = msg->rip6_nets;
204		if (size == sizeof (struct netinfo6) &&
205		    n->rip6_prefix_length == 0 &&
206		    n->rip6_metric == HOPCNT_INFINITY) {
207			rtcreate_prefix(&n->rip6_prefix, &prefix,
208			    n->rip6_prefix_length);
209			if (IN6_IS_ADDR_UNSPECIFIED(&prefix)) {
210				supply(from, ifp, 0,
211				    from->sin6_port == rip6_port);
212				return;
213			}
214		}
215		for (; size >= sizeof (struct netinfo6);
216		    size -= sizeof (struct netinfo6), n++) {
217			if (n->rip6_prefix_length > IPV6_ABITS) {
218				if (tracing & INPUT_BIT) {
219					(void) fprintf(ftrace,
220					    "Bad prefix length %d in request "
221					    "from %s\n",
222					    n->rip6_prefix_length, buf1);
223					(void) fflush(ftrace);
224				}
225				continue;
226			}
227			if (IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix) ||
228			    IN6_IS_ADDR_MULTICAST(&n->rip6_prefix)) {
229				if (tracing & INPUT_BIT) {
230					(void) fprintf(ftrace,
231					    "Bad prefix %s in request from "
232					    "%s\n",
233					    inet_ntop(AF_INET6,
234						(void *)&n->rip6_prefix, buf2,
235						sizeof (buf2)),
236					    buf1);
237					(void) fflush(ftrace);
238				}
239				continue;
240			}
241			rtcreate_prefix(&n->rip6_prefix, &prefix,
242			    n->rip6_prefix_length);
243			rt = rtlookup(&prefix, n->rip6_prefix_length);
244
245			n->rip6_metric = (rt == NULL ?
246			    HOPCNT_INFINITY :
247			    min(rt->rt_metric, HOPCNT_INFINITY));
248			newsize += sizeof (struct netinfo6);
249		}
250		if (size > 0) {
251			if (tracing & INPUT_BIT) {
252				(void) fprintf(ftrace,
253				    "Ignoring %d octets of trailing data in "
254				    "request from %s\n",
255				    size, buf1);
256				(void) fflush(ftrace);
257			}
258		}
259		if (answer && newsize > 0) {
260			/*
261			 * Adjust newsize by the length of the command, version
262			 * and reserved fields (which are in total 32-bit
263			 * aligned).
264			 */
265			msg->rip6_cmd = RIPCMD6_RESPONSE;
266			newsize += sizeof (msg->rip6_cmd) +
267			    sizeof (msg->rip6_vers) + sizeof (msg->rip6_res1);
268			sendpacket(from, ifp, newsize, 0);
269		}
270		return;
271
272	case RIPCMD6_RESPONSE:
273		if (hopcount != IPV6_MAX_HOPS) {
274			if (tracing & INPUT_BIT) {
275				(void) fprintf(ftrace,
276				    "Bad hop count %d in response from %s\n",
277				    hopcount, buf1);
278				(void) fflush(ftrace);
279			}
280			return;
281		}
282
283		if (from->sin6_port != rip6_port) {
284			if (tracing & INPUT_BIT) {
285				(void) fprintf(ftrace,
286				    "Bad source port %d in response from %s\n",
287				    from->sin6_port, buf1);
288				(void) fflush(ftrace);
289			}
290			return;
291		}
292
293		if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {
294			if (tracing & INPUT_BIT) {
295				(void) fprintf(ftrace,
296				    "Bad source address (not link-local) in "
297				    "response from %s\n", buf1);
298				(void) fflush(ftrace);
299			}
300			return;
301		}
302		ifp->int_ipackets++;
303
304		/*
305		 * Adjust size by the length of the command, version and
306		 * reserved fields (which are in total 32-bit aligned).
307		 */
308		size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) +
309		    sizeof (msg->rip6_res1);
310		for (n = msg->rip6_nets;
311		    supplier && size >= sizeof (struct netinfo6);
312		    size -= sizeof (struct netinfo6), n++) {
313			/*
314			 * From section 2.1.1 of RFC 2080:
315			 *
316			 * This is a next hop RTE if n->rip6_metric is set to
317			 * HOPCNT_NEXTHOP.  If the next hop address (which is
318			 * placed in the prefix field of this special RTE) is
319			 * unspecified or is not a link-local address, then use
320			 * the originator's address instead (effectively turning
321			 * off next hop RTE processing.)
322			 */
323			if (n->rip6_metric == HOPCNT_NEXTHOP) {
324				/*
325				 * First check to see if the unspecified address
326				 * was given as the next hop address.  This is
327				 * the correct way of specifying the end of use
328				 * of a next hop address.
329				 */
330				if (IN6_IS_ADDR_UNSPECIFIED(&n->rip6_prefix)) {
331					foundnexthop = _B_FALSE;
332					continue;
333				}
334				/*
335				 * A next hop address that is not a link-local
336				 * address is treated as the unspecified one.
337				 * Trace this event if input tracing is enabled.
338				 */
339				if (!IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix)) {
340					foundnexthop = _B_FALSE;
341					if (tracing & INPUT_BIT) {
342						(void) fprintf(ftrace,
343						    "Bad next hop %s in "
344						    "response from %s\n",
345						    inet_ntop(AF_INET6,
346							(void *)&n->rip6_prefix,
347							buf2, sizeof (buf2)),
348						    buf1);
349					}
350					continue;
351				}
352				/*
353				 * Verify that the next hop address is not one
354				 * of our own.
355				 */
356				sin6 = (struct sockaddr_in6 *)&sa.sa_addr;
357				sin6->sin6_family = AF_INET6;
358				sin6->sin6_addr = n->rip6_prefix;
359				if (ioctl(iocsoc, SIOCTMYADDR,
360				    (char *)&sa) < 0) {
361					syslog(LOG_ERR,
362					    "rip_input: "
363					    "ioctl (verify my address): %m");
364					return;
365				}
366				if (sa.sa_res != 0) {
367					foundnexthop = _B_FALSE;
368					if (tracing & INPUT_BIT) {
369						(void) fprintf(ftrace,
370						    "Bad next hop %s is self "
371						    "in response from %s\n",
372						    inet_ntop(AF_INET6,
373							(void *)&n->rip6_prefix,
374							buf2, sizeof (buf2)),
375						    buf1);
376					}
377					continue;
378				}
379				foundnexthop = _B_TRUE;
380				nexthop = n->rip6_prefix;
381				continue;
382			}
383			if (foundnexthop)
384				gate = &nexthop;
385			else
386				gate = &from->sin6_addr;
387
388			if (n->rip6_metric > HOPCNT_INFINITY ||
389			    n->rip6_metric < 1) {
390				if (tracing & INPUT_BIT) {
391					(void) fprintf(ftrace,
392					    "Bad metric %d in response from "
393					    "%s\n",
394					    n->rip6_metric, buf1);
395					(void) fflush(ftrace);
396				}
397				continue;
398			}
399			if (n->rip6_prefix_length > IPV6_ABITS) {
400				if (tracing & INPUT_BIT) {
401					(void) fprintf(ftrace,
402					    "Bad prefix length %d in response "
403					    "from %s\n",
404					    n->rip6_prefix_length, buf1);
405					(void) fflush(ftrace);
406				}
407				continue;
408			}
409
410			if (IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix) ||
411			    IN6_IS_ADDR_MULTICAST(&n->rip6_prefix)) {
412				if (tracing & INPUT_BIT) {
413
414					(void) fprintf(ftrace,
415					    "Bad prefix %s in response from "
416					    "%s\n",
417					    inet_ntop(AF_INET6,
418						(void *)&n->rip6_prefix, buf2,
419						sizeof (buf2)),
420					    buf1);
421					(void) fflush(ftrace);
422				}
423				continue;
424			}
425			/* Include metric for incoming interface */
426			n->rip6_metric += IFMETRIC(ifp);
427
428			rtcreate_prefix(&n->rip6_prefix, &prefix,
429			    n->rip6_prefix_length);
430			rt = rtlookup(&prefix, n->rip6_prefix_length);
431			if (rt == NULL) {
432				if (n->rip6_metric < HOPCNT_INFINITY) {
433					rtadd(&prefix,
434					    gate, n->rip6_prefix_length,
435					    n->rip6_metric, n->rip6_route_tag,
436					    _B_FALSE, ifp);
437					changes = _B_TRUE;
438				}
439				continue;
440			}
441
442			/*
443			 * If the supplied metric is at least HOPCNT_INFINITY
444			 * and the current metric of the route is
445			 * HOPCNT_INFINITY, then this particular RTE is ignored.
446			 */
447			if (n->rip6_metric >= HOPCNT_INFINITY &&
448			    rt->rt_metric == HOPCNT_INFINITY)
449				continue;
450
451			/*
452			 * From section 2.4.2 of RFC 2080:
453			 *
454			 * Update if any one of the following is true
455			 *
456			 *	1) From current gateway and a different metric.
457			 *	2) From current gateway and a different index.
458			 *	3) A shorter (smaller) metric.
459			 *	4) Equivalent metric and an age at least
460			 *	   one-half of EXPIRE_TIME.
461			 *
462			 * Otherwise, update timer for the interface on which
463			 * the packet arrived.
464			 */
465			if (IN6_ARE_ADDR_EQUAL(gate, &rt->rt_router)) {
466				if (n->rip6_metric != rt->rt_metric ||
467				    rt->rt_ifp != ifp) {
468					rtchange(rt, gate, n->rip6_metric, ifp);
469					changes = _B_TRUE;
470				} else if (n->rip6_metric < HOPCNT_INFINITY) {
471					rt->rt_timer = 0;
472				}
473			} else if (n->rip6_metric < rt->rt_metric ||
474			    (rt->rt_timer > (EXPIRE_TIME / 2) &&
475				rt->rt_metric == n->rip6_metric)) {
476				rtchange(rt, gate, n->rip6_metric, ifp);
477				changes = _B_TRUE;
478			}
479		}
480		if (changes && supplier)
481			dynamic_update(ifp);
482		return;
483
484	default:
485		if (tracing & INPUT_BIT) {
486			(void) fprintf(ftrace,
487			    "Bad command %d in packet from %s\n",
488			    msg->rip6_cmd, buf1);
489			(void) fflush(ftrace);
490		}
491		return;
492	}
493}
494
495/*
496 * If changes have occurred, and if we have not sent a multicast
497 * recently, send a dynamic update.  This update is sent only
498 * on interfaces other than the one on which we received notice
499 * of the change.  If we are within MIN_WAIT_TIME of a full update,
500 * don't bother sending; if we just sent a dynamic update
501 * and set a timer (nextmcast), delay until that time.
502 * If we just sent a full update, delay the dynamic update.
503 * Set a timer for a randomized value to suppress additional
504 * dynamic updates until it expires; if we delayed sending
505 * the current changes, set needupdate.
506 */
507void
508dynamic_update(struct interface *ifp)
509{
510	int delay;
511
512	if (now.tv_sec - lastfullupdate.tv_sec >=
513	    supplyinterval - MIN_WAIT_TIME)
514		return;
515
516	if (now.tv_sec - lastmcast.tv_sec >= MIN_WAIT_TIME &&
517	    /* BEGIN CSTYLED */
518	    timercmp(&nextmcast, &now, <)) {
519	    /* END CSTYLED */
520		TRACE_ACTION("send dynamic update",
521		    (struct rt_entry *)NULL);
522		supplyall(&allrouters, RTS_CHANGED, ifp, _B_TRUE);
523		lastmcast = now;
524		needupdate = _B_FALSE;
525		nextmcast.tv_sec = 0;
526	} else {
527		needupdate = _B_TRUE;
528		TRACE_ACTION("delay dynamic update",
529		    (struct rt_entry *)NULL);
530	}
531
532	if (nextmcast.tv_sec == 0) {
533		delay = GET_RANDOM(MIN_WAIT_TIME * 1000000,
534		    MAX_WAIT_TIME * 1000000);
535		if (tracing & ACTION_BIT) {
536			(void) fprintf(ftrace,
537			    "inhibit dynamic update for %d msec\n",
538			    delay / 1000);
539			(void) fflush(ftrace);
540		}
541		nextmcast.tv_sec = delay / 1000000;
542		nextmcast.tv_usec = delay % 1000000;
543		timevaladd(&nextmcast, &now);
544		/*
545		 * If the next possibly dynamic update
546		 * is within MIN_WAIT_TIME of the next full
547		 * update, force the delay past the full
548		 * update, or we might send a dynamic update
549		 * just before the full update.
550		 */
551		if (nextmcast.tv_sec >
552		    lastfullupdate.tv_sec + supplyinterval - MIN_WAIT_TIME) {
553			nextmcast.tv_sec =
554			    lastfullupdate.tv_sec + supplyinterval + 1;
555		}
556	}
557}
558