xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/input.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 
42 static char	buf1[INET6_ADDRSTRLEN];
43 static char	buf2[INET6_ADDRSTRLEN];
44 
45 static 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  */
52 static void *
53 find_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  */
70 void
71 in_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  */
127 static void
128 rip_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  */
507 void
508 dynamic_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