]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/route.c
xnu-1504.7.4.tar.gz
[apple/xnu.git] / bsd / net / route.c
index 937341b00a139fbc0d60355a646248c78b3196df..28f921b0dc43c876a38a3bf38910fb97a68bd138 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -62,6 +62,7 @@
  */
  
 #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
  *
@@ -300,6 +301,9 @@ static struct ifaddr *ifa_ifwithroute_common_locked(int,
 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;
 
@@ -367,6 +371,15 @@ static unsigned int primary_ifscope = IFSCOPE_NONE;
 #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
@@ -1208,6 +1221,8 @@ rtioctl(unsigned long req, caddr_t data, struct proc *p)
 #if INET && MROUTING
        return mrt_ioctl(req, data);
 #else
+#pragma unused(req)
+#pragma unused(data)
        return ENXIO;
 #endif
 }
@@ -1528,6 +1543,13 @@ rtrequest_common_locked(int req, struct sockaddr *dst0,
                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);
 
                /*
@@ -1724,6 +1746,19 @@ makeroute:
                                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 */
                }
 
                /*
@@ -2523,6 +2558,16 @@ rtinit_locked(struct ifaddr *ifa, int cmd, int flags)
                         * 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.
@@ -2555,6 +2600,30 @@ rtinit_locked(struct ifaddr *ifa, int cmd, int flags)
        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)
 {
@@ -2633,6 +2702,54 @@ rte_free(struct rtentry *p)
        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)
 {