summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/in_pcb.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/in_pcb.c')
-rw-r--r--freebsd/sys/netinet/in_pcb.c135
1 files changed, 110 insertions, 25 deletions
diff --git a/freebsd/sys/netinet/in_pcb.c b/freebsd/sys/netinet/in_pcb.c
index 0d388132..f89487b6 100644
--- a/freebsd/sys/netinet/in_pcb.c
+++ b/freebsd/sys/netinet/in_pcb.c
@@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$");
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/rmlock.h>
+#include <sys/smp.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
@@ -93,6 +94,9 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp_var.h>
+#ifdef TCPHPTS
+#include <netinet/tcp_hpts.h>
+#endif
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#endif
@@ -590,7 +594,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
INP_LOCK_ASSERT(inp);
INP_HASH_LOCK_ASSERT(pcbinfo);
- if (TAILQ_EMPTY(&V_in_ifaddrhead)) /* XXX broken! */
+ if (CK_STAILQ_EMPTY(&V_in_ifaddrhead)) /* XXX broken! */
return (EADDRNOTAVAIL);
laddr.s_addr = *laddrp;
if (nam != NULL && laddr.s_addr != INADDR_ANY)
@@ -802,7 +806,6 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
int error;
KASSERT(laddr != NULL, ("%s: laddr NULL", __func__));
-
/*
* Bypass source address selection and use the primary jail IP
* if requested.
@@ -835,15 +838,18 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
* network and try to find a corresponding interface to take
* the source address from.
*/
+ NET_EPOCH_ENTER();
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,
inp->inp_socket->so_fibnum));
- if (ia == NULL)
+ if (ia == NULL) {
ia = ifatoia(ifa_ifwithnet((struct sockaddr *)sin, 0,
inp->inp_socket->so_fibnum));
+
+ }
if (ia == NULL) {
error = ENETUNREACH;
goto done;
@@ -851,15 +857,13 @@ 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_RLOCK(ifp);
- TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
sa = ifa->ifa_addr;
if (sa->sa_family != AF_INET)
@@ -918,7 +922,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
ia = NULL;
ifp = sro.ro_rt->rt_ifp;
IF_ADDR_RLOCK(ifp);
- TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
sa = ifa->ifa_addr;
if (sa->sa_family != AF_INET)
continue;
@@ -972,7 +976,6 @@ 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;
}
@@ -981,10 +984,9 @@ 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_RLOCK(ifp);
- TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
sa = ifa->ifa_addr;
if (sa->sa_family != AF_INET)
@@ -1010,6 +1012,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr,
}
done:
+ NET_EPOCH_EXIT();
if (sro.ro_rt != NULL)
RTFREE(sro.ro_rt);
return (error);
@@ -1063,7 +1066,7 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
faddr = sin->sin_addr;
fport = sin->sin_port;
- if (!TAILQ_EMPTY(&V_in_ifaddrhead)) {
+ if (!CK_STAILQ_EMPTY(&V_in_ifaddrhead)) {
/*
* If the destination address is INADDR_ANY,
* use the primary local address.
@@ -1074,16 +1077,16 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
if (faddr.s_addr == INADDR_ANY) {
IN_IFADDR_RLOCK(&in_ifa_tracker);
faddr =
- IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr;
+ IA_SIN(CK_STAILQ_FIRST(&V_in_ifaddrhead))->sin_addr;
IN_IFADDR_RUNLOCK(&in_ifa_tracker);
if (cred != NULL &&
(error = prison_get_ip4(cred, &faddr)) != 0)
return (error);
} else if (faddr.s_addr == (u_long)INADDR_BROADCAST) {
IN_IFADDR_RLOCK(&in_ifa_tracker);
- if (TAILQ_FIRST(&V_in_ifaddrhead)->ia_ifp->if_flags &
+ if (CK_STAILQ_FIRST(&V_in_ifaddrhead)->ia_ifp->if_flags &
IFF_BROADCAST)
- faddr = satosin(&TAILQ_FIRST(
+ faddr = satosin(&CK_STAILQ_FIRST(
&V_in_ifaddrhead)->ia_broadaddr)->sin_addr;
IN_IFADDR_RUNLOCK(&in_ifa_tracker);
}
@@ -1104,7 +1107,7 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
if (imo->imo_multicast_ifp != NULL) {
ifp = imo->imo_multicast_ifp;
IN_IFADDR_RLOCK(&in_ifa_tracker);
- TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
+ CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
if ((ia->ia_ifp == ifp) &&
(cred == NULL ||
prison_check_ip4(cred,
@@ -1236,9 +1239,28 @@ in_pcbrele_rlocked(struct inpcb *inp)
}
return (0);
}
-
+
KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));
-
+#ifdef TCPHPTS
+ if (inp->inp_in_hpts || inp->inp_in_input) {
+ struct tcp_hpts_entry *hpts;
+ /*
+ * We should not be on the hpts at
+ * this point in any form. we must
+ * get the lock to be sure.
+ */
+ hpts = tcp_hpts_lock(inp);
+ if (inp->inp_in_hpts)
+ panic("Hpts:%p inp:%p at free still on hpts",
+ hpts, inp);
+ mtx_unlock(&hpts->p_mtx);
+ hpts = tcp_input_lock(inp);
+ if (inp->inp_in_input)
+ panic("Hpts:%p inp:%p at free still on input hpts",
+ hpts, inp);
+ mtx_unlock(&hpts->p_mtx);
+ }
+#endif
INP_RUNLOCK(inp);
pcbinfo = inp->inp_pcbinfo;
uma_zfree(pcbinfo->ipi_zone, inp);
@@ -1267,7 +1289,26 @@ in_pcbrele_wlocked(struct inpcb *inp)
}
KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));
-
+#ifdef TCPHPTS
+ if (inp->inp_in_hpts || inp->inp_in_input) {
+ struct tcp_hpts_entry *hpts;
+ /*
+ * We should not be on the hpts at
+ * this point in any form. we must
+ * get the lock to be sure.
+ */
+ hpts = tcp_hpts_lock(inp);
+ if (inp->inp_in_hpts)
+ panic("Hpts:%p inp:%p at free still on hpts",
+ hpts, inp);
+ mtx_unlock(&hpts->p_mtx);
+ hpts = tcp_input_lock(inp);
+ if (inp->inp_in_input)
+ panic("Hpts:%p inp:%p at free still on input hpts",
+ hpts, inp);
+ mtx_unlock(&hpts->p_mtx);
+ }
+#endif
INP_WUNLOCK(inp);
pcbinfo = inp->inp_pcbinfo;
uma_zfree(pcbinfo->ipi_zone, inp);
@@ -1284,6 +1325,28 @@ in_pcbrele(struct inpcb *inp)
return (in_pcbrele_wlocked(inp));
}
+void
+in_pcblist_rele_rlocked(epoch_context_t ctx)
+{
+ struct in_pcblist *il;
+ struct inpcb *inp;
+ struct inpcbinfo *pcbinfo;
+ int i, n;
+
+ il = __containerof(ctx, struct in_pcblist, il_epoch_ctx);
+ pcbinfo = il->il_pcbinfo;
+ n = il->il_count;
+ INP_INFO_WLOCK(pcbinfo);
+ for (i = 0; i < n; i++) {
+ inp = il->il_inp_list[i];
+ INP_RLOCK(inp);
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
+ }
+ INP_INFO_WUNLOCK(pcbinfo);
+ free(il, M_TEMP);
+}
+
/*
* Unconditionally schedule an inpcb to be freed by decrementing its
* reference count, which should occur only after the inpcb has been detached
@@ -1298,8 +1361,21 @@ in_pcbfree(struct inpcb *inp)
{
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+#ifdef INET6
+ struct ip6_moptions *im6o = NULL;
+#endif
+#ifdef INET
+ struct ip_moptions *imo = NULL;
+#endif
KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));
+ KASSERT((inp->inp_flags2 & INP_FREED) == 0,
+ ("%s: called twice for pcb %p", __func__, inp));
+ if (inp->inp_flags2 & INP_FREED) {
+ INP_WUNLOCK(inp);
+ return;
+ }
+
#ifdef INVARIANTS
if (pcbinfo == &V_tcbinfo) {
INP_INFO_LOCK_ASSERT(pcbinfo);
@@ -1309,6 +1385,10 @@ in_pcbfree(struct inpcb *inp)
#endif
INP_WLOCK_ASSERT(inp);
+#ifdef INET
+ imo = inp->inp_moptions;
+ inp->inp_moptions = NULL;
+#endif
/* XXXRW: Do as much as possible here. */
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (inp->inp_sp != NULL)
@@ -1321,16 +1401,12 @@ in_pcbfree(struct inpcb *inp)
#ifdef INET6
if (inp->inp_vflag & INP_IPV6PROTO) {
ip6_freepcbopts(inp->in6p_outputopts);
- if (inp->in6p_moptions != NULL)
- ip6_freemoptions(inp->in6p_moptions);
+ im6o = inp->in6p_moptions;
+ inp->in6p_moptions = NULL;
}
#endif
if (inp->inp_options)
(void)m_free(inp->inp_options);
-#ifdef INET
- if (inp->inp_moptions != NULL)
- inp_freemoptions(inp->inp_moptions);
-#endif
RO_INVALIDATE_CACHE(&inp->inp_route);
inp->inp_vflag = 0;
@@ -1339,6 +1415,12 @@ in_pcbfree(struct inpcb *inp)
#ifdef MAC
mac_inpcb_destroy(inp);
#endif
+#ifdef INET6
+ ip6_freemoptions(im6o);
+#endif
+#ifdef INET
+ inp_freemoptions(imo);
+#endif
if (!in_pcbrele_wlocked(inp))
INP_WUNLOCK(inp);
}
@@ -1492,11 +1574,14 @@ in_pcbpurgeif0(struct inpcbinfo *pcbinfo, struct ifnet *ifp)
/*
* Drop multicast group membership if we joined
* through the interface being detached.
+ *
+ * XXX This can all be deferred to an epoch_call
*/
for (i = 0, gap = 0; i < imo->imo_num_memberships;
i++) {
if (imo->imo_membership[i]->inm_ifp == ifp) {
- in_delmulti(imo->imo_membership[i]);
+ IN_MULTI_LOCK_ASSERT();
+ in_leavegroup_locked(imo->imo_membership[i], NULL);
gap++;
} else if (gap != 0)
imo->imo_membership[i - gap] =