diff options
author | rwatson <rwatson@FreeBSD.org> | 2009-06-23 20:19:09 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2009-06-23 20:19:09 +0000 |
commit | c9ef486fe1d7da6a2212a337eacc5ed5b40f85d9 (patch) | |
tree | 5ce1a7dad67026f119a839b3325454ebafa72c51 /sys/net/if.c | |
parent | f75c2385c686d82292982283b5f0a9c9988beda8 (diff) | |
download | FreeBSD-src-c9ef486fe1d7da6a2212a337eacc5ed5b40f85d9.zip FreeBSD-src-c9ef486fe1d7da6a2212a337eacc5ed5b40f85d9.tar.gz |
Modify most routines returning 'struct ifaddr *' to return references
rather than pointers, requiring callers to properly dispose of those
references. The following routines now return references:
ifaddr_byindex
ifa_ifwithaddr
ifa_ifwithbroadaddr
ifa_ifwithdstaddr
ifa_ifwithnet
ifaof_ifpforaddr
ifa_ifwithroute
ifa_ifwithroute_fib
rt_getifa
rt_getifa_fib
IFP_TO_IA
ip_rtaddr
in6_ifawithifp
in6ifa_ifpforlinklocal
in6ifa_ifpwithaddr
in6_ifadd
carp_iamatch6
ip6_getdstifaddr
Remove unused macro which didn't have required referencing:
IFP_TO_IA6
This closes many small races in which changes to interface
or address lists while an ifaddr was in use could lead to use of freed
memory (etc). In a few cases, add missing if_addr_list locking
required to safely acquire references.
Because of a lack of deep copying support, we accept a race in which
an in6_ifaddr pointed to by mbuf tags and extracted with
ip6_getdstifaddr() doesn't hold a reference while in transmit. Once
we have mbuf tag deep copy support, this can be fixed.
Reviewed by: bz
Obtained from: Apple, Inc. (portions)
MFC after: 6 weeks (portions)
Diffstat (limited to 'sys/net/if.c')
-rw-r--r-- | sys/net/if.c | 43 |
1 files changed, 32 insertions, 11 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index 4989039..46a2ca7 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -48,6 +48,7 @@ #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/protosw.h> +#include <sys/kdb.h> #include <sys/kernel.h> #include <sys/lock.h> #include <sys/refcount.h> @@ -261,6 +262,8 @@ ifaddr_byindex(u_short idx) IFNET_RLOCK(); ifa = ifnet_byindex_locked(idx)->if_addr; + if (ifa != NULL) + ifa_ref(ifa); IFNET_RUNLOCK(); return (ifa); } @@ -1464,7 +1467,7 @@ ifa_free(struct ifaddr *ifa) */ /*ARGSUSED*/ static struct ifaddr * -ifa_ifwithaddr_internal(struct sockaddr *addr) +ifa_ifwithaddr_internal(struct sockaddr *addr, int getref) { INIT_VNET_NET(curvnet); struct ifnet *ifp; @@ -1477,6 +1480,8 @@ ifa_ifwithaddr_internal(struct sockaddr *addr) if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (sa_equal(addr, ifa->ifa_addr)) { + if (getref) + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto done; } @@ -1485,6 +1490,8 @@ ifa_ifwithaddr_internal(struct sockaddr *addr) ifa->ifa_broadaddr && ifa->ifa_broadaddr->sa_len != 0 && sa_equal(ifa->ifa_broadaddr, addr)) { + if (getref) + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto done; } @@ -1501,14 +1508,14 @@ struct ifaddr * ifa_ifwithaddr(struct sockaddr *addr) { - return (ifa_ifwithaddr_internal(addr)); + return (ifa_ifwithaddr_internal(addr, 1)); } int ifa_ifwithaddr_check(struct sockaddr *addr) { - return (ifa_ifwithaddr_internal(addr) != NULL); + return (ifa_ifwithaddr_internal(addr, 0) != NULL); } /* @@ -1532,6 +1539,7 @@ ifa_ifwithbroadaddr(struct sockaddr *addr) ifa->ifa_broadaddr && ifa->ifa_broadaddr->sa_len != 0 && sa_equal(ifa->ifa_broadaddr, addr)) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto done; } @@ -1565,6 +1573,7 @@ ifa_ifwithdstaddr(struct sockaddr *addr) continue; if (ifa->ifa_dstaddr != NULL && sa_equal(addr, ifa->ifa_dstaddr)) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto done; } @@ -1587,7 +1596,7 @@ ifa_ifwithnet(struct sockaddr *addr) INIT_VNET_NET(curvnet); struct ifnet *ifp; struct ifaddr *ifa; - struct ifaddr *ifa_maybe = (struct ifaddr *) 0; + struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; char *addr_data = addr->sa_data, *cplim; @@ -1602,8 +1611,10 @@ ifa_ifwithnet(struct sockaddr *addr) } /* - * Scan though each interface, looking for ones that have - * addresses in this address family. + * Scan though each interface, looking for ones that have addresses + * in this address family. Maintain a reference on ifa_maybe once + * we find one, as we release the IF_ADDR_LOCK() that kept it stable + * when we move onto the next interface. */ IFNET_RLOCK(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { @@ -1624,6 +1635,7 @@ next: continue; */ if (ifa->ifa_dstaddr != NULL && sa_equal(addr, ifa->ifa_dstaddr)) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto done; } @@ -1634,6 +1646,7 @@ next: continue; */ if (ifa->ifa_claim_addr) { if ((*ifa->ifa_claim_addr)(ifa, addr)) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto done; } @@ -1664,17 +1677,24 @@ next: continue; * before continuing to search * for an even better one. */ - if (ifa_maybe == 0 || + if (ifa_maybe == NULL || rn_refines((caddr_t)ifa->ifa_netmask, - (caddr_t)ifa_maybe->ifa_netmask)) + (caddr_t)ifa_maybe->ifa_netmask)) { + if (ifa_maybe != NULL) + ifa_free(ifa_maybe); ifa_maybe = ifa; + ifa_ref(ifa_maybe); + } } } IF_ADDR_UNLOCK(ifp); } ifa = ifa_maybe; + ifa_maybe = NULL; done: IFNET_RUNLOCK(); + if (ifa_maybe != NULL) + ifa_free(ifa_maybe); return (ifa); } @@ -1688,7 +1708,7 @@ ifaof_ifpforaddr(struct sockaddr *addr, struct ifnet *ifp) struct ifaddr *ifa; char *cp, *cp2, *cp3; char *cplim; - struct ifaddr *ifa_maybe = 0; + struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; if (af >= AF_MAX) @@ -1697,7 +1717,7 @@ ifaof_ifpforaddr(struct sockaddr *addr, struct ifnet *ifp) TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != af) continue; - if (ifa_maybe == 0) + if (ifa_maybe == NULL) ifa_maybe = ifa; if (ifa->ifa_netmask == 0) { if (sa_equal(addr, ifa->ifa_addr) || @@ -1723,6 +1743,8 @@ ifaof_ifpforaddr(struct sockaddr *addr, struct ifnet *ifp) } ifa = ifa_maybe; done: + if (ifa != NULL) + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); return (ifa); } @@ -1748,7 +1770,6 @@ link_rtrequest(int cmd, struct rtentry *rt, struct rt_addrinfo *info) return; ifa = ifaof_ifpforaddr(dst, ifp); if (ifa) { - ifa_ref(ifa); /* XXX */ oifa = rt->rt_ifa; rt->rt_ifa = ifa; ifa_free(oifa); |