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 2002 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 #include "defs.h"
36
37 #define IF_SEPARATOR ':'
38
39 struct interface *ifnet;
40
41 static int setup_listen_sock(int ifindex);
42 static void addrouteforif(struct interface *ifp);
43 static void resetup_listen_sock(struct interface *, int);
44
45 /*
46 * This is called at startup and after that, every CHECK_INTERVAL seconds or
47 * when a SIGHUP is received.
48 */
49 void
initifs(void)50 initifs(void)
51 {
52 static char *buf = NULL;
53 static uint_t maxbufsize = 0;
54 int bufsize;
55 int numifs;
56 struct lifnum lifn;
57 struct lifconf lifc;
58 struct lifreq lifr;
59 struct lifreq *lifrp;
60 int n;
61 struct interface ifs;
62 struct interface *ifp;
63 int netmaskchange = 0;
64 boolean_t changes = _B_FALSE;
65
66 lifn.lifn_family = AF_INET6;
67 lifn.lifn_flags = 0;
68 if (ioctl(iocsoc, SIOCGLIFNUM, (char *)&lifn) < 0) {
69 syslog(LOG_ERR, "initifs: ioctl (get interface numbers): %m");
70 return;
71 }
72 numifs = lifn.lifn_count;
73 bufsize = numifs * sizeof (struct lifreq);
74
75 if (buf == NULL || bufsize > maxbufsize) {
76 if (buf != NULL)
77 free(buf);
78 maxbufsize = bufsize;
79 buf = (char *)malloc(maxbufsize);
80 if (buf == NULL) {
81 syslog(LOG_ERR, "initifs: out of memory");
82 return;
83 }
84 }
85
86 lifc.lifc_family = AF_INET6;
87 lifc.lifc_flags = 0;
88 lifc.lifc_len = bufsize;
89 lifc.lifc_buf = buf;
90 if (ioctl(iocsoc, SIOCGLIFCONF, (char *)&lifc) < 0) {
91 syslog(LOG_ERR,
92 "initifs: ioctl (get interface configuration): %m");
93 return;
94 }
95
96 /*
97 * Mark all of the currently known interfaces in order to determine
98 * which of the these interfaces no longer exist.
99 */
100 for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next)
101 ifp->int_flags |= RIP6_IFF_MARKED;
102 lifrp = lifc.lifc_req;
103 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
104 bzero((char *)&ifs, sizeof (ifs));
105 (void) strncpy(lifr.lifr_name, lifrp->lifr_name,
106 sizeof (lifr.lifr_name));
107 if (ioctl(iocsoc, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
108 syslog(LOG_ERR,
109 "initifs: ioctl (get interface flags): %m");
110 continue;
111 }
112 if (!(lifr.lifr_flags & IFF_IPV6) ||
113 !(lifr.lifr_flags & IFF_MULTICAST) ||
114 (lifr.lifr_flags & IFF_LOOPBACK))
115 continue;
116
117 ifp = if_ifwithname(lifr.lifr_name);
118 if (ifp != NULL)
119 ifp->int_flags &= ~RIP6_IFF_MARKED;
120 if (lifr.lifr_flags & IFF_POINTOPOINT)
121 ifs.int_flags |= RIP6_IFF_POINTOPOINT;
122 if (lifr.lifr_flags & IFF_NORTEXCH)
123 ifs.int_flags |= RIP6_IFF_NORTEXCH;
124 if (lifr.lifr_flags & IFF_PRIVATE)
125 ifs.int_flags |= RIP6_IFF_PRIVATE;
126 if (lifr.lifr_flags & IFF_UP) {
127 ifs.int_flags |= RIP6_IFF_UP;
128 } else {
129 if (ifp != NULL) {
130 if (ifp->int_flags & RIP6_IFF_UP) {
131 /*
132 * If there is an transition from up to
133 * down for an exisiting interface,
134 * increment the counter.
135 */
136 ifp->int_transitions++;
137 changes = _B_TRUE;
138 }
139 if_purge(ifp);
140 }
141 continue;
142 }
143
144 if (ifs.int_flags & RIP6_IFF_POINTOPOINT) {
145 /*
146 * For point-to-point interfaces, retrieve both the
147 * local and the remote addresses.
148 */
149 if (ioctl(iocsoc, SIOCGLIFADDR, (char *)&lifr) < 0) {
150 syslog(LOG_ERR,
151 "initifs: ioctl (get interface address): "
152 "%m");
153 continue;
154 }
155 ifs.int_addr =
156 ((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr;
157 if (ioctl(iocsoc, SIOCGLIFDSTADDR, (char *)&lifr) < 0) {
158 syslog(LOG_ERR,
159 "initifs: ioctl (get destination address): "
160 "%m");
161 continue;
162 }
163 ifs.int_dstaddr = ((struct sockaddr_in6 *)
164 &lifr.lifr_dstaddr)->sin6_addr;
165 ifs.int_prefix_length = IPV6_ABITS;
166 } else {
167 /*
168 * For other interfaces, retreieve the prefix (including
169 * the prefix length.
170 */
171 if (ioctl(iocsoc, SIOCGLIFSUBNET, (char *)&lifr) < 0) {
172 syslog(LOG_ERR,
173 "initifs: ioctl (get subnet prefix): %m");
174 continue;
175 }
176 /*
177 * This should never happen but check for it in any case
178 * since the kernel stores it as an signed integer.
179 */
180 if (lifr.lifr_addrlen < 0 ||
181 lifr.lifr_addrlen > IPV6_ABITS) {
182 syslog(LOG_ERR,
183 "initifs: ioctl (get subnet prefix) "
184 "returned invalid prefix length of %d",
185 lifr.lifr_addrlen);
186 continue;
187 }
188 ifs.int_prefix_length = lifr.lifr_addrlen;
189 ifs.int_addr = ((struct sockaddr_in6 *)
190 &lifr.lifr_subnet)->sin6_addr;
191 }
192
193 if (ioctl(iocsoc, SIOCGLIFMETRIC, (char *)&lifr) < 0 ||
194 lifr.lifr_metric < 0)
195 ifs.int_metric = 1;
196 else
197 ifs.int_metric = lifr.lifr_metric + 1;
198
199 if (ioctl(iocsoc, SIOCGLIFINDEX, (char *)&lifr) < 0) {
200 syslog(LOG_ERR, "initifs: ioctl (get index): %m");
201 continue;
202 }
203 ifs.int_ifindex = lifr.lifr_index;
204
205 if (ioctl(iocsoc, SIOCGLIFMTU, (char *)&lifr) < 0) {
206 syslog(LOG_ERR, "initifs: ioctl (get mtu): %m");
207 continue;
208 }
209
210 /*
211 * If the interface's recorded MTU doesn't make sense, use
212 * IPV6_MIN_MTU instead.
213 */
214 if (lifr.lifr_mtu < IPV6_MIN_MTU)
215 ifs.int_mtu = IPV6_MIN_MTU;
216 else
217 ifs.int_mtu = lifr.lifr_mtu;
218
219 if (ifp != NULL) {
220 /*
221 * RIP6_IFF_NORTEXCH flag change by itself shouldn't
222 * cause an if_purge() call, which also purges all the
223 * routes heard off this interface. So, let's suppress
224 * changes of RIP6_IFF_NORTEXCH in the following
225 * comparisons.
226 */
227 if (ifp->int_prefix_length == ifs.int_prefix_length &&
228 ((ifp->int_flags | RIP6_IFF_NORTEXCH) ==
229 (ifs.int_flags | RIP6_IFF_NORTEXCH)) &&
230 ifp->int_metric == ifs.int_metric &&
231 ifp->int_ifindex == ifs.int_ifindex) {
232 /*
233 * Now let's make sure we capture the latest
234 * value of RIP6_IFF_NORTEXCH flag.
235 */
236 if (ifs.int_flags & RIP6_IFF_NORTEXCH)
237 ifp->int_flags |= RIP6_IFF_NORTEXCH;
238 else
239 ifp->int_flags &= ~RIP6_IFF_NORTEXCH;
240
241 if (!(ifp->int_flags & RIP6_IFF_POINTOPOINT) &&
242 IN6_ARE_ADDR_EQUAL(&ifp->int_addr,
243 &ifs.int_addr))
244 continue;
245 if ((ifp->int_flags & RIP6_IFF_POINTOPOINT) &&
246 IN6_ARE_ADDR_EQUAL(&ifp->int_dstaddr,
247 &ifs.int_dstaddr))
248 continue;
249 }
250 if_purge(ifp);
251 if (ifp->int_prefix_length != ifs.int_prefix_length)
252 netmaskchange = 1;
253 ifp->int_addr = ifs.int_addr;
254 ifp->int_dstaddr = ifs.int_dstaddr;
255 ifp->int_metric = ifs.int_metric;
256 /*
257 * If there is an transition from down to up for an
258 * exisiting interface, increment the counter.
259 */
260 if (!(ifp->int_flags & RIP6_IFF_UP) &&
261 (ifs.int_flags & RIP6_IFF_UP))
262 ifp->int_transitions++;
263 ifp->int_flags |= ifs.int_flags;
264 ifp->int_prefix_length = ifs.int_prefix_length;
265
266 /*
267 * If the interface index has changed, we may need to
268 * set up the listen socket again.
269 */
270 if (ifp->int_ifindex != ifs.int_ifindex) {
271 if (ifp->int_sock != -1) {
272 resetup_listen_sock(ifp,
273 ifs.int_ifindex);
274 }
275 ifp->int_ifindex = ifs.int_ifindex;
276 }
277
278 ifp->int_mtu = ifs.int_mtu;
279 } else {
280 char *cp;
281 int log_num;
282
283 ifp = (struct interface *)
284 malloc(sizeof (struct interface));
285 if (ifp == NULL) {
286 syslog(LOG_ERR, "initifs: out of memory");
287 return;
288 }
289 *ifp = ifs;
290 ifp->int_name = ifp->int_ifbase = NULL;
291 ifp->int_name =
292 (char *)malloc((size_t)strlen(lifr.lifr_name) + 1);
293 if (ifp->int_name == NULL) {
294 free(ifp);
295 syslog(LOG_ERR, "initifs: out of memory");
296 return;
297 }
298 (void) strcpy(ifp->int_name, lifr.lifr_name);
299 ifp->int_ifbase =
300 (char *)malloc((size_t)strlen(lifr.lifr_name) + 1);
301 if (ifp->int_ifbase == NULL) {
302 free(ifp->int_name);
303 free(ifp);
304 syslog(LOG_ERR, "initifs: out of memory");
305 return;
306 }
307 (void) strcpy(ifp->int_ifbase, lifr.lifr_name);
308 cp = (char *)index(ifp->int_ifbase, IF_SEPARATOR);
309 if (cp != NULL) {
310 /*
311 * Verify that the value following the separator
312 * is an integer greater than zero (the only
313 * possible value for a logical interface).
314 */
315 log_num = atoi((char *)(cp + 1));
316 if (log_num <= 0) {
317 free(ifp->int_ifbase);
318 free(ifp->int_name);
319 free(ifp);
320 syslog(LOG_ERR,
321 "initifs: interface name %s could "
322 "not be parsed", ifp->int_name);
323 return;
324 }
325 *cp = '\0';
326 } else {
327 log_num = 0;
328 }
329 if (log_num == 0) {
330 ifp->int_sock =
331 setup_listen_sock(ifp->int_ifindex);
332 } else {
333 ifp->int_sock = -1;
334 }
335 ifp->int_next = ifnet;
336 ifnet = ifp;
337 traceinit(ifp);
338 }
339 addrouteforif(ifp);
340 changes = _B_TRUE;
341 }
342
343 /*
344 * Any remaining interfaces that are still marked and which were in an
345 * up state (RIP6_IFF_UP) need to removed from the routing table.
346 */
347 for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
348 if ((ifp->int_flags & (RIP6_IFF_MARKED | RIP6_IFF_UP)) ==
349 (RIP6_IFF_MARKED | RIP6_IFF_UP)) {
350 if_purge(ifp);
351 ifp->int_flags &= ~RIP6_IFF_MARKED;
352 changes = _B_TRUE;
353 }
354 }
355 if (netmaskchange)
356 rtchangeall();
357 if (supplier & changes)
358 dynamic_update((struct interface *)NULL);
359 }
360
361 static void
addrouteforif(struct interface * ifp)362 addrouteforif(struct interface *ifp)
363 {
364 struct rt_entry *rt;
365 struct in6_addr *dst;
366
367 if (ifp->int_flags & RIP6_IFF_POINTOPOINT)
368 dst = &ifp->int_dstaddr;
369 else
370 dst = &ifp->int_addr;
371
372 rt = rtlookup(dst, ifp->int_prefix_length);
373
374 if (rt != NULL) {
375 if (rt->rt_state & RTS_INTERFACE)
376 return;
377 rtdelete(rt);
378 }
379 rtadd(dst, &ifp->int_addr, ifp->int_prefix_length, ifp->int_metric, 0,
380 _B_TRUE, ifp);
381 }
382
383 static int
setup_listen_sock(int ifindex)384 setup_listen_sock(int ifindex)
385 {
386 int sock;
387 struct sockaddr_in6 sin6;
388 uint_t hops;
389 struct ipv6_mreq allrouters_mreq;
390 int on = 1;
391 int off = 0;
392 int recvsize;
393
394 sock = socket(AF_INET6, SOCK_DGRAM, 0);
395 if (sock == -1)
396 goto sock_fail;
397
398 if (setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, (char *)&ifindex,
399 sizeof (ifindex)) < 0) {
400 syslog(LOG_ERR,
401 "setup_listen_sock: setsockopt: IPV6_BOUND_IF: %m");
402 goto sock_fail;
403 }
404
405 hops = IPV6_MAX_HOPS;
406 if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&hops,
407 sizeof (hops)) < 0) {
408 syslog(LOG_ERR,
409 "setup_listen_sock: setsockopt: IPV6_UNICAST_HOPS: %m");
410 goto sock_fail;
411 }
412
413 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops,
414 sizeof (hops)) < 0) {
415 syslog(LOG_ERR,
416 "setup_listen_sock: setsockopt: IPV6_MULTICAST_HOPS: %m");
417 goto sock_fail;
418 }
419
420 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&off,
421 sizeof (off)) < 0) {
422 syslog(LOG_ERR,
423 "setup_listen_sock: setsockopt: IPV6_MULTICAST_LOOP: %m");
424 goto sock_fail;
425 }
426
427 allrouters_mreq.ipv6mr_multiaddr = allrouters_in6;
428 allrouters_mreq.ipv6mr_interface = ifindex;
429 if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
430 (char *)&allrouters_mreq, sizeof (allrouters_mreq)) < 0) {
431 if (errno != EADDRINUSE) {
432 syslog(LOG_ERR,
433 "setup_listen_sock: setsockopt: "
434 "IPV6_JOIN_GROUP: %m");
435 goto sock_fail;
436 }
437 }
438
439 if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (char *)&on,
440 sizeof (off)) < 0) {
441 syslog(LOG_ERR,
442 "setup_listen_sock: setsockopt: IPV6_RECVHOPLIMIT: %m");
443 goto sock_fail;
444 }
445
446 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
447 sizeof (on)) < 0) {
448 syslog(LOG_ERR,
449 "setup_listen_sock: setsockopt: SO_REUSEADDR: %m");
450 goto sock_fail;
451 }
452
453 recvsize = RCVBUFSIZ;
454 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize,
455 sizeof (int)) < 0) {
456 syslog(LOG_ERR, "setup_listen_sock: setsockopt: SO_RCVBUF: %m");
457 goto sock_fail;
458 }
459
460 bzero((char *)&sin6, sizeof (sin6));
461 sin6.sin6_family = AF_INET6;
462 sin6.sin6_port = rip6_port;
463 if (bind(sock, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
464 syslog(LOG_ERR, "setup_listen_sock: bind: %m");
465 goto sock_fail;
466 }
467
468 poll_ifs_num++;
469 if (poll_ifs == NULL) {
470 poll_ifs = (struct pollfd *)
471 malloc(max_poll_ifs * sizeof (struct pollfd));
472 } else if (poll_ifs_num > max_poll_ifs) {
473 max_poll_ifs *= 2;
474 poll_ifs = (struct pollfd *)realloc((char *)poll_ifs,
475 max_poll_ifs * sizeof (struct pollfd));
476 }
477 if (poll_ifs == NULL) {
478 syslog(LOG_ERR, "setup_listen_sock: out of memory");
479 goto sock_fail;
480 }
481
482 poll_ifs[poll_ifs_num - 1].fd = sock;
483 poll_ifs[poll_ifs_num - 1].events = POLLIN;
484 return (sock);
485
486 sock_fail:
487 if (sock > 0)
488 (void) close(sock);
489 return (-1);
490 }
491
492 /*
493 * resetup_listen_sock is primarily used in the case where a tunnel was
494 * plumbed, unplumbed, then plumbed again. This would cause the binding set by
495 * IPV6_BOUND_IF to be useless, and sends to the associated socket will be
496 * transmitted on the wrong interface. resetup_listen_sock
497 * closes the socket,
498 * removes the socket from poll_ifs[]
499 * plugs the hole in poll_ifs[]
500 * calls setup_listen_sock to set up the socket again
501 */
502 void
resetup_listen_sock(struct interface * ifp,int newindex)503 resetup_listen_sock(struct interface *ifp, int newindex)
504 {
505 int i;
506
507 (void) close(ifp->int_sock);
508
509 /* Remove socket from poll_ifs[]. */
510 for (i = poll_ifs_num - 1; i >= 0; i--) {
511
512 if (poll_ifs[i].fd == ifp->int_sock) {
513
514 poll_ifs[i].fd = 0;
515 poll_ifs[i].events = 0;
516
517 /*
518 * Remove hole in poll_ifs. Possibly exchange
519 * poll_ifs[i] with poll_ifs[poll_ifs_num-1].
520 */
521 if (i != poll_ifs_num - 1) {
522 poll_ifs[i] = poll_ifs[poll_ifs_num - 1];
523 poll_ifs[poll_ifs_num - 1].fd = 0;
524 poll_ifs[poll_ifs_num - 1].events = 0;
525 }
526 poll_ifs_num--;
527
528 /* Now set everything up again. */
529 ifp->int_sock = setup_listen_sock(newindex);
530 break;
531 }
532 }
533 }
534