/*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
*/
#include <sys/param.h>
+#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
* be done without rt_lock: RTF_GATEWAY, RTF_HOST, RTF_DYNAMIC,
* RTF_DONE, RTF_XRESOLVE, RTF_STATIC, RTF_BLACKHOLE, RTF_ANNOUNCE,
* RTF_USETRAILERS, RTF_WASCLONED, RTF_PINNED, RTF_LOCAL,
- * RTF_BROADCAST, RTF_MULTICAST, RTF_IFSCOPE.
+ * RTF_BROADCAST, RTF_MULTICAST, RTF_IFSCOPE, RTF_IFREF.
*
* rt_key, rt_gateway, rt_ifp, rt_ifa
*
static struct rtentry *rte_alloc(void);
static void rte_free(struct rtentry *);
static void rtfree_common(struct rtentry *, boolean_t);
+#if IFNET_ROUTE_REFCNT
+static void rte_if_ref(struct ifnet *, int);
+#endif /* IFNET_ROUTE_REFCNT */
uint32_t route_generation = 0;
#define RT(r) ((struct rtentry *)r)
#define RT_HOST(r) (RT(r)->rt_flags & RTF_HOST)
+#if IFNET_ROUTE_REFCNT
+SYSCTL_DECL(_net_idle_route);
+
+static int rt_if_idle_expire_timeout = RT_IF_IDLE_EXPIRE_TIMEOUT;
+SYSCTL_INT(_net_idle_route, OID_AUTO, expire_timeout, CTLFLAG_RW,
+ &rt_if_idle_expire_timeout, 0, "Default expiration time on routes for "
+ "interface idle reference counting");
+#endif /* IFNET_ROUTE_REFCNT */
+
/*
* Given a route, determine whether or not it is the non-scoped default
* route; dst typically comes from rt_key(rt) but may be coming from
#if INET && MROUTING
return mrt_ioctl(req, data);
#else
+#pragma unused(req)
+#pragma unused(data)
return ENXIO;
#endif
}
if (rt_inet_default(rt, rt_key(rt)))
set_primary_ifscope(IFSCOPE_NONE);
+#if IFNET_ROUTE_REFCNT
+ if (rt->rt_if_ref_fn != NULL) {
+ rt->rt_if_ref_fn(rt->rt_ifp, -1);
+ rt->rt_flags &= ~RTF_IFREF;
+ }
+#endif /* IFNET_ROUTE_REFCNT */
+
RT_UNLOCK(rt);
/*
RT_ADDREF_LOCKED(*ret_nrt);
}
RT_UNLOCK(*ret_nrt);
+
+#if IFNET_ROUTE_REFCNT
+ /*
+ * Enable interface reference counting for unicast
+ * cloned routes and bump up the reference count.
+ */
+ if (rt->rt_parent != NULL &&
+ !(rt->rt_flags & (RTF_BROADCAST | RTF_MULTICAST))) {
+ rt->rt_if_ref_fn = rte_if_ref;
+ rt->rt_if_ref_fn(rt->rt_ifp, 1);
+ rt->rt_flags |= RTF_IFREF;
+ }
+#endif /* IFNET_ROUTE_REFCNT */
}
/*
* Set the route's ifa.
*/
rtsetifa(rt, ifa);
+#if IFNET_ROUTE_REFCNT
+ /*
+ * Adjust route ref count for the interfaces.
+ */
+ if (rt->rt_if_ref_fn != NULL &&
+ rt->rt_ifp != ifa->ifa_ifp) {
+ rt->rt_if_ref_fn(ifa->ifa_ifp, 1);
+ rt->rt_if_ref_fn(rt->rt_ifp, -1);
+ }
+#endif /* IFNET_ROUTE_REFCNT */
/*
* And substitute in references to the ifaddr
* we are adding.
return (error);
}
+u_int64_t
+rt_expiry(struct rtentry *rt, u_int64_t base, u_int32_t delta)
+{
+#if IFNET_ROUTE_REFCNT
+ u_int64_t retval;
+
+ /*
+ * If the interface of the route doesn't demand aggressive draining,
+ * return the expiration time based on the caller-supplied delta.
+ * Otherwise use the more aggressive route expiration delta (or
+ * the caller-supplied delta, whichever is less.)
+ */
+ if (rt->rt_ifp == NULL || rt->rt_ifp->if_want_aggressive_drain == 0)
+ retval = base + delta;
+ else
+ retval = base + MIN(rt_if_idle_expire_timeout, delta);
+
+ return (retval);
+#else
+#pragma unused(rt)
+ return (base + delta);
+#endif /* IFNET_ROUTE_REFCNT */
+}
+
static void
rte_lock_init(struct rtentry *rt)
{
zfree(rte_zone, p);
}
+#if IFNET_ROUTE_REFCNT
+static void
+rte_if_ref(struct ifnet *ifp, int cnt)
+{
+ struct kev_msg ev_msg;
+ struct net_event_data ev_data;
+ uint32_t old;
+
+ /* Force cnt to 1 increment/decrement */
+ if (cnt < -1 || cnt > 1)
+ panic("%s: invalid count argument (%d)", __func__, cnt);
+
+ old = atomic_add_32_ov(&ifp->if_route_refcnt, cnt);
+ if (cnt < 0 && old == 0)
+ panic("%s: ifp=%p negative route refcnt!", __func__, ifp);
+
+ /*
+ * The following is done without first holding the ifnet lock,
+ * for performance reasons. The relevant ifnet fields, with
+ * the exception of the if_idle_flags, are never changed
+ * during the lifetime of the ifnet. The if_idle_flags
+ * may possibly be modified, so in the event that the value
+ * is stale because IFRF_IDLE_NOTIFY was cleared, we'd end up
+ * sending the event anyway. This is harmless as it is just
+ * a notification to the monitoring agent in user space, and
+ * it is expected to check via SIOCGIFGETRTREFCNT again anyway.
+ */
+ if ((ifp->if_idle_flags & IFRF_IDLE_NOTIFY) && cnt < 0 && old == 1) {
+ bzero(&ev_msg, sizeof (ev_msg));
+ bzero(&ev_data, sizeof (ev_data));
+
+ ev_msg.vendor_code = KEV_VENDOR_APPLE;
+ ev_msg.kev_class = KEV_NETWORK_CLASS;
+ ev_msg.kev_subclass = KEV_DL_SUBCLASS;
+ ev_msg.event_code = KEV_DL_IF_IDLE_ROUTE_REFCNT;
+
+ strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ);
+
+ ev_data.if_family = ifp->if_family;
+ ev_data.if_unit = ifp->if_unit;
+ ev_msg.dv[0].data_length = sizeof (struct net_event_data);
+ ev_msg.dv[0].data_ptr = &ev_data;
+
+ kev_post_msg(&ev_msg);
+ }
+}
+#endif /* IFNET_ROUTE_REFCNT */
+
static inline struct rtentry *
rte_alloc_debug(void)
{