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/netinet6/in6.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/netinet6/in6.c')
-rw-r--r-- | sys/netinet6/in6.c | 123 |
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; |