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