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 *
find_ancillary(struct msghdr * rmsg,int cmsg_type)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
in_data(struct interface * ifp)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
rip_input(struct sockaddr_in6 * from,int size,uint_t hopcount,struct interface * ifp)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
dynamic_update(struct interface * ifp)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