summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet/in_mcast.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet/in_mcast.c')
-rw-r--r--freebsd/sys/netinet/in_mcast.c149
1 files changed, 110 insertions, 39 deletions
diff --git a/freebsd/sys/netinet/in_mcast.c b/freebsd/sys/netinet/in_mcast.c
index 4112046c..3d68718e 100644
--- a/freebsd/sys/netinet/in_mcast.c
+++ b/freebsd/sys/netinet/in_mcast.c
@@ -40,23 +40,28 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
+#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/sysctl.h>
#include <sys/ktr.h>
+#include <sys/taskqueue.h>
#include <sys/tree.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
+#include <netinet/in_fib.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip_var.h>
@@ -148,6 +153,8 @@ static void inm_purge(struct in_multi *);
static void inm_reap(struct in_multi *);
static struct ip_moptions *
inp_findmoptions(struct inpcb *);
+static void inp_freemoptions_internal(struct ip_moptions *);
+static void inp_gcmoptions(void *, int);
static int inp_get_source_filters(struct inpcb *, struct sockopt *);
static int inp_join_group(struct inpcb *, struct sockopt *);
static int inp_leave_group(struct inpcb *, struct sockopt *);
@@ -164,25 +171,26 @@ static SYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast, CTLFLAG_RW, 0,
static u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER;
SYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc,
- CTLFLAG_RW | CTLFLAG_TUN, &in_mcast_maxgrpsrc, 0,
+ CTLFLAG_RWTUN, &in_mcast_maxgrpsrc, 0,
"Max source filters per group");
-TUNABLE_ULONG("net.inet.ip.mcast.maxgrpsrc", &in_mcast_maxgrpsrc);
static u_long in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER;
SYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxsocksrc,
- CTLFLAG_RW | CTLFLAG_TUN, &in_mcast_maxsocksrc, 0,
+ CTLFLAG_RWTUN, &in_mcast_maxsocksrc, 0,
"Max source filters per socket");
-TUNABLE_ULONG("net.inet.ip.mcast.maxsocksrc", &in_mcast_maxsocksrc);
int in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP;
-SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_TUN,
+SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RWTUN,
&in_mcast_loop, 0, "Loopback multicast datagrams by default");
-TUNABLE_INT("net.inet.ip.mcast.loop", &in_mcast_loop);
static SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters,
CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters,
"Per-interface stack-wide source filters");
+static STAILQ_HEAD(, ip_moptions) imo_gc_list =
+ STAILQ_HEAD_INITIALIZER(imo_gc_list);
+static struct task imo_gc_task = TASK_INITIALIZER(0, inp_gcmoptions, NULL);
+
#ifdef KTR
/*
* Inline function which wraps assertions for a valid ifp.
@@ -222,6 +230,49 @@ imf_init(struct in_mfilter *imf, const int st0, const int st1)
}
/*
+ * Function for looking up an in_multi record for an IPv4 multicast address
+ * on a given interface. ifp must be valid. If no record found, return NULL.
+ * The IN_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held.
+ */
+struct in_multi *
+inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina)
+{
+ struct ifmultiaddr *ifma;
+ struct in_multi *inm;
+
+ IN_MULTI_LOCK_ASSERT();
+ IF_ADDR_LOCK_ASSERT(ifp);
+
+ inm = NULL;
+ TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) {
+ if (ifma->ifma_addr->sa_family == AF_INET) {
+ inm = (struct in_multi *)ifma->ifma_protospec;
+ if (inm->inm_addr.s_addr == ina.s_addr)
+ break;
+ inm = NULL;
+ }
+ }
+ return (inm);
+}
+
+/*
+ * Wrapper for inm_lookup_locked().
+ * The IF_ADDR_LOCK will be taken on ifp and released on return.
+ */
+struct in_multi *
+inm_lookup(struct ifnet *ifp, const struct in_addr ina)
+{
+ struct in_multi *inm;
+
+ IN_MULTI_LOCK_ASSERT();
+ IF_ADDR_RLOCK(ifp);
+ inm = inm_lookup_locked(ifp, ina);
+ IF_ADDR_RUNLOCK(ifp);
+
+ return (inm);
+}
+
+/*
* Resize the ip_moptions vector to the next power-of-two minus 1.
* May be called with locks held; do not sleep.
*/
@@ -467,8 +518,8 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group,
*/
inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO);
if (inm == NULL) {
- if_delmulti_ifma(ifma);
IF_ADDR_WUNLOCK(ifp);
+ if_delmulti_ifma(ifma);
return (ENOMEM);
}
inm->inm_addr = *group;
@@ -477,12 +528,7 @@ in_getmulti(struct ifnet *ifp, const struct in_addr *group,
inm->inm_ifma = ifma;
inm->inm_refcount = 1;
inm->inm_state = IGMP_NOT_MEMBER;
-
- /*
- * Pending state-changes per group are subject to a bounds check.
- */
- IFQ_SET_MAXLEN(&inm->inm_scq, IGMP_MAX_STATE_CHANGES);
-
+ mbufq_init(&inm->inm_scq, IGMP_MAX_STATE_CHANGES);
inm->inm_st[0].iss_fmode = MCAST_UNDEFINED;
inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
RB_INIT(&inm->inm_srcs);
@@ -575,7 +621,7 @@ inm_clear_recorded(struct in_multi *inm)
*
* Return 0 if the source didn't exist or was already marked as recorded.
* Return 1 if the source was marked as recorded by this function.
- * Return <0 if any error occured (negated errno code).
+ * Return <0 if any error occurred (negated errno code).
*/
int
inm_record_source(struct in_multi *inm, const in_addr_t naddr)
@@ -1177,11 +1223,8 @@ out_inm_release:
int
in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
{
- struct ifnet *ifp;
int error;
- ifp = inm->inm_ifp;
-
IN_MULTI_LOCK();
error = in_leavegroup_locked(inm, imf);
IN_MULTI_UNLOCK();
@@ -1238,7 +1281,9 @@ in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
KASSERT(error == 0, ("%s: failed to merge inm state", __func__));
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
+ CURVNET_SET(inm->inm_ifp->if_vnet);
error = igmp_change_state(inm);
+ CURVNET_RESTORE();
if (error)
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
@@ -1526,17 +1571,29 @@ inp_findmoptions(struct inpcb *inp)
}
/*
- * Discard the IP multicast options (and source filters).
+ * Discard the IP multicast options (and source filters). To minimize
+ * the amount of work done while holding locks such as the INP's
+ * pcbinfo lock (which is used in the receive path), the free
+ * operation is performed asynchronously in a separate task.
*
* SMPng: NOTE: assumes INP write lock is held.
*/
void
inp_freemoptions(struct ip_moptions *imo)
{
- struct in_mfilter *imf;
- size_t idx, nmships;
KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__));
+ IN_MULTI_LOCK();
+ STAILQ_INSERT_TAIL(&imo_gc_list, imo, imo_link);
+ IN_MULTI_UNLOCK();
+ taskqueue_enqueue(taskqueue_thread, &imo_gc_task);
+}
+
+static void
+inp_freemoptions_internal(struct ip_moptions *imo)
+{
+ struct in_mfilter *imf;
+ size_t idx, nmships;
nmships = imo->imo_num_memberships;
for (idx = 0; idx < nmships; ++idx) {
@@ -1554,6 +1611,22 @@ inp_freemoptions(struct ip_moptions *imo)
free(imo, M_IPMOPTS);
}
+static void
+inp_gcmoptions(void *context, int pending)
+{
+ struct ip_moptions *imo;
+
+ IN_MULTI_LOCK();
+ while (!STAILQ_EMPTY(&imo_gc_list)) {
+ imo = STAILQ_FIRST(&imo_gc_list);
+ STAILQ_REMOVE_HEAD(&imo_gc_list, imo_link);
+ IN_MULTI_UNLOCK();
+ inp_freemoptions_internal(imo);
+ IN_MULTI_LOCK();
+ }
+ IN_MULTI_UNLOCK();
+}
+
/*
* Atomically get source filters on a socket for an IPv4 multicast group.
* Called with INP lock held; returns with lock released.
@@ -1680,6 +1753,7 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
int
inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
{
+ struct rm_priotracker in_ifa_tracker;
struct ip_mreqn mreqn;
struct ip_moptions *imo;
struct ifnet *ifp;
@@ -1719,7 +1793,7 @@ inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
mreqn.imr_address = imo->imo_multicast_addr;
} else if (ifp != NULL) {
mreqn.imr_ifindex = ifp->if_index;
- IFP_TO_IA(ifp, ia);
+ IFP_TO_IA(ifp, ia, &in_ifa_tracker);
if (ia != NULL) {
mreqn.imr_address =
IA_SIN(ia)->sin_addr;
@@ -1738,7 +1812,7 @@ inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
break;
case IP_MULTICAST_TTL:
- if (imo == 0)
+ if (imo == NULL)
optval = coptval = IP_DEFAULT_MULTICAST_TTL;
else
optval = coptval = imo->imo_multicast_ttl;
@@ -1750,7 +1824,7 @@ inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
break;
case IP_MULTICAST_LOOP:
- if (imo == 0)
+ if (imo == NULL)
optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
else
optval = coptval = imo->imo_multicast_loop;
@@ -1810,7 +1884,10 @@ static struct ifnet *
inp_lookup_mcast_ifp(const struct inpcb *inp,
const struct sockaddr_in *gsin, const struct in_addr ina)
{
+ struct rm_priotracker in_ifa_tracker;
struct ifnet *ifp;
+ struct nhop4_basic nh4;
+ uint32_t fibnum;
KASSERT(gsin->sin_family == AF_INET, ("%s: not AF_INET", __func__));
KASSERT(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)),
@@ -1820,21 +1897,15 @@ inp_lookup_mcast_ifp(const struct inpcb *inp,
if (!in_nullhost(ina)) {
INADDR_TO_IFP(ina, ifp);
} else {
- struct route ro;
-
- ro.ro_rt = NULL;
- memcpy(&ro.ro_dst, gsin, sizeof(struct sockaddr_in));
- in_rtalloc_ign(&ro, 0, inp ? inp->inp_inc.inc_fibnum : 0);
- if (ro.ro_rt != NULL) {
- ifp = ro.ro_rt->rt_ifp;
- KASSERT(ifp != NULL, ("%s: null ifp", __func__));
- RTFREE(ro.ro_rt);
- } else {
+ fibnum = inp ? inp->inp_inc.inc_fibnum : 0;
+ if (fib4_lookup_nh_basic(fibnum, gsin->sin_addr, 0, 0, &nh4)==0)
+ ifp = nh4.nh_ifp;
+ else {
struct in_ifaddr *ia;
struct ifnet *mifp;
mifp = NULL;
- IN_IFADDR_RLOCK();
+ IN_IFADDR_RLOCK(&in_ifa_tracker);
TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
mifp = ia->ia_ifp;
if (!(mifp->if_flags & IFF_LOOPBACK) &&
@@ -1843,7 +1914,7 @@ inp_lookup_mcast_ifp(const struct inpcb *inp,
break;
}
}
- IN_IFADDR_RUNLOCK();
+ IN_IFADDR_RUNLOCK(&in_ifa_tracker);
}
}
@@ -2855,7 +2926,7 @@ sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
return (retval);
}
-#ifdef KTR
+#if defined(KTR) && (KTR_COMPILE & KTR_IGMPV3)
static const char *inm_modestrs[] = { "un", "in", "ex" };
@@ -2910,7 +2981,7 @@ inm_print(const struct in_multi *inm)
inm->inm_timer,
inm_state_str(inm->inm_state),
inm->inm_refcount,
- inm->inm_scq.ifq_len);
+ inm->inm_scq.mq_len);
printf("igi %p nsrc %lu sctimer %u scrv %u\n",
inm->inm_igi,
inm->inm_nsrc,
@@ -2927,7 +2998,7 @@ inm_print(const struct in_multi *inm)
printf("%s: --- end inm %p ---\n", __func__, inm);
}
-#else /* !KTR */
+#else /* !KTR || !(KTR_COMPILE & KTR_IGMPV3) */
void
inm_print(const struct in_multi *inm)
@@ -2935,6 +3006,6 @@ inm_print(const struct in_multi *inm)
}
-#endif /* KTR */
+#endif /* KTR && (KTR_COMPILE & KTR_IGMPV3) */
RB_GENERATE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);