#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);
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;
}
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) {
ifp->int_transitions++;
changes = _B_TRUE;
}
if_purge(ifp);
}
continue;
}
if (ifs.int_flags & RIP6_IFF_POINTOPOINT) {
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 {
if (ioctl(iocsoc, SIOCGLIFSUBNET, (char *)&lifr) < 0) {
syslog(LOG_ERR,
"initifs: ioctl (get subnet prefix): %m");
continue;
}
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 (lifr.lifr_mtu < IPV6_MIN_MTU)
ifs.int_mtu = IPV6_MIN_MTU;
else
ifs.int_mtu = lifr.lifr_mtu;
if (ifp != NULL) {
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) {
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 (!(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 (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) {
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;
}
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);
}
void
resetup_listen_sock(struct interface *ifp, int newindex)
{
int i;
(void) close(ifp->int_sock);
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;
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--;
ifp->int_sock = setup_listen_sock(newindex);
break;
}
}
}