summaryrefslogtreecommitdiffstats
path: root/zebra/ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/ioctl.c')
-rw-r--r--zebra/ioctl.c643
1 files changed, 643 insertions, 0 deletions
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
new file mode 100644
index 0000000..8da1ae3
--- /dev/null
+++ b/zebra/ioctl.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Common ioctl functions.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "prefix.h"
+#include "ioctl.h"
+#include "log.h"
+#include "privs.h"
+#include "lib_errors.h"
+
+#include "vty.h"
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/debug.h"
+
+#ifdef HAVE_BSD_LINK_DETECT
+#include <net/if_media.h>
+#endif /* HAVE_BSD_LINK_DETECT*/
+
+extern struct zebra_privs_t zserv_privs;
+
+/* clear and set interface name string */
+void ifreq_set_name(struct ifreq *ifreq, struct interface *ifp)
+{
+ strlcpy(ifreq->ifr_name, ifp->name, sizeof(ifreq->ifr_name));
+}
+
+#ifndef HAVE_NETLINK
+/* call ioctl system call */
+int if_ioctl(unsigned long request, caddr_t buffer)
+{
+ int sock;
+ int ret;
+ int err = 0;
+
+ frr_with_privs(&zserv_privs) {
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ zlog_err("Cannot create UDP socket: %s",
+ safe_strerror(errno));
+ exit(1);
+ }
+ if ((ret = ioctl(sock, request, buffer)) < 0)
+ err = errno;
+ }
+ close(sock);
+
+ if (ret < 0) {
+ errno = err;
+ return ret;
+ }
+ return 0;
+}
+#endif
+
+/* call ioctl system call */
+int vrf_if_ioctl(unsigned long request, caddr_t buffer, vrf_id_t vrf_id)
+{
+ int sock;
+ int ret;
+ int err = 0;
+
+ frr_with_privs(&zserv_privs) {
+ sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL);
+ if (sock < 0) {
+ zlog_err("Cannot create UDP socket: %s",
+ safe_strerror(errno));
+ exit(1);
+ }
+ ret = vrf_ioctl(vrf_id, sock, request, buffer);
+ if (ret < 0)
+ err = errno;
+ }
+ close(sock);
+
+ if (ret < 0) {
+ errno = err;
+ return ret;
+ }
+ return 0;
+}
+
+#ifndef HAVE_NETLINK
+static int if_ioctl_ipv6(unsigned long request, caddr_t buffer)
+{
+ int sock;
+ int ret;
+ int err = 0;
+
+ frr_with_privs(&zserv_privs) {
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ zlog_err("Cannot create IPv6 datagram socket: %s",
+ safe_strerror(errno));
+ exit(1);
+ }
+
+ if ((ret = ioctl(sock, request, buffer)) < 0)
+ err = errno;
+ }
+ close(sock);
+
+ if (ret < 0) {
+ errno = err;
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * get interface metric
+ * -- if value is not avaliable set -1
+ */
+void if_get_metric(struct interface *ifp)
+{
+#ifdef SIOCGIFMETRIC
+ struct ifreq ifreq = {};
+
+ ifreq_set_name(&ifreq, ifp);
+
+ if (vrf_if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq, ifp->vrf->vrf_id) < 0)
+ return;
+ ifp->metric = ifreq.ifr_metric;
+ if (ifp->metric == 0)
+ ifp->metric = 1;
+#else /* SIOCGIFMETRIC */
+ ifp->metric = -1;
+#endif /* SIOCGIFMETRIC */
+}
+
+/* get interface MTU */
+void if_get_mtu(struct interface *ifp)
+{
+ struct ifreq ifreq = {};
+
+ ifreq_set_name(&ifreq, ifp);
+
+#if defined(SIOCGIFMTU)
+ if (vrf_if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq, ifp->vrf->vrf_id) < 0) {
+ zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU) for %s(%u)",
+ ifp->name, ifp->vrf->vrf_id);
+ ifp->mtu6 = ifp->mtu = -1;
+ return;
+ }
+
+ ifp->mtu6 = ifp->mtu = ifreq.ifr_mtu;
+
+ /* propogate */
+ zebra_interface_up_update(ifp);
+
+#else
+ zlog_info("Can't lookup mtu on this system for %s(%u)", ifp->name,
+ ifp->vrf->vrf_id);
+ ifp->mtu6 = ifp->mtu = -1;
+#endif
+}
+#endif /* ! HAVE_NETLINK */
+
+/*
+ * Handler for interface address programming via the zebra dplane,
+ * for non-netlink platforms. This handler dispatches to per-platform
+ * helpers, based on the operation requested.
+ */
+#ifndef HAVE_NETLINK
+
+/* Prototypes: these are placed in this block so that they're only seen
+ * on non-netlink platforms.
+ */
+static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx);
+static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx);
+static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx);
+static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx);
+
+enum zebra_dplane_result kernel_address_update_ctx(
+ struct zebra_dplane_ctx *ctx)
+{
+ int ret = -1;
+ const struct prefix *p;
+
+ p = dplane_ctx_get_intf_addr(ctx);
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) {
+ if (p->family == AF_INET)
+ ret = if_set_prefix_ctx(ctx);
+ else
+ ret = if_set_prefix6_ctx(ctx);
+ } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_UNINSTALL) {
+ if (p->family == AF_INET)
+ ret = if_unset_prefix_ctx(ctx);
+ else
+ ret = if_unset_prefix6_ctx(ctx);
+ } else {
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("Invalid op in interface-addr install");
+ }
+
+ return (ret == 0 ?
+ ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+}
+
+#ifdef HAVE_STRUCT_IFALIASREQ
+
+/*
+ * Helper for interface-addr install, non-netlink
+ */
+static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int ret;
+ struct ifaliasreq addreq;
+ struct sockaddr_in addr, mask, peer;
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
+
+ memset(&addreq, 0, sizeof(addreq));
+ strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
+ sizeof(addreq.ifra_name));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_addr = p->prefix;
+ addr.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ addr.sin_len = sizeof(struct sockaddr_in);
+#endif
+ memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in));
+
+ if (dplane_ctx_intf_is_connected(ctx)) {
+ p = (struct prefix_ipv4 *)dplane_ctx_get_intf_dest(ctx);
+ memset(&mask, 0, sizeof(mask));
+ peer.sin_addr = p->prefix;
+ peer.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ peer.sin_len = sizeof(struct sockaddr_in);
+#endif
+ memcpy(&addreq.ifra_broadaddr, &peer,
+ sizeof(struct sockaddr_in));
+ }
+
+ memset(&mask, 0, sizeof(mask));
+ masklen2ip(p->prefixlen, &mask.sin_addr);
+ mask.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ mask.sin_len = sizeof(struct sockaddr_in);
+#endif
+ memcpy(&addreq.ifra_mask, &mask, sizeof(struct sockaddr_in));
+
+ ret = if_ioctl(SIOCAIFADDR, (caddr_t)&addreq);
+ if (ret < 0)
+ return ret;
+ return 0;
+
+}
+
+/*
+ * Helper for interface-addr un-install, non-netlink
+ */
+static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int ret;
+ struct ifaliasreq addreq;
+ struct sockaddr_in addr, mask, peer;
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
+
+ memset(&addreq, 0, sizeof(addreq));
+ strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
+ sizeof(addreq.ifra_name));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_addr = p->prefix;
+ addr.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ addr.sin_len = sizeof(struct sockaddr_in);
+#endif
+ memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in));
+
+ if (dplane_ctx_intf_is_connected(ctx)) {
+ p = (struct prefix_ipv4 *)dplane_ctx_get_intf_dest(ctx);
+ memset(&mask, 0, sizeof(mask));
+ peer.sin_addr = p->prefix;
+ peer.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ peer.sin_len = sizeof(struct sockaddr_in);
+#endif
+ memcpy(&addreq.ifra_broadaddr, &peer,
+ sizeof(struct sockaddr_in));
+ }
+
+ memset(&mask, 0, sizeof(mask));
+ masklen2ip(p->prefixlen, &mask.sin_addr);
+ mask.sin_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ mask.sin_len = sizeof(struct sockaddr_in);
+#endif
+ memcpy(&addreq.ifra_mask, &mask, sizeof(struct sockaddr_in));
+
+ ret = if_ioctl(SIOCDIFADDR, (caddr_t)&addreq);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+#else
+/* Set up interface's address, netmask (and broadcas? ). Linux or
+ Solaris uses ifname:number semantics to set IP address aliases. */
+int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int ret;
+ struct ifreq ifreq;
+ struct sockaddr_in addr;
+ struct sockaddr_in broad;
+ struct sockaddr_in mask;
+ struct prefix_ipv4 ifaddr;
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
+
+ ifaddr = *p;
+
+ strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx),
+ sizeof(ifreq.ifr_name));
+
+ addr.sin_addr = p->prefix;
+ addr.sin_family = p->family;
+ memcpy(&ifreq.ifr_addr, &addr, sizeof(struct sockaddr_in));
+ ret = if_ioctl(SIOCSIFADDR, (caddr_t)&ifreq);
+ if (ret < 0)
+ return ret;
+
+ /* We need mask for make broadcast addr. */
+ masklen2ip(p->prefixlen, &mask.sin_addr);
+
+ if (dplane_ctx_intf_is_broadcast(ctx)) {
+ apply_mask_ipv4(&ifaddr);
+ addr.sin_addr = ifaddr.prefix;
+
+ broad.sin_addr.s_addr =
+ (addr.sin_addr.s_addr | ~mask.sin_addr.s_addr);
+ broad.sin_family = p->family;
+
+ memcpy(&ifreq.ifr_broadaddr, &broad,
+ sizeof(struct sockaddr_in));
+ ret = if_ioctl(SIOCSIFBRDADDR, (caddr_t)&ifreq);
+ if (ret < 0)
+ return ret;
+ }
+
+ mask.sin_family = p->family;
+ memcpy(&ifreq.ifr_addr, &mask, sizeof(struct sockaddr_in));
+ ret = if_ioctl(SIOCSIFNETMASK, (caddr_t)&ifreq);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Set up interface's address, netmask (and broadcas? ). Linux or
+ Solaris uses ifname:number semantics to set IP address aliases. */
+int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int ret;
+ struct ifreq ifreq;
+ struct sockaddr_in addr;
+ struct prefix_ipv4 *p;
+
+ p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
+
+ strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx),
+ sizeof(ifreq.ifr_name));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = p->family;
+ memcpy(&ifreq.ifr_addr, &addr, sizeof(struct sockaddr_in));
+ ret = if_ioctl(SIOCSIFADDR, (caddr_t)&ifreq);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#endif /* HAVE_STRUCT_IFALIASREQ */
+#endif /* HAVE_NETLINK */
+
+/* get interface flags */
+void if_get_flags(struct interface *ifp)
+{
+ int ret;
+ struct ifreq ifreqflags = {};
+ struct ifreq ifreqdata = {};
+
+ ifreq_set_name(&ifreqflags, ifp);
+ ifreq_set_name(&ifreqdata, ifp);
+
+ ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreqflags,
+ ifp->vrf->vrf_id);
+ if (ret < 0) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL,
+ "vrf_if_ioctl(SIOCGIFFLAGS %s) failed: %s",
+ ifp->name, safe_strerror(errno));
+ return;
+ }
+
+ if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))
+ goto out;
+
+ /* Per-default, IFF_RUNNING is held high, unless link-detect
+ * says otherwise - we abuse IFF_RUNNING inside zebra as a
+ * link-state flag, following practice on Linux and Solaris
+ * kernels
+ */
+
+#ifdef SIOCGIFDATA
+ /*
+ * BSD gets link state from ifi_link_link in struct if_data.
+ * All BSD's have this in getifaddrs(3) ifa_data for AF_LINK
+ * addresses. We can also access it via SIOCGIFDATA.
+ */
+
+#ifdef __NetBSD__
+ struct ifdatareq ifdr = {.ifdr_data.ifi_link_state = 0};
+ struct if_data *ifdata = &ifdr.ifdr_data;
+
+ strlcpy(ifdr.ifdr_name, ifp->name, sizeof(ifdr.ifdr_name));
+ ret = vrf_if_ioctl(SIOCGIFDATA, (caddr_t)&ifdr, ifp->vrf->vrf_id);
+#else
+ struct if_data ifd = {.ifi_link_state = 0};
+ struct if_data *ifdata = &ifd;
+
+ ifreqdata.ifr_data = (caddr_t)ifdata;
+ ret = vrf_if_ioctl(SIOCGIFDATA, (caddr_t)&ifreqdata, ifp->vrf->vrf_id);
+#endif
+
+ if (ret == -1)
+ /* Very unlikely. Did the interface disappear? */
+ flog_err_sys(EC_LIB_SYSTEM_CALL,
+ "if_ioctl(SIOCGIFDATA %s) failed: %s", ifp->name,
+ safe_strerror(errno));
+ else {
+ if (ifdata->ifi_link_state >= LINK_STATE_UP)
+ SET_FLAG(ifreqflags.ifr_flags, IFF_RUNNING);
+ else if (ifdata->ifi_link_state == LINK_STATE_UNKNOWN)
+ /* BSD traditionally treats UNKNOWN as UP */
+ SET_FLAG(ifreqflags.ifr_flags, IFF_RUNNING);
+ else
+ UNSET_FLAG(ifreqflags.ifr_flags, IFF_RUNNING);
+ }
+
+#elif defined(HAVE_BSD_LINK_DETECT)
+ /*
+ * This is only needed for FreeBSD older than FreeBSD-13.
+ * Valid and active media generally means the link state is
+ * up, but this is not always the case.
+ * For example, some BSD's with a net80211 interface in MONITOR
+ * mode will treat the media as valid and active but the
+ * link state is down - because we cannot send anything.
+ * Also, virtual interfaces such as PPP, VLAN, etc generally
+ * don't support media at all, so the ioctl will just fail.
+ */
+ struct ifmediareq ifmr = {.ifm_status = 0};
+
+ strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name));
+
+ if (if_ioctl(SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
+ if (errno != EINVAL)
+ flog_err_sys(EC_LIB_SYSTEM_CALL,
+ "if_ioctl(SIOCGIFMEDIA %s) failed: %s",
+ ifp->name, safe_strerror(errno));
+ } else if (ifmr.ifm_status & IFM_AVALID) { /* media state is valid */
+ if (ifmr.ifm_status & IFM_ACTIVE) /* media is active */
+ SET_FLAG(ifreqflags.ifr_flags, IFF_RUNNING);
+ else
+ UNSET_FLAG(ifreqflags.ifr_flags, IFF_RUNNING);
+ }
+#endif /* HAVE_BSD_LINK_DETECT */
+
+out:
+ if_flags_update(ifp, (ifreqflags.ifr_flags & 0x0000ffff));
+}
+
+/* Set interface flags */
+int if_set_flags(struct interface *ifp, uint64_t flags)
+{
+ int ret;
+ struct ifreq ifreq;
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ ifreq_set_name(&ifreq, ifp);
+
+ ifreq.ifr_flags = ifp->flags;
+ ifreq.ifr_flags |= flags;
+
+ ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf->vrf_id);
+
+ if (ret < 0) {
+ zlog_info("can't set interface %s(%u) flags %" PRIu64,
+ ifp->name, ifp->vrf->vrf_id, flags);
+ return ret;
+ }
+ return 0;
+}
+
+/* Unset interface's flag. */
+int if_unset_flags(struct interface *ifp, uint64_t flags)
+{
+ int ret;
+ struct ifreq ifreq;
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ ifreq_set_name(&ifreq, ifp);
+
+ ifreq.ifr_flags = ifp->flags;
+ ifreq.ifr_flags &= ~flags;
+
+ ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf->vrf_id);
+
+ if (ret < 0) {
+ zlog_warn("can't unset interface %s(%u) flags %" PRIu64,
+ ifp->name, ifp->vrf->vrf_id, flags);
+ return ret;
+ }
+ return 0;
+}
+
+#ifndef LINUX_IPV6 /* Netlink has its own code */
+
+#ifdef HAVE_STRUCT_IN6_ALIASREQ
+#ifndef ND6_INFINITE_LIFETIME
+#define ND6_INFINITE_LIFETIME 0xffffffffL
+#endif /* ND6_INFINITE_LIFETIME */
+
+/*
+ * Helper for interface-addr install, non-netlink
+ */
+static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int ret;
+ struct in6_aliasreq addreq;
+ struct sockaddr_in6 addr;
+ struct sockaddr_in6 mask;
+ struct prefix_ipv6 *p;
+
+ p = (struct prefix_ipv6 *)dplane_ctx_get_intf_addr(ctx);
+
+ memset(&addreq, 0, sizeof(addreq));
+ strlcpy((char *)&addreq.ifra_name,
+ dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_addr = p->prefix;
+ addr.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ addr.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in6));
+
+ memset(&mask, 0, sizeof(mask));
+ masklen2ip6(p->prefixlen, &mask.sin6_addr);
+ mask.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ memcpy(&addreq.ifra_prefixmask, &mask, sizeof(struct sockaddr_in6));
+
+ addreq.ifra_lifetime.ia6t_vltime = 0xffffffff;
+ addreq.ifra_lifetime.ia6t_pltime = 0xffffffff;
+
+#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
+ addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+#endif
+
+ ret = if_ioctl_ipv6(SIOCAIFADDR_IN6, (caddr_t)&addreq);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/*
+ * Helper for interface-addr un-install, non-netlink
+ */
+static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ int ret;
+ struct in6_aliasreq addreq;
+ struct sockaddr_in6 addr;
+ struct sockaddr_in6 mask;
+ struct prefix_ipv6 *p;
+
+ p = (struct prefix_ipv6 *)dplane_ctx_get_intf_addr(ctx);
+
+ memset(&addreq, 0, sizeof(addreq));
+ strlcpy((char *)&addreq.ifra_name,
+ dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_addr = p->prefix;
+ addr.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ addr.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in6));
+
+ memset(&mask, 0, sizeof(mask));
+ masklen2ip6(p->prefixlen, &mask.sin6_addr);
+ mask.sin6_family = p->family;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ memcpy(&addreq.ifra_prefixmask, &mask, sizeof(struct sockaddr_in6));
+
+#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
+ addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+#endif
+
+ ret = if_ioctl_ipv6(SIOCDIFADDR_IN6, (caddr_t)&addreq);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+#else
+/* The old, pre-dataplane code here just returned, so we're retaining that
+ * choice.
+ */
+static int if_set_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ return 0;
+}
+
+static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx *ctx)
+{
+ return 0;
+}
+#endif /* HAVE_STRUCT_IN6_ALIASREQ */
+
+#endif /* LINUX_IPV6 */