summaryrefslogtreecommitdiffstats
path: root/sys/net/if.c
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2009-06-23 20:19:09 +0000
committerrwatson <rwatson@FreeBSD.org>2009-06-23 20:19:09 +0000
commitc9ef486fe1d7da6a2212a337eacc5ed5b40f85d9 (patch)
tree5ce1a7dad67026f119a839b3325454ebafa72c51 /sys/net/if.c
parentf75c2385c686d82292982283b5f0a9c9988beda8 (diff)
downloadFreeBSD-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.c43
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);
OpenPOWER on IntegriCloud