diff --git a/sys/contrib/rdma/rdma_addr.c b/sys/contrib/rdma/rdma_addr.c index 971b4dec2d98..b3f734963c0e 100644 --- a/sys/contrib/rdma/rdma_addr.c +++ b/sys/contrib/rdma/rdma_addr.c @@ -129,13 +129,16 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr) struct ifaddr *ifa; struct sockaddr_in *sin = (struct sockaddr_in *)addr; uint16_t port = sin->sin_port; + int ret; sin->sin_port = 0; ifa = ifa_ifwithaddr(addr); sin->sin_port = port; if (!ifa) return (EADDRNOTAVAIL); - return rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL); + ret = rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL); + ifa_free(ifa); + return (ret); } static void queue_req(struct addr_req *req) diff --git a/sys/contrib/rdma/rdma_cma.c b/sys/contrib/rdma/rdma_cma.c index d5f7cae31d05..23d56f1d9985 100644 --- a/sys/contrib/rdma/rdma_cma.c +++ b/sys/contrib/rdma/rdma_cma.c @@ -1337,6 +1337,7 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, } dev = ifa->ifa_ifp; ret = rdma_copy_addr(&conn_id->id.route.addr.dev_addr, dev, NULL); + ifa_free(ifa); if (ret) { cma_enable_remove(conn_id); rdma_destroy_id(new_cm_id); diff --git a/sys/net/if.c b/sys/net/if.c index 4989039cbf0d..46a2ca7e4371 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/sys/net/route.c b/sys/net/route.c index 6efc1762db65..a3dc1693a36e 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -559,6 +559,7 @@ rtredirect_fib(struct sockaddr *dst, struct ifaddr *ifa; struct radix_node_head *rnh; + ifa = NULL; rnh = rt_tables_get_rnh(fibnum, dst->sa_family); if (rnh == NULL) { error = EAFNOSUPPORT; @@ -664,6 +665,8 @@ out: info.rti_info[RTAX_NETMASK] = netmask; info.rti_info[RTAX_AUTHOR] = src; rt_missmsg(RTM_REDIRECT, &info, flags, error); + if (ifa != NULL) + ifa_free(ifa); } int @@ -693,6 +696,9 @@ rtioctl_fib(u_long req, caddr_t data, u_int fibnum) #endif /* INET */ } +/* + * For both ifa_ifwithroute() routines, 'ifa' is returned referenced. + */ struct ifaddr * ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway) { @@ -749,11 +755,13 @@ ifa_ifwithroute_fib(int flags, struct sockaddr *dst, struct sockaddr *gateway, default: break; } + if (!not_found && rt->rt_ifa != NULL) { + ifa = rt->rt_ifa; + ifa_ref(ifa); + } RT_REMREF(rt); RT_UNLOCK(rt); - if (not_found) - return (NULL); - if ((ifa = rt->rt_ifa) == NULL) + if (not_found || ifa == NULL) return (NULL); } if (ifa->ifa_addr->sa_family != dst->sa_family) { @@ -761,6 +769,8 @@ ifa_ifwithroute_fib(int flags, struct sockaddr *dst, struct sockaddr *gateway, ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); if (ifa == NULL) ifa = oifa; + else + ifa_free(oifa); } return (ifa); } @@ -819,6 +829,10 @@ rt_getifa(struct rt_addrinfo *info) return (rt_getifa_fib(info, 0)); } +/* + * Look up rt_addrinfo for a specific fib. Note that if rti_ifa is defined, + * it will be referenced so the caller must free it. + */ int rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) { @@ -831,8 +845,10 @@ rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) */ if (info->rti_ifp == NULL && ifpaddr != NULL && ifpaddr->sa_family == AF_LINK && - (ifa = ifa_ifwithnet(ifpaddr)) != NULL) + (ifa = ifa_ifwithnet(ifpaddr)) != NULL) { info->rti_ifp = ifa->ifa_ifp; + ifa_free(ifa); + } if (info->rti_ifa == NULL && ifaaddr != NULL) info->rti_ifa = ifa_ifwithaddr(ifaaddr); if (info->rti_ifa == NULL) { @@ -1123,12 +1139,19 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) senderr(EINVAL); - if (info->rti_ifa == NULL && (error = rt_getifa_fib(info, fibnum))) - senderr(error); + if (info->rti_ifa == NULL) { + error = rt_getifa_fib(info, fibnum); + if (error) + senderr(error); + } else + ifa_ref(info->rti_ifa); ifa = info->rti_ifa; rt = uma_zalloc(V_rtzone, M_NOWAIT | M_ZERO); - if (rt == NULL) + if (rt == NULL) { + if (ifa != NULL) + ifa_free(ifa); senderr(ENOBUFS); + } RT_LOCK_INIT(rt); rt->rt_flags = RTF_UP | flags; rt->rt_fibnum = fibnum; @@ -1139,6 +1162,8 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, RT_LOCK(rt); if ((error = rt_setgate(rt, dst, gateway)) != 0) { RT_LOCK_DESTROY(rt); + if (ifa != NULL) + ifa_free(ifa); uma_zfree(V_rtzone, rt); senderr(error); } @@ -1157,11 +1182,10 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, bcopy(dst, ndst, dst->sa_len); /* - * Note that we now have a reference to the ifa. + * We use the ifa reference returned by rt_getifa_fib(). * This moved from below so that rnh->rnh_addaddr() can * examine the ifa and ifa->ifa_ifp if it so desires. */ - ifa_ref(ifa); rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; rt->rt_rmx.rmx_weight = 1; diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index ab3855721587..532070d6b483 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -683,6 +683,13 @@ route_output(struct mbuf *m, struct socket *so) RT_UNLOCK(rt); RADIX_NODE_HEAD_LOCK(rnh); error = rt_getifa_fib(&info, rt->rt_fibnum); + /* + * XXXRW: Really we should release this + * reference later, but this maintains + * historical behavior. + */ + if (info.rti_ifa != NULL) + ifa_free(info.rti_ifa); RADIX_NODE_HEAD_UNLOCK(rnh); if (error != 0) senderr(error); diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 657530553025..ee2854b381a7 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -301,6 +301,7 @@ ieee80211_ifattach(struct ieee80211com *ic, sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */ sdl->sdl_alen = IEEE80211_ADDR_LEN; IEEE80211_ADDR_COPY(LLADDR(sdl), macaddr); + ifa_free(ifa); } /* diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c index 276159004540..be102d442e78 100644 --- a/sys/netinet/igmp.c +++ b/sys/netinet/igmp.c @@ -1233,8 +1233,10 @@ igmp_input_v1_report(struct ifnet *ifp, /*const*/ struct ip *ip, */ if (V_igmp_recvifkludge && in_nullhost(ip->ip_src)) { IFP_TO_IA(ifp, ia); - if (ia != NULL) + if (ia != NULL) { ip->ip_src.s_addr = htonl(ia->ia_subnet); + ifa_free(&ia->ia_ifa); + } } CTR3(KTR_IGMPV3, "process v1 report %s on ifp %p(%s)", @@ -1326,16 +1328,23 @@ igmp_input_v2_report(struct ifnet *ifp, /*const*/ struct ip *ip, * group. */ IFP_TO_IA(ifp, ia); - if (ia != NULL && in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr)) + if (ia != NULL && in_hosteq(ip->ip_src, IA_SIN(ia)->sin_addr)) { + ifa_free(&ia->ia_ifa); return (0); + } IGMPSTAT_INC(igps_rcv_reports); - if (ifp->if_flags & IFF_LOOPBACK) + if (ifp->if_flags & IFF_LOOPBACK) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (0); + } if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || !in_hosteq(igmp->igmp_group, ip->ip_dst)) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); IGMPSTAT_INC(igps_rcv_badreports); return (EINVAL); } @@ -1351,6 +1360,8 @@ igmp_input_v2_report(struct ifnet *ifp, /*const*/ struct ip *ip, if (ia != NULL) ip->ip_src.s_addr = htonl(ia->ia_subnet); } + if (ia != NULL) + ifa_free(&ia->ia_ifa); CTR3(KTR_IGMPV3, "process v2 report %s on ifp %p(%s)", inet_ntoa(igmp->igmp_group), ifp, ifp->if_xname); @@ -3534,8 +3545,10 @@ igmp_v3_encap_report(struct ifnet *ifp, struct mbuf *m) struct in_ifaddr *ia; IFP_TO_IA(ifp, ia); - if (ia != NULL) + if (ia != NULL) { ip->ip_src = ia->ia_addr.sin_addr; + ifa_free(&ia->ia_ifa); + } } ip->ip_dst.s_addr = htonl(INADDR_ALLRPTS_GROUP); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 1b9a79d38bc5..2b6fd185eed5 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -219,7 +219,6 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, register struct ifaddr *ifa; struct in_addr allhosts_addr; struct in_addr dst; - struct in_ifaddr *oia; struct in_ifinfo *ii; struct in_aliasreq *ifra = (struct in_aliasreq *)data; struct sockaddr_in oldaddr; @@ -323,8 +322,10 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, break; } } - IF_ADDR_LOCK(ifp); + if (ia != NULL) + ifa_ref(&ia->ia_ifa); if (ia == NULL) { + IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { iap = ifatoia(ifa); if (iap->ia_addr.sin_family == AF_INET) { @@ -336,6 +337,9 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, break; } } + if (ia != NULL) + ifa_ref(&ia->ia_ifa); + IF_ADDR_UNLOCK(ifp); } if (ia == NULL) iaIsFirst = 1; @@ -345,23 +349,29 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, case SIOCAIFADDR: case SIOCDIFADDR: if (ifra->ifra_addr.sin_family == AF_INET) { + struct in_ifaddr *oia; + for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) { if (ia->ia_ifp == ifp && ia->ia_addr.sin_addr.s_addr == ifra->ifra_addr.sin_addr.s_addr) break; } + if (ia != NULL && ia != oia) + ifa_ref(&ia->ia_ifa); + if (oia != NULL && ia != oia) + ifa_free(&oia->ia_ifa); if ((ifp->if_flags & IFF_POINTOPOINT) && (cmd == SIOCAIFADDR) && (ifra->ifra_dstaddr.sin_addr.s_addr == INADDR_ANY)) { error = EDESTADDRREQ; - goto out_unlock; + goto out; } } if (cmd == SIOCDIFADDR && ia == NULL) { error = EADDRNOTAVAIL; - goto out_unlock; + goto out; } /* FALLTHROUGH */ case SIOCSIFADDR: @@ -373,7 +383,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, M_ZERO); if (ia == NULL) { error = ENOBUFS; - goto out_unlock; + goto out; } ifa = &ia->ia_ifa; @@ -390,7 +400,11 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, } ia->ia_ifp = ifp; + ifa_ref(ifa); /* if_addrhead */ + IF_ADDR_LOCK(ifp); TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); + IF_ADDR_UNLOCK(ifp); + ifa_ref(ifa); /* in_ifaddrhead */ s = splnet(); TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link); splx(s); @@ -405,64 +419,53 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, case SIOCGIFBRDADDR: if (ia == NULL) { error = EADDRNOTAVAIL; - goto out_unlock; + goto out; } break; } /* - * Most paths in this switch return directly or via out_unlock. Only - * paths that remove the address break in order to hit common removal - * code. - * - * XXXRW: We enter the switch with IF_ADDR_LOCK() held, but leave - * without it. This is a bug. + * Most paths in this switch return directly or via out. Only paths + * that remove the address break in order to hit common removal code. */ - IF_ADDR_LOCK_ASSERT(ifp); switch (cmd) { case SIOCGIFADDR: *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; - goto out_unlock; + goto out; case SIOCGIFBRDADDR: if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EINVAL; - goto out_unlock; + goto out; } *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; - goto out_unlock; + goto out; case SIOCGIFDSTADDR: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { error = EINVAL; - goto out_unlock; + goto out; } *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; - goto out_unlock; + goto out; case SIOCGIFNETMASK: *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; - goto out_unlock; + goto out; case SIOCSIFDSTADDR: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { error = EINVAL; - goto out_unlock; + goto out; } oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; - IF_ADDR_UNLOCK(ifp); - - /* - * XXXRW: Locks dropped for if_ioctl and rtinit, but ia is - * still being used. - */ if (ifp->if_ioctl != NULL) { error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, (caddr_t)ia); if (error) { ia->ia_dstaddr = oldaddr; - return (error); + goto out; } } if (ia->ia_flags & IFA_ROUTE) { @@ -472,23 +475,17 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, (struct sockaddr *)&ia->ia_dstaddr; rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); } - return (0); + goto out; case SIOCSIFBRDADDR: if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EINVAL; - goto out_unlock; + goto out; } ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; - goto out_unlock; + goto out; case SIOCSIFADDR: - IF_ADDR_UNLOCK(ifp); - - /* - * XXXRW: Locks dropped for in_ifinit and in_joingroup, but ia - * is still being used. - */ error = in_ifinit(ifp, ia, (struct sockaddr_in *) &ifr->ifr_addr, 1); if (error != 0 && iaIsNew) @@ -502,12 +499,13 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, } EVENTHANDLER_INVOKE(ifaddr_event, ifp); } - return (0); + error = 0; + goto out; case SIOCSIFNETMASK: ia->ia_sockmask.sin_addr = ifra->ifra_addr.sin_addr; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); - goto out_unlock; + goto out; case SIOCAIFADDR: maskIsNew = 0; @@ -521,12 +519,6 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, ia->ia_addr.sin_addr.s_addr) hostIsNew = 0; } - IF_ADDR_UNLOCK(ifp); - - /* - * XXXRW: Locks dropped for in_ifscrub and in_ifinit, but ia - * is still being used. - */ if (ifra->ifra_mask.sin_len) { in_ifscrub(ifp, ia); ia->ia_sockmask = ifra->ifra_mask; @@ -545,7 +537,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, (hostIsNew || maskIsNew)) error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); if (error != 0 && iaIsNew) - break; + goto out; if ((ifp->if_flags & IFF_BROADCAST) && (ifra->ifra_broadaddr.sin_family == AF_INET)) @@ -559,15 +551,10 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, } EVENTHANDLER_INVOKE(ifaddr_event, ifp); } - return (error); + goto out; case SIOCDIFADDR: - IF_ADDR_UNLOCK(ifp); - /* - * XXXRW: Locks dropped for in_ifscrub and in_ifadown, but ia - * is still being used. - * * in_ifscrub kills the interface route. */ in_ifscrub(ifp, ia); @@ -587,25 +574,25 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, panic("in_control: unsupported ioctl"); } - /* - * XXXRW: In a more ideal world, we would still be holding - * IF_ADDR_LOCK here. - */ 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 */ s = splnet(); TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link); + ifa_free(&ia->ia_ifa); /* in_ifaddrhead */ if (ia->ia_addr.sin_family == AF_INET) { + struct in_ifaddr *if_ia; + LIST_REMOVE(ia, ia_hash); /* * If this is the last IPv4 address configured on this * interface, leave the all-hosts group. * No state-change report need be transmitted. */ - oia = NULL; - IFP_TO_IA(ifp, oia); - if (oia == NULL) { + if_ia = NULL; + IFP_TO_IA(ifp, if_ia); + if (if_ia == NULL) { ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); IN_MULTI_LOCK(); if (ii->ii_allhosts) { @@ -614,15 +601,13 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, ii->ii_allhosts = NULL; } IN_MULTI_UNLOCK(); - } + } else + ifa_free(&if_ia->ia_ifa); } - ifa_free(&ia->ia_ifa); splx(s); - - return (error); - -out_unlock: - IF_ADDR_UNLOCK(ifp); +out: + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (error); } diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index d7cbd35b15e2..a856676cdc00 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -1722,6 +1722,7 @@ inp_getmoptions(struct inpcb *inp, struct sockopt *sopt) if (ia != NULL) { mreqn.imr_address = IA_SIN(ia)->sin_addr; + ifa_free(&ia->ia_ifa); } } } diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 4e30572b88b7..574ce63a03f9 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -549,7 +549,6 @@ static int in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, struct ucred *cred) { - struct in_ifaddr *ia; struct ifaddr *ifa; struct sockaddr *sa; struct sockaddr_in *sin; @@ -559,7 +558,6 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, KASSERT(laddr != NULL, ("%s: laddr NULL", __func__)); error = 0; - ia = NULL; bzero(&sro, sizeof(sro)); sin = (struct sockaddr_in *)&sro.ro_dst; @@ -585,6 +583,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, * the source address from. */ if (sro.ro_rt == NULL || sro.ro_rt->rt_ifp == NULL) { + struct in_ifaddr *ia; struct ifnet *ifp; ia = ifatoia(ifa_ifwithdstaddr((struct sockaddr *)sin)); @@ -597,10 +596,12 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, if (cred == NULL || !prison_flag(cred, PR_IP4)) { laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + ifa_free(&ia->ia_ifa); goto done; } ifp = ia->ia_ifp; + ifa_free(&ia->ia_ifa); ia = NULL; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { @@ -636,6 +637,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, * 3. as a last resort return the 'default' jail address. */ if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) { + struct in_ifaddr *ia; struct ifnet *ifp; /* If not jailed, use the default returned. */ @@ -658,10 +660,10 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, * 2. Check if we have any address on the outgoing interface * belonging to this jail. */ + ia = NULL; ifp = sro.ro_rt->rt_ifp; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - sa = ifa->ifa_addr; if (sa->sa_family != AF_INET) continue; @@ -694,6 +696,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, */ if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { struct sockaddr_in sain; + struct in_ifaddr *ia; bzero(&sain, sizeof(struct sockaddr_in)); sain.sin_family = AF_INET; @@ -710,6 +713,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, goto done; } laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + ifa_free(&ia->ia_ifa); goto done; } @@ -718,6 +722,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, struct ifnet *ifp; ifp = ia->ia_ifp; + ifa_free(&ia->ia_ifa); ia = NULL; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 2b59e929b5f4..dbacbface287 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -146,14 +146,16 @@ do { \ * Macro for finding the internet address structure (in_ifaddr) corresponding * to a given interface (ifnet structure). */ -#define IFP_TO_IA(ifp, ia) \ - /* struct ifnet *ifp; */ \ - /* struct in_ifaddr *ia; */ \ -{ \ - for ((ia) = TAILQ_FIRST(&V_in_ifaddrhead); \ - (ia) != NULL && (ia)->ia_ifp != (ifp); \ - (ia) = TAILQ_NEXT((ia), ia_link)) \ - continue; \ +#define IFP_TO_IA(ifp, ia) \ + /* struct ifnet *ifp; */ \ + /* struct in_ifaddr *ia; */ \ +{ \ + for ((ia) = TAILQ_FIRST(&V_in_ifaddrhead); \ + (ia) != NULL && (ia)->ia_ifp != (ifp); \ + (ia) = TAILQ_NEXT((ia), ia_link)) \ + continue; \ + if ((ia) != NULL) \ + ifa_ref(&(ia)->ia_ifa); \ } #endif diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index fa0726ad31bb..fcfe28a712d4 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1239,6 +1239,7 @@ carp_iamatch6(void *v, struct in6_addr *taddr) (SC2IFP(vh)->if_flags & IFF_UP) && (SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) && vh->sc_state == MASTER) { + ifa_ref(ifa); IF_ADDR_UNLOCK(SC2IFP(vh)); CARP_UNLOCK(cif); return (ifa); diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index 0b72c652eb3e..680156b0c710 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -464,6 +464,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin, goto cantsend; } m->m_pkthdr.rcvif = ifa->ifa_ifp; + ifa_free(ifa); } #ifdef MAC mac_socket_create_mbuf(so, m); diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index cfd27f0dc857..3cd6530b41fd 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -536,10 +536,12 @@ icmp_input(struct mbuf *m, int off) } ia = (struct in_ifaddr *)ifaof_ifpforaddr( (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif); - if (ia == 0) + if (ia == NULL) break; - if (ia->ia_ifp == 0) + if (ia->ia_ifp == NULL) { + ifa_free(&ia->ia_ifa); break; + } icp->icmp_type = ICMP_MASKREPLY; if (V_icmpmaskfake == 0) icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr; @@ -551,6 +553,7 @@ icmp_input(struct mbuf *m, int off) else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; } + ifa_free(&ia->ia_ifa); reflect: ip->ip_len += hlen; /* since ip_input deducts this */ ICMPSTAT_INC(icps_reflect); @@ -748,6 +751,7 @@ icmp_reflect(struct mbuf *m) goto done; } t = IA_SIN(ia)->sin_addr; + ifa_free(&ia->ia_ifa); match: #ifdef MAC mac_netinet_icmp_replyinplace(m); diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 8642e319498d..be4b084c631c 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -622,8 +622,10 @@ passin: * enabled. */ if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr && - (!checkif || ia->ia_ifp == ifp)) + (!checkif || ia->ia_ifp == ifp)) { + ifa_ref(&ia->ia_ifa); goto ours; + } } /* * Check for broadcast addresses. @@ -641,15 +643,18 @@ passin: ia = ifatoia(ifa); if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == ip->ip_dst.s_addr) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto ours; } if (ia->ia_netbroadcast.s_addr == ip->ip_dst.s_addr) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto ours; } #ifdef BOOTP_COMPAT if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY) { + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); goto ours; } @@ -742,6 +747,7 @@ ours: if (ia != NULL) { ia->ia_ifa.if_ipackets++; ia->ia_ifa.if_ibytes += m->m_pkthdr.len; + ifa_free(&ia->ia_ifa); } /* @@ -1335,8 +1341,8 @@ ipproto_unregister(u_char ipproto) } /* - * Given address of next destination (final or next hop), - * return internet address info of interface to be used to get there. + * Given address of next destination (final or next hop), return (referenced) + * internet address info of interface to be used to get there. */ struct in_ifaddr * ip_rtaddr(struct in_addr dst, u_int fibnum) @@ -1356,6 +1362,7 @@ ip_rtaddr(struct in_addr dst, u_int fibnum) return (NULL); ifa = ifatoia(sro.ro_rt->rt_ifa); + ifa_ref(&ifa->ia_ifa); RTFREE(sro.ro_rt); return (ifa); } @@ -1530,11 +1537,16 @@ ip_forward(struct mbuf *m, int srcrt) else { if (mcopy) m_freem(mcopy); + if (ia != NULL) + ifa_free(&ia->ia_ifa); return; } } - if (mcopy == NULL) + if (mcopy == NULL) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); return; + } switch (error) { @@ -1592,6 +1604,8 @@ ip_forward(struct mbuf *m, int srcrt) */ if (V_ip_sendsourcequench == 0) { m_freem(mcopy); + if (ia != NULL) + ifa_free(&ia->ia_ifa); return; } else { type = ICMP_SOURCEQUENCH; @@ -1601,8 +1615,12 @@ ip_forward(struct mbuf *m, int srcrt) case EACCES: /* ipfw denied packet */ m_freem(mcopy); + if (ia != NULL) + ifa_free(&ia->ia_ifa); return; } + if (ia != NULL) + ifa_free(&ia->ia_ifa); icmp_error(mcopy, type, code, dest.s_addr, mtu); } diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c index 091bd213593f..3c3c014238aa 100644 --- a/sys/netinet/ip_mroute.c +++ b/sys/netinet/ip_mroute.c @@ -883,6 +883,7 @@ add_vif(struct vifctl *vifcp) return EADDRNOTAVAIL; } ifp = ifa->ifa_ifp; + ifa_free(ifa); } if ((vifcp->vifc_flags & VIFF_TUNNEL) != 0) { diff --git a/sys/netinet/ip_options.c b/sys/netinet/ip_options.c index b59d708c6d42..09a1d6279202 100644 --- a/sys/netinet/ip_options.c +++ b/sys/netinet/ip_options.c @@ -164,9 +164,8 @@ ip_dooptions(struct mbuf *m, int pass) goto bad; } ipaddr.sin_addr = ip->ip_dst; - ia = (struct in_ifaddr *) - ifa_ifwithaddr((struct sockaddr *)&ipaddr); - if (ia == NULL) { + if (ifa_ifwithaddr_check((struct sockaddr *)&ipaddr) + == 0) { if (opt == IPOPT_SSRR) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; @@ -245,6 +244,7 @@ dropit: ip->ip_dst = ipaddr.sin_addr; (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), sizeof(struct in_addr)); + ifa_free(&ia->ia_ifa); cp[IPOPT_OFFSET] += sizeof(struct in_addr); /* * Let ip_intr's mcast routing check handle mcast pkts @@ -286,6 +286,7 @@ dropit: } (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), sizeof(struct in_addr)); + ifa_free(&ia->ia_ifa); cp[IPOPT_OFFSET] += sizeof(struct in_addr); break; @@ -331,6 +332,7 @@ dropit: continue; (void)memcpy(sin, &IA_SIN(ia)->sin_addr, sizeof(struct in_addr)); + ifa_free(&ia->ia_ifa); cp[IPOPT_OFFSET] += sizeof(struct in_addr); off += sizeof(struct in_addr); break; diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index c1e0d37c5efb..8ed63a524682 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -288,6 +288,7 @@ again: goto bad; } ia = ifatoia(ro->ro_rt->rt_ifa); + ifa_ref(&ia->ia_ifa); ifp = ro->ro_rt->rt_ifp; ro->ro_rt->rt_rmx.rmx_pksent++; if (ro->ro_rt->rt_flags & RTF_GATEWAY) @@ -667,6 +668,8 @@ done: if (ro == &iproute && ro->ro_rt && !nortfree) { RTFREE(ro->ro_rt); } + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (error); bad: m_freem(m); diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 371dd1eff4d7..cad7bbef4c5a 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -285,6 +285,7 @@ tcp6_input(struct mbuf **mp, int *offp, int proto) if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) { struct ip6_hdr *ip6; + ifa_free(&ia6->ia_ifa); ip6 = mtod(m, struct ip6_hdr *); icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, (caddr_t)&ip6->ip6_dst - (caddr_t)ip6); @@ -939,8 +940,10 @@ findpcb: if (isipv6 && !V_ip6_use_deprecated) { struct in6_ifaddr *ia6; - if ((ia6 = ip6_getdstifaddr(m)) && + ia6 = ip6_getdstifaddr(m); + if (ia6 != NULL && (ia6->ia6_flags & IN6_IFF_DEPRECATED)) { + ifa_free(&ia6->ia_ifa); if ((s = tcp_log_addrs(&inc, th, NULL, NULL))) log(LOG_DEBUG, "%s; %s: Listen socket: " "Connection attempt to deprecated " @@ -949,6 +952,7 @@ findpcb: rstreason = BANDLIM_RST_OPENPORT; goto dropwithreset; } + ifa_free(&ia6->ia_ifa); } #endif /* diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c index 2f4e256f1b59..3f57802528d9 100644 --- a/sys/netinet6/frag6.c +++ b/sys/netinet6/frag6.c @@ -185,8 +185,10 @@ frag6_input(struct mbuf **mp, int *offp, int proto) dstifp = NULL; #ifdef IN6_IFSTAT_STRICT /* find the destination interface of the packet. */ - if ((ia = ip6_getdstifaddr(m)) != NULL) + if ((ia = ip6_getdstifaddr(m)) != NULL) { dstifp = ia->ia_ifp; + ifa_free(&ia->ia_ifa); + } #else /* we are violating the spec, this is not the destination interface */ if ((m->m_flags & M_PKTHDR) != 0) diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 11e6cd66c931..786ddc2dd80b 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1244,11 +1244,13 @@ ni6_input(struct mbuf *m, int off) if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) && !(V_icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) { + ifa_free(&ia6->ia_ifa); nd6log((LOG_DEBUG, "ni6_input: ignore node info to " "a temporary address in %s:%d", __FILE__, __LINE__)); goto bad; } + ifa_free(&ia6->ia_ifa); } /* validate query Subject field. */ @@ -2074,7 +2076,7 @@ icmp6_reflect(struct mbuf *m, size_t off) INIT_VNET_INET6(curvnet); struct ip6_hdr *ip6; struct icmp6_hdr *icmp6; - struct in6_ifaddr *ia; + struct in6_ifaddr *ia = NULL; int plen; int type, code; struct ifnet *outif = NULL; @@ -2220,9 +2222,13 @@ icmp6_reflect(struct mbuf *m, size_t off) if (outif) icmp6_ifoutstat_inc(outif, type, code); + if (ia != NULL) + ifa_free(&ia->ia_ifa); return; bad: + if (ia != NULL) + ifa_free(&ia->ia_ifa); m_freem(m); return; } @@ -2541,6 +2547,8 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) IN6_IFF_ANYCAST)) == NULL) goto fail; ifp_ll6 = &ia->ia_addr.sin6_addr; + /* XXXRW: reference released prematurely. */ + ifa_free(&ia->ia_ifa); } /* get ip6 linklocal address for the router. */ diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 441e02f6f619..243fd214f3f8 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; diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 188801728f3a..ebfdf6e5691c 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -253,6 +253,7 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6) return -1; found: + IF_ADDR_LOCK_ASSERT(ifp); addr = LLADDR(sdl); addrlen = sdl->sdl_alen; @@ -513,6 +514,7 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp) /* NOTREACHED */ } #endif + ifa_free(&ia->ia_ifa); /* * Make the link-local prefix (fe80::%link/64) as on-link. @@ -737,11 +739,15 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp) * XXX multiple loopback interface case. */ if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + struct ifaddr *ifa; + in6 = in6addr_loopback; - if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &in6); + if (ifa == NULL) { if (in6_ifattach_loopback(ifp) != 0) return; - } + } else + ifa_free(ifa); } /* @@ -755,7 +761,8 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp) } else { /* failed to assign linklocal address. bark? */ } - } + } else + ifa_free(&ia->ia_ifa); } #ifdef IFT_STF /* XXX */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 051cd9b889c1..485efb90266f 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -162,10 +162,11 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam, if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { - struct ifaddr *ia; + struct ifaddr *ifa; sin6->sin6_port = 0; /* yech... */ - if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == NULL && + if ((ifa = ifa_ifwithaddr((struct sockaddr *)sin6)) == + NULL && (inp->inp_flags & INP_BINDANY) == 0) { return (EADDRNOTAVAIL); } @@ -176,11 +177,14 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam, * We should allow to bind to a deprecated address, since * the application dares to use it. */ - if (ia && - ((struct in6_ifaddr *)ia)->ia6_flags & + if (ifa != NULL && + ((struct in6_ifaddr *)ifa)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { + ifa_free(ifa); return (EADDRNOTAVAIL); } + if (ifa != NULL) + ifa_free(ifa); } if (lport) { struct inpcb *t; diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 7ae30387b130..5443234ec2e2 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -245,15 +245,26 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0) return (NULL); - ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock)); + ia6 = (struct in6_ifaddr *)ifa_ifwithaddr( + (struct sockaddr *)&srcsock); if (ia6 == NULL || (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { + if (ia6 != NULL) + ifa_free(&ia6->ia_ifa); *errorp = EADDRNOTAVAIL; return (NULL); } pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */ if (ifpp) *ifpp = ifp; + + /* + * XXXRW: This returns a pointer into a structure with no + * refcount. in6_selectsrc() should return it to caller- + * provided memory using call-by-reference rather than + * returning pointers into other memory. + */ + ifa_free(&ia6->ia_ifa); return (&ia6->ia_addr.sin6_addr); } diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 9a846c3d3009..37c3c6a900e4 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -498,25 +498,6 @@ do { \ extern struct in6_addr zeroin6_addr; extern u_char inet6ctlerrmap[]; - -/* - * Macro for finding the internet address structure (in6_ifaddr) corresponding - * to a given interface (ifnet structure). - */ - -#define IFP_TO_IA6(ifp, ia) \ -/* struct ifnet *ifp; */ \ -/* struct in6_ifaddr *ia; */ \ -do { \ - struct ifaddr *ifa; \ - IF_ADDR_LOCK_ASSERT(ifp); \ - TAILQ_FOREACH(ifa, &(ifp)->if_addrhead, ifa_link) { \ - if (ifa->ifa_addr->sa_family == AF_INET6) \ - break; \ - } \ - (ia) = (struct in6_ifaddr *)ifa; \ -} while (/*CONSTCOND*/ 0) - #endif /* _KERNEL */ /* diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 182b5af47ec8..53cfdf859718 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -724,6 +724,7 @@ passin: * to the upper layers. */ } + ifa_free(&ia6->ia_ifa); } } @@ -919,6 +920,11 @@ out: /* * set/grab in6_ifaddr correspond to IPv6 destination address. * XXX backward compatibility wrapper + * + * XXXRW: We should bump the refcount on ia6 before sticking it in the m_tag, + * and then bump it when the tag is copied, and release it when the tag is + * freed. Unfortunately, m_tags don't support deep copies (yet), so instead + * we just bump the ia refcount when we receive it. This should be fixed. */ static struct ip6aux * ip6_setdstifaddr(struct mbuf *m, struct in6_ifaddr *ia6) @@ -935,11 +941,14 @@ struct in6_ifaddr * ip6_getdstifaddr(struct mbuf *m) { struct ip6aux *ip6a; + struct in6_ifaddr *ia; ip6a = ip6_findaux(m); - if (ip6a) - return ip6a->ip6a_dstia6; - else + if (ip6a) { + ia = ip6a->ip6a_dstia6; + ifa_ref(&ia->ia_ifa); + return ia; + } else return NULL; } diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 2755573d1e80..713dd28a54be 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -904,6 +904,7 @@ passout: /* Record statistics for this interface address. */ ia6->ia_ifa.if_opackets++; ia6->ia_ifa.if_obytes += m->m_pkthdr.len; + ifa_free(&ia6->ia_ifa); } error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); goto done; diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 012c672c0688..61fff113b40a 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1152,8 +1152,13 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6, */ ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); if ((ia && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia))) || - (ia == NULL && IN6_IS_ADDR_UNSPECIFIED(&src))) + (ia == NULL && IN6_IS_ADDR_UNSPECIFIED(&src))) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (0); + } + if (ia != NULL) + ifa_free(&ia->ia_ifa); CTR3(KTR_MLD, "process v1 report %s on ifp %p(%s)", ip6_sprintf(ip6tbuf, &mld->mld_addr), ifp, ifp->if_xname); @@ -1796,11 +1801,16 @@ mld_v1_transmit_report(struct in6_multi *in6m, const int type) /* ia may be NULL if link-local address is tentative. */ MGETHDR(mh, M_DONTWAIT, MT_HEADER); - if (mh == NULL) + if (mh == NULL) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (ENOMEM); + } MGET(md, M_DONTWAIT, MT_DATA); if (md == NULL) { m_free(mh); + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (ENOMEM); } mh->m_next = md; @@ -1839,6 +1849,8 @@ mld_v1_transmit_report(struct in6_multi *in6m, const int type) mld_dispatch_packet(mh); + if (ia != NULL) + ifa_free(&ia->ia_ifa); return (0); } @@ -3136,6 +3148,8 @@ mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m) MGETHDR(mh, M_DONTWAIT, MT_HEADER); if (mh == NULL) { + if (ia != NULL) + ifa_free(&ia->ia_ifa); m_freem(m); return (NULL); } @@ -3154,6 +3168,8 @@ mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m) ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any; + if (ia != NULL) + ifa_free(&ia->ia_ifa); ip6->ip6_dst = in6addr_linklocal_allv2routers; /* scope ID will be set in netisr */ @@ -3168,7 +3184,6 @@ mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m) mh->m_next = m; mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), sizeof(struct mldv2_report) + mldreclen); - return (mh); } diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index ce5aa9cbb479..e5fa6ae218e3 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -957,8 +957,13 @@ nd6_is_new_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) * a p2p interface, the address should be a neighbor. */ dstaddr = ifa_ifwithdstaddr((struct sockaddr *)addr); - if ((dstaddr != NULL) && (dstaddr->ifa_ifp == ifp)) - return (1); + if (dstaddr != NULL) { + if (dstaddr->ifa_ifp == ifp) { + ifa_free(dstaddr); + return (1); + } + ifa_free(dstaddr); + } /* * If the default router list is empty, all addresses are regarded diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 944bb3305934..1e6592713442 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -355,6 +355,8 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, tlladdr, (struct sockaddr *)proxydl); freeit: + if (ifa != NULL) + ifa_free(ifa); m_freem(m); return; @@ -366,6 +368,8 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(ip6bufs, &taddr6))); ICMP6STAT_INC(icp6s_badns); + if (ifa != NULL) + ifa_free(ifa); m_freem(m); } @@ -456,6 +460,8 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, goto bad; } if (!dad) { + struct ifaddr *ifa; + /* * RFC2461 7.2.2: * "If the source address of the packet prompting the @@ -486,9 +492,11 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, else hsrc = NULL; } - if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc)) + if (hsrc && (ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, + hsrc)) != NULL) { src = hsrc; - else { + ifa_free(ifa); + } else { int error; struct sockaddr_in6 dst_sa; @@ -679,12 +687,14 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) */ if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { + ifa_free(ifa); nd6_dad_na_input(ifa); goto freeit; } /* Just for safety, maybe unnecessary. */ if (ifa) { + ifa_free(ifa); log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", ip6_sprintf(ip6bufs, &taddr6)); diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 2160e291e31a..6b76e3021dfb 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -435,6 +435,7 @@ nd6_rtmsg(int cmd, struct rtentry *rt) { struct rt_addrinfo info; struct ifnet *ifp; + struct ifaddr *ifa; bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = rt_key(rt); @@ -443,13 +444,17 @@ nd6_rtmsg(int cmd, struct rtentry *rt) ifp = rt->rt_ifp; if (ifp != NULL) { IF_ADDR_LOCK(ifp); - info.rti_info[RTAX_IFP] = - TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr; + ifa = TAILQ_FIRST(&ifp->if_addrhead); + info.rti_info[RTAX_IFP] = ifa->ifa_addr; + ifa_ref(ifa); IF_ADDR_UNLOCK(ifp); info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; - } + } else + ifa = NULL; rt_missmsg(cmd, &info, rt->rt_flags, 0); + if (ifa != NULL) + ifa_free(ifa); } void @@ -1307,6 +1312,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, e)); } } + ifa_free(&ia6->ia_ifa); /* * A newly added address might affect the status @@ -1597,10 +1603,14 @@ nd6_prefix_onlink(struct nd_prefix *pr) IN6_IFF_NOTREADY | IN6_IFF_ANYCAST); if (ifa == NULL) { /* XXX: freebsd does not have ifa_ifwithaf */ + IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET6) break; } + if (ifa != NULL) + ifa_ref(ifa); + IF_ADDR_UNLOCK(ifp); /* should we care about ia6_flags? */ } if (ifa == NULL) { @@ -1661,6 +1671,8 @@ nd6_prefix_onlink(struct nd_prefix *pr) RT_REMREF(rt); RT_UNLOCK(rt); } + if (ifa != NULL) + ifa_free(ifa); return (error); } @@ -1802,6 +1814,7 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) /* prefixlen + ifidlen must be equal to 128 */ plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); if (prefixlen != plen0) { + ifa_free(ifa); nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s " "(prefix=%d ifid=%d)\n", if_name(ifp), prefixlen, 128 - plen0)); @@ -1834,6 +1847,7 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); ifra.ifra_addr.sin6_addr.s6_addr32[3] |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); + ifa_free(ifa); /* new prefix mask. */ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); @@ -1854,7 +1868,10 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) * usually not happen, but we can still see this case, e.g., if we * have manually configured the exact address to be configured. */ - if (in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr) != NULL) { + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, + &ifra.ifra_addr.sin6_addr); + if (ifa != NULL) { + ifa_free(ifa); /* this should be rare enough to make an explicit log */ log(LOG_INFO, "in6_ifadd: %s is already configured\n", ip6_sprintf(ip6buf, &ifra.ifra_addr.sin6_addr)); @@ -1879,8 +1896,12 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast) } ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); - - return (ia); /* this is always non-NULL */ + /* + * XXXRW: Assumption of non-NULLness here might not be true with + * fine-grained locking -- should we validate it? Or just return + * earlier ifa rather than looking it up again? + */ + return (ia); /* this is always non-NULL and referenced. */ } /* @@ -2000,6 +2021,7 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay) } newia->ia6_ndpr = ia0->ia6_ndpr; newia->ia6_ndpr->ndpr_refcnt++; + ifa_free(&newia->ia_ifa); /* * A newly added address might affect the status of other addresses. diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 3c514d888259..cd7d8173de4f 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -714,7 +714,7 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) INIT_VNET_INET6(so->so_vnet); struct inpcb *inp; struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; - struct ifaddr *ia = NULL; + struct ifaddr *ifa = NULL; int error = 0; inp = sotoinpcb(so); @@ -730,14 +730,17 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) return (error); if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && - (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) + (ifa = ifa_ifwithaddr((struct sockaddr *)addr)) == NULL) return (EADDRNOTAVAIL); - if (ia && - ((struct in6_ifaddr *)ia)->ia6_flags & + if (ifa != NULL && + ((struct in6_ifaddr *)ifa)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + ifa_free(ifa); return (EADDRNOTAVAIL); } + if (ifa != NULL) + ifa_free(ifa); INP_INFO_WLOCK(&V_ripcbinfo); INP_WLOCK(inp); inp->in6p_laddr = addr->sin6_addr; diff --git a/sys/netipx/ipx_pcb.c b/sys/netipx/ipx_pcb.c index 4c8eba0ae369..77fbfe868685 100644 --- a/sys/netipx/ipx_pcb.c +++ b/sys/netipx/ipx_pcb.c @@ -158,7 +158,6 @@ noname: int ipx_pcbconnect(struct ipxpcb *ipxp, struct sockaddr *nam, struct thread *td) { - struct ipx_ifaddr *ia; struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)nam; struct ipx_addr *dst; struct route *ro; @@ -167,8 +166,6 @@ ipx_pcbconnect(struct ipxpcb *ipxp, struct sockaddr *nam, struct thread *td) IPX_LIST_LOCK_ASSERT(); IPX_LOCK_ASSERT(ipxp); - ia = NULL; - if (sipx->sipx_family != AF_IPX) return (EAFNOSUPPORT); if (sipx->sipx_port == 0 || ipx_nullhost(sipx->sipx_addr)) @@ -213,6 +210,8 @@ ipx_pcbconnect(struct ipxpcb *ipxp, struct sockaddr *nam, struct thread *td) rtalloc_ign(ro, 0); } if (ipx_neteqnn(ipxp->ipxp_laddr.x_net, ipx_zeronet)) { + struct ipx_ifaddr *ia = NULL; + /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. @@ -225,40 +224,10 @@ ipx_pcbconnect(struct ipxpcb *ipxp, struct sockaddr *nam, struct thread *td) if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) { IPX_IFADDR_RLOCK(); for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) - if (ia->ia_ifp == ifp) - break; - IPX_IFADDR_RUNLOCK(); - } - if (ia == NULL) { - u_short fport = sipx->sipx_addr.x_port; - sipx->sipx_addr.x_port = 0; - ia = (struct ipx_ifaddr *) - ifa_ifwithdstaddr((struct sockaddr *)sipx); - sipx->sipx_addr.x_port = fport; - if (ia == NULL) - ia = ipx_iaonnetof(&sipx->sipx_addr); - if (ia == NULL) - ia = ipx_ifaddr; - if (ia == NULL) - return (EADDRNOTAVAIL); - } - ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net; - } - if (ipx_nullhost(ipxp->ipxp_laddr)) { - /* - * If route is known or can be allocated now, - * our src addr is taken from the i/f, else punt. - */ - - /* - * If we found a route, use the address - * corresponding to the outgoing interface - */ - if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) { - IPX_IFADDR_RLOCK(); - for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) - if (ia->ia_ifp == ifp) + if (ia->ia_ifp == ifp) { + ifa_ref(&ia->ia_ifa); break; + } IPX_IFADDR_RUNLOCK(); } if (ia == NULL) { @@ -270,17 +239,68 @@ ipx_pcbconnect(struct ipxpcb *ipxp, struct sockaddr *nam, struct thread *td) if (ia == NULL) { IPX_IFADDR_RLOCK(); ia = ipx_iaonnetof(&sipx->sipx_addr); + if (ia != NULL) + ifa_ref(&ia->ia_ifa); IPX_IFADDR_RUNLOCK(); } if (ia == NULL) { IPX_IFADDR_RLOCK(); ia = ipx_ifaddr; + if (ia != NULL) + ifa_ref(&ia->ia_ifa); + IPX_IFADDR_RUNLOCK(); + } + if (ia == NULL) + return (EADDRNOTAVAIL); + } + ipxp->ipxp_laddr.x_net = satoipx_addr(ia->ia_addr).x_net; + ifa_free(&ia->ia_ifa); + } + if (ipx_nullhost(ipxp->ipxp_laddr)) { + struct ipx_ifaddr *ia = NULL; + /* + * If route is known or can be allocated now, + * our src addr is taken from the i/f, else punt. + */ + + /* + * If we found a route, use the address + * corresponding to the outgoing interface + */ + if (ro->ro_rt != NULL && (ifp = ro->ro_rt->rt_ifp) != NULL) { + IPX_IFADDR_RLOCK(); + for (ia = ipx_ifaddr; ia != NULL; ia = ia->ia_next) + if (ia->ia_ifp == ifp) { + ifa_ref(&ia->ia_ifa); + break; + } + IPX_IFADDR_RUNLOCK(); + } + if (ia == NULL) { + u_short fport = sipx->sipx_addr.x_port; + sipx->sipx_addr.x_port = 0; + ia = (struct ipx_ifaddr *) + ifa_ifwithdstaddr((struct sockaddr *)sipx); + sipx->sipx_addr.x_port = fport; + if (ia == NULL) { + IPX_IFADDR_RLOCK(); + ia = ipx_iaonnetof(&sipx->sipx_addr); + if (ia != NULL) + ifa_ref(&ia->ia_ifa); + IPX_IFADDR_RUNLOCK(); + } + if (ia == NULL) { + IPX_IFADDR_RLOCK(); + ia = ipx_ifaddr; + if (ia != NULL) + ifa_ref(&ia->ia_ifa); IPX_IFADDR_RUNLOCK(); } if (ia == NULL) return (EADDRNOTAVAIL); } ipxp->ipxp_laddr.x_host = satoipx_addr(ia->ia_addr).x_host; + ifa_free(&ia->ia_ifa); } if (ipx_pcblookup(&sipx->sipx_addr, ipxp->ipxp_lport, 0)) return (EADDRINUSE);