summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/in6.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/netinet6/in6.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/netinet6/in6.c')
-rw-r--r--sys/netinet6/in6.c123
1 files changed, 86 insertions, 37 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 441e02f..243fd21 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -321,8 +321,6 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
break;
}
if (sa6 && sa6->sin6_family == AF_INET6) {
- int error = 0;
-
if (sa6->sin6_scope_id != 0)
error = sa6_embedscope(sa6, 0);
else
@@ -345,7 +343,8 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
* on a single interface, SIOCSIFxxx ioctls are deprecated.
*/
/* we decided to obsolete this command (20000704) */
- return (EINVAL);
+ error = EINVAL;
+ goto out;
case SIOCDIFADDR_IN6:
/*
@@ -355,8 +354,10 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
* interface address from the day one, we consider "remove the
* first one" semantics to be not preferable.
*/
- if (ia == NULL)
- return (EADDRNOTAVAIL);
+ if (ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto out;
+ }
/* FALLTHROUGH */
case SIOCAIFADDR_IN6:
/*
@@ -364,16 +365,17 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
* the corresponding operation.
*/
if (ifra->ifra_addr.sin6_family != AF_INET6 ||
- ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6))
- return (EAFNOSUPPORT);
+ ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) {
+ error = EAFNOSUPPORT;
+ goto out;
+ }
if (td != NULL) {
error = priv_check(td, (cmd == SIOCDIFADDR_IN6) ?
PRIV_NET_DELIFADDR : PRIV_NET_ADDIFADDR);
if (error)
- return (error);
+ goto out;
}
-
break;
case SIOCGIFADDR_IN6:
@@ -384,9 +386,12 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
case SIOCGIFDSTADDR_IN6:
case SIOCGIFALIFETIME_IN6:
/* must think again about its semantics */
- if (ia == NULL)
- return (EADDRNOTAVAIL);
+ if (ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto out;
+ }
break;
+
case SIOCSIFALIFETIME_IN6:
{
struct in6_addrlifetime *lt;
@@ -394,42 +399,47 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
if (td != NULL) {
error = priv_check(td, PRIV_NETINET_ALIFETIME6);
if (error)
- return (error);
+ goto out;
+ }
+ if (ia == NULL) {
+ error = EADDRNOTAVAIL;
+ goto out;
}
- if (ia == NULL)
- return (EADDRNOTAVAIL);
/* sanity for overflow - beware unsigned */
lt = &ifr->ifr_ifru.ifru_lifetime;
if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME &&
lt->ia6t_vltime + time_second < time_second) {
- return EINVAL;
+ error = EINVAL;
+ goto out;
}
if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME &&
lt->ia6t_pltime + time_second < time_second) {
- return EINVAL;
+ error = EINVAL;
+ goto out;
}
break;
}
}
switch (cmd) {
-
case SIOCGIFADDR_IN6:
ifr->ifr_addr = ia->ia_addr;
if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0)
- return (error);
+ goto out;
break;
case SIOCGIFDSTADDR_IN6:
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
- return (EINVAL);
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
+ error = EINVAL;
+ goto out;
+ }
/*
* XXX: should we check if ifa_dstaddr is NULL and return
* an error?
*/
ifr->ifr_dstaddr = ia->ia_dstaddr;
if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0)
- return (error);
+ goto out;
break;
case SIOCGIFNETMASK_IN6:
@@ -441,8 +451,10 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
break;
case SIOCGIFSTAT_IN6:
- if (ifp == NULL)
- return EINVAL;
+ if (ifp == NULL) {
+ error = EINVAL;
+ goto out;
+ }
bzero(&ifr->ifr_ifru.ifru_stat,
sizeof(ifr->ifr_ifru.ifru_stat));
ifr->ifr_ifru.ifru_stat =
@@ -450,8 +462,10 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
break;
case SIOCGIFSTAT_ICMP6:
- if (ifp == NULL)
- return EINVAL;
+ if (ifp == NULL) {
+ error = EINVAL;
+ goto out;
+ }
bzero(&ifr->ifr_ifru.ifru_icmp6stat,
sizeof(ifr->ifr_ifru.ifru_icmp6stat));
ifr->ifr_ifru.ifru_icmp6stat =
@@ -515,7 +529,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
case SIOCAIFADDR_IN6:
{
- int i, error = 0;
+ int i;
struct nd_prefixctl pr0;
struct nd_prefix *pr;
@@ -524,7 +538,9 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
* and link it to the list.
*/
if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
- return (error);
+ goto out;
+ if (ia != NULL)
+ ifa_free(&ia->ia_ifa);
if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
== NULL) {
/*
@@ -578,11 +594,12 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
* interface route.
*/
if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0)
- return (error);
+ goto out;
if (pr == NULL) {
log(LOG_ERR, "nd6_prelist_add succeeded but "
"no prefix\n");
- return (EINVAL); /* XXX panic here? */
+ error = EINVAL;
+ goto out;
}
}
@@ -640,12 +657,19 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
}
default:
- if (ifp == NULL || ifp->if_ioctl == 0)
- return (EOPNOTSUPP);
- return ((*ifp->if_ioctl)(ifp, cmd, data));
+ if (ifp == NULL || ifp->if_ioctl == 0) {
+ error = EOPNOTSUPP;
+ goto out;
+ }
+ error = (*ifp->if_ioctl)(ifp, cmd, data);
+ goto out;
}
- return (0);
+ error = 0;
+out:
+ if (ia != NULL)
+ ifa_free(&ia->ia_ifa);
+ return (error);
}
/*
@@ -811,6 +835,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
} else
V_in6_ifaddr = ia;
+ ifa_ref(&ia->ia_ifa); /* if_addrhead */
IF_ADDR_LOCK(ifp);
TAILQ_INSERT_TAIL(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
IF_ADDR_UNLOCK(ifp);
@@ -1085,8 +1110,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
* XXX It may be of use, if we can administratively
* disable DAD.
*/
- if (hostIsNew && in6if_do_dad(ifp) &&
- ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) &&
+ if (in6if_do_dad(ifp) && ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) &&
(ia->ia6_flags & IN6_IFF_TENTATIVE))
{
int mindelay, maxdelay;
@@ -1120,6 +1144,8 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
nd6_dad_start((struct ifaddr *)ia, delay);
}
+ KASSERT(hostIsNew, ("in6_update_ifa: !hostIsNew"));
+ ifa_free(&ia->ia_ifa);
return (error);
unlink:
@@ -1127,11 +1153,15 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
* XXX: if a change of an existing address failed, keep the entry
* anyway.
*/
- if (hostIsNew)
+ if (hostIsNew) {
+ ifa_free(&ia->ia_ifa);
in6_unlink_ifa(ia, ifp);
+ }
return (error);
cleanup:
+ KASSERT(hostIsNew, ("in6_update_ifa: cleanup: !hostIsNew"));
+ ifa_free(&ia->ia_ifa);
in6_purgeaddr(&ia->ia_ifa);
return error;
}
@@ -1155,6 +1185,7 @@ in6_purgeaddr(struct ifaddr *ifa)
* link-local and node-local all-nodes multicast
* address routes
*/
+ IF_ADDR_LOCK(ifp);
TAILQ_FOREACH_SAFE(ifa0, &ifp->if_addrhead, ifa_link, nifa) {
if ((ifa0->ifa_addr->sa_family != AF_INET6) ||
memcmp(&satosin6(ifa0->ifa_addr)->sin6_addr,
@@ -1164,6 +1195,9 @@ in6_purgeaddr(struct ifaddr *ifa)
else
break;
}
+ if (ifa0 != NULL)
+ ifa_ref(ifa0);
+ IF_ADDR_UNLOCK(ifp);
/* stop DAD processing */
nd6_dad_stop(ifa);
@@ -1331,6 +1365,8 @@ cleanup:
return;
ia->ia_flags &= ~IFA_ROUTE;
}
+ if (ifa0 != NULL)
+ ifa_free(ifa0);
in6_unlink_ifa(ia, ifp);
}
@@ -1345,6 +1381,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
IF_ADDR_LOCK(ifp);
TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
IF_ADDR_UNLOCK(ifp);
+ ifa_free(&ia->ia_ifa); /* if_addrhead */
oia = ia;
if (oia == (ia = V_in6_ifaddr))
@@ -1481,6 +1518,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
struct in6_addr *hostid = NULL;
int prefixlen;
+ ifa = NULL;
if ((iflr->flags & IFLR_PREFIX) != 0) {
struct sockaddr_in6 *sin6;
@@ -1532,6 +1570,8 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data,
hostid->s6_addr32[3];
}
}
+ if (ifa != NULL)
+ ifa_free(ifa);
ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen);
@@ -1779,6 +1819,7 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
/*
* Find an IPv6 interface link-local address specific to an interface.
+ * ifaddr is returned referenced.
*/
struct in6_ifaddr *
in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
@@ -1793,6 +1834,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
if ((((struct in6_ifaddr *)ifa)->ia6_flags &
ignoreflags) != 0)
continue;
+ ifa_ref(ifa);
break;
}
}
@@ -1804,6 +1846,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
/*
* find the internet address corresponding to a given interface and address.
+ * ifaddr is returned referenced.
*/
struct in6_ifaddr *
in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
@@ -1814,8 +1857,10 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
- if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
+ if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) {
+ ifa_ref(ifa);
break;
+ }
}
IF_ADDR_UNLOCK(ifp);
@@ -2049,6 +2094,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
}
}
if (besta) {
+ ifa_ref(&besta->ia_ifa);
IF_ADDR_UNLOCK(ifp);
return (besta);
}
@@ -2068,6 +2114,8 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
continue;
}
+ if (ifa != NULL)
+ ifa_ref(ifa);
IF_ADDR_UNLOCK(ifp);
return (struct in6_ifaddr *)ifa;
}
@@ -2319,6 +2367,7 @@ in6_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr)
/* XXX ifaof_ifpforaddr should take a const param */
ifa = ifaof_ifpforaddr(__DECONST(struct sockaddr *, l3addr), ifp);
if (ifa != NULL) {
+ ifa_free(ifa);
if (rt != NULL)
RTFREE_LOCKED(rt);
return 0;
OpenPOWER on IntegriCloud