#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);
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);
}
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) {
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));
}
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);
}
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 (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:
ifp->int_ipackets++;
newsize = 0;
size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) +
sizeof (msg->rip6_res1);
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) {
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++;
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++) {
if (n->rip6_metric == HOPCNT_NEXTHOP) {
if (IN6_IS_ADDR_UNSPECIFIED(&n->rip6_prefix)) {
foundnexthop = _B_FALSE;
continue;
}
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;
}
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;
}
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 (n->rip6_metric >= HOPCNT_INFINITY &&
rt->rt_metric == HOPCNT_INFINITY)
continue;
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;
}
}
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 &&
timercmp(&nextmcast, &now, <)) {
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 (nextmcast.tv_sec >
lastfullupdate.tv_sec + supplyinterval - MIN_WAIT_TIME) {
nextmcast.tv_sec =
lastfullupdate.tv_sec + supplyinterval + 1;
}
}
}