summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/raw_ip.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/raw_ip.c')
-rw-r--r--freebsd/sys/netinet/raw_ip.c102
1 files changed, 60 insertions, 42 deletions
diff --git a/freebsd/sys/netinet/raw_ip.c b/freebsd/sys/netinet/raw_ip.c
index 7dea3ec1..a97eadae 100644
--- a/freebsd/sys/netinet/raw_ip.c
+++ b/freebsd/sys/netinet/raw_ip.c
@@ -172,7 +172,7 @@ rip_inshash(struct inpcb *inp)
} else
hash = 0;
pcbhash = &pcbinfo->ipi_hashbase[hash];
- LIST_INSERT_HEAD(pcbhash, inp, inp_hash);
+ CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash);
}
static void
@@ -182,7 +182,7 @@ rip_delhash(struct inpcb *inp)
INP_INFO_WLOCK_ASSERT(inp->inp_pcbinfo);
INP_WLOCK_ASSERT(inp);
- LIST_REMOVE(inp, inp_hash);
+ CK_LIST_REMOVE(inp, inp_hash);
}
#endif /* INET */
@@ -287,6 +287,7 @@ rip_input(struct mbuf **mp, int *offp, int proto)
struct ip *ip = mtod(m, struct ip *);
struct inpcb *inp, *last;
struct sockaddr_in ripsrc;
+ struct epoch_tracker et;
int hash;
*mp = NULL;
@@ -301,8 +302,8 @@ rip_input(struct mbuf **mp, int *offp, int proto)
hash = INP_PCBHASH_RAW(proto, ip->ip_src.s_addr,
ip->ip_dst.s_addr, V_ripcbinfo.ipi_hashmask);
- INP_INFO_RLOCK(&V_ripcbinfo);
- LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[hash], inp_hash) {
+ INP_INFO_RLOCK_ET(&V_ripcbinfo, et);
+ CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[hash], inp_hash) {
if (inp->inp_ip_p != proto)
continue;
#ifdef INET6
@@ -314,27 +315,33 @@ rip_input(struct mbuf **mp, int *offp, int proto)
continue;
if (inp->inp_faddr.s_addr != ip->ip_src.s_addr)
continue;
- if (jailed_without_vnet(inp->inp_cred)) {
- /*
- * XXX: If faddr was bound to multicast group,
- * jailed raw socket will drop datagram.
- */
- if (prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
- continue;
- }
if (last != NULL) {
struct mbuf *n;
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n != NULL)
- (void) rip_append(last, ip, n, &ripsrc);
+ (void) rip_append(last, ip, n, &ripsrc);
/* XXX count dropped packet */
INP_RUNLOCK(last);
+ last = NULL;
}
INP_RLOCK(inp);
+ if (__predict_false(inp->inp_flags2 & INP_FREED))
+ goto skip_1;
+ if (jailed_without_vnet(inp->inp_cred)) {
+ /*
+ * XXX: If faddr was bound to multicast group,
+ * jailed raw socket will drop datagram.
+ */
+ if (prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
+ goto skip_1;
+ }
last = inp;
+ continue;
+ skip_1:
+ INP_RUNLOCK(inp);
}
- LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[0], inp_hash) {
+ CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[0], inp_hash) {
if (inp->inp_ip_p && inp->inp_ip_p != proto)
continue;
#ifdef INET6
@@ -348,6 +355,19 @@ rip_input(struct mbuf **mp, int *offp, int proto)
if (!in_nullhost(inp->inp_faddr) &&
!in_hosteq(inp->inp_faddr, ip->ip_src))
continue;
+ if (last != NULL) {
+ struct mbuf *n;
+
+ n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
+ if (n != NULL)
+ (void) rip_append(last, ip, n, &ripsrc);
+ /* XXX count dropped packet */
+ INP_RUNLOCK(last);
+ last = NULL;
+ }
+ INP_RLOCK(inp);
+ if (__predict_false(inp->inp_flags2 & INP_FREED))
+ goto skip_2;
if (jailed_without_vnet(inp->inp_cred)) {
/*
* Allow raw socket in jail to receive multicast;
@@ -356,7 +376,7 @@ rip_input(struct mbuf **mp, int *offp, int proto)
*/
if (!IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
prison_check_ip4(inp->inp_cred, &ip->ip_dst) != 0)
- continue;
+ goto skip_2;
}
/*
* If this raw socket has multicast state, and we
@@ -397,22 +417,15 @@ rip_input(struct mbuf **mp, int *offp, int proto)
if (blocked != MCAST_PASS) {
IPSTAT_INC(ips_notmember);
- continue;
+ goto skip_2;
}
}
- if (last != NULL) {
- struct mbuf *n;
-
- n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
- if (n != NULL)
- (void) rip_append(last, ip, n, &ripsrc);
- /* XXX count dropped packet */
- INP_RUNLOCK(last);
- }
- INP_RLOCK(inp);
last = inp;
+ continue;
+ skip_2:
+ INP_RUNLOCK(inp);
}
- INP_INFO_RUNLOCK(&V_ripcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_ripcbinfo, et);
if (last != NULL) {
if (rip_append(last, ip, m, &ripsrc) != 0)
IPSTAT_INC(ips_delivered);
@@ -853,7 +866,6 @@ rip_detach(struct socket *so)
ip_rsvp_force_done(so);
if (so == V_ip_rsvpd)
ip_rsvp_done();
- /* XXX defer to epoch_call */
in_pcbdetach(inp);
in_pcbfree(inp);
INP_INFO_WUNLOCK(&V_ripcbinfo);
@@ -1023,10 +1035,10 @@ static int
rip_pcblist(SYSCTL_HANDLER_ARGS)
{
int error, i, n;
- struct in_pcblist *il;
struct inpcb *inp, **inp_list;
inp_gen_t gencnt;
struct xinpgen xig;
+ struct epoch_tracker et;
/*
* The process of preparing the TCB list is too time-consuming and
@@ -1045,10 +1057,10 @@ rip_pcblist(SYSCTL_HANDLER_ARGS)
/*
* OK, now we're committed to doing something.
*/
- INP_INFO_RLOCK(&V_ripcbinfo);
+ INP_INFO_WLOCK(&V_ripcbinfo);
gencnt = V_ripcbinfo.ipi_gencnt;
n = V_ripcbinfo.ipi_count;
- INP_INFO_RUNLOCK(&V_ripcbinfo);
+ INP_INFO_WUNLOCK(&V_ripcbinfo);
xig.xig_len = sizeof xig;
xig.xig_count = n;
@@ -1058,12 +1070,11 @@ rip_pcblist(SYSCTL_HANDLER_ARGS)
if (error)
return (error);
- il = malloc(sizeof(struct in_pcblist) + n * sizeof(struct inpcb *), M_TEMP, M_WAITOK|M_ZERO_INVARIANTS);
- inp_list = il->il_inp_list;
+ inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK);
- INP_INFO_RLOCK(&V_ripcbinfo);
- for (inp = LIST_FIRST(V_ripcbinfo.ipi_listhead), i = 0; inp && i < n;
- inp = LIST_NEXT(inp, inp_list)) {
+ INP_INFO_RLOCK_ET(&V_ripcbinfo, et);
+ for (inp = CK_LIST_FIRST(V_ripcbinfo.ipi_listhead), i = 0; inp && i < n;
+ inp = CK_LIST_NEXT(inp, inp_list)) {
INP_WLOCK(inp);
if (inp->inp_gencnt <= gencnt &&
cr_canseeinpcb(req->td->td_ucred, inp) == 0) {
@@ -1072,7 +1083,7 @@ rip_pcblist(SYSCTL_HANDLER_ARGS)
}
INP_WUNLOCK(inp);
}
- INP_INFO_RUNLOCK(&V_ripcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_ripcbinfo, et);
n = i;
error = 0;
@@ -1088,24 +1099,31 @@ rip_pcblist(SYSCTL_HANDLER_ARGS)
} else
INP_RUNLOCK(inp);
}
- il->il_count = n;
- il->il_pcbinfo = &V_ripcbinfo;
- epoch_call(net_epoch_preempt, &il->il_epoch_ctx, in_pcblist_rele_rlocked);
+ INP_INFO_WLOCK(&V_ripcbinfo);
+ for (i = 0; i < n; i++) {
+ inp = inp_list[i];
+ INP_RLOCK(inp);
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
+ }
+ INP_INFO_WUNLOCK(&V_ripcbinfo);
if (!error) {
+ struct epoch_tracker et;
/*
* Give the user an updated idea of our state. If the
* generation differs from what we told her before, she knows
* that something happened while we were processing this
* request, and it might be necessary to retry.
*/
- INP_INFO_RLOCK(&V_ripcbinfo);
+ INP_INFO_RLOCK_ET(&V_ripcbinfo, et);
xig.xig_gen = V_ripcbinfo.ipi_gencnt;
xig.xig_sogen = so_gencnt;
xig.xig_count = V_ripcbinfo.ipi_count;
- INP_INFO_RUNLOCK(&V_ripcbinfo);
+ INP_INFO_RUNLOCK_ET(&V_ripcbinfo, et);
error = SYSCTL_OUT(req, &xig, sizeof xig);
}
+ free(inp_list, M_TEMP);
return (error);
}