summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--zebra/ioctl.c94
1 files changed, 69 insertions, 25 deletions
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index 11b252fc1..42a5bfd9d 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.c
@@ -411,47 +411,91 @@ void if_get_flags(struct interface *ifp)
{
int ret;
struct ifreq ifreq;
-#ifdef HAVE_BSD_LINK_DETECT
- struct ifmediareq ifmr;
-#endif /* HAVE_BSD_LINK_DETECT */
ifreq_set_name(&ifreq, ifp);
ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
if (ret < 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL,
- "vrf_if_ioctl(SIOCGIFFLAGS) failed: %s",
- safe_strerror(errno));
+ "vrf_if_ioctl(SIOCGIFFLAGS %s) failed: %s",
+ ifp->name, safe_strerror(errno));
return;
}
-#ifdef HAVE_BSD_LINK_DETECT /* Detect BSD link-state at start-up */
- /* 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
+ 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_id);
+#else
+ struct if_data ifd = {.ifi_link_state = 0};
+ struct if_data *ifdata = &ifd;
+
+ ifreq.ifr_data = (caddr_t)ifdata;
+ ret = vrf_if_ioctl(SIOCGIFDATA, (caddr_t)&ifreq, ifp->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(ifreq.ifr_flags, IFF_RUNNING);
+ else if (ifdata->ifi_link_state == LINK_STATE_UNKNOWN)
+ /* BSD traditionally treats UNKNOWN as UP */
+ SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
+ else
+ UNSET_FLAG(ifreq.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.
*/
- SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
+ struct ifmediareq ifmr = {.ifm_status = 0};
- if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) {
- (void)memset(&ifmr, 0, sizeof(ifmr));
- strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name));
+ strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name));
- /* Seems not all interfaces implement this ioctl */
- if (if_ioctl(SIOCGIFMEDIA, (caddr_t)&ifmr) == -1 &&
- errno != EINVAL)
+ if (if_ioctl(SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
+ if (errno != EINVAL)
flog_err_sys(EC_LIB_SYSTEM_CALL,
- "if_ioctl(SIOCGIFMEDIA) failed: %s",
- safe_strerror(errno));
- else if (ifmr.ifm_status & IFM_AVALID) /* Link state is valid */
- {
- if (ifmr.ifm_status & IFM_ACTIVE)
- SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
- else
- UNSET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
- }
+ "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(ifreq.ifr_flags, IFF_RUNNING);
+ else
+ UNSET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
}
#endif /* HAVE_BSD_LINK_DETECT */
+out:
if_flags_update(ifp, (ifreq.ifr_flags & 0x0000ffff));
}