X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/7e4a7d3939db04e70062ae6c7bf24b8c8b2f5a7c..d1ecb069dfe24481e4a83f44cb5217a2b06746d7:/bsd/net/route.c diff --git a/bsd/net/route.c b/bsd/net/route.c index 937341b00..28f921b0d 100644 --- a/bsd/net/route.c +++ b/bsd/net/route.c @@ -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 +#include #include #include #include @@ -130,7 +131,7 @@ * 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) {