# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. SVCMETHOD= svc-ripng MANIFEST= ripng.xml PROG= in.ripngd OBJS= if.o input.o main.o output.o startup.o tables.o timer.o trace.o SRCS= $(OBJS:%.o=%.c) include ../../../Makefile.cmd ROOTMANIFESTDIR= $(ROOTSVCNETWORKROUTING) # these #defines are required to use UNIX 98 interfaces _D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ $(OBJS) : CFLAGS += $(CCVERBOSE) $(OBJS) : CPPFLAGS += $(_D_UNIX98_EXTN) LINTFLAGS += $(_D_UNIX98_EXTN) # not linted SMATCH=off # in.ripngd uses the ancillary data feature which is available only through # UNIX 98 standards version of Socket interface. This interface is supposed to # be accessed by -lxnet. In addition, -lsocket is used to # capture new not-yet-standard interfaces. Someday -lxnet alone should be enough # when IPv6 inspired new interfaces are part of standards. LDLIBS += -lxnet -lsocket .KEEP_STATE: .PARALLEL: $(OBJS) all: $(PROG) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) include ../Makefile.lib install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) check: $(CHKMANIFEST) clean: $(RM) $(OBJS) lint: lint_SRCS include ../../../Makefile.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ #ifndef _IN_RIPNGD_DEFS_H #define _IN_RIPNGD_DEFS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "table.h" #include "trace.h" #include "interface.h" #define PATH_PID "/var/run/in.ripngd.pid" /* * Timer values (in seconds) used in managing the routing table. * Every update forces an entry's timer to be reset. After * EXPIRE_TIME without updates, the entry is marked invalid, * but held onto until GARBAGE_TIME so that others may * see it "be deleted". */ #define EXPIRE_TIME 180 /* time to mark entry invalid */ #define GARBAGE_TIME 300 /* time to garbage collect */ #define MIN_SUPPLY_TIME 15 /* min. time to supply tables */ #define MAX_SUPPLY_TIME 45 /* max. time to supply tables */ #define MIN_WAIT_TIME 1 /* min. interval to multicast changes */ #define MAX_WAIT_TIME 5 /* max. time to delay changes */ /* * Return a random number from a an range inclusive of the endpoints */ #define GET_RANDOM(LOW, HIGH) (random() % ((HIGH) - (LOW) + 1) + (LOW)) /* * When we find any interfaces marked down we rescan the * kernel every CHECK_INTERVAL seconds to see if they've * come up. */ #define CHECK_INTERVAL 60 #define START_POLL_SIZE 5 #define min(a, b) ((a) > (b) ? (b) : (a)) /* * The maximum receive buffer size is controlled via Solaris' NDD udp_max_buf * tunable. */ #define RCVBUFSIZ 65536 #define TIME_TO_MSECS(tval) ((tval).tv_sec * 1000 + (tval).tv_usec / 1000) #define HOPCNT_INFINITY 16 /* RFC 2080, section 2.1 */ #define HOPCNT_NEXTHOP 255 /* RFC 2080, section 2.1.1 */ /* * XXX Some of these are defined in under _KERNEL (but should be * defined in for completeness). */ #define IPV6_MAX_HOPS 255 /* Max IPv6 hops */ #define IPV6_MAX_PACKET 65535 /* maximum IPv6 packet size */ #define IPV6_MIN_MTU 1280 /* Minimum IPv6 MTU */ extern struct sockaddr_in6 allrouters; extern struct in6_addr allrouters_in6; extern char *control; extern boolean_t dopoison; extern struct interface *ifnet; extern boolean_t install; extern int iocsoc; extern struct timeval lastfullupdate; extern struct timeval lastmcast; extern int max_poll_ifs; extern struct rip6 *msg; extern boolean_t needupdate; extern struct timeval nextmcast; extern struct timeval now; extern char *packet; extern struct pollfd *poll_ifs; extern int poll_ifs_num; extern int rip6_port; extern int supplyinterval; extern boolean_t supplier; extern void dynamic_update(struct interface *); extern void in_data(struct interface *); extern void initifs(void); extern void sendpacket(struct sockaddr_in6 *, struct interface *, int, int); extern void setup_rtsock(void); extern void solicitall(struct sockaddr_in6 *); extern void supply(struct sockaddr_in6 *, struct interface *, int, boolean_t); extern void supplyall(struct sockaddr_in6 *, int, struct interface *, boolean_t); extern void term(void); extern void timer(void); extern void timevaladd(struct timeval *, struct timeval *); #ifdef __cplusplus } #endif #endif /* _IN_RIPNGD_DEFS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing Table Management Daemon */ #include "defs.h" /* * Find the interface with given name. */ struct interface * if_ifwithname(char *name) { struct interface *ifp; for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { if (ifp->int_name != NULL && strcmp(ifp->int_name, name) == 0) break; } return (ifp); } /* * An interface has declared itself down - remove it completely * from our routing tables but keep the interface structure around. */ void if_purge(struct interface *pifp) { rtpurgeif(pifp); pifp->int_flags &= ~RIP6_IFF_UP; } static void if_dump2(FILE *fp) { struct interface *ifp; char buf1[INET6_ADDRSTRLEN]; static struct bits { uint_t t_bits; char *t_name; } flagbits[] = { /* BEGIN CSTYLED */ { RIP6_IFF_UP, "UP" }, { RIP6_IFF_POINTOPOINT, "POINTOPOINT" }, { RIP6_IFF_MARKED, "MARKED" }, { RIP6_IFF_NORTEXCH, "NORTEXCH" }, { RIP6_IFF_PRIVATE, "PRIVATE" }, { 0, NULL } /* END CSTYLED */ }; struct bits *p; char c; boolean_t first; for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { (void) fprintf(fp, "interface %s:\n", (ifp->int_name != NULL) ? ifp->int_name : "(noname)"); (void) fprintf(fp, "\tflags "); c = ' '; for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) { if ((ifp->int_flags & p->t_bits) == 0) continue; (void) fprintf(fp, "%c%s", c, p->t_name); if (first) { c = '|'; first = _B_FALSE; } } if (first) (void) fprintf(fp, " 0"); (void) fprintf(fp, "\n\tpackets received %d\n", ifp->int_ipackets); (void) fprintf(fp, "\tpackets sent %d\n", ifp->int_opackets); (void) fprintf(fp, "\ttransitions %d\n", ifp->int_transitions); if ((ifp->int_flags & RIP6_IFF_UP) == 0) continue; if (ifp->int_flags & RIP6_IFF_POINTOPOINT) { (void) fprintf(fp, "\tlocal %s\n", inet_ntop(AF_INET6, (void *)&ifp->int_addr, buf1, sizeof (buf1))); (void) fprintf(fp, "\tremote %s\n", inet_ntop(AF_INET6, (void *)&ifp->int_dstaddr, buf1, sizeof (buf1))); } else { (void) fprintf(fp, "\tprefix %s/%d\n", inet_ntop(AF_INET6, (void *)&ifp->int_addr, buf1, sizeof (buf1)), ifp->int_prefix_length); } (void) fprintf(fp, "\tmetric %d\n", ifp->int_metric); (void) fprintf(fp, "\tmtu %d\n", ifp->int_mtu); } (void) fflush(fp); } void if_dump(void) { if (ftrace != NULL) if_dump2(ftrace); else if_dump2(stderr); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing Table Management Daemon */ #include "defs.h" static char buf1[INET6_ADDRSTRLEN]; static char buf2[INET6_ADDRSTRLEN]; static void rip_input(struct sockaddr_in6 *from, int size, uint_t hopcount, struct interface *ifp); /* * Return a pointer to the specified option buffer. * If not found return NULL. */ static void * find_ancillary(struct msghdr *rmsg, int cmsg_type) { struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(rmsg); cmsg != NULL; cmsg = CMSG_NXTHDR(rmsg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == cmsg_type) { return (CMSG_DATA(cmsg)); } } return (NULL); } /* * Read a packet and passes it to rip_input() for processing. */ void in_data(struct interface *ifp) { struct sockaddr_in6 from; int len; struct msghdr rmsg; struct iovec iov; uchar_t *hopcntopt; iov.iov_base = packet; iov.iov_len = IPV6_MAX_PACKET; rmsg.msg_name = &from; rmsg.msg_namelen = (socklen_t)sizeof (from); rmsg.msg_iov = &iov; rmsg.msg_iovlen = 1; rmsg.msg_control = control; rmsg.msg_controllen = IPV6_MAX_PACKET; if ((len = recvmsg(ifp->int_sock, &rmsg, 0)) < 0) { /* * Only syslog if a true error occurred. */ if (errno != EINTR) syslog(LOG_ERR, "in_data: recvmsg: %m"); return; } if (len == 0) return; if (tracing & INPUT_BIT) { (void) inet_ntop(from.sin6_family, &from.sin6_addr, buf1, sizeof (buf1)); } /* Ignore packets > 64k or control buffers that don't fit */ if (rmsg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { if (tracing & INPUT_BIT) { (void) fprintf(stderr, "Truncated message: msg_flags 0x%x from %s\n", rmsg.msg_flags, buf1); } return; } if ((hopcntopt = find_ancillary(&rmsg, IPV6_HOPLIMIT)) == NULL) { if (tracing & INPUT_BIT) { (void) fprintf(stderr, "Unknown hop limit from %s\n", buf1); } return; } rip_input(&from, len, *(uint_t *)hopcntopt, ifp); } /* * Process a newly received packet. */ static void rip_input(struct sockaddr_in6 *from, int size, uint_t hopcount, struct interface *ifp) { struct rt_entry *rt; struct netinfo6 *n; int newsize; boolean_t changes = _B_FALSE; int answer = supplier; struct in6_addr prefix; struct in6_addr nexthop; struct in6_addr *gate; boolean_t foundnexthop = _B_FALSE; struct sioc_addrreq sa; struct sockaddr_in6 *sin6; TRACE_INPUT(ifp, from, size); if (tracing & INPUT_BIT) { (void) inet_ntop(from->sin6_family, (void *)&from->sin6_addr, buf1, sizeof (buf1)); } /* * If the packet is recevied on an interface with IFF_NORTEXCH flag set, * we ignore the packet. */ if (ifp->int_flags & RIP6_IFF_NORTEXCH) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Ignore received RIPng packet on %s " "(no route exchange on interface)\n", ifp->int_name); (void) fflush(ftrace); } return; } if (msg->rip6_vers != RIPVERSION6) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad version number %d in packet from %s\n", msg->rip6_vers, buf1); (void) fflush(ftrace); } return; } if (ntohs(msg->rip6_res1) != 0) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Non-zero reserved octets found in packet from " "%s\n", buf1); (void) fflush(ftrace); } } switch (msg->rip6_cmd) { case RIPCMD6_REQUEST: /* multicasted request */ ifp->int_ipackets++; newsize = 0; /* * Adjust size by the length of the command, version and * reserved fields (which are in total 32-bit aligned). */ size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) + sizeof (msg->rip6_res1); /* * From section 2.4.1 of RFC 2080: * * If there is exactly one entry in the request with a * destination prefix of zero, a prefix length of zero and * an infinite metric, then supply the entire routing * table. */ n = msg->rip6_nets; if (size == sizeof (struct netinfo6) && n->rip6_prefix_length == 0 && n->rip6_metric == HOPCNT_INFINITY) { rtcreate_prefix(&n->rip6_prefix, &prefix, n->rip6_prefix_length); if (IN6_IS_ADDR_UNSPECIFIED(&prefix)) { supply(from, ifp, 0, from->sin6_port == rip6_port); return; } } for (; size >= sizeof (struct netinfo6); size -= sizeof (struct netinfo6), n++) { if (n->rip6_prefix_length > IPV6_ABITS) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad prefix length %d in request " "from %s\n", n->rip6_prefix_length, buf1); (void) fflush(ftrace); } continue; } if (IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix) || IN6_IS_ADDR_MULTICAST(&n->rip6_prefix)) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad prefix %s in request from " "%s\n", inet_ntop(AF_INET6, (void *)&n->rip6_prefix, buf2, sizeof (buf2)), buf1); (void) fflush(ftrace); } continue; } rtcreate_prefix(&n->rip6_prefix, &prefix, n->rip6_prefix_length); rt = rtlookup(&prefix, n->rip6_prefix_length); n->rip6_metric = (rt == NULL ? HOPCNT_INFINITY : min(rt->rt_metric, HOPCNT_INFINITY)); newsize += sizeof (struct netinfo6); } if (size > 0) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Ignoring %d octets of trailing data in " "request from %s\n", size, buf1); (void) fflush(ftrace); } } if (answer && newsize > 0) { /* * Adjust newsize by the length of the command, version * and reserved fields (which are in total 32-bit * aligned). */ msg->rip6_cmd = RIPCMD6_RESPONSE; newsize += sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) + sizeof (msg->rip6_res1); sendpacket(from, ifp, newsize, 0); } return; case RIPCMD6_RESPONSE: if (hopcount != IPV6_MAX_HOPS) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad hop count %d in response from %s\n", hopcount, buf1); (void) fflush(ftrace); } return; } if (from->sin6_port != rip6_port) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad source port %d in response from %s\n", from->sin6_port, buf1); (void) fflush(ftrace); } return; } if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad source address (not link-local) in " "response from %s\n", buf1); (void) fflush(ftrace); } return; } ifp->int_ipackets++; /* * Adjust size by the length of the command, version and * reserved fields (which are in total 32-bit aligned). */ size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) + sizeof (msg->rip6_res1); for (n = msg->rip6_nets; supplier && size >= sizeof (struct netinfo6); size -= sizeof (struct netinfo6), n++) { /* * From section 2.1.1 of RFC 2080: * * This is a next hop RTE if n->rip6_metric is set to * HOPCNT_NEXTHOP. If the next hop address (which is * placed in the prefix field of this special RTE) is * unspecified or is not a link-local address, then use * the originator's address instead (effectively turning * off next hop RTE processing.) */ if (n->rip6_metric == HOPCNT_NEXTHOP) { /* * First check to see if the unspecified address * was given as the next hop address. This is * the correct way of specifying the end of use * of a next hop address. */ if (IN6_IS_ADDR_UNSPECIFIED(&n->rip6_prefix)) { foundnexthop = _B_FALSE; continue; } /* * A next hop address that is not a link-local * address is treated as the unspecified one. * Trace this event if input tracing is enabled. */ if (!IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix)) { foundnexthop = _B_FALSE; if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad next hop %s in " "response from %s\n", inet_ntop(AF_INET6, (void *)&n->rip6_prefix, buf2, sizeof (buf2)), buf1); } continue; } /* * Verify that the next hop address is not one * of our own. */ sin6 = (struct sockaddr_in6 *)&sa.sa_addr; sin6->sin6_family = AF_INET6; sin6->sin6_addr = n->rip6_prefix; if (ioctl(iocsoc, SIOCTMYADDR, (char *)&sa) < 0) { syslog(LOG_ERR, "rip_input: " "ioctl (verify my address): %m"); return; } if (sa.sa_res != 0) { foundnexthop = _B_FALSE; if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad next hop %s is self " "in response from %s\n", inet_ntop(AF_INET6, (void *)&n->rip6_prefix, buf2, sizeof (buf2)), buf1); } continue; } foundnexthop = _B_TRUE; nexthop = n->rip6_prefix; continue; } if (foundnexthop) gate = &nexthop; else gate = &from->sin6_addr; if (n->rip6_metric > HOPCNT_INFINITY || n->rip6_metric < 1) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad metric %d in response from " "%s\n", n->rip6_metric, buf1); (void) fflush(ftrace); } continue; } if (n->rip6_prefix_length > IPV6_ABITS) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad prefix length %d in response " "from %s\n", n->rip6_prefix_length, buf1); (void) fflush(ftrace); } continue; } if (IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix) || IN6_IS_ADDR_MULTICAST(&n->rip6_prefix)) { if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad prefix %s in response from " "%s\n", inet_ntop(AF_INET6, (void *)&n->rip6_prefix, buf2, sizeof (buf2)), buf1); (void) fflush(ftrace); } continue; } /* Include metric for incoming interface */ n->rip6_metric += IFMETRIC(ifp); rtcreate_prefix(&n->rip6_prefix, &prefix, n->rip6_prefix_length); rt = rtlookup(&prefix, n->rip6_prefix_length); if (rt == NULL) { if (n->rip6_metric < HOPCNT_INFINITY) { rtadd(&prefix, gate, n->rip6_prefix_length, n->rip6_metric, n->rip6_route_tag, _B_FALSE, ifp); changes = _B_TRUE; } continue; } /* * If the supplied metric is at least HOPCNT_INFINITY * and the current metric of the route is * HOPCNT_INFINITY, then this particular RTE is ignored. */ if (n->rip6_metric >= HOPCNT_INFINITY && rt->rt_metric == HOPCNT_INFINITY) continue; /* * From section 2.4.2 of RFC 2080: * * Update if any one of the following is true * * 1) From current gateway and a different metric. * 2) From current gateway and a different index. * 3) A shorter (smaller) metric. * 4) Equivalent metric and an age at least * one-half of EXPIRE_TIME. * * Otherwise, update timer for the interface on which * the packet arrived. */ if (IN6_ARE_ADDR_EQUAL(gate, &rt->rt_router)) { if (n->rip6_metric != rt->rt_metric || rt->rt_ifp != ifp) { rtchange(rt, gate, n->rip6_metric, ifp); changes = _B_TRUE; } else if (n->rip6_metric < HOPCNT_INFINITY) { rt->rt_timer = 0; } } else if (n->rip6_metric < rt->rt_metric || (rt->rt_timer > (EXPIRE_TIME / 2) && rt->rt_metric == n->rip6_metric)) { rtchange(rt, gate, n->rip6_metric, ifp); changes = _B_TRUE; } } if (changes && supplier) dynamic_update(ifp); return; default: if (tracing & INPUT_BIT) { (void) fprintf(ftrace, "Bad command %d in packet from %s\n", msg->rip6_cmd, buf1); (void) fflush(ftrace); } return; } } /* * If changes have occurred, and if we have not sent a multicast * recently, send a dynamic update. This update is sent only * on interfaces other than the one on which we received notice * of the change. If we are within MIN_WAIT_TIME of a full update, * don't bother sending; if we just sent a dynamic update * and set a timer (nextmcast), delay until that time. * If we just sent a full update, delay the dynamic update. * Set a timer for a randomized value to suppress additional * dynamic updates until it expires; if we delayed sending * the current changes, set needupdate. */ void dynamic_update(struct interface *ifp) { int delay; if (now.tv_sec - lastfullupdate.tv_sec >= supplyinterval - MIN_WAIT_TIME) return; if (now.tv_sec - lastmcast.tv_sec >= MIN_WAIT_TIME && /* BEGIN CSTYLED */ timercmp(&nextmcast, &now, <)) { /* END CSTYLED */ TRACE_ACTION("send dynamic update", (struct rt_entry *)NULL); supplyall(&allrouters, RTS_CHANGED, ifp, _B_TRUE); lastmcast = now; needupdate = _B_FALSE; nextmcast.tv_sec = 0; } else { needupdate = _B_TRUE; TRACE_ACTION("delay dynamic update", (struct rt_entry *)NULL); } if (nextmcast.tv_sec == 0) { delay = GET_RANDOM(MIN_WAIT_TIME * 1000000, MAX_WAIT_TIME * 1000000); if (tracing & ACTION_BIT) { (void) fprintf(ftrace, "inhibit dynamic update for %d msec\n", delay / 1000); (void) fflush(ftrace); } nextmcast.tv_sec = delay / 1000000; nextmcast.tv_usec = delay % 1000000; timevaladd(&nextmcast, &now); /* * If the next possibly dynamic update * is within MIN_WAIT_TIME of the next full * update, force the delay past the full * update, or we might send a dynamic update * just before the full update. */ if (nextmcast.tv_sec > lastfullupdate.tv_sec + supplyinterval - MIN_WAIT_TIME) { nextmcast.tv_sec = lastfullupdate.tv_sec + supplyinterval + 1; } } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ #ident "%Z%%M% %I% %E% SMI" struct interface { struct interface *int_next; struct in6_addr int_addr; /* address on this if */ struct in6_addr int_dstaddr; /* other end of p-to-p link */ int int_metric; /* init's routing entry */ uint_t int_flags; /* see below */ int int_prefix_length; /* prefix length on this if */ char *int_name; /* from kernel if structure */ char *int_ifbase; /* name of physical interface */ int int_sock; /* socket on if to send/recv */ int int_ifindex; /* interface index */ uint_t int_mtu; /* maximum transmission unit */ struct ifdebug int_input, int_output; /* packet tracing stuff */ int int_ipackets; /* input packets received */ int int_opackets; /* output packets sent */ ushort_t int_transitions; /* times gone up-down */ }; #define RIP6_IFF_UP 0x1 /* interface is up */ #define RIP6_IFF_POINTOPOINT 0x2 /* interface is p-to-p link */ #define RIP6_IFF_MARKED 0x4 /* to determine removed ifs */ #define RIP6_IFF_NORTEXCH 0x8 /* don't exchange route info */ #define RIP6_IFF_PRIVATE 0x10 /* interface is private */ #define IFMETRIC(ifp) ((ifp != NULL) ? (ifp)->int_metric : 1) extern void if_dump(void); extern struct interface *if_ifwithname(char *); extern void if_purge(struct interface *); /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ #include "defs.h" struct sockaddr_in6 allrouters; char *control; boolean_t dopoison = _B_TRUE; /* Do poison reverse */ int iocsoc; struct timeval lastfullupdate; /* last time full table multicast */ struct timeval lastmcast; /* last time all/changes multicast */ int max_poll_ifs = START_POLL_SIZE; struct rip6 *msg; boolean_t needupdate; /* true if need update at nextmcast */ struct timeval nextmcast; /* time to wait before changes mcast */ struct timeval now; /* current idea of time */ char *packet; struct pollfd *poll_ifs = NULL; int poll_ifs_num = 0; int rip6_port; boolean_t supplier = _B_TRUE; /* process should supply updates */ struct in6_addr allrouters_in6 = { /* BEGIN CSTYLED */ { 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9 } /* END CSTYLED */ }; static void timevalsub(struct timeval *t1, struct timeval *t2); static void usage(char *fname) { (void) fprintf(stderr, "usage: " "%s [ -P ] [ -p port ] [ -q ] [ -s ] [ -t ] [ -v ] []\n", fname); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int i, n; struct interface *ifp; int c; struct timeval waittime; int timeout; boolean_t daemon = _B_TRUE; /* Fork off a detached daemon */ FILE *pidfp; mode_t pidmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */ rip6_port = htons(IPPORT_ROUTESERVER6); allrouters.sin6_family = AF_INET6; allrouters.sin6_port = rip6_port; allrouters.sin6_addr = allrouters_in6; while ((c = getopt(argc, argv, "nsqvTtdgPp:")) != EOF) { switch (c) { case 'n': install = _B_FALSE; break; case 's': supplier = _B_TRUE; break; case 'q': supplier = _B_FALSE; break; case 'v': tracing |= ACTION_BIT; break; case 'T': daemon = _B_FALSE; break; case 't': tracepackets = _B_TRUE; daemon = _B_FALSE; tracing |= (INPUT_BIT | OUTPUT_BIT); break; case 'd': break; case 'P': dopoison = _B_FALSE; break; case 'p': rip6_port = htons(atoi(optarg)); allrouters.sin6_port = rip6_port; break; default: usage(argv[0]); /* NOTREACHED */ } } /* * Any extra argument is considered * a tracing log file. */ if (optind < argc) { traceon(argv[optind]); } else if (tracing && !daemon) { traceonfp(stdout); } else if (tracing) { (void) fprintf(stderr, "Need logfile with -v\n"); usage(argv[0]); /* NOTREACHED */ } if (daemon) { int t; if (fork()) exit(EXIT_SUCCESS); for (t = 0; t < 20; t++) { if (!tracing || (t != fileno(ftrace))) (void) close(t); } (void) open("/", 0); (void) dup2(0, 1); (void) dup2(0, 2); (void) setsid(); } /* Store our process id, blow away any existing file if it exists. */ if ((pidfp = fopen(PATH_PID, "w")) == NULL) { (void) fprintf(stderr, "%s: unable to open " PATH_PID ": %s\n", argv[0], strerror(errno)); } else { (void) fprintf(pidfp, "%ld\n", getpid()); (void) fclose(pidfp); (void) chmod(PATH_PID, pidmode); } iocsoc = socket(AF_INET6, SOCK_DGRAM, 0); if (iocsoc < 0) { syslog(LOG_ERR, "main: socket: %m"); exit(EXIT_FAILURE); } setup_rtsock(); /* * Allocate the buffer to hold the RIPng packet. In reality, it will be * smaller than IPV6_MAX_PACKET octets due to (at least) the IPv6 and * UDP headers but IPV6_MAX_PACKET is a convenient size. */ packet = (char *)malloc(IPV6_MAX_PACKET); if (packet == NULL) { syslog(LOG_ERR, "main: malloc: %m"); exit(EXIT_FAILURE); } msg = (struct rip6 *)packet; /* * Allocate the buffer to hold the ancillary data. This data is used to * insure that the incoming hop count of a RIPCMD6_RESPONSE message is * IPV6_MAX_HOPS which indicates that it came from a direct neighbor * (namely, no intervening router decremented it). */ control = (char *)malloc(IPV6_MAX_PACKET); if (control == NULL) { syslog(LOG_ERR, "main: malloc: %m"); exit(EXIT_FAILURE); } openlog("in.ripngd", LOG_PID | LOG_CONS, LOG_DAEMON); (void) gettimeofday(&now, (struct timezone *)NULL); initifs(); solicitall(&allrouters); if (supplier) supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE); (void) sigset(SIGALRM, (void (*)(int))timer); (void) sigset(SIGHUP, (void (*)(int))initifs); (void) sigset(SIGTERM, (void (*)(int))term); (void) sigset(SIGUSR1, (void (*)(int))if_dump); (void) sigset(SIGUSR2, (void (*)(int))rtdump); /* * Seed the pseudo-random number generator for GET_RANDOM(). */ srandom((uint_t)gethostid()); timer(); for (;;) { if (needupdate) { waittime = nextmcast; timevalsub(&waittime, &now); if (waittime.tv_sec < 0) { timeout = 0; } else { timeout = TIME_TO_MSECS(waittime); } if (tracing & ACTION_BIT) { (void) fprintf(ftrace, "poll until dynamic update in %d msec\n", timeout); (void) fflush(ftrace); } } else { timeout = INFTIM; } if ((n = poll(poll_ifs, poll_ifs_num, timeout)) < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "main: poll: %m"); exit(EXIT_FAILURE); } (void) sighold(SIGALRM); (void) sighold(SIGHUP); /* * Poll timed out. */ if (n == 0) { if (needupdate) { TRACE_ACTION("send delayed dynamic update", (struct rt_entry *)NULL); (void) gettimeofday(&now, (struct timezone *)NULL); supplyall(&allrouters, RTS_CHANGED, (struct interface *)NULL, _B_TRUE); lastmcast = now; needupdate = _B_FALSE; nextmcast.tv_sec = 0; } (void) sigrelse(SIGHUP); (void) sigrelse(SIGALRM); continue; } (void) gettimeofday(&now, (struct timezone *)NULL); for (i = 0; i < poll_ifs_num; i++) { /* * This case should never happen. */ if (poll_ifs[i].revents & POLLERR) { syslog(LOG_ERR, "main: poll returned a POLLERR event"); continue; } if (poll_ifs[i].revents & POLLIN) { for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { if (poll_ifs[i].fd == ifp->int_sock) in_data(ifp); } } } (void) sigrelse(SIGHUP); (void) sigrelse(SIGALRM); } return (0); } void timevaladd(struct timeval *t1, struct timeval *t2) { t1->tv_sec += t2->tv_sec; if ((t1->tv_usec += t2->tv_usec) > 1000000) { t1->tv_sec++; t1->tv_usec -= 1000000; } } void timevalsub(struct timeval *t1, struct timeval *t2) { t1->tv_sec -= t2->tv_sec; if ((t1->tv_usec -= t2->tv_usec) < 0) { t1->tv_sec--; t1->tv_usec += 1000000; } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ /* * Routing Table Management Daemon */ #include "defs.h" /* * Apply the function "supply" to all active * interfaces with a link-local address. */ void supplyall(struct sockaddr_in6 *sin6, int rtstate, struct interface *skipif, boolean_t splith) { struct interface *ifp; for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { if ((ifp->int_flags & RIP6_IFF_UP) == 0) continue; if (ifp->int_flags & RIP6_IFF_NORTEXCH) { if (tracing & OUTPUT_BIT) { (void) fprintf(ftrace, "Suppress sending RIPng response packet " "on %s (no route exchange on interface)\n", ifp->int_name); (void) fflush(ftrace); } continue; } if (ifp->int_sock == -1) continue; if (ifp == skipif) continue; if (!IN6_IS_ADDR_LINKLOCAL(&ifp->int_addr)) continue; supply(sin6, ifp, rtstate, splith); } } static void solicit(struct sockaddr_in6 *sin6, struct interface *ifp) { msg->rip6_cmd = RIPCMD6_REQUEST; msg->rip6_vers = RIPVERSION6; msg->rip6_nets[0].rip6_prefix = in6addr_any; msg->rip6_nets[0].rip6_prefix_length = 0; msg->rip6_nets[0].rip6_metric = HOPCNT_INFINITY; sendpacket(sin6, ifp, sizeof (struct rip6), 0); } void solicitall(struct sockaddr_in6 *sin6) { struct interface *ifp; for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { if ((ifp->int_flags & RIP6_IFF_UP) == 0) continue; if (ifp->int_flags & RIP6_IFF_NORTEXCH) { if (tracing & OUTPUT_BIT) { (void) fprintf(ftrace, "Suppress sending RIPng request packet " "on %s (no route exchange on interface)\n", ifp->int_name); (void) fflush(ftrace); } continue; } if (ifp->int_sock == -1) continue; solicit(sin6, ifp); } } /* * Output a preformed packet. */ /*ARGSUSED*/ void sendpacket(struct sockaddr_in6 *sin6, struct interface *ifp, int size, int flags) { if (sendto(ifp->int_sock, packet, size, flags, (struct sockaddr *)sin6, sizeof (*sin6)) < 0) { syslog(LOG_ERR, "sendpacket: sendto: %m"); return; } TRACE_OUTPUT(ifp, sin6, sizeof (struct rip6)); ifp->int_opackets++; } /* * Supply dst with the contents of the routing tables. * If this won't fit in one packet, chop it up into several. */ void supply(struct sockaddr_in6 *sin6, struct interface *ifp, int rtstate, boolean_t splith) { struct rt_entry *rt; struct netinfo6 *n = msg->rip6_nets; struct rthash *rh; int size, i, maxsize; uint8_t rtmetric; msg->rip6_cmd = RIPCMD6_RESPONSE; msg->rip6_vers = RIPVERSION6; /* * Initialize maxsize to the size of the largest RIPng packet supported * on the outgoing interface. */ maxsize = ifp->int_mtu - sizeof (ip6_t) - sizeof (struct udphdr); for (i = IPV6_ABITS; i >= 0; i--) { if (net_hashes[i] == NULL) continue; for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) { for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { if (IN6_IS_ADDR_LINKLOCAL(&rt->rt_dst)) continue; if (IN6_IS_ADDR_UNSPECIFIED(&rt->rt_dst)) continue; /* do not send if private */ if (rt->rt_state & RTS_PRIVATE) continue; /* * Don't resend the information * on the network from which it was received. */ if (splith && rt->rt_ifp != NULL && strcmp(ifp->int_ifbase, rt->rt_ifp->int_ifbase) == 0) { if (dopoison) rtmetric = HOPCNT_INFINITY; else continue; } else { rtmetric = rt->rt_metric; } /* * For dynamic updates, limit update to routes * with the specified state. */ if (rtstate != 0 && (rt->rt_state & rtstate) == 0) continue; /* * Check if there is space for another RTE. If * not, send the packet built up and reset n for * the remaining RTEs. */ size = (char *)n - packet; if (size > maxsize - sizeof (struct netinfo6)) { sendpacket(sin6, ifp, size, 0); TRACE_OUTPUT(ifp, sin6, size); n = msg->rip6_nets; } n->rip6_prefix = rt->rt_dst; n->rip6_route_tag = rt->rt_tag; n->rip6_prefix_length = rt->rt_prefix_length; n->rip6_metric = min(rtmetric, HOPCNT_INFINITY); n++; } /* end of hash chain */ } /* end of particular prefix length */ } /* end of all prefix lengths */ if (n != msg->rip6_nets) { size = (char *)n - packet; sendpacket(sin6, ifp, size, 0); TRACE_OUTPUT(ifp, sin6, size); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ #include "defs.h" #define IF_SEPARATOR ':' struct interface *ifnet; static int setup_listen_sock(int ifindex); static void addrouteforif(struct interface *ifp); static void resetup_listen_sock(struct interface *, int); /* * This is called at startup and after that, every CHECK_INTERVAL seconds or * when a SIGHUP is received. */ void initifs(void) { static char *buf = NULL; static uint_t maxbufsize = 0; int bufsize; int numifs; struct lifnum lifn; struct lifconf lifc; struct lifreq lifr; struct lifreq *lifrp; int n; struct interface ifs; struct interface *ifp; int netmaskchange = 0; boolean_t changes = _B_FALSE; lifn.lifn_family = AF_INET6; lifn.lifn_flags = 0; if (ioctl(iocsoc, SIOCGLIFNUM, (char *)&lifn) < 0) { syslog(LOG_ERR, "initifs: ioctl (get interface numbers): %m"); return; } numifs = lifn.lifn_count; bufsize = numifs * sizeof (struct lifreq); if (buf == NULL || bufsize > maxbufsize) { if (buf != NULL) free(buf); maxbufsize = bufsize; buf = (char *)malloc(maxbufsize); if (buf == NULL) { syslog(LOG_ERR, "initifs: out of memory"); return; } } lifc.lifc_family = AF_INET6; lifc.lifc_flags = 0; lifc.lifc_len = bufsize; lifc.lifc_buf = buf; if (ioctl(iocsoc, SIOCGLIFCONF, (char *)&lifc) < 0) { syslog(LOG_ERR, "initifs: ioctl (get interface configuration): %m"); return; } /* * Mark all of the currently known interfaces in order to determine * which of the these interfaces no longer exist. */ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) ifp->int_flags |= RIP6_IFF_MARKED; lifrp = lifc.lifc_req; for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { bzero((char *)&ifs, sizeof (ifs)); (void) strncpy(lifr.lifr_name, lifrp->lifr_name, sizeof (lifr.lifr_name)); if (ioctl(iocsoc, SIOCGLIFFLAGS, (char *)&lifr) < 0) { syslog(LOG_ERR, "initifs: ioctl (get interface flags): %m"); continue; } if (!(lifr.lifr_flags & IFF_IPV6) || !(lifr.lifr_flags & IFF_MULTICAST) || (lifr.lifr_flags & IFF_LOOPBACK)) continue; ifp = if_ifwithname(lifr.lifr_name); if (ifp != NULL) ifp->int_flags &= ~RIP6_IFF_MARKED; if (lifr.lifr_flags & IFF_POINTOPOINT) ifs.int_flags |= RIP6_IFF_POINTOPOINT; if (lifr.lifr_flags & IFF_NORTEXCH) ifs.int_flags |= RIP6_IFF_NORTEXCH; if (lifr.lifr_flags & IFF_PRIVATE) ifs.int_flags |= RIP6_IFF_PRIVATE; if (lifr.lifr_flags & IFF_UP) { ifs.int_flags |= RIP6_IFF_UP; } else { if (ifp != NULL) { if (ifp->int_flags & RIP6_IFF_UP) { /* * If there is an transition from up to * down for an exisiting interface, * increment the counter. */ ifp->int_transitions++; changes = _B_TRUE; } if_purge(ifp); } continue; } if (ifs.int_flags & RIP6_IFF_POINTOPOINT) { /* * For point-to-point interfaces, retrieve both the * local and the remote addresses. */ if (ioctl(iocsoc, SIOCGLIFADDR, (char *)&lifr) < 0) { syslog(LOG_ERR, "initifs: ioctl (get interface address): " "%m"); continue; } ifs.int_addr = ((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr; if (ioctl(iocsoc, SIOCGLIFDSTADDR, (char *)&lifr) < 0) { syslog(LOG_ERR, "initifs: ioctl (get destination address): " "%m"); continue; } ifs.int_dstaddr = ((struct sockaddr_in6 *) &lifr.lifr_dstaddr)->sin6_addr; ifs.int_prefix_length = IPV6_ABITS; } else { /* * For other interfaces, retreieve the prefix (including * the prefix length. */ if (ioctl(iocsoc, SIOCGLIFSUBNET, (char *)&lifr) < 0) { syslog(LOG_ERR, "initifs: ioctl (get subnet prefix): %m"); continue; } /* * This should never happen but check for it in any case * since the kernel stores it as an signed integer. */ if (lifr.lifr_addrlen < 0 || lifr.lifr_addrlen > IPV6_ABITS) { syslog(LOG_ERR, "initifs: ioctl (get subnet prefix) " "returned invalid prefix length of %d", lifr.lifr_addrlen); continue; } ifs.int_prefix_length = lifr.lifr_addrlen; ifs.int_addr = ((struct sockaddr_in6 *) &lifr.lifr_subnet)->sin6_addr; } if (ioctl(iocsoc, SIOCGLIFMETRIC, (char *)&lifr) < 0 || lifr.lifr_metric < 0) ifs.int_metric = 1; else ifs.int_metric = lifr.lifr_metric + 1; if (ioctl(iocsoc, SIOCGLIFINDEX, (char *)&lifr) < 0) { syslog(LOG_ERR, "initifs: ioctl (get index): %m"); continue; } ifs.int_ifindex = lifr.lifr_index; if (ioctl(iocsoc, SIOCGLIFMTU, (char *)&lifr) < 0) { syslog(LOG_ERR, "initifs: ioctl (get mtu): %m"); continue; } /* * If the interface's recorded MTU doesn't make sense, use * IPV6_MIN_MTU instead. */ if (lifr.lifr_mtu < IPV6_MIN_MTU) ifs.int_mtu = IPV6_MIN_MTU; else ifs.int_mtu = lifr.lifr_mtu; if (ifp != NULL) { /* * RIP6_IFF_NORTEXCH flag change by itself shouldn't * cause an if_purge() call, which also purges all the * routes heard off this interface. So, let's suppress * changes of RIP6_IFF_NORTEXCH in the following * comparisons. */ if (ifp->int_prefix_length == ifs.int_prefix_length && ((ifp->int_flags | RIP6_IFF_NORTEXCH) == (ifs.int_flags | RIP6_IFF_NORTEXCH)) && ifp->int_metric == ifs.int_metric && ifp->int_ifindex == ifs.int_ifindex) { /* * Now let's make sure we capture the latest * value of RIP6_IFF_NORTEXCH flag. */ if (ifs.int_flags & RIP6_IFF_NORTEXCH) ifp->int_flags |= RIP6_IFF_NORTEXCH; else ifp->int_flags &= ~RIP6_IFF_NORTEXCH; if (!(ifp->int_flags & RIP6_IFF_POINTOPOINT) && IN6_ARE_ADDR_EQUAL(&ifp->int_addr, &ifs.int_addr)) continue; if ((ifp->int_flags & RIP6_IFF_POINTOPOINT) && IN6_ARE_ADDR_EQUAL(&ifp->int_dstaddr, &ifs.int_dstaddr)) continue; } if_purge(ifp); if (ifp->int_prefix_length != ifs.int_prefix_length) netmaskchange = 1; ifp->int_addr = ifs.int_addr; ifp->int_dstaddr = ifs.int_dstaddr; ifp->int_metric = ifs.int_metric; /* * If there is an transition from down to up for an * exisiting interface, increment the counter. */ if (!(ifp->int_flags & RIP6_IFF_UP) && (ifs.int_flags & RIP6_IFF_UP)) ifp->int_transitions++; ifp->int_flags |= ifs.int_flags; ifp->int_prefix_length = ifs.int_prefix_length; /* * If the interface index has changed, we may need to * set up the listen socket again. */ if (ifp->int_ifindex != ifs.int_ifindex) { if (ifp->int_sock != -1) { resetup_listen_sock(ifp, ifs.int_ifindex); } ifp->int_ifindex = ifs.int_ifindex; } ifp->int_mtu = ifs.int_mtu; } else { char *cp; int log_num; ifp = (struct interface *) malloc(sizeof (struct interface)); if (ifp == NULL) { syslog(LOG_ERR, "initifs: out of memory"); return; } *ifp = ifs; ifp->int_name = ifp->int_ifbase = NULL; ifp->int_name = (char *)malloc((size_t)strlen(lifr.lifr_name) + 1); if (ifp->int_name == NULL) { free(ifp); syslog(LOG_ERR, "initifs: out of memory"); return; } (void) strcpy(ifp->int_name, lifr.lifr_name); ifp->int_ifbase = (char *)malloc((size_t)strlen(lifr.lifr_name) + 1); if (ifp->int_ifbase == NULL) { free(ifp->int_name); free(ifp); syslog(LOG_ERR, "initifs: out of memory"); return; } (void) strcpy(ifp->int_ifbase, lifr.lifr_name); cp = (char *)index(ifp->int_ifbase, IF_SEPARATOR); if (cp != NULL) { /* * Verify that the value following the separator * is an integer greater than zero (the only * possible value for a logical interface). */ log_num = atoi((char *)(cp + 1)); if (log_num <= 0) { free(ifp->int_ifbase); free(ifp->int_name); free(ifp); syslog(LOG_ERR, "initifs: interface name %s could " "not be parsed", ifp->int_name); return; } *cp = '\0'; } else { log_num = 0; } if (log_num == 0) { ifp->int_sock = setup_listen_sock(ifp->int_ifindex); } else { ifp->int_sock = -1; } ifp->int_next = ifnet; ifnet = ifp; traceinit(ifp); } addrouteforif(ifp); changes = _B_TRUE; } /* * Any remaining interfaces that are still marked and which were in an * up state (RIP6_IFF_UP) need to removed from the routing table. */ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { if ((ifp->int_flags & (RIP6_IFF_MARKED | RIP6_IFF_UP)) == (RIP6_IFF_MARKED | RIP6_IFF_UP)) { if_purge(ifp); ifp->int_flags &= ~RIP6_IFF_MARKED; changes = _B_TRUE; } } if (netmaskchange) rtchangeall(); if (supplier & changes) dynamic_update((struct interface *)NULL); } static void addrouteforif(struct interface *ifp) { struct rt_entry *rt; struct in6_addr *dst; if (ifp->int_flags & RIP6_IFF_POINTOPOINT) dst = &ifp->int_dstaddr; else dst = &ifp->int_addr; rt = rtlookup(dst, ifp->int_prefix_length); if (rt != NULL) { if (rt->rt_state & RTS_INTERFACE) return; rtdelete(rt); } rtadd(dst, &ifp->int_addr, ifp->int_prefix_length, ifp->int_metric, 0, _B_TRUE, ifp); } static int setup_listen_sock(int ifindex) { int sock; struct sockaddr_in6 sin6; uint_t hops; struct ipv6_mreq allrouters_mreq; int on = 1; int off = 0; int recvsize; sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1) goto sock_fail; if (setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, (char *)&ifindex, sizeof (ifindex)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: IPV6_BOUND_IF: %m"); goto sock_fail; } hops = IPV6_MAX_HOPS; if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&hops, sizeof (hops)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: IPV6_UNICAST_HOPS: %m"); goto sock_fail; } if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, sizeof (hops)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: IPV6_MULTICAST_HOPS: %m"); goto sock_fail; } if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&off, sizeof (off)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: IPV6_MULTICAST_LOOP: %m"); goto sock_fail; } allrouters_mreq.ipv6mr_multiaddr = allrouters_in6; allrouters_mreq.ipv6mr_interface = ifindex; if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&allrouters_mreq, sizeof (allrouters_mreq)) < 0) { if (errno != EADDRINUSE) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: " "IPV6_JOIN_GROUP: %m"); goto sock_fail; } } if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (char *)&on, sizeof (off)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: IPV6_RECVHOPLIMIT: %m"); goto sock_fail; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: SO_REUSEADDR: %m"); goto sock_fail; } recvsize = RCVBUFSIZ; if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize, sizeof (int)) < 0) { syslog(LOG_ERR, "setup_listen_sock: setsockopt: SO_RCVBUF: %m"); goto sock_fail; } bzero((char *)&sin6, sizeof (sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = rip6_port; if (bind(sock, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) { syslog(LOG_ERR, "setup_listen_sock: bind: %m"); goto sock_fail; } poll_ifs_num++; if (poll_ifs == NULL) { poll_ifs = (struct pollfd *) malloc(max_poll_ifs * sizeof (struct pollfd)); } else if (poll_ifs_num > max_poll_ifs) { max_poll_ifs *= 2; poll_ifs = (struct pollfd *)realloc((char *)poll_ifs, max_poll_ifs * sizeof (struct pollfd)); } if (poll_ifs == NULL) { syslog(LOG_ERR, "setup_listen_sock: out of memory"); goto sock_fail; } poll_ifs[poll_ifs_num - 1].fd = sock; poll_ifs[poll_ifs_num - 1].events = POLLIN; return (sock); sock_fail: if (sock > 0) (void) close(sock); return (-1); } /* * resetup_listen_sock is primarily used in the case where a tunnel was * plumbed, unplumbed, then plumbed again. This would cause the binding set by * IPV6_BOUND_IF to be useless, and sends to the associated socket will be * transmitted on the wrong interface. resetup_listen_sock * closes the socket, * removes the socket from poll_ifs[] * plugs the hole in poll_ifs[] * calls setup_listen_sock to set up the socket again */ void resetup_listen_sock(struct interface *ifp, int newindex) { int i; (void) close(ifp->int_sock); /* Remove socket from poll_ifs[]. */ for (i = poll_ifs_num - 1; i >= 0; i--) { if (poll_ifs[i].fd == ifp->int_sock) { poll_ifs[i].fd = 0; poll_ifs[i].events = 0; /* * Remove hole in poll_ifs. Possibly exchange * poll_ifs[i] with poll_ifs[poll_ifs_num-1]. */ if (i != poll_ifs_num - 1) { poll_ifs[i] = poll_ifs[poll_ifs_num - 1]; poll_ifs[poll_ifs_num - 1].fd = 0; poll_ifs[poll_ifs_num - 1].events = 0; } poll_ifs_num--; /* Now set everything up again. */ ifp->int_sock = setup_listen_sock(newindex); break; } } } #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" . /lib/svc/share/smf_include.sh . /lib/svc/share/routing_include.sh smf_configure_ip || exit $SMF_EXIT_OK daemon_args=`get_daemon_args $SMF_FMRI` options="sqp:Ptv" # # Handle upgrade - routing/daemon-args property must be mapped to properties # in routeadm property group. Note the SMF-incompatible -t option is not # supported, since it requires that in.ripngd run in the foreground. # if [ -n "$daemon_args" ]; then set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ "$options" "q" quiet_mode true false set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ "$options" "s" supply_routes true false set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ "$options" "p" udp_port 521 set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ "$options" "P" poison_reverse false true set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ "$options" "v" verbose true false set_daemon_nonoption_properties "$SMF_FMRI" "$daemon_args" \ "$options" "log_file" clear_daemon_args $SMF_FMRI fi # # Assemble arguments to daemon from properties # args="`get_daemon_option_from_boolean_property $SMF_FMRI \ quiet_mode -q true`" args="$args `get_daemon_option_from_boolean_property $SMF_FMRI \ supply_routes -s true`" args="$args `get_daemon_option_from_property $SMF_FMRI udp_port p 521`" args="$args `get_daemon_option_from_boolean_property $SMF_FMRI \ poison_reverse -P false`" args="$args `get_daemon_option_from_boolean_property $SMF_FMRI \ verbose -v true`" args="$args `get_daemon_nonoption_property $SMF_FMRI log_file`" /usr/lib/inet/in.ripngd $args [ "$?" = 0 ] || exit $SMF_EXIT_ERR_FATAL exit "$SMF_EXIT_OK" /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing table management daemon. */ /* * Routing table structure; differs a bit from kernel tables. */ struct rthash { struct rt_entry *rt_forw; struct rt_entry *rt_back; }; struct rt_entry { struct rt_entry *rt_forw; struct rt_entry *rt_back; uint_t rt_hash; /* for net or host */ struct in6_addr rt_dst; /* match value */ struct in6_addr rt_router; /* who to forward to */ int rt_prefix_length; /* bits in prefix */ struct interface *rt_ifp; /* interface to take */ uint_t rt_flags; /* kernel flags */ uint_t rt_state; /* see below */ int rt_timer; /* for invalidation */ int rt_metric; /* cost of route including the if */ int rt_tag; /* route tag attribute */ }; #define ROUTEHASHSIZ 32 /* must be a power of 2 */ #define ROUTEHASHMASK (ROUTEHASHSIZ - 1) /* * "State" of routing table entry. */ #define RTS_CHANGED 0x1 /* route has been altered recently */ #define RTS_INTERFACE 0x2 /* route is for network interface */ #define RTS_PRIVATE 0x4 /* route is private, do not advertise */ /* * XXX This is defined in (but should be defined in * for completeness). */ #define IPV6_ABITS 128 /* Number of bits in an IPv6 address */ extern struct rthash *net_hashes[IPV6_ABITS + 1]; extern void rtadd(struct in6_addr *, struct in6_addr *, int, int, int, boolean_t, struct interface *); extern void rtchange(struct rt_entry *, struct in6_addr *, short, struct interface *); extern void rtchangeall(void); extern void rtcreate_prefix(struct in6_addr *, struct in6_addr *, int); extern void rtdelete(struct rt_entry *); extern void rtdown(struct rt_entry *); extern void rtdump(void); extern struct rt_entry *rtlookup(struct in6_addr *, int); extern void rtpurgeif(struct interface *); /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing Table Management Daemon */ #include "defs.h" boolean_t install = _B_TRUE; /* update kernel routing table */ struct rthash *net_hashes[IPV6_ABITS + 1]; /* * Size of routing socket message used by in.ripngd which includes the header, * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6) * plus space for the RTA_IFP (a sockaddr_dl). */ #define RIPNG_RTM_MSGLEN sizeof (struct rt_msghdr) + \ sizeof (struct sockaddr_in6) + \ sizeof (struct sockaddr_in6) + \ sizeof (struct sockaddr_in6) + \ sizeof (struct sockaddr_dl) static int rtmseq; /* rtm_seq sequence number */ static int rtsock; /* Routing socket */ static struct rt_msghdr *rt_msg; /* Routing socket message */ static struct sockaddr_in6 *rta_dst; /* RTA_DST sockaddr */ static struct sockaddr_in6 *rta_gateway; /* RTA_GATEWAY sockaddr */ static struct sockaddr_in6 *rta_netmask; /* RTA_NETMASK sockaddr */ static struct sockaddr_dl *rta_ifp; /* RTA_IFP sockaddr */ /* simulate vax insque and remque instructions. */ typedef struct vq { caddr_t fwd, back; } vq_t; #define insque(e, p) ((vq_t *)(e))->back = (caddr_t)(p); \ ((vq_t *)(e))->fwd = \ (caddr_t)((vq_t *)((vq_t *)(p))->fwd); \ ((vq_t *)((vq_t *)(p))->fwd)->back = (caddr_t)(e); \ ((vq_t *)(p))->fwd = (caddr_t)(e); #define remque(e) ((vq_t *)((vq_t *)(e))->back)->fwd = \ (caddr_t)((vq_t *)(e))->fwd; \ ((vq_t *)((vq_t *)(e))->fwd)->back = \ (caddr_t)((vq_t *)(e))->back; \ ((vq_t *)(e))->fwd = NULL; \ ((vq_t *)(e))->back = NULL; static void log_change(int level, struct rt_entry *orig, struct rt_entry *new) { char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; char buf3[INET6_ADDRSTRLEN]; (void) inet_ntop(AF_INET6, (void *) &new->rt_dst, buf1, sizeof (buf1)); (void) inet_ntop(AF_INET6, (void *) &orig->rt_router, buf2, sizeof (buf2)); (void) inet_ntop(AF_INET6, (void *) &new->rt_router, buf3, sizeof (buf3)); syslog(level, "\tdst %s from gw %s if %s to gw %s if %s metric %d", buf1, buf2, (orig->rt_ifp != NULL && orig->rt_ifp->int_name != NULL) ? orig->rt_ifp->int_name : "(noname)", buf3, (new->rt_ifp != NULL && new->rt_ifp->int_name != NULL) ? new->rt_ifp->int_name : "(noname)", new->rt_metric); } static void log_single(int level, struct rt_entry *rt) { char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; (void) inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1)); (void) inet_ntop(AF_INET6, (void *)&rt->rt_router, buf2, sizeof (buf2)); syslog(level, "\tdst %s gw %s if %s metric %d", buf1, buf2, (rt->rt_ifp != NULL && rt->rt_ifp->int_name != NULL) ? rt->rt_ifp->int_name : "(noname)", rt->rt_metric); } /* * Computes a hash by XOR-ing the (up to sixteen) octets that make up an IPv6 * address. This function assumes that that there are no one-bits in the * address beyond the prefix length. */ static uint8_t rthash(struct in6_addr *dst, int prefix_length) { uint8_t val = 0; int i; for (i = 0; prefix_length > 0; prefix_length -= 8, i++) val ^= dst->s6_addr[i]; return (val); } /* * Given a prefix length, fill in the struct in6_addr representing an IPv6 * netmask. */ static void rtmask_to_bits(uint_t prefix_length, struct in6_addr *prefix) { uint_t mask = 0xff; int i; bzero((caddr_t)prefix, sizeof (struct in6_addr)); for (i = 0; prefix_length >= 8; prefix_length -= 8, i++) prefix->s6_addr[i] = 0xff; mask = (mask << (8 - prefix_length)); if (mask != 0) prefix->s6_addr[i] = mask; } void rtcreate_prefix(struct in6_addr *p1, struct in6_addr *dst, int bits) { uchar_t mask; int j; for (j = 0; bits >= 8; bits -= 8, j++) dst->s6_addr[j] = p1->s6_addr[j]; if (bits != 0) { mask = 0xff << (8 - bits); dst->s6_addr[j] = p1->s6_addr[j] & mask; j++; } for (; j < 16; j++) dst->s6_addr[j] = 0; } /* * Lookup dst in the tables for an exact match. */ struct rt_entry * rtlookup(struct in6_addr *dst, int prefix_length) { struct rt_entry *rt; struct rthash *rh; uint_t hash; if (net_hashes[prefix_length] == NULL) return (NULL); hash = rthash(dst, prefix_length); rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK]; for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { if (rt->rt_hash != hash) continue; if (IN6_ARE_ADDR_EQUAL(&rt->rt_dst, dst) && rt->rt_prefix_length == prefix_length) return (rt); } return (NULL); } /* * Given an IPv6 prefix (destination and prefix length), a gateway, an * interface name and route flags, send down the requested command returning * the return value and errno (in the case of error) from the write() on the * routing socket. */ static int rtcmd(uchar_t type, struct in6_addr *dst, struct in6_addr *gateway, uint_t prefix_length, char *name, int flags) { int rlen; rta_ifp->sdl_index = if_nametoindex(name); if (rta_ifp->sdl_index == 0) return (-1); rta_dst->sin6_addr = *dst; rta_gateway->sin6_addr = *gateway; rtmask_to_bits(prefix_length, &rta_netmask->sin6_addr); rt_msg->rtm_type = type; rt_msg->rtm_flags = flags; rt_msg->rtm_seq = ++rtmseq; rlen = write(rtsock, rt_msg, RIPNG_RTM_MSGLEN); if (rlen >= 0 && rlen < RIPNG_RTM_MSGLEN) { syslog(LOG_ERR, "rtcmd: write to routing socket got only %d for rlen\n", rlen); } return (rlen); } void rtadd(struct in6_addr *dst, struct in6_addr *gate, int prefix_length, int metric, int tag, boolean_t ifroute, struct interface *ifp) { struct rt_entry *rt; struct rthash *rh; uint_t hash; struct in6_addr pdst; int rlen; if (metric >= HOPCNT_INFINITY) return; if (net_hashes[prefix_length] == NULL) { struct rthash *trh; rh = (struct rthash *) calloc(ROUTEHASHSIZ, sizeof (struct rt_entry)); if (rh == NULL) return; for (trh = rh; trh < &rh[ROUTEHASHSIZ]; trh++) trh->rt_forw = trh->rt_back = (struct rt_entry *)trh; net_hashes[prefix_length] = rh; } rtcreate_prefix(dst, &pdst, prefix_length); hash = rthash(&pdst, prefix_length); rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK]; rt = (struct rt_entry *)malloc(sizeof (*rt)); if (rt == NULL) { /* * In the event of an allocation failure, log the error and * continue since on the next update another attempt will be * made. */ syslog(LOG_ERR, "rtadd: malloc: %m"); return; } rt->rt_hash = hash; rt->rt_dst = pdst; rt->rt_prefix_length = prefix_length; rt->rt_router = *gate; rt->rt_metric = metric; rt->rt_tag = tag; rt->rt_timer = 0; rt->rt_flags = RTF_UP; if (prefix_length == IPV6_ABITS) rt->rt_flags |= RTF_HOST; rt->rt_state = RTS_CHANGED; if (ifroute) { rt->rt_state |= RTS_INTERFACE; if (ifp->int_flags & RIP6_IFF_PRIVATE) rt->rt_state |= RTS_PRIVATE; } else { rt->rt_flags |= RTF_GATEWAY; } rt->rt_ifp = ifp; insque(rt, rh); TRACE_ACTION("ADD", rt); /* * If the RTM_ADD fails because the gateway is unreachable * from this host, discard the entry. This should never * happen. */ if (install && (rt->rt_state & RTS_INTERFACE) == 0) { rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router, prefix_length, ifp->int_name, rt->rt_flags); if (rlen < 0) { if (errno != EEXIST) { syslog(LOG_ERR, "rtadd: RTM_ADD: %m"); log_single(LOG_ERR, rt); } if (errno == ENETUNREACH) { TRACE_ACTION("DELETE", rt); remque(rt); free((char *)rt); } } else if (rlen < RIPNG_RTM_MSGLEN) { log_single(LOG_ERR, rt); } } } /* * Handle the case when the metric changes but the gateway is the same (or the * interface index associated with the gateway changes), or when both gateway * and metric changes, or when only the gateway changes but the existing route * is more than one-half of EXPIRE_TIME in age. Note that routes with metric >= * HOPCNT_INFINITY are not in the kernel. */ void rtchange(struct rt_entry *rt, struct in6_addr *gate, short metric, struct interface *ifp) { boolean_t dokern = _B_FALSE; boolean_t dokerndelete; boolean_t metricchanged = _B_FALSE; int oldmetric; struct rt_entry oldroute; int rlen; if (metric >= HOPCNT_INFINITY) { rtdown(rt); return; } if (!IN6_ARE_ADDR_EQUAL(&rt->rt_router, gate) || rt->rt_ifp != ifp) dokern = _B_TRUE; oldmetric = rt->rt_metric; if (oldmetric >= HOPCNT_INFINITY) dokerndelete = _B_FALSE; else dokerndelete = dokern; if (metric != rt->rt_metric) metricchanged = _B_TRUE; rt->rt_timer = 0; if (dokern || metricchanged) { TRACE_ACTION("CHANGE FROM", rt); if ((rt->rt_state & RTS_INTERFACE) && metric != 0) { rt->rt_state &= ~RTS_INTERFACE; if (rt->rt_ifp != NULL) { syslog(LOG_ERR, "rtchange: changing route from " "interface %s (timed out)", (rt->rt_ifp->int_name != NULL) ? rt->rt_ifp->int_name : "(noname)"); } else { syslog(LOG_ERR, "rtchange: " "changing route no interface for route"); } } if (dokern) { oldroute = *rt; rt->rt_router = *gate; rt->rt_ifp = ifp; } rt->rt_metric = metric; if (!(rt->rt_state & RTS_INTERFACE)) rt->rt_flags |= RTF_GATEWAY; else rt->rt_flags &= ~RTF_GATEWAY; rt->rt_state |= RTS_CHANGED; TRACE_ACTION("CHANGE TO", rt); } if (install && (rt->rt_state & RTS_INTERFACE) == 0) { if (dokerndelete) { rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router, rt->rt_prefix_length, rt->rt_ifp->int_name, rt->rt_flags); if (rlen < 0) { if (errno != EEXIST) { syslog(LOG_ERR, "rtchange: RTM_ADD: %m"); log_change(LOG_ERR, rt, (struct rt_entry *)&oldroute); } } else if (rlen < RIPNG_RTM_MSGLEN) { log_change(LOG_ERR, rt, (struct rt_entry *)&oldroute); } rlen = rtcmd(RTM_DELETE, &oldroute.rt_dst, &oldroute.rt_router, oldroute.rt_prefix_length, oldroute.rt_ifp->int_name, oldroute.rt_flags); if (rlen < 0) { syslog(LOG_ERR, "rtchange: RTM_DELETE: %m"); log_change(LOG_ERR, rt, (struct rt_entry *)&oldroute); } else if (rlen < RIPNG_RTM_MSGLEN) { log_change(LOG_ERR, rt, (struct rt_entry *)&oldroute); } } else if (dokern || oldmetric >= HOPCNT_INFINITY) { rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router, rt->rt_prefix_length, ifp->int_name, rt->rt_flags); if (rlen < 0 && errno != EEXIST) { syslog(LOG_ERR, "rtchange: RTM_ADD: %m"); log_change(LOG_ERR, rt, (struct rt_entry *)&oldroute); } else if (rlen < RIPNG_RTM_MSGLEN) { log_change(LOG_ERR, rt, (struct rt_entry *)&oldroute); } } } } void rtdown(struct rt_entry *rt) { int rlen; if (rt->rt_metric != HOPCNT_INFINITY) { TRACE_ACTION("DELETE", rt); if (install && (rt->rt_state & RTS_INTERFACE) == 0) { rlen = rtcmd(RTM_DELETE, &rt->rt_dst, &rt->rt_router, rt->rt_prefix_length, rt->rt_ifp->int_name, rt->rt_flags); if (rlen < 0) { syslog(LOG_ERR, "rtdown: RTM_DELETE: %m"); log_single(LOG_ERR, rt); } else if (rlen < RIPNG_RTM_MSGLEN) { log_single(LOG_ERR, rt); } } rt->rt_metric = HOPCNT_INFINITY; rt->rt_state |= RTS_CHANGED; } if (rt->rt_timer < EXPIRE_TIME) rt->rt_timer = EXPIRE_TIME; } void rtdelete(struct rt_entry *rt) { if (rt->rt_state & RTS_INTERFACE) { if (rt->rt_ifp != NULL) { syslog(LOG_ERR, "rtdelete: " "deleting route to interface %s (timed out)", (rt->rt_ifp->int_name != NULL) ? rt->rt_ifp->int_name : "(noname)"); log_single(LOG_ERR, rt); } } rtdown(rt); remque(rt); free((char *)rt); } /* * Mark all the routes heard off a particular interface "down". Unlike the * routes managed by in.routed, all of these routes have an interface associated * with them. */ void rtpurgeif(struct interface *ifp) { struct rthash *rh; struct rt_entry *rt; int i; for (i = IPV6_ABITS; i >= 0; i--) { if (net_hashes[i] == NULL) continue; for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) { for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { if (rt->rt_ifp == ifp) { rtdown(rt); rt->rt_ifp = NULL; rt->rt_state &= ~RTS_INTERFACE; } } } } } /* * Called when the subnetmask has changed on one or more interfaces. * Re-evaluates all non-interface routes by doing a rtchange so that * routes that were believed to be host routes before the netmask change * can be converted to network routes and vice versa. */ void rtchangeall(void) { struct rthash *rh; struct rt_entry *rt; int i; for (i = IPV6_ABITS; i >= 0; i--) { if (net_hashes[i] == NULL) continue; for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) { for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { if ((rt->rt_state & RTS_INTERFACE) == 0) { rtchange(rt, &rt->rt_router, rt->rt_metric, rt->rt_ifp); } } } } } static void rtdumpentry(FILE *fp, struct rt_entry *rt) { char buf1[INET6_ADDRSTRLEN]; static struct bits { ulong_t t_bits; char *t_name; } flagbits[] = { /* BEGIN CSTYLED */ { RTF_UP, "UP" }, { RTF_GATEWAY, "GATEWAY" }, { RTF_HOST, "HOST" }, { 0, NULL } /* END CSTYLED */ }, statebits[] = { /* BEGIN CSTYLED */ { RTS_INTERFACE, "INTERFACE" }, { RTS_CHANGED, "CHANGED" }, { RTS_PRIVATE, "PRIVATE" }, { 0, NULL } /* END CSTYLED */ }; struct bits *p; boolean_t first; char c; (void) fprintf(fp, "prefix %s/%d ", inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1)), rt->rt_prefix_length); (void) fprintf(fp, "via %s metric %d timer %d", inet_ntop(AF_INET6, (void *)&rt->rt_router, buf1, sizeof (buf1)), rt->rt_metric, rt->rt_timer); if (rt->rt_ifp != NULL) { (void) fprintf(fp, " if %s", (rt->rt_ifp->int_name != NULL) ? rt->rt_ifp->int_name : "(noname)"); } (void) fprintf(fp, " state"); c = ' '; for (first = _B_TRUE, p = statebits; p->t_bits > 0; p++) { if ((rt->rt_state & p->t_bits) == 0) continue; (void) fprintf(fp, "%c%s", c, p->t_name); if (first) { c = '|'; first = _B_FALSE; } } if (first) (void) fprintf(fp, " 0"); if (rt->rt_flags & (RTF_UP | RTF_GATEWAY)) { c = ' '; for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) { if ((rt->rt_flags & p->t_bits) == 0) continue; (void) fprintf(fp, "%c%s", c, p->t_name); if (first) { c = '|'; first = _B_FALSE; } } } (void) putc('\n', fp); (void) fflush(fp); } static void rtdump2(FILE *fp) { struct rthash *rh; struct rt_entry *rt; int i; for (i = IPV6_ABITS; i >= 0; i--) { if (net_hashes[i] == NULL) continue; for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) { for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { rtdumpentry(fp, rt); } } } } void rtdump(void) { if (ftrace != NULL) rtdump2(ftrace); else rtdump2(stderr); } /* * Create a routing socket for sending RTM_ADD and RTM_DELETE messages and * initialize the routing socket message header and as much of the sockaddrs * as possible. */ void setup_rtsock(void) { char *cp; int off = 0; rtsock = socket(PF_ROUTE, SOCK_RAW, AF_INET6); if (rtsock < 0) { syslog(LOG_ERR, "setup_rtsock: socket: %m"); exit(EXIT_FAILURE); } /* We don't want to listen to our own messages */ if (setsockopt(rtsock, SOL_SOCKET, SO_USELOOPBACK, (char *)&off, sizeof (off)) < 0) { syslog(LOG_ERR, "setup_rtsock: setsockopt: SO_USELOOPBACK: %m"); exit(EXIT_FAILURE); } /* * Allocate storage for the routing socket message. */ rt_msg = (struct rt_msghdr *)malloc(RIPNG_RTM_MSGLEN); if (rt_msg == NULL) { syslog(LOG_ERR, "setup_rtsock: malloc: %m"); exit(EXIT_FAILURE); } /* * Initialize the routing socket message by zero-filling it and then * setting the fields where are constant through the lifetime of the * process. */ bzero(rt_msg, RIPNG_RTM_MSGLEN); rt_msg->rtm_msglen = RIPNG_RTM_MSGLEN; rt_msg->rtm_version = RTM_VERSION; rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP; rt_msg->rtm_pid = getpid(); if (rt_msg->rtm_pid < 0) { syslog(LOG_ERR, "setup_rtsock: getpid: %m"); exit(EXIT_FAILURE); } /* * Initialize the constant portion of the RTA_DST sockaddr. */ cp = (char *)rt_msg + sizeof (struct rt_msghdr); rta_dst = (struct sockaddr_in6 *)cp; rta_dst->sin6_family = AF_INET6; /* * Initialize the constant portion of the RTA_GATEWAY sockaddr. */ cp += sizeof (struct sockaddr_in6); rta_gateway = (struct sockaddr_in6 *)cp; rta_gateway->sin6_family = AF_INET6; /* * Initialize the constant portion of the RTA_NETMASK sockaddr. */ cp += sizeof (struct sockaddr_in6); rta_netmask = (struct sockaddr_in6 *)cp; rta_netmask->sin6_family = AF_INET6; /* * Initialize the constant portion of the RTA_IFP sockaddr. */ cp += sizeof (struct sockaddr_in6); rta_ifp = (struct sockaddr_dl *)cp; rta_ifp->sdl_family = AF_LINK; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing Table Management Daemon */ #include "defs.h" int supplyinterval; /* current supply interval */ /* * Timer routine. Performs routing information supply * duties and manages timers on routing table entries. * Management of the RTS_CHANGED bit assumes that we multicast * each time called. */ void timer(void) { struct rthash *rh; struct rt_entry *rt; boolean_t timetomulticast = _B_FALSE; int i; static int iftime; /* interface timer */ static int mtime; /* periodic mcast supply timer */ static int alarmtime = 0; /* time elapsed since last call */ int mintime; /* tracks when next timer will expire */ /* * On the initial call to timer(), the various times that are kept track * of need to be initialized. After initializing everything, "remember" * (via a static) how long until the next timer expires. */ if (alarmtime == 0) { supplyinterval = GET_RANDOM(MIN_SUPPLY_TIME, MAX_SUPPLY_TIME); iftime = 0; mtime = supplyinterval; alarmtime = supplyinterval; (void) alarm(alarmtime); return; } /* * Initialize mintime to a suitable "large" value and then compare it to * other times in the future to determine which event will occur next. */ mintime = INT_MAX; (void) sighold(SIGHUP); (void) sighold(SIGUSR1); (void) sighold(SIGUSR2); iftime += alarmtime; if (iftime >= CHECK_INTERVAL) { initifs(); iftime = 0; } mintime = min(mintime, CHECK_INTERVAL - iftime); mtime += alarmtime; if (mtime >= supplyinterval) { if (supplier) timetomulticast = _B_TRUE; mtime = 0; supplyinterval = GET_RANDOM(MIN_SUPPLY_TIME, MAX_SUPPLY_TIME); } mintime = min(mintime, supplyinterval - mtime); for (i = IPV6_ABITS; i >= 0; i--) { if (net_hashes[i] == NULL) continue; for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) { for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { /* * We don't advance time on a routing entry for * an interface because we catch * interfaces going up and down in initifs. */ rt->rt_state &= ~RTS_CHANGED; if ((rt->rt_state & RTS_INTERFACE) != 0) continue; rt->rt_timer += alarmtime; if (rt->rt_timer >= GARBAGE_TIME) { rt = rt->rt_back; rtdelete(rt->rt_forw); continue; } if (rt->rt_timer >= EXPIRE_TIME) { rtdown(rt); mintime = min(mintime, GARBAGE_TIME - rt->rt_timer); } else { mintime = min(mintime, EXPIRE_TIME - rt->rt_timer); } } } } if (timetomulticast) { supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE); (void) gettimeofday(&now, (struct timezone *)NULL); lastmcast = now; lastfullupdate = now; needupdate = _B_FALSE; /* cancel any pending dynamic update */ nextmcast.tv_sec = 0; } (void) sigrelse(SIGUSR2); (void) sigrelse(SIGUSR1); (void) sigrelse(SIGHUP); /* * "Remember" (via a static) how long until the next timer expires. */ alarmtime = mintime; (void) alarm(alarmtime); } /* * On SIGTERM, let everyone know we're going away. */ void term(void) { struct rthash *rh; struct rt_entry *rt; int i; if (!supplier) exit(EXIT_SUCCESS); for (i = IPV6_ABITS; i >= 0; i--) { if (net_hashes[i] == NULL) continue; for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) { for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { rt->rt_metric = HOPCNT_INFINITY; } } } supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE); (void) unlink(PATH_PID); exit(EXIT_SUCCESS); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing Table Management Daemon */ #include "defs.h" #define NRECORDS 50 /* size of circular trace buffer */ boolean_t tracepackets; /* watch packets as they go by */ int tracing; /* bitmask: */ FILE *ftrace; /* output trace file */ static int iftraceinit(struct interface *ifp, struct ifdebug *ifd); static void dumpif(FILE *fp, struct interface *ifp); static void dumptrace(FILE *fp, char *dir, struct ifdebug *ifd); void traceinit(struct interface *ifp) { if (iftraceinit(ifp, &ifp->int_input) && iftraceinit(ifp, &ifp->int_output)) return; tracing = 0; (void) fprintf(stderr, "traceinit: can't init %s\n", (ifp->int_name != NULL) ? ifp->int_name : "(noname)"); } static int iftraceinit(struct interface *ifp, struct ifdebug *ifd) { struct iftrace *t; ifd->ifd_records = (struct iftrace *) malloc((size_t)NRECORDS * sizeof (struct iftrace)); if (ifd->ifd_records == NULL) return (0); ifd->ifd_front = ifd->ifd_records; ifd->ifd_count = 0; for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) { t->ift_size = 0; t->ift_packet = NULL; } ifd->ifd_if = ifp; return (1); } void traceon(char *file) { struct stat stbuf; if (ftrace != NULL) return; if (stat(file, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) != S_IFREG) return; ftrace = fopen(file, "a"); if (ftrace == NULL) return; (void) dup2(fileno(ftrace), 1); (void) dup2(fileno(ftrace), 2); } void traceonfp(FILE *fp) { if (ftrace != NULL) return; ftrace = fp; if (ftrace == NULL) return; (void) dup2(fileno(ftrace), 1); (void) dup2(fileno(ftrace), 2); } void trace(struct ifdebug *ifd, struct sockaddr_in6 *who, char *p, int len, int m) { struct iftrace *t; if (ifd->ifd_records == 0) return; t = ifd->ifd_front++; if (ifd->ifd_front >= ifd->ifd_records + NRECORDS) ifd->ifd_front = ifd->ifd_records; if (ifd->ifd_count < NRECORDS) ifd->ifd_count++; if (t->ift_size > 0 && t->ift_size < len && t->ift_packet != NULL) { free(t->ift_packet); t->ift_packet = NULL; } (void) time(&t->ift_stamp); t->ift_who = *who; if (len > 0 && t->ift_packet == NULL) { t->ift_packet = (char *)malloc((size_t)len); if (t->ift_packet == NULL) len = 0; } if (len > 0) bcopy(p, t->ift_packet, len); t->ift_size = len; t->ift_metric = m; } void traceaction(FILE *fp, char *action, struct rt_entry *rt) { static struct bits { ulong_t t_bits; char *t_name; } flagbits[] = { /* BEGIN CSTYLED */ { RTF_UP, "UP" }, { RTF_GATEWAY, "GATEWAY" }, { RTF_HOST, "HOST" }, { 0, NULL } /* END CSTYLED */ }, statebits[] = { /* BEGIN CSTYLED */ { RTS_INTERFACE, "INTERFACE" }, { RTS_CHANGED, "CHANGED" }, { RTS_PRIVATE, "PRIVATE" }, { 0, NULL } /* END CSTYLED */ }; struct bits *p; boolean_t first; char c; time_t t; if (fp == NULL) return; (void) time(&t); (void) fprintf(fp, "%.15s %s ", ctime(&t) + 4, action); if (rt != NULL) { char buf1[INET6_ADDRSTRLEN]; (void) fprintf(fp, "prefix %s/%d ", inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1)), rt->rt_prefix_length); (void) fprintf(fp, "via %s metric %d", inet_ntop(AF_INET6, (void *)&rt->rt_router, buf1, sizeof (buf1)), rt->rt_metric); if (rt->rt_ifp != NULL) { (void) fprintf(fp, " if %s", (rt->rt_ifp->int_name != NULL) ? rt->rt_ifp->int_name : "(noname)"); } (void) fprintf(fp, " state"); c = ' '; for (first = _B_TRUE, p = statebits; p->t_bits > 0; p++) { if ((rt->rt_state & p->t_bits) == 0) continue; (void) fprintf(fp, "%c%s", c, p->t_name); if (first) { c = '|'; first = _B_FALSE; } } if (first) (void) fprintf(fp, " 0"); if (rt->rt_flags & (RTF_UP | RTF_GATEWAY)) { c = ' '; for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) { if ((rt->rt_flags & p->t_bits) == 0) continue; (void) fprintf(fp, "%c%s", c, p->t_name); if (first) { c = '|'; first = _B_FALSE; } } } } (void) putc('\n', fp); if (!tracepackets && rt != NULL && rt->rt_ifp != NULL) dumpif(fp, rt->rt_ifp); (void) fflush(fp); } static void dumpif(FILE *fp, struct interface *ifp) { if (ifp->int_input.ifd_count != 0 || ifp->int_output.ifd_count != 0) { (void) fprintf(fp, "*** Packet history for interface %s ***\n", (ifp->int_name != NULL) ? ifp->int_name : "(noname)"); dumptrace(fp, "to", &ifp->int_output); dumptrace(fp, "from", &ifp->int_input); (void) fprintf(fp, "*** end packet history ***\n"); } (void) fflush(fp); } static void dumptrace(FILE *fp, char *dir, struct ifdebug *ifd) { struct iftrace *t; char *cp = (strcmp(dir, "to") != 0) ? "Output" : "Input"; if (ifd->ifd_front == ifd->ifd_records && ifd->ifd_front->ift_size == 0) { (void) fprintf(fp, "%s: no packets.\n", cp); (void) fflush(fp); return; } (void) fprintf(fp, "%s trace:\n", cp); t = ifd->ifd_front - ifd->ifd_count; if (t < ifd->ifd_records) t += NRECORDS; for (; ifd->ifd_count; ifd->ifd_count--, t++) { if (t >= ifd->ifd_records + NRECORDS) t = ifd->ifd_records; if (t->ift_size == 0) continue; (void) fprintf(fp, "%.24s: metric=%d\n", ctime(&t->ift_stamp), t->ift_metric); dumppacket(fp, dir, (struct sockaddr_in6 *)&t->ift_who, t->ift_packet, t->ift_size); } } /*ARGSUSED*/ void dumppacket(FILE *fp, char *dir, struct sockaddr_in6 *who, char *cp, int size) { /* XXX Output contents of the RIP packet */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ /* * Routing table management daemon. */ /* * Trace record format. */ struct iftrace { time_t ift_stamp; /* time stamp */ struct sockaddr_in6 ift_who; /* from/to */ char *ift_packet; /* pointer to packet */ int ift_size; /* size of packet */ int ift_metric; /* metric on associated metric */ }; /* * Per interface packet tracing buffers. An incoming and * outgoing circular buffer of packets is maintained, per * interface, for debugging. Buffers are dumped whenever * an interface is marked down. */ struct ifdebug { struct iftrace *ifd_records; /* array of trace records */ struct iftrace *ifd_front; /* next empty trace record */ int ifd_count; /* number of unprinted records */ struct interface *ifd_if; /* for locating stuff */ }; /* * Packet tracing stuff. */ extern FILE *ftrace; extern boolean_t tracepackets; extern int tracing; #define ACTION_BIT 0x0001 #define INPUT_BIT 0x0002 #define OUTPUT_BIT 0x0004 #define TRACE_ACTION(action, route) { \ if (tracing & ACTION_BIT) \ traceaction(ftrace, (action), (route)); \ } #define TRACE_INPUT(ifp, src, size) { \ if ((tracing & INPUT_BIT) && ((ifp) != NULL)) { \ trace(&(ifp)->int_input, (src), packet, (size), \ (ifp)->int_metric); \ } \ if (tracepackets) { \ dumppacket(stdout, "from", (struct sockaddr_in6 *)(src), \ packet, (size)); \ } \ } #define TRACE_OUTPUT(ifp, dst, size) { \ if ((tracing & OUTPUT_BIT) && ((ifp) != NULL)) { \ trace(&(ifp)->int_output, (dst), packet, (size), \ (ifp)->int_metric); \ } \ if (tracepackets) { \ dumppacket(stdout, "to", (struct sockaddr_in6 *)(dst), \ packet, (size)); \ } \ } extern void dumppacket(FILE *, char *, struct sockaddr_in6 *, char *, int); extern void trace(struct ifdebug *, struct sockaddr_in6 *, char *, int, int); extern void traceaction(FILE *, char *, struct rt_entry *); extern void traceinit(struct interface *); extern void traceon(char *); extern void traceonfp(FILE *);