dhcpcd/if-sun.c
2016-09-15 10:36:58 +00:00

1653 lines
37 KiB
C

/*
* Solaris interface driver for dhcpcd
* Copyright (c) 2016 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <libdlpi.h>
#include <stddef.h>
#include <stdlib.h>
#include <stropts.h>
#include <string.h>
#include <unistd.h>
#include <inet/ip.h>
/* private interface we can hook into to get
* a better getifaddrs(3). */
#include <libsocket_priv.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/if_ether.h>
#include <netinet/udp.h>
#include <sys/ioctl.h>
#include <sys/pfmod.h>
#include <sys/tihdr.h>
#include <sys/utsname.h>
#include "config.h"
#include "common.h"
#include "dhcp.h"
#include "if.h"
#include "if-options.h"
#include "ipv4.h"
#include "ipv6.h"
#include "ipv6nd.h"
#ifndef ARP_MOD_NAME
# define ARP_MOD_NAME "arp"
#endif
#ifndef RT_ROUNDUP
#define RT_ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define RT_ADVANCE(x, n) (x += RT_ROUNDUP(salen(n)))
#endif
#define COPYOUT(sin, sa) do { \
if ((sa) && ((sa)->sa_family == AF_INET)) \
(sin) = ((const struct sockaddr_in *)(const void *) \
(sa))->sin_addr; \
} while (0)
#define COPYOUT6(sin, sa) do { \
if ((sa) && ((sa)->sa_family == AF_INET6)) \
(sin) = ((const struct sockaddr_in6 *)(const void *) \
(sa))->sin6_addr; \
} while (0)
#ifndef CLLADDR
# define CLLADDR(s) (const void *)((s)->sdl_data + (s)->sdl_nlen)
#endif
#ifdef INET
/* Instead of using DLPI directly, we use libdlpi which is
* Solaris sepcific. */
struct dl_if {
TAILQ_ENTRY(dl_if) next;
struct interface *iface;
int fd;
dlpi_handle_t dh;
uint8_t broadcast[DLPI_PHYSADDR_MAX];
};
TAILQ_HEAD(dl_if_head, dl_if);
#endif
struct priv {
#ifdef INET
struct dl_if_head dl_ifs;
#endif
#ifdef INET6
int pf_inet6_fd;
#endif
};
int
if_init(__unused struct interface *ifp)
{
return 0;
}
int
if_conf(__unused struct interface *ifp)
{
return 0;
}
int
if_opensockets_os(struct dhcpcd_ctx *ctx)
{
struct priv *priv;
if ((priv = malloc(sizeof(*priv))) == NULL)
return -1;
ctx->priv = priv;
#ifdef INET
TAILQ_INIT(&priv->dl_ifs);
#endif
#ifdef INET6
priv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
/* Don't return an error so we at least work on kernels witout INET6
* even though we expect INET6 support.
* We will fail noisily elsewhere anyway. */
#endif
ctx->link_fd = socket(PF_ROUTE,
SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
#ifdef INET
if (ctx->link_fd == -1)
free(ctx->priv);
#endif
return ctx->link_fd == -1 ? -1 : 0;
}
void
if_closesockets_os(struct dhcpcd_ctx *ctx)
{
#ifdef INET6
struct priv *priv;
priv = (struct priv *)ctx->priv;
if (priv->pf_inet6_fd != -1)
close(priv->pf_inet6_fd);
#endif
/* each interface should have closed itself */
free(ctx->priv);
}
int
if_getssid(struct interface *ifp)
{
UNUSED(ifp);
errno = ENOTSUP;
return -1;
}
int
if_vimaster(__unused const struct dhcpcd_ctx *ctx, __unused const char *ifname)
{
return 0;
}
int
if_machinearch(__unused char *str, __unused size_t len)
{
/* There is no extra data really.
* isainfo -v does return amd64, but also i386. */
return 0;
}
struct linkwalk {
struct ifaddrs *lw_ifa;
int lw_error;
};
static boolean_t
if_newaddr(const char *ifname, void *arg)
{
struct linkwalk *lw = arg;
struct ifaddrs *ifa;
dlpi_handle_t dh;
dlpi_info_t dlinfo;
uint8_t pa[DLPI_PHYSADDR_MAX];
size_t pa_len;
struct sockaddr_dl *sdl;
ifa = NULL;
if (dlpi_open(ifname, &dh, 0) != DLPI_SUCCESS)
goto failed1;
if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS)
goto failed;
/* For some reason, dlpi_info won't return the
* physical address, it's all zero's.
* So cal dlpi_get_physaddr. */
pa_len = DLPI_PHYSADDR_MAX;
if (dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR,
pa, &pa_len) != DLPI_SUCCESS)
goto failed;
if ((ifa = calloc(1, sizeof(*ifa))) == NULL)
goto failed;
if ((ifa->ifa_name = strdup(ifname)) == NULL)
goto failed;
if ((sdl = calloc(1, sizeof(*sdl))) == NULL)
goto failed;
ifa->ifa_addr = (struct sockaddr *)sdl;
sdl->sdl_index = if_nametoindex(ifname);
sdl->sdl_family = AF_LINK;
switch (dlinfo.di_mactype) {
case DL_ETHER:
sdl->sdl_type = IFT_ETHER;
break;
case DL_IB:
sdl->sdl_type = IFT_IB;
break;
default:
sdl->sdl_type = IFT_OTHER;
break;
}
sdl->sdl_alen = pa_len;
memcpy(sdl->sdl_data, pa, pa_len);
ifa->ifa_next = lw->lw_ifa;
lw->lw_ifa = ifa;
dlpi_close(dh);
return (B_FALSE);
failed:
dlpi_close(dh);
if (ifa != NULL) {
free(ifa->ifa_name);
free(ifa->ifa_addr);
free(ifa);
}
failed1:
lw->lw_error = errno;
return (B_TRUE);
}
/* Creates an empty sockaddr_dl for lo0. */
static struct ifaddrs *
if_ifa_lo0(void)
{
struct ifaddrs *ifa;
struct sockaddr_dl *sdl;
if ((ifa = calloc(1, sizeof(*ifa))) == NULL)
return NULL;
if ((sdl = calloc(1, sizeof(*sdl))) == NULL) {
free(ifa);
return NULL;
}
if ((ifa->ifa_name = strdup("lo0")) == NULL) {
free(ifa);
free(sdl);
return NULL;
}
ifa->ifa_addr = (struct sockaddr *)sdl;
ifa->ifa_flags = IFF_LOOPBACK;
sdl->sdl_family = AF_LINK;
sdl->sdl_index = if_nametoindex("lo0");
return ifa;
}
/* getifaddrs(3) does not support AF_LINK, strips aliases and won't
* report addresses that are not UP.
* As such it's just totally useless, so we need to roll our own. */
int
if_getifaddrs(struct ifaddrs **ifap)
{
struct linkwalk lw;
struct ifaddrs *ifa;
/* Private libc function which we should not have to call
* to get non UP addresses. */
if (getallifaddrs(AF_UNSPEC, &lw.lw_ifa, 0) == -1)
return -1;
/* Start with some AF_LINK addresses. */
lw.lw_error = 0;
dlpi_walk(if_newaddr, &lw, 0);
if (lw.lw_error != 0) {
freeifaddrs(lw.lw_ifa);
errno = lw.lw_error;
return -1;
}
/* lo0 doesn't appear in dlpi_walk, so fudge it. */
if ((ifa = if_ifa_lo0()) == NULL) {
freeifaddrs(lw.lw_ifa);
return -1;
}
ifa->ifa_next = lw.lw_ifa;
*ifap = ifa;
return 0;
}
static int
salen(const struct sockaddr *sa)
{
switch (sa->sa_family) {
case AF_LINK:
return sizeof(struct sockaddr_dl);
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
default:
return sizeof(struct sockaddr);
}
}
static void
if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)
{
memset(sdl, 0, sizeof(*sdl));
sdl->sdl_family = AF_LINK;
sdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0;
sdl->sdl_index = (unsigned short)ifp->index;
}
static int
get_addrs(int type, const void *data, const struct sockaddr **sa)
{
const char *cp;
int i;
cp = data;
for (i = 0; i < RTAX_MAX; i++) {
if (type & (1 << i)) {
sa[i] = (const struct sockaddr *)cp;
RT_ADVANCE(cp, sa[i]);
} else
sa[i] = NULL;
}
return 0;
}
static struct interface *
if_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl)
{
if (sdl->sdl_index)
return if_findindex(ctx->ifaces, sdl->sdl_index);
if (sdl->sdl_nlen) {
char ifname[IF_NAMESIZE];
memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);
ifname[sdl->sdl_nlen] = '\0';
return if_find(ctx->ifaces, ifname);
}
if (sdl->sdl_alen) {
struct interface *ifp;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (ifp->hwlen == sdl->sdl_alen &&
memcmp(ifp->hwaddr,
sdl->sdl_data, sdl->sdl_alen) == 0)
return ifp;
}
}
errno = ENOENT;
return NULL;
}
static struct interface *
if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
{
if (sa == NULL) {
errno = EINVAL;
return NULL;
}
switch (sa->sa_family) {
case AF_LINK:
{
const struct sockaddr_dl *sdl;
sdl = (const void *)sa;
return if_findsdl(ctx, sdl);
}
#ifdef INET
case AF_INET:
{
const struct sockaddr_in *sin;
struct ipv4_addr *ia;
sin = (const void *)sa;
if ((ia = ipv4_findmaskaddr(ctx, &sin->sin_addr)))
return ia->iface;
break;
}
#endif
#ifdef INET6
case AF_INET6:
{
const struct sockaddr_in6 *sin;
struct ipv6_addr *ia;
sin = (const void *)sa;
if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
return ia->iface;
break;
}
#endif
default:
errno = EAFNOSUPPORT;
return NULL;
}
errno = ENOENT;
return NULL;
}
#ifdef INET
static void
if_finishrt(struct rt *rt)
{
/* Solaris has a subnet route with the gateway
* of the owning address.
* dhcpcd has a blank gateway here to indicate a
* subnet route. */
if (rt->gate.s_addr != ntohl(INADDR_ANY) &&
ipv4_iffindaddr(UNCONST(rt->iface), &rt->gate, NULL))
rt->gate.s_addr = ntohl(INADDR_ANY);
/* Solaris likes to set route MTU to match
* interface MTU when adding routes.
* This confuses dhcpcd as it expects MTU to be 0
* when no explicit MTU has been set. */
if (rt->mtu == (unsigned int)if_getmtu(rt->iface))
rt->mtu = 0;
}
static int
if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
{
const struct sockaddr *sa, *rti_info[RTAX_MAX];
sa = (const void *)(rtm + 1);
if (sa->sa_family != AF_INET)
return -1;
if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
return -1;
get_addrs(rtm->rtm_addrs, sa, rti_info);
memset(rt, 0, sizeof(*rt));
rt->flags = (unsigned int)rtm->rtm_flags;
COPYOUT(rt->dest, rti_info[RTAX_DST]);
if (rtm->rtm_addrs & RTA_NETMASK)
COPYOUT(rt->mask, rti_info[RTAX_NETMASK]);
else
rt->mask.s_addr = INADDR_BROADCAST;
COPYOUT(rt->gate, rti_info[RTAX_GATEWAY]);
COPYOUT(rt->src, rti_info[RTAX_SRC]);
rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
if (rtm->rtm_index)
rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index);
else if (rtm->rtm_addrs & RTA_IFP)
rt->iface = if_findsa(ctx, rti_info[RTAX_IFP]);
else if (rtm->rtm_addrs & RTA_GATEWAY)
rt->iface = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
/* If we don't have an interface and it's a host route, it maybe
* to a local ip via the loopback interface. */
if (rt->iface == NULL &&
!(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
{
struct ipv4_addr *ia;
if ((ia = ipv4_findaddr(ctx, &rt->dest)))
rt->iface = ia->iface;
}
if (rt->iface == NULL) {
errno = ESRCH;
return -1;
}
if_finishrt(rt);
return 0;
}
#endif
#ifdef INET6
static void
if_finishrt6(struct rt6 *rt)
{
/* Solaris has a subnet route with the gateway
* of the owning address.
* dhcpcd has a blank gateway here to indicate a
* subnet route. */
if (!IN6_IS_ADDR_UNSPECIFIED(&rt->gate) &&
ipv6_iffindaddr(UNCONST(rt->iface), &rt->gate, 0))
rt->gate = in6addr_any;
/* Solarais likes to set route MTU to match
* interface MTU when adding routes.
* This confuses dhcpcd as it expects MTU to be 0
* when no explicit MTU has been set. */
if (rt->mtu == (unsigned int)if_getmtu(rt->iface))
rt->mtu = 0;
}
static int
if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, const struct rt_msghdr *rtm)
{
const struct sockaddr *sa, *rti_info[RTAX_MAX];
sa = (const void *)(rtm + 1);
if (sa->sa_family != AF_INET6)
return -1;
if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
return -1;
get_addrs(rtm->rtm_addrs, sa, rti_info);
memset(rt, 0, sizeof(*rt));
rt->flags = (unsigned int)rtm->rtm_flags;
COPYOUT6(rt->dest, rti_info[RTAX_DST]);
if (rtm->rtm_addrs & RTA_NETMASK)
COPYOUT6(rt->mask, rti_info[RTAX_NETMASK]);
else
ipv6_mask(&rt->mask, 128);
COPYOUT6(rt->gate, rti_info[RTAX_GATEWAY]);
rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
if (rtm->rtm_index)
rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index);
else if (rtm->rtm_addrs & RTA_IFP)
rt->iface = if_findsa(ctx, rti_info[RTAX_IFP]);
else if (rtm->rtm_addrs & RTA_GATEWAY)
rt->iface = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
/* If we don't have an interface and it's a host route, it maybe
* to a local ip via the loopback interface. */
if (rt->iface == NULL &&
!(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
{
struct ipv6_addr *ia;
if ((ia = ipv6_findaddr(ctx, &rt->dest, 0)))
rt->iface = ia->iface;
}
if (rt->iface == NULL) {
errno = ESRCH;
return -1;
}
if_finishrt6(rt);
return 0;
}
#endif
static void
if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
{
const struct sockaddr *sa;
/* Ignore messages generated by us */
if (rtm->rtm_pid == getpid()) {
ctx->options &= ~DHCPCD_RTM_PPID;
return;
}
/* Ignore messages sent by the parent after forking */
if ((ctx->options &
(DHCPCD_RTM_PPID | DHCPCD_DAEMONISED)) ==
(DHCPCD_RTM_PPID | DHCPCD_DAEMONISED) &&
rtm->rtm_pid == ctx->ppid)
{
/* If this is the last successful message sent,
* clear the check flag as it's possible another
* process could re-use the same pid and also
* manipulate therouting table. */
if (rtm->rtm_seq == ctx->pseq)
ctx->options &= ~DHCPCD_RTM_PPID;
return;
}
sa = (const void *)(rtm + 1);
switch (sa->sa_family) {
#ifdef INET
case AF_INET:
{
struct rt rt;
if (if_copyrt(ctx, &rt, rtm) == 0)
ipv4_handlert(ctx, rtm->rtm_type, &rt, 0);
break;
}
#endif
#ifdef INET6
case AF_INET6:
{
struct rt6 rt6;
if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
break;
/*
* BSD announces host routes.
* But does this work on Solaris?
* As such, we should be notified of reachability by its
* existance with a hardware address.
*/
if (rtm->rtm_flags & (RTF_HOST)) {
const struct sockaddr *rti_info[RTAX_MAX];
struct in6_addr dst6;
struct sockaddr_dl sdl;
get_addrs(rtm->rtm_addrs, sa, rti_info);
COPYOUT6(dst6, rti_info[RTAX_DST]);
if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK)
memcpy(&sdl, rti_info[RTAX_GATEWAY],
sizeof(sdl));
else
sdl.sdl_alen = 0;
ipv6nd_neighbour(ctx, &dst6,
rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ?
IPV6ND_REACHABLE : 0);
break;
}
if (if_copyrt6(ctx, &rt6, rtm) == 0)
ipv6_handlert(ctx, rtm->rtm_type, &rt6);
break;
}
#endif
}
}
static void
if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
{
struct interface *ifp;
const struct sockaddr *sa, *rti_info[RTAX_MAX];
/* XXX We have no way of knowing who generated these
* messages wich truely sucks because we want to
* avoid listening to our own delete messages. */
if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)
return;
sa = (const void *)(ifam + 1);
get_addrs(ifam->ifam_addrs, sa, rti_info);
if (rti_info[RTAX_IFA] == NULL)
return;
switch (rti_info[RTAX_IFA]->sa_family) {
case AF_LINK:
{
struct sockaddr_dl sdl;
if (ifam->ifam_type != RTM_CHGADDR &&
ifam->ifam_type != RTM_NEWADDR)
break;
memcpy(&sdl, rti_info[RTAX_IFA], sizeof(sdl));
dhcpcd_handlehwaddr(ctx, ifp->name, CLLADDR(&sdl),sdl.sdl_alen);
break;
}
#ifdef INET
case AF_INET:
{
struct in_addr addr, mask, bcast;
COPYOUT(addr, rti_info[RTAX_IFA]);
COPYOUT(mask, rti_info[RTAX_NETMASK]);
COPYOUT(bcast, rti_info[RTAX_BRD]);
ipv4_handleifa(ctx,
ifam->ifam_type == RTM_CHGADDR ?
RTM_NEWADDR : ifam->ifam_type,
NULL, ifp->name, &addr, &mask, &bcast);
break;
}
#endif
#ifdef INET6
case AF_INET6:
{
struct in6_addr addr6, mask6;
const struct sockaddr_in6 *sin6;
sin6 = (const void *)rti_info[RTAX_IFA];
addr6 = sin6->sin6_addr;
sin6 = (const void *)rti_info[RTAX_NETMASK];
mask6 = sin6->sin6_addr;
ipv6_handleifa(ctx,
ifam->ifam_type == RTM_CHGADDR ?
RTM_NEWADDR : ifam->ifam_type,
NULL, ifp->name, &addr6, ipv6_prefixlen(&mask6));
break;
}
#endif
}
}
static void
if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
{
struct interface *ifp;
int state;
if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)
return;
if (ifm->ifm_flags & IFF_OFFLINE || !(ifm->ifm_flags & IFF_UP))
state = LINK_DOWN;
else
state = LINK_UP;
dhcpcd_handlecarrier(ctx, state,
(unsigned int)ifm->ifm_flags, ifp->name);
}
static void
if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
{
if (rtm->rtm_version != RTM_VERSION)
return;
switch(rtm->rtm_type) {
case RTM_IFINFO:
if_ifinfo(ctx, (const void *)rtm);
break;
case RTM_ADD: /* FALLTHROUGH */
case RTM_CHANGE: /* FALLTHROUGH */
case RTM_DELETE:
if_rtm(ctx, (const void *)rtm);
break;
case RTM_CHGADDR: /* FALLTHROUGH */
case RTM_DELADDR: /* FALLTHROUGH */
case RTM_NEWADDR:
if_ifa(ctx, (const void *)rtm);
break;
}
}
int
if_handlelink(struct dhcpcd_ctx *ctx)
{
struct msghdr msg;
ssize_t len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = ctx->iov;
msg.msg_iovlen = 1;
if ((len = recvmsg_realloc(ctx->link_fd, &msg, 0)) == -1)
return -1;
if (len != 0)
if_dispatch(ctx, ctx->iov[0].iov_base);
return 0;
}
static void
if_octetstr(char *buf, const Octet_t *o, ssize_t len)
{
int i;
char *p;
p = buf;
for (i = 0; i < o->o_length; i++) {
if ((p + 1) - buf < len)
*p++ = o->o_bytes[i];
else
break;
}
*p = '\0';
}
static int
if_parsert(struct dhcpcd_ctx *ctx, unsigned int level, unsigned int name,
int (*walkrt)(struct dhcpcd_ctx *, char *, size_t))
{
int s, retval, code, flags;
uintptr_t buf[512 / sizeof(uintptr_t)];
struct strbuf ctlbuf, databuf;
struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
struct T_error_ack *tea = (struct T_error_ack *)buf;
struct opthdr *req;
if ((s = open("/dev/arp", O_RDWR)) == -1)
return -1;
/* Assume we are erroring. */
retval = -1;
tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
tor->OPT_offset = sizeof (struct T_optmgmt_req);
tor->OPT_length = sizeof (struct opthdr);
tor->MGMT_flags = T_CURRENT;
req = (struct opthdr *)&tor[1];
req->level = EXPER_IP_AND_ALL_IRES;
req->name = 0;
req->len = 1;
ctlbuf.buf = (char *)buf;
ctlbuf.len = tor->OPT_length + tor->OPT_offset;
if (putmsg(s, &ctlbuf, NULL, 0) == 1)
goto out;
req = (struct opthdr *)&toa[1];
ctlbuf.maxlen = sizeof(buf);
/* Create a reasonable buffer to start with */
databuf.maxlen = BUFSIZ * 2;
if ((databuf.buf = malloc(databuf.maxlen)) == NULL)
goto out;
for (;;) {
flags = 0;
if ((code = getmsg(s, &ctlbuf, 0, &flags)) == -1)
break;
if (code == 0 &&
toa->PRIM_type == T_OPTMGMT_ACK &&
toa->MGMT_flags == T_SUCCESS &&
(size_t)ctlbuf.len >= sizeof(struct T_optmgmt_ack))
{
/* End of messages, so return success! */
retval = 0;
break;
}
if (tea->PRIM_type == T_ERROR_ACK) {
errno = tea->TLI_error == TSYSERR ?
tea->UNIX_error : EPROTO;
break;
}
if (code != MOREDATA ||
toa->PRIM_type != T_OPTMGMT_ACK ||
toa->MGMT_flags != T_SUCCESS)
{
errno = ENOMSG;
break;
}
/* Try to ensure out buffer is big enough
* for future messages as well. */
if ((size_t)databuf.maxlen < req->len) {
size_t newlen;
free(databuf.buf);
newlen = roundup(req->len, BUFSIZ);
if ((databuf.buf = malloc(newlen)) == NULL)
break;
databuf.maxlen = newlen;
}
flags = 0;
if (getmsg(s, NULL, &databuf, &flags) == -1)
break;
/* We always have to get the data before moving onto
* the next item, so don't move this test higher up
* to avoid the buffer allocation and getmsg calls. */
if (req->level == level && req->name == name) {
if (walkrt(ctx, databuf.buf, req->len) == -1)
break;
}
}
free(databuf.buf);
out:
close(s);
return retval;
}
static int
if_addaddr(int fd, const char *ifname,
struct sockaddr_storage *addr, struct sockaddr_storage *mask)
{
struct lifreq lifr;
memset(&lifr, 0, sizeof(lifr));
strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
/* First assign the netmask. */
lifr.lifr_addr = *mask;
if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1)
return -1;
/* Then assign the address. */
lifr.lifr_addr = *addr;
if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1)
return -1;
/* Now bring it up. */
if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
return -1;
if (!(lifr.lifr_flags & IFF_UP)) {
lifr.lifr_flags |= IFF_UP;
if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1)
return -1;
}
return 0;
}
static int
if_plumblif(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)
{
struct lifreq lifr;
int s;
memset(&lifr, 0, sizeof(lifr));
strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
lifr.lifr_addr.ss_family = af;
if (af == AF_INET)
s = ctx->pf_inet_fd;
else {
struct priv *priv;
priv = (struct priv *)ctx->priv;
s = priv->pf_inet6_fd;
}
return ioctl(s,
cmd == RTM_NEWADDR ? SIOCLIFADDIF : SIOCLIFREMOVEIF,
&lifr) == -1 && errno != EEXIST ? -1 : 0;
}
static int
if_plumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
{
dlpi_handle_t dh;
int fd, af_fd, mux_fd, retval;
struct lifreq lifr;
const char *udp_dev;
memset(&lifr, 0, sizeof(lifr));
switch (af) {
case AF_INET:
lifr.lifr_flags = IFF_IPV4;
af_fd = ctx->pf_inet_fd;
udp_dev = UDP_DEV_NAME;
break;
case AF_INET6:
{
struct priv *priv;
/* We will take care of setting the link local address. */
lifr.lifr_flags = IFF_IPV6 | IFF_NOLINKLOCAL;
priv = (struct priv *)ctx->priv;
af_fd = priv->pf_inet6_fd;
udp_dev = UDP6_DEV_NAME;
break;
}
default:
errno = EPROTONOSUPPORT;
return -1;
}
if (dlpi_open(ifname, &dh, DLPI_NOATTACH) != DLPI_SUCCESS) {
errno = EINVAL;
return -1;
}
fd = dlpi_fd(dh);
retval = -1;
mux_fd = -1;
if (ioctl(fd, I_PUSH, IP_MOD_NAME) == -1)
goto out;
strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
goto out;
/* Get full flags. */
if (ioctl(af_fd, SIOCGLIFFLAGS, &lifr) == -1)
goto out;
/* Open UDP as a multiplexor to PLINK the interface stream.
* UDP is used because STREAMS will not let you PLINK a driver
* under itself and IP is generally at the bottom of the stream. */
if ((mux_fd = open(udp_dev, O_RDWR)) == -1)
goto out;
/* POP off all undesired modules. */
while (ioctl(mux_fd, I_POP, 0) != -1)
;
if (errno != EINVAL)
goto out;
if (lifr.lifr_flags & IFF_IPV4 && !(lifr.lifr_flags & IFF_NOARP)) {
if (ioctl(mux_fd, I_PUSH, ARP_MOD_NAME) == -1)
goto out;
}
/* PLINK the interface stream so it persists. */
if (ioctl(mux_fd, I_PLINK, fd) == -1)
goto out;
retval = 0;
out:
dlpi_close(dh);
if (mux_fd != -1)
close(mux_fd);
return retval;
}
static int
if_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
{
struct sockaddr_storage addr, mask;
/* For the time being, don't unplumb the interface, just
* set the address to zero. */
memset(&addr, 0, sizeof(addr));
addr.ss_family = af;
memset(&mask, 0, sizeof(mask));
mask.ss_family = af;
return if_addaddr(ctx->pf_inet_fd, ifname , &addr, &mask);
}
static int
if_plumb(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)
{
struct if_spec spec;
if (if_nametospec(ifname, &spec) == -1)
return -1;
if (spec.lun != -1)
return if_plumblif(cmd, ctx, af, ifname);
if (cmd == RTM_NEWADDR)
return if_plumbif(ctx, af, ifname);
else
return if_unplumbif(ctx, af, ifname);
}
static int
if_rtmsg(unsigned char cmd, const struct interface *ifp,
int addrs, int flags,
const struct sockaddr *dst, const struct sockaddr *mask,
const struct sockaddr *gate, const struct sockaddr *src,
uint32_t mtu)
{
struct rtm
{
struct rt_msghdr hdr;
char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
} rtm;
char *bp = rtm.buffer;
size_t l;
/* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.
* This includes subnet/prefix routes. */
if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) &&
ifp->ctx->options & DHCPCD_DAEMONISE &&
!(ifp->ctx->options & DHCPCD_DAEMONISED))
ifp->ctx->options |= DHCPCD_RTM_PPID;
#define ADDSA(sa) { \
l = RT_ROUNDUP(salen((sa))); \
memcpy(bp, (sa), l); \
bp += l; \
}
memset(&rtm, 0, sizeof(rtm));
rtm.hdr.rtm_version = RTM_VERSION;
rtm.hdr.rtm_type = cmd;
rtm.hdr.rtm_seq = ++ifp->ctx->seq;
rtm.hdr.rtm_flags = flags;
rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | addrs;
if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
rtm.hdr.rtm_flags |= RTF_UP;
if (!(rtm.hdr.rtm_flags & RTF_REJECT) ||
!(rtm.hdr.rtm_flags & RTF_GATEWAY))
rtm.hdr.rtm_addrs |= RTA_IFP;
if (mtu != 0) {
rtm.hdr.rtm_inits |= RTV_MTU;
rtm.hdr.rtm_rmx.rmx_mtu = mtu;
}
}
ADDSA(dst);
ADDSA(gate);
if (rtm.hdr.rtm_addrs & RTA_NETMASK)
ADDSA(mask);
if (rtm.hdr.rtm_addrs & RTA_IFP) {
struct sockaddr_dl sdl;
if_linkaddr(&sdl, ifp);
ADDSA((struct sockaddr *)&sdl);
}
/* This no workie :/ */
#if 0
/* route(1M) says RTA_IFA is accepted but ignored
* it's unclear how RTA_SRC is different. */
if (rtm.hdr.rtm_addrs & RTA_IFA) {
rtm.hdr.rtm_addrs &= ~RTA_IFA;
rtm.hdr.rtm_addrs |= RTA_SRC;
}
if (rtm.hdr.rtm_addrs & RTA_SRC)
ADDSA(src);
#endif
#undef ADDSA
rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
if (write(ifp->ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
return -1;
ifp->ctx->sseq = ifp->ctx->seq;
return 0;
}
static int
if_addrflags0(int fd, const char *ifname)
{
struct lifreq lifr;
memset(&lifr, 0, sizeof(lifr));
strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
return -1;
return lifr.lifr_flags;
}
#ifdef INET
const char *if_pfname = "DLPI";
static struct dl_if *
if_findraw(struct interface *ifp, int fd)
{
struct dl_if *di;
struct priv *priv;
priv = (struct priv *)ifp->ctx->priv;
TAILQ_FOREACH(di, &priv->dl_ifs, next) {
if (di->fd == fd)
return di;
}
errno = ENXIO;
return NULL;
}
void
if_closeraw(struct interface *ifp, int fd)
{
struct dl_if *di;
if ((di = if_findraw(ifp, fd)) != NULL) {
struct priv *priv;
priv = (struct priv *)ifp->ctx->priv;
TAILQ_REMOVE(&priv->dl_ifs, di, next);
dlpi_close(di->dh);
free(di);
}
}
int
if_openraw(struct interface *ifp, uint16_t protocol)
{
dlpi_handle_t dh;
struct priv *priv;
struct dl_if *di;
dlpi_info_t dlinfo;
struct packetfilt pf;
ushort_t *pfp;
struct strioctl sioc;
if (dlpi_open(ifp->name, &dh, 0) != DLPI_SUCCESS)
return -1;
/* We need to register pfmod, which is similar to BPF
* so the kernel can filter out the packets we don't need. */
if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS ||
dlpi_bind(dh, protocol, NULL) != DLPI_SUCCESS ||
(di = malloc(sizeof(*di))) == NULL)
goto failed1;
di->iface = ifp;
di->dh = dh;
di->fd = dlpi_fd(dh);
memcpy(di->broadcast, dlinfo.di_bcastaddr, dlinfo.di_bcastaddrlen);
priv = (struct priv *)ifp->ctx->priv;
pf.Pf_Priority = 0;
pfp = pf.Pf_Filter;
/* pfmod operates on 16 bits, so divide offsets by 2.
* When working on a 8 bits, mask off the bits not teested. */
switch (protocol) {
case ETHERTYPE_IP:
/* Filter fragments. */
*pfp++ = ENF_PUSHWORD + (offsetof(struct ip, ip_off) / 2);
*pfp++ = ENF_PUSHLIT | ENF_AND;
*pfp++ = htons(0x1fff | IP_MF);
*pfp++ = ENF_PUSHZERO | ENF_CAND;
/* Filter UDP. */
*pfp++ = ENF_PUSHWORD + (offsetof(struct ip, ip_p) / 2);
*pfp++ = ENF_PUSHFF00 | ENF_AND;
*pfp++ = ENF_PUSHLIT | ENF_CAND;
*pfp++ = htons(IPPROTO_UDP);
/* Filter BOOTPC. */
*pfp++ = ENF_PUSHWORD +
((sizeof(struct ip) +
offsetof(struct udphdr, uh_dport)) / 2);
*pfp++ = ENF_PUSHLIT | ENF_CAND;
*pfp++ = htons(BOOTPC);
break;
case ETHERTYPE_ARP:
/* We are only interested in IP. */
*pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_hrd) / 2);
*pfp++ = ENF_PUSHLIT | ENF_CAND;
*pfp++ = htons(ARPHRD_ETHER);
/* Must be REQUEST or REPLY. */
*pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_op) / 2);
*pfp++ = ENF_PUSHLIT | ENF_CNAND;
*pfp++ = htons(ARPOP_REQUEST);
*pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_op) / 2);
*pfp++ = ENF_PUSHLIT | ENF_CAND;
*pfp++ = htons(ARPOP_REPLY);
break;
default:
errno = EPROTOTYPE;
goto failed;
}
pf.Pf_FilterLen = pfp - pf.Pf_Filter;
sioc.ic_cmd = PFIOCSETF;
sioc.ic_timout = INFTIM;
sioc.ic_len = sizeof(pf);
sioc.ic_dp = (void *)&pf;
/* Install the filter and then flush the stream. */
if (ioctl(di->fd, I_PUSH, "pfmod") == -1 ||
ioctl(di->fd, I_STR, &sioc) == -1 ||
ioctl(di->fd, I_FLUSH, FLUSHR) == -1)
goto failed;
TAILQ_INSERT_TAIL(&priv->dl_ifs, di, next);
return di->fd;
failed:
free(di);
failed1:
dlpi_close(dh);
return -1;
}
ssize_t
if_sendraw(const struct interface *cifp, int fd, __unused uint16_t protocol,
const void *data, size_t len)
{
struct dl_if *di;
int r;
struct interface *ifp = UNCONST(cifp);
if ((di = if_findraw(ifp, fd)) == NULL)
return -1;
r = dlpi_send(di->dh, di->broadcast, ifp->hwlen, data, len, NULL);
return r == DLPI_SUCCESS ? (ssize_t)len : -1;
}
ssize_t
if_readraw(struct interface *ifp, int fd,
void *data, size_t len, int *flags)
{
struct dl_if *di;
int r;
size_t mlen;
if ((di = if_findraw(ifp, fd)) == NULL)
return -1;
*flags = RAW_EOF; /* We only ever read one packet. */
mlen = len;
*flags = RAW_EOF; /* We only ever read one packet. */
r = dlpi_recv(di->dh, NULL, NULL, data, &mlen, -1, NULL);
return r == DLPI_SUCCESS ? (ssize_t)mlen : -1;
}
int
if_address(unsigned char cmd, const struct ipv4_addr *ia)
{
struct sockaddr_storage ss_addr, ss_mask;
struct sockaddr_in *sin_addr, *sin_mask;
/* Either remove the alias or ensure it exists. */
if (if_plumb(cmd, ia->iface->ctx, AF_INET, ia->alias) == -1)
return -1;
if (cmd == RTM_DELADDR)
return 0;
if (cmd != RTM_NEWADDR) {
errno = EINVAL;
return -1;
}
sin_addr = (struct sockaddr_in *)&ss_addr;
sin_addr->sin_family = AF_INET;
sin_addr->sin_addr = ia->addr;
sin_mask = (struct sockaddr_in *)&ss_mask;
sin_mask->sin_family = AF_INET;
sin_mask->sin_addr = ia->mask;
return if_addaddr(ia->iface->ctx->pf_inet_fd,
ia->alias, &ss_addr, &ss_mask);
}
int
if_addrflags(const struct ipv4_addr *ia)
{
int flags, aflags;
aflags = if_addrflags0(ia->iface->ctx->pf_inet_fd, ia->alias);
if (aflags == -1)
return -1;
flags = 0;
if (aflags & IFF_DUPLICATE)
flags |= IN_IFF_DUPLICATED;
return flags;
}
int
if_route(unsigned char cmd, const struct rt *rt)
{
struct sockaddr_in dst = {
.sin_family = AF_INET,
.sin_addr = rt->dest
};
struct sockaddr_in mask = {
.sin_family = AF_INET,
.sin_addr = rt->mask
};
struct sockaddr_in gate = {
.sin_family = AF_INET,
.sin_addr = rt->gate
};
struct sockaddr_in src = {
.sin_family = AF_INET,
.sin_addr = rt->src
};
struct sockaddr_in *g;
int addrs, flags;
addrs = 0;
flags = 0;
if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
addrs |= RTA_GATEWAY | RTA_IFP;
/* Subnet routes are cloning or connected if supported.
* All other routes are static. */
if (rt->gate.s_addr != ntohl(INADDR_ANY))
flags |= RTF_STATIC;
if (rt->src.s_addr != ntohl(INADDR_ANY))
addrs |= RTA_IFA;
}
if (rt->mask.s_addr == htonl(INADDR_BROADCAST) &&
rt->gate.s_addr == htonl(INADDR_ANY))
{
flags |= RTF_HOST;
} else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) &&
rt->mask.s_addr == htonl(INADDR_BROADCAST))
{
flags |= RTF_HOST | RTF_GATEWAY;
/* Going via lo0 so remove the interface flags */
addrs &= ~(RTA_IFA | RTA_IFP);
} else {
addrs |= RTA_NETMASK;
if (flags & RTF_STATIC)
flags |= RTF_GATEWAY;
if (rt->mask.s_addr == htonl(INADDR_BROADCAST))
flags |= RTF_HOST;
}
if ((flags & RTF_HOST && rt->gate.s_addr == htonl(INADDR_ANY)) ||
!(flags & RTF_STATIC))
g = &src;
else
g = &gate;
return if_rtmsg(cmd, rt->iface, addrs, flags,
(struct sockaddr *)&dst, (struct sockaddr *)&mask,
(struct sockaddr *)g, (struct sockaddr *)&src, rt->mtu);
}
static int
if_walkrt(struct dhcpcd_ctx *ctx, char *data, size_t len)
{
mib2_ipRouteEntry_t *re, *e;
struct rt rt;
char ifname[IF_NAMESIZE];
if (len % sizeof(*re) != 0) {
errno = EINVAL;
return -1;
}
re = (mib2_ipRouteEntry_t *)data;
e = (mib2_ipRouteEntry_t *)(data + len);
do {
/* Skip route types we don't want. */
switch (re->ipRouteInfo.re_ire_type) {
case IRE_IF_CLONE:
case IRE_BROADCAST:
case IRE_MULTICAST:
case IRE_NOROUTE:
case IRE_LOCAL:
continue;
default:
break;
}
memset(&rt, 0, sizeof(rt));
rt.dest.s_addr = re->ipRouteDest;
rt.mask.s_addr = re->ipRouteMask;
rt.gate.s_addr = re->ipRouteNextHop;
rt.flags = re->ipRouteInfo.re_flags;
rt.src.s_addr = re->ipRouteInfo.re_src_addr;
rt.mtu = re->ipRouteInfo.re_max_frag;
if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));
rt.iface = if_find(ctx->ifaces, ifname);
if (rt.iface != NULL) {
if_finishrt(&rt);
ipv4_handlert(ctx, RTM_ADD, &rt, 1);
} else {
char destbuf[INET6_ADDRSTRLEN];
char gatebuf[INET6_ADDRSTRLEN];
const char *dest, *gate;
dest = inet_ntop(AF_INET, &rt.dest,
destbuf, INET6_ADDRSTRLEN);
gate = inet_ntop(AF_INET, &rt.gate,
gatebuf, INET6_ADDRSTRLEN);
logger(ctx, LOG_ERR,
"no iface (%s) for route to %s via %s",
ifname, dest, gate);
}
} while (++re < e);
return 0;
}
int
if_initrt(struct dhcpcd_ctx *ctx)
{
ipv4_freerts(ctx->ipv4_kroutes);
return if_parsert(ctx, MIB2_IP,MIB2_IP_ROUTE, if_walkrt);
}
#endif
#ifdef INET6
int
if_address6(unsigned char cmd, const struct ipv6_addr *ia)
{
struct sockaddr_storage ss_addr, ss_mask;
struct sockaddr_in6 *sin6_addr, *sin6_mask;
struct priv *priv;
int r;
/* Either remove the alias or ensure it exists. */
if (if_plumb(cmd, ia->iface->ctx, AF_INET6, ia->alias) == -1)
return -1;
if (cmd == RTM_DELADDR)
return 0;
if (cmd != RTM_NEWADDR) {
errno = EINVAL;
return -1;
}
priv = (struct priv *)ia->iface->ctx->priv;
sin6_addr = (struct sockaddr_in6 *)&ss_addr;
sin6_addr->sin6_family = AF_INET6;
sin6_addr->sin6_addr = ia->addr;
sin6_mask = (struct sockaddr_in6 *)&ss_mask;
sin6_mask->sin6_family = AF_INET6;
ipv6_mask(&sin6_mask->sin6_addr, ia->prefix_len);
r = if_addaddr(priv->pf_inet6_fd,
ia->alias, &ss_addr, &ss_mask);
if (r == -1 && errno == EEXIST)
return 0;
return r;
}
int
if_addrflags6(const struct ipv6_addr *ia)
{
struct priv *priv;
int aflags, flags;
priv = (struct priv *)ia->iface->ctx->priv;
aflags = if_addrflags0(priv->pf_inet6_fd, ia->alias);
flags = 0;
if (aflags & IFF_DUPLICATE)
flags |= IN6_IFF_DUPLICATED;
return flags;
}
int
if_getlifetime6(struct ipv6_addr *addr)
{
UNUSED(addr);
errno = ENOTSUP;
return -1;
}
int
if_route6(unsigned char cmd, const struct rt6 *rt)
{
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_addr = rt->dest
};
struct sockaddr_in6 mask = {
.sin6_family = AF_INET6,
.sin6_addr = rt->mask
};
struct sockaddr_in6 gate = {
.sin6_family = AF_INET6,
.sin6_addr = rt->gate
};
struct sockaddr_in6 src = {
.sin6_family = AF_INET6,
.sin6_addr = rt->src
};
struct sockaddr_in6 *g;
int addrs, flags;
addrs = RTA_NETMASK;
flags = 0;
if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
/* XXX FIXME: How to tell kernel route is subnet? */
} else
flags |= RTF_GATEWAY | RTF_STATIC;
if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate))
g = &src;
else
g = &gate;
return if_rtmsg(cmd, rt->iface, addrs, flags,
(struct sockaddr *)&dst, (struct sockaddr *)&mask,
(struct sockaddr *)g, (struct sockaddr *)&src, rt->mtu);
}
static int
if_walkrt6(struct dhcpcd_ctx *ctx, char *data, size_t len)
{
mib2_ipv6RouteEntry_t *re, *e;
struct rt6 rt;
char ifname[IF_NAMESIZE];
if (len % sizeof(*re) != 0) {
errno = EINVAL;
return -1;
}
re = (mib2_ipv6RouteEntry_t *)data;
e = (mib2_ipv6RouteEntry_t *)(data + len);
do {
/* Skip route types we don't want. */
switch (re->ipv6RouteInfo.re_ire_type) {
case IRE_IF_CLONE:
case IRE_BROADCAST:
case IRE_MULTICAST:
case IRE_NOROUTE:
case IRE_LOCAL:
continue;
default:
break;
}
memset(&rt, 0, sizeof(rt));
rt.dest = re->ipv6RouteDest;
ipv6_mask(&rt.mask, re->ipv6RoutePfxLength);
rt.gate = re->ipv6RouteNextHop;
rt.mtu = re->ipv6RouteInfo.re_max_frag;
if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));
rt.iface = if_find(ctx->ifaces, ifname);
if (rt.iface != NULL) {
if_finishrt6(&rt);
ipv6_handlert(ctx, RTM_ADD, &rt);
} else {
char destbuf[INET6_ADDRSTRLEN];
char gatebuf[INET6_ADDRSTRLEN];
const char *dest, *gate;
dest = inet_ntop(AF_INET6, &rt.dest,
destbuf, INET6_ADDRSTRLEN);
gate = inet_ntop(AF_INET6, &rt.gate,
gatebuf, INET6_ADDRSTRLEN);
logger(ctx, LOG_ERR,
"no iface (%s) for route to %s via %s",
ifname, dest, gate);
}
} while (++re < e);
return 0;
}
int
if_initrt6(struct dhcpcd_ctx *ctx)
{
ipv6_freerts(&ctx->ipv6->kroutes);
return if_parsert(ctx, MIB2_IP6, MIB2_IP6_ROUTE, if_walkrt6);
}
int
if_checkipv6(__unused struct dhcpcd_ctx *ctx,
__unused const struct interface *ifp, int __unused own)
{
return 0;
}
#endif