summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2019-07-09 11:14:53 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2019-07-09 14:45:47 +0200
commit5283630d2c9d40deb0183696d278e02644fe4326 (patch)
treed5695aea058ed05d4d9a4b84b5cb9986aaefd23f
parentEPOCH(9): Add epoch_drain_callbacks() (diff)
downloadrtems-libbsd-5283630d2c9d40deb0183696d278e02644fe4326.tar.bz2
Update to FreeBSD stable/12 2019-07-09
Git mirror commit 3427c3416aa3c0f25124070959cca78024b94d85.
m---------freebsd-org0
-rw-r--r--freebsd/lib/libc/include/libc_private.h1
-rw-r--r--freebsd/lib/libcapsicum/capsicum_helpers.h29
-rw-r--r--freebsd/sbin/dhclient/options.c3
-rw-r--r--freebsd/sbin/dhclient/packet.c4
-rw-r--r--freebsd/sys/cam/scsi/scsi_all.c4
-rw-r--r--freebsd/sys/dev/pci/pci_user.c8
-rw-r--r--freebsd/sys/kern/kern_synch.c21
-rw-r--r--freebsd/sys/kern/kern_uuid.c2
-rw-r--r--freebsd/sys/kern/subr_sbuf.c15
-rw-r--r--freebsd/sys/kern/subr_sleepqueue.c41
-rw-r--r--freebsd/sys/kern/subr_taskqueue.c2
-rwxr-xr-xfreebsd/sys/kern/sys_pipe.c30
-rw-r--r--freebsd/sys/kern/tty.c6
-rw-r--r--freebsd/sys/kern/uipc_socket.c2
-rw-r--r--freebsd/sys/net/if.c9
-rw-r--r--freebsd/sys/netinet/in_mcast.c433
-rw-r--r--freebsd/sys/netinet/in_pcb.c32
-rw-r--r--freebsd/sys/netinet/in_var.h52
-rw-r--r--freebsd/sys/netinet/ip_carp.c100
-rw-r--r--freebsd/sys/netinet/ip_fw.h1
-rw-r--r--freebsd/sys/netinet/ip_mroute.c5
-rw-r--r--freebsd/sys/netinet/ip_var.h10
-rw-r--r--freebsd/sys/netinet6/in6_ifattach.c2
-rw-r--r--freebsd/sys/netinet6/in6_mcast.c441
-rw-r--r--freebsd/sys/netinet6/in6_pcb.c25
-rw-r--r--freebsd/sys/netinet6/in6_var.h52
-rw-r--r--freebsd/sys/netinet6/ip6_var.h11
-rw-r--r--freebsd/sys/netpfil/pf/if_pfsync.c45
-rw-r--r--freebsd/sys/powerpc/include/machine/spr.h1
-rw-r--r--freebsd/sys/sys/sbuf.h1
-rw-r--r--freebsd/sys/sys/sleepqueue.h1
-rw-r--r--freebsd/sys/sys/systm.h1
-rw-r--r--rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h11
34 files changed, 711 insertions, 690 deletions
diff --git a/freebsd-org b/freebsd-org
-Subproject aa83598855d14cdbf7aef6b05d0617e90f87ca2
+Subproject 3427c3416aa3c0f25124070959cca78024b94d8
diff --git a/freebsd/lib/libc/include/libc_private.h b/freebsd/lib/libc/include/libc_private.h
index 2231241f..fb3a4bb2 100644
--- a/freebsd/lib/libc/include/libc_private.h
+++ b/freebsd/lib/libc/include/libc_private.h
@@ -180,6 +180,7 @@ typedef enum {
PJT_MUTEX_CONSISTENT,
PJT_MUTEXATTR_GETROBUST,
PJT_MUTEXATTR_SETROBUST,
+ PJT_GETTHREADID_NP,
PJT_MAX
} pjt_index_t;
diff --git a/freebsd/lib/libcapsicum/capsicum_helpers.h b/freebsd/lib/libcapsicum/capsicum_helpers.h
index 60c894f3..d1c46e74 100644
--- a/freebsd/lib/libcapsicum/capsicum_helpers.h
+++ b/freebsd/lib/libcapsicum/capsicum_helpers.h
@@ -136,6 +136,35 @@ caph_enter(void)
return (0);
}
+static __inline int
+caph_rights_limit(int fd, const cap_rights_t *rights)
+{
+
+ if (cap_rights_limit(fd, rights) < 0 && errno != ENOSYS)
+ return (-1);
+
+ return (0);
+}
+
+static __inline int
+caph_ioctls_limit(int fd, const unsigned long *cmds, size_t ncmds)
+{
+
+ if (cap_ioctls_limit(fd, cmds, ncmds) < 0 && errno != ENOSYS)
+ return (-1);
+
+ return (0);
+}
+
+static __inline int
+caph_fcntls_limit(int fd, uint32_t fcntlrights)
+{
+
+ if (cap_fcntls_limit(fd, fcntlrights) < 0 && errno != ENOSYS)
+ return (-1);
+
+ return (0);
+}
static __inline int
caph_enter_casper(void)
diff --git a/freebsd/sbin/dhclient/options.c b/freebsd/sbin/dhclient/options.c
index b31ab32a..a38d6440 100644
--- a/freebsd/sbin/dhclient/options.c
+++ b/freebsd/sbin/dhclient/options.c
@@ -898,6 +898,5 @@ do_packet(struct interface_info *interface, struct dhcp_packet *packet,
/* Free the data associated with the options. */
for (i = 0; i < 256; i++)
- if (tp.options[i].len && tp.options[i].data)
- free(tp.options[i].data);
+ free(tp.options[i].data);
}
diff --git a/freebsd/sbin/dhclient/packet.c b/freebsd/sbin/dhclient/packet.c
index b3c719b2..51f7e1ab 100644
--- a/freebsd/sbin/dhclient/packet.c
+++ b/freebsd/sbin/dhclient/packet.c
@@ -185,7 +185,7 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
ip_packets_seen++;
if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
ip_packets_bad_checksum++;
- if (ip_packets_seen > 4 &&
+ if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 &&
(ip_packets_seen / ip_packets_bad_checksum) < 2) {
note("%d bad IP checksums seen in %d packets",
ip_packets_bad_checksum, ip_packets_seen);
@@ -237,7 +237,7 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
udp_packets_seen++;
if (usum && usum != sum) {
udp_packets_bad_checksum++;
- if (udp_packets_seen > 4 &&
+ if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 &&
(udp_packets_seen / udp_packets_bad_checksum) < 2) {
note("%d bad udp checksums in %d packets",
udp_packets_bad_checksum, udp_packets_seen);
diff --git a/freebsd/sys/cam/scsi/scsi_all.c b/freebsd/sys/cam/scsi/scsi_all.c
index d0eb431a..36757230 100644
--- a/freebsd/sys/cam/scsi/scsi_all.c
+++ b/freebsd/sys/cam/scsi/scsi_all.c
@@ -5584,6 +5584,7 @@ scsi_devid_is_naa_ieee_reg(uint8_t *bufp)
{
struct scsi_vpd_id_descriptor *descr;
struct scsi_vpd_id_naa_basic *naa;
+ int n;
descr = (struct scsi_vpd_id_descriptor *)bufp;
naa = (struct scsi_vpd_id_naa_basic *)descr->identifier;
@@ -5591,7 +5592,8 @@ scsi_devid_is_naa_ieee_reg(uint8_t *bufp)
return 0;
if (descr->length < sizeof(struct scsi_vpd_id_naa_ieee_reg))
return 0;
- if ((naa->naa >> SVPD_ID_NAA_NAA_SHIFT) != SVPD_ID_NAA_IEEE_REG)
+ n = naa->naa >> SVPD_ID_NAA_NAA_SHIFT;
+ if (n != SVPD_ID_NAA_LOCAL_REG && n != SVPD_ID_NAA_IEEE_REG)
return 0;
return 1;
}
diff --git a/freebsd/sys/dev/pci/pci_user.c b/freebsd/sys/dev/pci/pci_user.c
index 3e2c3c7e..bdf8a935 100644
--- a/freebsd/sys/dev/pci/pci_user.c
+++ b/freebsd/sys/dev/pci/pci_user.c
@@ -858,6 +858,7 @@ pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm)
struct thread *td;
struct sglist *sg;
struct pci_map *pm;
+ vm_paddr_t membase;
vm_paddr_t pbase;
vm_size_t plen;
vm_offset_t addr;
@@ -880,8 +881,9 @@ pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm)
return (EBUSY); /* XXXKIB enable if _ACTIVATE */
if (!PCI_BAR_MEM(pm->pm_value))
return (EIO);
- pbase = trunc_page(pm->pm_value);
- plen = round_page(pm->pm_value + ((pci_addr_t)1 << pm->pm_size)) -
+ membase = pm->pm_value & PCIM_BAR_MEM_BASE;
+ pbase = trunc_page(membase);
+ plen = round_page(membase + ((pci_addr_t)1 << pm->pm_size)) -
pbase;
prot = VM_PROT_READ | (((pbm->pbm_flags & PCIIO_BAR_MMAP_RW) != 0) ?
VM_PROT_WRITE : 0);
@@ -913,7 +915,7 @@ pci_bar_mmap(device_t pcidev, struct pci_bar_mmap *pbm)
}
pbm->pbm_map_base = (void *)addr;
pbm->pbm_map_length = plen;
- pbm->pbm_bar_off = pm->pm_value - pbase;
+ pbm->pbm_bar_off = membase - pbase;
pbm->pbm_bar_length = (pci_addr_t)1 << pm->pm_size;
out:
diff --git a/freebsd/sys/kern/kern_synch.c b/freebsd/sys/kern/kern_synch.c
index 9c0d1206..0335b269 100644
--- a/freebsd/sys/kern/kern_synch.c
+++ b/freebsd/sys/kern/kern_synch.c
@@ -138,7 +138,9 @@ int
_sleep(void *ident, struct lock_object *lock, int priority,
const char *wmesg, sbintime_t sbt, sbintime_t pr, int flags)
{
+#ifndef __rtems__
struct thread *td;
+#endif /* __rtems__ */
struct lock_class *class;
uintptr_t lock_state;
#ifndef __rtems__
@@ -148,7 +150,9 @@ _sleep(void *ident, struct lock_object *lock, int priority,
#endif /* __rtems__ */
WITNESS_SAVE_DECL(lock_witness);
+#ifndef __rtems__
td = curthread;
+#endif /* __rtems__ */
#ifdef KTRACE
if (KTRPOINT(td, KTR_CSW))
ktrcsw(1, 0, wmesg);
@@ -402,6 +406,23 @@ wakeup_one(void *ident)
kick_proc0();
}
+void
+wakeup_any(void *ident)
+#ifndef __rtems__
+{
+ int wakeup_swapper;
+
+ sleepq_lock(ident);
+ wakeup_swapper = sleepq_signal(ident, SLEEPQ_SLEEP | SLEEPQ_UNFAIR,
+ 0, 0);
+ sleepq_release(ident);
+ if (wakeup_swapper)
+ kick_proc0();
+}
+#else /* __rtems__ */
+RTEMS_ALIAS(_bsd_wakeup_one);
+#endif /* __rtems__ */
+
#ifndef __rtems__
static void
kdb_switch(void)
diff --git a/freebsd/sys/kern/kern_uuid.c b/freebsd/sys/kern/kern_uuid.c
index a2316b16..c2a5986a 100644
--- a/freebsd/sys/kern/kern_uuid.c
+++ b/freebsd/sys/kern/kern_uuid.c
@@ -301,7 +301,7 @@ sbuf_printf_uuid(struct sbuf *sb, struct uuid *uuid)
char buf[38];
snprintf_uuid(buf, sizeof(buf), uuid);
- return (sbuf_printf(sb, "%s", buf));
+ return (sbuf_cat(sb, buf));
}
/*
diff --git a/freebsd/sys/kern/subr_sbuf.c b/freebsd/sys/kern/subr_sbuf.c
index b51ed52c..c9696ab7 100644
--- a/freebsd/sys/kern/subr_sbuf.c
+++ b/freebsd/sys/kern/subr_sbuf.c
@@ -344,6 +344,21 @@ sbuf_setpos(struct sbuf *s, ssize_t pos)
}
/*
+ * Drain into a counter. Counts amount of data without producing output.
+ * Useful for cases like sysctl, where user may first request only size.
+ * This allows to avoid pointless allocation/freeing of large buffers.
+ */
+int
+sbuf_count_drain(void *arg, const char *data __unused, int len)
+{
+ size_t *sizep;
+
+ sizep = (size_t *)arg;
+ *sizep += len;
+ return (len);
+}
+
+/*
* Set up a drain function and argument on an sbuf to flush data to
* when the sbuf buffer overflows.
*/
diff --git a/freebsd/sys/kern/subr_sleepqueue.c b/freebsd/sys/kern/subr_sleepqueue.c
index 042c0b0d..537206fd 100644
--- a/freebsd/sys/kern/subr_sleepqueue.c
+++ b/freebsd/sys/kern/subr_sleepqueue.c
@@ -132,7 +132,7 @@ CTASSERT(powerof2(SC_TABLESIZE));
* c - sleep queue chain lock
*/
struct sleepqueue {
- TAILQ_HEAD(, thread) sq_blocked[NR_SLEEPQS]; /* (c) Blocked threads. */
+ struct threadqueue sq_blocked[NR_SLEEPQS]; /* (c) Blocked threads. */
u_int sq_blockedcnt[NR_SLEEPQS]; /* (c) N. of blocked threads. */
LIST_ENTRY(sleepqueue) sq_hash; /* (c) Chain and free list. */
LIST_HEAD(, sleepqueue) sq_free; /* (c) Free queues. */
@@ -1139,13 +1139,15 @@ sleepq_init(void *mem, int size, int flags)
}
/*
- * Find the highest priority thread sleeping on a wait channel and resume it.
+ * Find thread sleeping on a wait channel and resume it.
*/
int
sleepq_signal(void *wchan, int flags, int pri, int queue)
{
+ struct sleepqueue_chain *sc;
struct sleepqueue *sq;
#ifndef __rtems__
+ struct threadqueue *head;
struct thread *td, *besttd;
#else /* __rtems__ */
struct thread *besttd;
@@ -1162,16 +1164,33 @@ sleepq_signal(void *wchan, int flags, int pri, int queue)
("%s: mismatch between sleep/wakeup and cv_*", __func__));
#ifndef __rtems__
- /*
- * Find the highest priority thread on the queue. If there is a
- * tie, use the thread that first appears in the queue as it has
- * been sleeping the longest since threads are always added to
- * the tail of sleep queues.
- */
- besttd = TAILQ_FIRST(&sq->sq_blocked[queue]);
- TAILQ_FOREACH(td, &sq->sq_blocked[queue], td_slpq) {
- if (td->td_priority < besttd->td_priority)
+ head = &sq->sq_blocked[queue];
+ if (flags & SLEEPQ_UNFAIR) {
+ /*
+ * Find the most recently sleeping thread, but try to
+ * skip threads still in process of context switch to
+ * avoid spinning on the thread lock.
+ */
+ sc = SC_LOOKUP(wchan);
+ besttd = TAILQ_LAST_FAST(head, thread, td_slpq);
+ while (besttd->td_lock != &sc->sc_lock) {
+ td = TAILQ_PREV_FAST(besttd, head, thread, td_slpq);
+ if (td == NULL)
+ break;
besttd = td;
+ }
+ } else {
+ /*
+ * Find the highest priority thread on the queue. If there
+ * is a tie, use the thread that first appears in the queue
+ * as it has been sleeping the longest since threads are
+ * always added to the tail of sleep queues.
+ */
+ besttd = td = TAILQ_FIRST(head);
+ while ((td = TAILQ_NEXT(td, td_slpq)) != NULL) {
+ if (td->td_priority < besttd->td_priority)
+ besttd = td;
+ }
}
#else /* __rtems__ */
besttd = TAILQ_FIRST(&sq->sq_blocked[queue]);
diff --git a/freebsd/sys/kern/subr_taskqueue.c b/freebsd/sys/kern/subr_taskqueue.c
index 39d9f939..67e62fc8 100644
--- a/freebsd/sys/kern/subr_taskqueue.c
+++ b/freebsd/sys/kern/subr_taskqueue.c
@@ -841,7 +841,7 @@ taskqueue_thread_enqueue(void *context)
tqp = context;
tq = *tqp;
- wakeup_one(tq);
+ wakeup_any(tq);
}
TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, NULL,
diff --git a/freebsd/sys/kern/sys_pipe.c b/freebsd/sys/kern/sys_pipe.c
index 817190b7..d714af89 100755
--- a/freebsd/sys/kern/sys_pipe.c
+++ b/freebsd/sys/kern/sys_pipe.c
@@ -817,7 +817,7 @@ pipe_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
rpipe->pipe_map.pos += size;
rpipe->pipe_map.cnt -= size;
if (rpipe->pipe_map.cnt == 0) {
- rpipe->pipe_state &= ~(PIPE_DIRECTW|PIPE_WANTW);
+ rpipe->pipe_state &= ~PIPE_WANTW;
wakeup(rpipe);
}
#endif
@@ -1020,13 +1020,17 @@ pipe_build_write_buffer(struct pipe *wpipe, struct uio *uio)
}
/*
- * unmap and unwire the process buffer
+ * Unwire the process buffer.
*/
static void
pipe_destroy_write_buffer(struct pipe *wpipe)
{
PIPE_LOCK_ASSERT(wpipe, MA_OWNED);
+ KASSERT((wpipe->pipe_state & PIPE_DIRECTW) != 0,
+ ("%s: PIPE_DIRECTW not set on %p", __func__, wpipe));
+
+ wpipe->pipe_state &= ~PIPE_DIRECTW;
vm_page_unhold_pages(wpipe->pipe_map.ms, wpipe->pipe_map.npages);
wpipe->pipe_map.npages = 0;
}
@@ -1045,13 +1049,15 @@ pipe_clone_write_buffer(struct pipe *wpipe)
int pos;
PIPE_LOCK_ASSERT(wpipe, MA_OWNED);
+ KASSERT((wpipe->pipe_state & PIPE_DIRECTW) != 0,
+ ("%s: PIPE_DIRECTW not set on %p", __func__, wpipe));
+
size = wpipe->pipe_map.cnt;
pos = wpipe->pipe_map.pos;
wpipe->pipe_buffer.in = size;
wpipe->pipe_buffer.out = 0;
wpipe->pipe_buffer.cnt = size;
- wpipe->pipe_state &= ~PIPE_DIRECTW;
PIPE_UNLOCK(wpipe);
iov.iov_base = wpipe->pipe_buffer.buffer;
@@ -1090,7 +1096,7 @@ retry:
pipeunlock(wpipe);
goto error1;
}
- while (wpipe->pipe_state & PIPE_DIRECTW) {
+ if (wpipe->pipe_state & PIPE_DIRECTW) {
if (wpipe->pipe_state & PIPE_WANTR) {
wpipe->pipe_state &= ~PIPE_WANTR;
wakeup(wpipe);
@@ -1133,8 +1139,7 @@ retry:
goto error1;
}
- error = 0;
- while (!error && (wpipe->pipe_state & PIPE_DIRECTW)) {
+ while (wpipe->pipe_map.cnt != 0) {
if (wpipe->pipe_state & PIPE_EOF) {
pipe_destroy_write_buffer(wpipe);
pipeselwakeup(wpipe);
@@ -1152,20 +1157,19 @@ retry:
error = msleep(wpipe, PIPE_MTX(wpipe), PRIBIO | PCATCH,
"pipdwt", 0);
pipelock(wpipe, 0);
+ if (error != 0)
+ break;
}
if (wpipe->pipe_state & PIPE_EOF)
error = EPIPE;
- if (wpipe->pipe_state & PIPE_DIRECTW) {
- /*
- * this bit of trickery substitutes a kernel buffer for
- * the process that might be going away.
- */
+ if (error == EINTR || error == ERESTART)
pipe_clone_write_buffer(wpipe);
- } else {
+ else
pipe_destroy_write_buffer(wpipe);
- }
pipeunlock(wpipe);
+ KASSERT((wpipe->pipe_state & PIPE_DIRECTW) == 0,
+ ("pipe %p leaked PIPE_DIRECTW", wpipe));
return (error);
error1:
diff --git a/freebsd/sys/kern/tty.c b/freebsd/sys/kern/tty.c
index f4a2b01f..cce61944 100644
--- a/freebsd/sys/kern/tty.c
+++ b/freebsd/sys/kern/tty.c
@@ -233,9 +233,6 @@ ttydev_leave(struct tty *tp)
tp->t_flags |= TF_OPENCLOSE;
- /* Stop asynchronous I/O. */
- funsetown(&tp->t_sigio);
-
#ifndef __rtems__
/* Remove console TTY. */
if (constty == tp)
@@ -1138,6 +1135,9 @@ tty_rel_free(struct tty *tp)
return;
}
+ /* Stop asynchronous I/O. */
+ funsetown(&tp->t_sigio);
+
/* TTY can be deallocated. */
dev = tp->t_dev;
tp->t_dev = NULL;
diff --git a/freebsd/sys/kern/uipc_socket.c b/freebsd/sys/kern/uipc_socket.c
index 77356b83..762dfb56 100644
--- a/freebsd/sys/kern/uipc_socket.c
+++ b/freebsd/sys/kern/uipc_socket.c
@@ -2227,7 +2227,7 @@ soreceive_stream(struct socket *so, struct sockaddr **psa, struct uio *uio,
/* Prevent other readers from entering the socket. */
error = sblock(sb, SBLOCKWAIT(flags));
if (error)
- goto out;
+ return (error);
SOCKBUF_LOCK(sb);
/* Easy one, no space to copyout anything. */
diff --git a/freebsd/sys/net/if.c b/freebsd/sys/net/if.c
index 5f0329fd..a9c28263 100644
--- a/freebsd/sys/net/if.c
+++ b/freebsd/sys/net/if.c
@@ -1109,6 +1109,15 @@ if_detach_internal(struct ifnet *ifp, int vmove, struct if_clone **ifcp)
curvnet->vnet_ifcnt--;
#endif
epoch_wait_preempt(net_epoch_preempt);
+
+ /*
+ * Ensure all pending EPOCH(9) callbacks have been executed. This
+ * fixes issues about late destruction of multicast options
+ * which lead to leave group calls, which in turn access the
+ * belonging ifnet structure:
+ */
+ epoch_drain_callbacks(net_epoch_preempt);
+
/*
* In any case (destroy or vmove) detach us from the groups
* and remove/wait for pending events on the taskq.
diff --git a/freebsd/sys/netinet/in_mcast.c b/freebsd/sys/netinet/in_mcast.c
index b989944c..ff442399 100644
--- a/freebsd/sys/netinet/in_mcast.c
+++ b/freebsd/sys/netinet/in_mcast.c
@@ -96,7 +96,9 @@ static MALLOC_DEFINE(M_IPMSOURCE, "ip_msource",
/*
* Locking:
- * - Lock order is: Giant, INP_WLOCK, IN_MULTI_LIST_LOCK, IGMP_LOCK, IF_ADDR_LOCK.
+ *
+ * - Lock order is: Giant, IN_MULTI_LOCK, INP_WLOCK,
+ * IN_MULTI_LIST_LOCK, IGMP_LOCK, IF_ADDR_LOCK.
* - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however
* it can be taken by code in net/if.c also.
* - ip_moptions and in_mfilter are covered by the INP_WLOCK.
@@ -146,12 +148,11 @@ static int imf_prune(struct in_mfilter *, const struct sockaddr_in *);
static void imf_purge(struct in_mfilter *);
static void imf_rollback(struct in_mfilter *);
static void imf_reap(struct in_mfilter *);
-static int imo_grow(struct ip_moptions *);
-static size_t imo_match_group(const struct ip_moptions *,
+static struct in_mfilter *
+ imo_match_group(const struct ip_moptions *,
const struct ifnet *, const struct sockaddr *);
static struct in_msource *
- imo_match_source(const struct ip_moptions *, const size_t,
- const struct sockaddr *);
+ imo_match_source(struct in_mfilter *, const struct sockaddr *);
static void ims_merge(struct ip_msource *ims,
const struct in_msource *lims, const int rollback);
static int in_getmulti(struct ifnet *, const struct in_addr *,
@@ -335,6 +336,26 @@ imf_init(struct in_mfilter *imf, const int st0, const int st1)
imf->imf_st[1] = st1;
}
+struct in_mfilter *
+ip_mfilter_alloc(const int mflags, const int st0, const int st1)
+{
+ struct in_mfilter *imf;
+
+ imf = malloc(sizeof(*imf), M_INMFILTER, mflags);
+ if (imf != NULL)
+ imf_init(imf, st0, st1);
+
+ return (imf);
+}
+
+void
+ip_mfilter_free(struct in_mfilter *imf)
+{
+
+ imf_purge(imf);
+ free(imf, M_INMFILTER);
+}
+
/*
* 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.
@@ -380,89 +401,30 @@ inm_lookup(struct ifnet *ifp, const struct in_addr ina)
}
/*
- * Resize the ip_moptions vector to the next power-of-two minus 1.
- * May be called with locks held; do not sleep.
- */
-static int
-imo_grow(struct ip_moptions *imo)
-{
- struct in_multi **nmships;
- struct in_multi **omships;
- struct in_mfilter *nmfilters;
- struct in_mfilter *omfilters;
- size_t idx;
- size_t newmax;
- size_t oldmax;
-
- nmships = NULL;
- nmfilters = NULL;
- omships = imo->imo_membership;
- omfilters = imo->imo_mfilters;
- oldmax = imo->imo_max_memberships;
- newmax = ((oldmax + 1) * 2) - 1;
-
- if (newmax <= IP_MAX_MEMBERSHIPS) {
- nmships = (struct in_multi **)realloc(omships,
- sizeof(struct in_multi *) * newmax, M_IPMOPTS, M_NOWAIT);
- nmfilters = (struct in_mfilter *)realloc(omfilters,
- sizeof(struct in_mfilter) * newmax, M_INMFILTER, M_NOWAIT);
- if (nmships != NULL && nmfilters != NULL) {
- /* Initialize newly allocated source filter heads. */
- for (idx = oldmax; idx < newmax; idx++) {
- imf_init(&nmfilters[idx], MCAST_UNDEFINED,
- MCAST_EXCLUDE);
- }
- imo->imo_max_memberships = newmax;
- imo->imo_membership = nmships;
- imo->imo_mfilters = nmfilters;
- }
- }
-
- if (nmships == NULL || nmfilters == NULL) {
- if (nmships != NULL)
- free(nmships, M_IPMOPTS);
- if (nmfilters != NULL)
- free(nmfilters, M_INMFILTER);
- return (ETOOMANYREFS);
- }
-
- return (0);
-}
-
-/*
* Find an IPv4 multicast group entry for this ip_moptions instance
* which matches the specified group, and optionally an interface.
* Return its index into the array, or -1 if not found.
*/
-static size_t
+static struct in_mfilter *
imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp,
const struct sockaddr *group)
{
const struct sockaddr_in *gsin;
- struct in_multi **pinm;
- int idx;
- int nmships;
+ struct in_mfilter *imf;
+ struct in_multi *inm;
gsin = (const struct sockaddr_in *)group;
- /* The imo_membership array may be lazy allocated. */
- if (imo->imo_membership == NULL || imo->imo_num_memberships == 0)
- return (-1);
-
- nmships = imo->imo_num_memberships;
- pinm = &imo->imo_membership[0];
- for (idx = 0; idx < nmships; idx++, pinm++) {
- if (*pinm == NULL)
+ IP_MFILTER_FOREACH(imf, &imo->imo_head) {
+ inm = imf->imf_inm;
+ if (inm == NULL)
continue;
- if ((ifp == NULL || ((*pinm)->inm_ifp == ifp)) &&
- in_hosteq((*pinm)->inm_addr, gsin->sin_addr)) {
+ if ((ifp == NULL || (inm->inm_ifp == ifp)) &&
+ in_hosteq(inm->inm_addr, gsin->sin_addr)) {
break;
}
}
- if (idx >= nmships)
- idx = -1;
-
- return (idx);
+ return (imf);
}
/*
@@ -473,22 +435,13 @@ imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp,
* it exists, which may not be the desired behaviour.
*/
static struct in_msource *
-imo_match_source(const struct ip_moptions *imo, const size_t gidx,
- const struct sockaddr *src)
+imo_match_source(struct in_mfilter *imf, const struct sockaddr *src)
{
struct ip_msource find;
- struct in_mfilter *imf;
struct ip_msource *ims;
const sockunion_t *psa;
KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__));
- KASSERT(gidx != -1 && gidx < imo->imo_num_memberships,
- ("%s: invalid index %d\n", __func__, (int)gidx));
-
- /* The imo_mfilters array may be lazy allocated. */
- if (imo->imo_mfilters == NULL)
- return (NULL);
- imf = &imo->imo_mfilters[gidx];
/* Source trees are keyed in host byte order. */
psa = (const sockunion_t *)src;
@@ -508,14 +461,14 @@ int
imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp,
const struct sockaddr *group, const struct sockaddr *src)
{
- size_t gidx;
+ struct in_mfilter *imf;
struct in_msource *ims;
int mode;
KASSERT(ifp != NULL, ("%s: null ifp", __func__));
- gidx = imo_match_group(imo, ifp, group);
- if (gidx == -1)
+ imf = imo_match_group(imo, ifp, group);
+ if (imf == NULL)
return (MCAST_NOTGMEMBER);
/*
@@ -527,8 +480,8 @@ imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp,
* NOTE: We are comparing group state here at IGMP t1 (now)
* with socket-layer t0 (since last downcall).
*/
- mode = imo->imo_mfilters[gidx].imf_st[1];
- ims = imo_match_source(imo, gidx, src);
+ mode = imf->imf_st[1];
+ ims = imo_match_source(imf, src);
if ((ims == NULL && mode == MCAST_INCLUDE) ||
(ims != NULL && ims->imsl_st[0] != mode))
@@ -1453,7 +1406,6 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
struct ip_moptions *imo;
struct in_msource *ims;
struct in_multi *inm;
- size_t idx;
uint16_t fmode;
int error, doblock;
@@ -1532,20 +1484,18 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
return (EINVAL);
+ IN_MULTI_LOCK();
+
/*
* Check if we are actually a member of this group.
*/
imo = inp_findmoptions(inp);
- idx = imo_match_group(imo, ifp, &gsa->sa);
- if (idx == -1 || imo->imo_mfilters == NULL) {
+ imf = imo_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
error = EADDRNOTAVAIL;
goto out_inp_locked;
}
-
- KASSERT(imo->imo_mfilters != NULL,
- ("%s: imo_mfilters not allocated", __func__));
- imf = &imo->imo_mfilters[idx];
- inm = imo->imo_membership[idx];
+ inm = imf->imf_inm;
/*
* Attempting to use the delta-based API on an
@@ -1563,7 +1513,7 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
* Asked to unblock, but nothing to unblock.
* If adding a new block entry, allocate it.
*/
- ims = imo_match_source(imo, idx, &ssa->sa);
+ ims = imo_match_source(imf, &ssa->sa);
if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
CTR3(KTR_IGMPV3, "%s: source 0x%08x %spresent", __func__,
ntohl(ssa->sin.sin_addr.s_addr), doblock ? "" : "not ");
@@ -1594,14 +1544,13 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
/*
* Begin state merge transaction at IGMP layer.
*/
- IN_MULTI_LOCK();
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
IN_MULTI_LIST_LOCK();
error = inm_merge(inm, imf);
if (error) {
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
IN_MULTI_LIST_UNLOCK();
- goto out_in_multi_locked;
+ goto out_imf_rollback;
}
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
@@ -1610,9 +1559,6 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
if (error)
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
-out_in_multi_locked:
-
- IN_MULTI_UNLOCK();
out_imf_rollback:
if (error)
imf_rollback(imf);
@@ -1623,6 +1569,7 @@ out_imf_rollback:
out_inp_locked:
INP_WUNLOCK(inp);
+ IN_MULTI_UNLOCK();
return (error);
}
@@ -1637,9 +1584,6 @@ static struct ip_moptions *
inp_findmoptions(struct inpcb *inp)
{
struct ip_moptions *imo;
- struct in_multi **immp;
- struct in_mfilter *imfp;
- size_t idx;
INP_WLOCK(inp);
if (inp->inp_moptions != NULL)
@@ -1648,29 +1592,16 @@ inp_findmoptions(struct inpcb *inp)
INP_WUNLOCK(inp);
imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK);
- immp = malloc(sizeof(*immp) * IP_MIN_MEMBERSHIPS, M_IPMOPTS,
- M_WAITOK | M_ZERO);
- imfp = malloc(sizeof(struct in_mfilter) * IP_MIN_MEMBERSHIPS,
- M_INMFILTER, M_WAITOK);
imo->imo_multicast_ifp = NULL;
imo->imo_multicast_addr.s_addr = INADDR_ANY;
imo->imo_multicast_vif = -1;
imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
imo->imo_multicast_loop = in_mcast_loop;
- imo->imo_num_memberships = 0;
- imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
- imo->imo_membership = immp;
-
- /* Initialize per-group source filters. */
- for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++)
- imf_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
- imo->imo_mfilters = imfp;
+ STAILQ_INIT(&imo->imo_head);
INP_WLOCK(inp);
if (inp->inp_moptions != NULL) {
- free(imfp, M_INMFILTER);
- free(immp, M_IPMOPTS);
free(imo, M_IPMOPTS);
return (inp->inp_moptions);
}
@@ -1681,32 +1612,25 @@ inp_findmoptions(struct inpcb *inp)
static void
inp_gcmoptions(struct ip_moptions *imo)
{
- struct in_mfilter *imf;
+ struct in_mfilter *imf;
struct in_multi *inm;
struct ifnet *ifp;
- size_t idx, nmships;
-
- nmships = imo->imo_num_memberships;
- for (idx = 0; idx < nmships; ++idx) {
- imf = imo->imo_mfilters ? &imo->imo_mfilters[idx] : NULL;
- if (imf)
- imf_leave(imf);
- inm = imo->imo_membership[idx];
- ifp = inm->inm_ifp;
- if (ifp != NULL) {
- CURVNET_SET(ifp->if_vnet);
- (void)in_leavegroup(inm, imf);
- CURVNET_RESTORE();
- } else {
- (void)in_leavegroup(inm, imf);
+
+ while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
+ ip_mfilter_remove(&imo->imo_head, imf);
+
+ imf_leave(imf);
+ if ((inm = imf->imf_inm) != NULL) {
+ if ((ifp = inm->inm_ifp) != NULL) {
+ CURVNET_SET(ifp->if_vnet);
+ (void)in_leavegroup(inm, imf);
+ CURVNET_RESTORE();
+ } else {
+ (void)in_leavegroup(inm, imf);
+ }
}
- if (imf)
- imf_purge(imf);
+ ip_mfilter_free(imf);
}
-
- if (imo->imo_mfilters)
- free(imo->imo_mfilters, M_INMFILTER);
- free(imo->imo_membership, M_IPMOPTS);
free(imo, M_IPMOPTS);
}
@@ -1742,7 +1666,7 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
struct sockaddr_storage *ptss;
struct sockaddr_storage *tss;
int error;
- size_t idx, nsrcs, ncsrcs;
+ size_t nsrcs, ncsrcs;
INP_WLOCK_ASSERT(inp);
@@ -1769,12 +1693,11 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
* Lookup group on the socket.
*/
gsa = (sockunion_t *)&msfr.msfr_group;
- idx = imo_match_group(imo, ifp, &gsa->sa);
- if (idx == -1 || imo->imo_mfilters == NULL) {
+ imf = imo_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
INP_WUNLOCK(inp);
return (EADDRNOTAVAIL);
}
- imf = &imo->imo_mfilters[idx];
/*
* Ignore memberships which are in limbo.
@@ -2032,14 +1955,11 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
struct ip_moptions *imo;
struct in_multi *inm;
struct in_msource *lims;
- size_t idx;
int error, is_new;
ifp = NULL;
- imf = NULL;
lims = NULL;
error = 0;
- is_new = 0;
memset(&gsr, 0, sizeof(struct group_source_req));
gsa = (sockunion_t *)&gsr.gsr_group;
@@ -2147,13 +2067,25 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
return (EADDRNOTAVAIL);
+ IN_MULTI_LOCK();
+
+ /*
+ * Find the membership in the membership list.
+ */
imo = inp_findmoptions(inp);
- idx = imo_match_group(imo, ifp, &gsa->sa);
- if (idx == -1) {
+ imf = imo_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
is_new = 1;
+ inm = NULL;
+
+ if (ip_mfilter_count(&imo->imo_head) >= IP_MAX_MEMBERSHIPS) {
+ error = ENOMEM;
+ goto out_inp_locked;
+ }
} else {
- inm = imo->imo_membership[idx];
- imf = &imo->imo_mfilters[idx];
+ is_new = 0;
+ inm = imf->imf_inm;
+
if (ssa->ss.ss_family != AF_UNSPEC) {
/*
* MCAST_JOIN_SOURCE_GROUP on an exclusive membership
@@ -2180,7 +2112,7 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
* full-state SSM API with the delta-based API,
* which is discouraged in the relevant RFCs.
*/
- lims = imo_match_source(imo, idx, &ssa->sa);
+ lims = imo_match_source(imf, &ssa->sa);
if (lims != NULL /*&&
lims->imsl_st[1] == MCAST_INCLUDE*/) {
error = EADDRNOTAVAIL;
@@ -2213,27 +2145,6 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
*/
INP_WLOCK_ASSERT(inp);
- if (is_new) {
- if (imo->imo_num_memberships == imo->imo_max_memberships) {
- error = imo_grow(imo);
- if (error)
- goto out_inp_locked;
- }
- /*
- * Allocate the new slot upfront so we can deal with
- * grafting the new source filter in same code path
- * as for join-source on existing membership.
- */
- idx = imo->imo_num_memberships;
- imo->imo_membership[idx] = NULL;
- imo->imo_num_memberships++;
- KASSERT(imo->imo_mfilters != NULL,
- ("%s: imf_mfilters vector was not allocated", __func__));
- imf = &imo->imo_mfilters[idx];
- KASSERT(RB_EMPTY(&imf->imf_sources),
- ("%s: imf_sources not empty", __func__));
- }
-
/*
* Graft new source into filter list for this inpcb's
* membership of the group. The in_multi may not have
@@ -2249,7 +2160,11 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
/* Membership starts in IN mode */
if (is_new) {
CTR1(KTR_IGMPV3, "%s: new join w/source", __func__);
- imf_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE);
+ imf = ip_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_INCLUDE);
+ if (imf == NULL) {
+ error = ENOMEM;
+ goto out_inp_locked;
+ }
} else {
CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow");
}
@@ -2258,34 +2173,41 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
CTR1(KTR_IGMPV3, "%s: merge imf state failed",
__func__);
error = ENOMEM;
- goto out_imo_free;
+ goto out_inp_locked;
}
} else {
/* No address specified; Membership starts in EX mode */
if (is_new) {
CTR1(KTR_IGMPV3, "%s: new join w/o source", __func__);
- imf_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE);
+ imf = ip_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_EXCLUDE);
+ if (imf == NULL) {
+ error = ENOMEM;
+ goto out_inp_locked;
+ }
}
}
/*
* Begin state merge transaction at IGMP layer.
*/
- in_pcbref(inp);
- INP_WUNLOCK(inp);
- IN_MULTI_LOCK();
-
if (is_new) {
+ in_pcbref(inp);
+ INP_WUNLOCK(inp);
+
error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf,
- &inm);
+ &imf->imf_inm);
+
+ INP_WLOCK(inp);
+ if (in_pcbrele_wlocked(inp)) {
+ error = ENXIO;
+ goto out_inp_unlocked;
+ }
if (error) {
CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed",
__func__);
- IN_MULTI_LIST_UNLOCK();
- goto out_imo_free;
+ goto out_inp_locked;
}
- inm_acquire(inm);
- imo->imo_membership[idx] = inm;
+ inm_acquire(imf->imf_inm);
} else {
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
IN_MULTI_LIST_LOCK();
@@ -2294,7 +2216,9 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
__func__);
IN_MULTI_LIST_UNLOCK();
- goto out_in_multi_locked;
+ imf_rollback(imf);
+ imf_reap(imf);
+ goto out_inp_locked;
}
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
error = igmp_change_state(inm);
@@ -2302,40 +2226,30 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
if (error) {
CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
__func__);
- goto out_in_multi_locked;
+ imf_rollback(imf);
+ imf_reap(imf);
+ goto out_inp_locked;
}
}
+ if (is_new)
+ ip_mfilter_insert(&imo->imo_head, imf);
-out_in_multi_locked:
+ imf_commit(imf);
+ imf = NULL;
+out_inp_locked:
+ INP_WUNLOCK(inp);
+out_inp_unlocked:
IN_MULTI_UNLOCK();
- INP_WLOCK(inp);
- if (in_pcbrele_wlocked(inp))
- return (ENXIO);
- if (error) {
- imf_rollback(imf);
- if (is_new)
- imf_purge(imf);
- else
- imf_reap(imf);
- } else {
- imf_commit(imf);
- }
-out_imo_free:
- if (error && is_new) {
- inm = imo->imo_membership[idx];
- if (inm != NULL) {
+ if (is_new && imf) {
+ if (imf->imf_inm != NULL) {
IN_MULTI_LIST_LOCK();
- inm_release_deferred(inm);
+ inm_release_deferred(imf->imf_inm);
IN_MULTI_LIST_UNLOCK();
}
- imo->imo_membership[idx] = NULL;
- --imo->imo_num_memberships;
+ ip_mfilter_free(imf);
}
-
-out_inp_locked:
- INP_WUNLOCK(inp);
return (error);
}
@@ -2354,12 +2268,12 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
struct ip_moptions *imo;
struct in_msource *ims;
struct in_multi *inm;
- size_t idx;
- int error, is_final;
+ int error;
+ bool is_final;
ifp = NULL;
error = 0;
- is_final = 1;
+ is_final = true;
memset(&gsr, 0, sizeof(struct group_source_req));
gsa = (sockunion_t *)&gsr.gsr_group;
@@ -2459,20 +2373,21 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
return (EINVAL);
+ IN_MULTI_LOCK();
+
/*
- * Find the membership in the membership array.
+ * Find the membership in the membership list.
*/
imo = inp_findmoptions(inp);
- idx = imo_match_group(imo, ifp, &gsa->sa);
- if (idx == -1) {
+ imf = imo_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
error = EADDRNOTAVAIL;
goto out_inp_locked;
}
- inm = imo->imo_membership[idx];
- imf = &imo->imo_mfilters[idx];
+ inm = imf->imf_inm;
if (ssa->ss.ss_family != AF_UNSPEC)
- is_final = 0;
+ is_final = false;
/*
* Begin state merge transaction at socket layer.
@@ -2484,13 +2399,14 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
* MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
*/
if (is_final) {
+ ip_mfilter_remove(&imo->imo_head, imf);
imf_leave(imf);
} else {
if (imf->imf_st[0] == MCAST_EXCLUDE) {
error = EADDRNOTAVAIL;
goto out_inp_locked;
}
- ims = imo_match_source(imo, idx, &ssa->sa);
+ ims = imo_match_source(imf, &ssa->sa);
if (ims == NULL) {
CTR3(KTR_IGMPV3, "%s: source 0x%08x %spresent",
__func__, ntohl(ssa->sin.sin_addr.s_addr), "not ");
@@ -2509,17 +2425,7 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
/*
* Begin state merge transaction at IGMP layer.
*/
- in_pcbref(inp);
- INP_WUNLOCK(inp);
- IN_MULTI_LOCK();
-
- if (is_final) {
- /*
- * Give up the multicast address record to which
- * the membership points.
- */
- (void)in_leavegroup_locked(inm, imf);
- } else {
+ if (!is_final) {
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
IN_MULTI_LIST_LOCK();
error = inm_merge(inm, imf);
@@ -2527,7 +2433,9 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
__func__);
IN_MULTI_LIST_UNLOCK();
- goto out_in_multi_locked;
+ imf_rollback(imf);
+ imf_reap(imf);
+ goto out_inp_locked;
}
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
@@ -2536,38 +2444,27 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
if (error) {
CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
__func__);
+ imf_rollback(imf);
+ imf_reap(imf);
+ goto out_inp_locked;
}
}
-
-out_in_multi_locked:
-
- IN_MULTI_UNLOCK();
- INP_WLOCK(inp);
- if (in_pcbrele_wlocked(inp))
- return (ENXIO);
-
- if (error)
- imf_rollback(imf);
- else
- imf_commit(imf);
-
+ imf_commit(imf);
imf_reap(imf);
- if (is_final) {
- /* Remove the gap in the membership and filter array. */
- KASSERT(RB_EMPTY(&imf->imf_sources),
- ("%s: imf_sources not empty", __func__));
- for (++idx; idx < imo->imo_num_memberships; ++idx) {
- imo->imo_membership[idx - 1] = imo->imo_membership[idx];
- imo->imo_mfilters[idx - 1] = imo->imo_mfilters[idx];
- }
- imf_init(&imo->imo_mfilters[idx - 1], MCAST_UNDEFINED,
- MCAST_EXCLUDE);
- imo->imo_num_memberships--;
- }
-
out_inp_locked:
INP_WUNLOCK(inp);
+
+ if (is_final && imf) {
+ /*
+ * Give up the multicast address record to which
+ * the membership points.
+ */
+ (void) in_leavegroup_locked(imf->imf_inm, imf);
+ ip_mfilter_free(imf);
+ }
+
+ IN_MULTI_UNLOCK();
return (error);
}
@@ -2657,7 +2554,6 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
struct in_mfilter *imf;
struct ip_moptions *imo;
struct in_multi *inm;
- size_t idx;
int error;
error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
@@ -2689,18 +2585,19 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
if (ifp == NULL)
return (EADDRNOTAVAIL);
+ IN_MULTI_LOCK();
+
/*
* Take the INP write lock.
* Check if this socket is a member of this group.
*/
imo = inp_findmoptions(inp);
- idx = imo_match_group(imo, ifp, &gsa->sa);
- if (idx == -1 || imo->imo_mfilters == NULL) {
+ imf = imo_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
error = EADDRNOTAVAIL;
goto out_inp_locked;
}
- inm = imo->imo_membership[idx];
- imf = &imo->imo_mfilters[idx];
+ inm = imf->imf_inm;
/*
* Begin state merge transaction at socket layer.
@@ -2777,7 +2674,6 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
goto out_imf_rollback;
INP_WLOCK_ASSERT(inp);
- IN_MULTI_LOCK();
/*
* Begin state merge transaction at IGMP layer.
@@ -2788,7 +2684,7 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
if (error) {
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
IN_MULTI_LIST_UNLOCK();
- goto out_in_multi_locked;
+ goto out_imf_rollback;
}
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
@@ -2797,10 +2693,6 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
if (error)
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
-out_in_multi_locked:
-
- IN_MULTI_UNLOCK();
-
out_imf_rollback:
if (error)
imf_rollback(imf);
@@ -2811,6 +2703,7 @@ out_imf_rollback:
out_inp_locked:
INP_WUNLOCK(inp);
+ IN_MULTI_UNLOCK();
return (error);
}
diff --git a/freebsd/sys/netinet/in_pcb.c b/freebsd/sys/netinet/in_pcb.c
index abcee41b..d4376951 100644
--- a/freebsd/sys/netinet/in_pcb.c
+++ b/freebsd/sys/netinet/in_pcb.c
@@ -92,6 +92,9 @@ __FBSDID("$FreeBSD$");
#if defined(INET) || defined(INET6)
#include <netinet/in.h>
#include <netinet/in_pcb.h>
+#ifdef INET
+#include <netinet/in_var.h>
+#endif
#include <netinet/ip_var.h>
#include <netinet/tcp_var.h>
#ifdef TCPHPTS
@@ -99,16 +102,13 @@ __FBSDID("$FreeBSD$");
#endif
#include <netinet/udp.h>
#include <netinet/udp_var.h>
-#endif
-#ifdef INET
-#include <netinet/in_var.h>
-#endif
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h>
#endif /* INET6 */
+#endif
#include <netipsec/ipsec_support.h>
@@ -1789,8 +1789,9 @@ void
in_pcbpurgeif0(struct inpcbinfo *pcbinfo, struct ifnet *ifp)
{
struct inpcb *inp;
+ struct in_multi *inm;
+ struct in_mfilter *imf;
struct ip_moptions *imo;
- int i, gap;
INP_INFO_WLOCK(pcbinfo);
CK_LIST_FOREACH(inp, pcbinfo->ipi_listhead, inp_list) {
@@ -1811,17 +1812,18 @@ in_pcbpurgeif0(struct inpcbinfo *pcbinfo, struct ifnet *ifp)
*
* 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_MULTI_LOCK_ASSERT();
- in_leavegroup_locked(imo->imo_membership[i], NULL);
- gap++;
- } else if (gap != 0)
- imo->imo_membership[i - gap] =
- imo->imo_membership[i];
+restart:
+ IP_MFILTER_FOREACH(imf, &imo->imo_head) {
+ if ((inm = imf->imf_inm) == NULL)
+ continue;
+ if (inm->inm_ifp != ifp)
+ continue;
+ ip_mfilter_remove(&imo->imo_head, imf);
+ IN_MULTI_LOCK_ASSERT();
+ in_leavegroup_locked(inm, NULL);
+ ip_mfilter_free(imf);
+ goto restart;
}
- imo->imo_num_memberships -= gap;
}
INP_WUNLOCK(inp);
}
diff --git a/freebsd/sys/netinet/in_var.h b/freebsd/sys/netinet/in_var.h
index 5b7a464b..50112481 100644
--- a/freebsd/sys/netinet/in_var.h
+++ b/freebsd/sys/netinet/in_var.h
@@ -232,9 +232,61 @@ struct in_mfilter {
struct ip_msource_tree imf_sources; /* source list for (S,G) */
u_long imf_nsrc; /* # of source entries */
uint8_t imf_st[2]; /* state before/at commit */
+ struct in_multi *imf_inm; /* associated multicast address */
+ STAILQ_ENTRY(in_mfilter) imf_entry; /* list entry */
};
/*
+ * Helper types and functions for IPv4 multicast filters.
+ */
+STAILQ_HEAD(ip_mfilter_head, in_mfilter);
+
+struct in_mfilter *ip_mfilter_alloc(int mflags, int st0, int st1);
+void ip_mfilter_free(struct in_mfilter *);
+
+static inline void
+ip_mfilter_init(struct ip_mfilter_head *head)
+{
+
+ STAILQ_INIT(head);
+}
+
+static inline struct in_mfilter *
+ip_mfilter_first(const struct ip_mfilter_head *head)
+{
+
+ return (STAILQ_FIRST(head));
+}
+
+static inline void
+ip_mfilter_insert(struct ip_mfilter_head *head, struct in_mfilter *imf)
+{
+
+ STAILQ_INSERT_TAIL(head, imf, imf_entry);
+}
+
+static inline void
+ip_mfilter_remove(struct ip_mfilter_head *head, struct in_mfilter *imf)
+{
+
+ STAILQ_REMOVE(head, imf, in_mfilter, imf_entry);
+}
+
+#define IP_MFILTER_FOREACH(imf, head) \
+ STAILQ_FOREACH(imf, head, imf_entry)
+
+static inline size_t
+ip_mfilter_count(struct ip_mfilter_head *head)
+{
+ struct in_mfilter *imf;
+ size_t num = 0;
+
+ STAILQ_FOREACH(imf, head, imf_entry)
+ num++;
+ return (num);
+}
+
+/*
* IPv4 group descriptor.
*
* For every entry on an ifnet's if_multiaddrs list which represents
diff --git a/freebsd/sys/netinet/ip_carp.c b/freebsd/sys/netinet/ip_carp.c
index 1fb208a2..02a24bb8 100644
--- a/freebsd/sys/netinet/ip_carp.c
+++ b/freebsd/sys/netinet/ip_carp.c
@@ -1369,25 +1369,24 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
case AF_INET:
{
struct ip_moptions *imo = &cif->cif_imo;
+ struct in_mfilter *imf;
struct in_addr addr;
- if (imo->imo_membership)
+ if (ip_mfilter_first(&imo->imo_head) != NULL)
return (0);
- imo->imo_membership = (struct in_multi **)malloc(
- (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP,
- M_WAITOK);
- imo->imo_mfilters = NULL;
- imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
+ imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
+ ip_mfilter_init(&imo->imo_head);
imo->imo_multicast_vif = -1;
addr.s_addr = htonl(INADDR_CARP_GROUP);
if ((error = in_joingroup(ifp, &addr, NULL,
- &imo->imo_membership[0])) != 0) {
- free(imo->imo_membership, M_CARP);
+ &imf->imf_inm)) != 0) {
+ ip_mfilter_free(imf);
break;
}
- imo->imo_num_memberships++;
+
+ ip_mfilter_insert(&imo->imo_head, imf);
imo->imo_multicast_ifp = ifp;
imo->imo_multicast_ttl = CARP_DFLTTL;
imo->imo_multicast_loop = 0;
@@ -1398,17 +1397,16 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
case AF_INET6:
{
struct ip6_moptions *im6o = &cif->cif_im6o;
+ struct in6_mfilter *im6f[2];
struct in6_addr in6;
- struct in6_multi *in6m;
- if (im6o->im6o_membership)
+ if (ip6_mfilter_first(&im6o->im6o_head))
return (0);
- im6o->im6o_membership = (struct in6_multi **)malloc(
- (sizeof(struct in6_multi *) * IPV6_MIN_MEMBERSHIPS), M_CARP,
- M_ZERO | M_WAITOK);
- im6o->im6o_mfilters = NULL;
- im6o->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS;
+ im6f[0] = ip6_mfilter_alloc(M_WAITOK, 0, 0);
+ im6f[1] = ip6_mfilter_alloc(M_WAITOK, 0, 0);
+
+ ip6_mfilter_init(&im6o->im6o_head);
im6o->im6o_multicast_hlim = CARP_DFLTTL;
im6o->im6o_multicast_ifp = ifp;
@@ -1417,17 +1415,15 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
in6.s6_addr16[0] = htons(0xff02);
in6.s6_addr8[15] = 0x12;
if ((error = in6_setscope(&in6, ifp, NULL)) != 0) {
- free(im6o->im6o_membership, M_CARP);
+ ip6_mfilter_free(im6f[0]);
+ ip6_mfilter_free(im6f[1]);
break;
}
- in6m = NULL;
- if ((error = in6_joingroup(ifp, &in6, NULL, &in6m, 0)) != 0) {
- free(im6o->im6o_membership, M_CARP);
+ if ((error = in6_joingroup(ifp, &in6, NULL, &im6f[0]->im6f_in6m, 0)) != 0) {
+ ip6_mfilter_free(im6f[0]);
+ ip6_mfilter_free(im6f[1]);
break;
}
- in6m_acquire(in6m);
- im6o->im6o_membership[0] = in6m;
- im6o->im6o_num_memberships++;
/* Join solicited multicast address. */
bzero(&in6, sizeof(in6));
@@ -1436,20 +1432,21 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
in6.s6_addr32[2] = htonl(1);
in6.s6_addr32[3] = 0;
in6.s6_addr8[12] = 0xff;
+
if ((error = in6_setscope(&in6, ifp, NULL)) != 0) {
- in6_leavegroup(im6o->im6o_membership[0], NULL);
- free(im6o->im6o_membership, M_CARP);
+ ip6_mfilter_free(im6f[0]);
+ ip6_mfilter_free(im6f[1]);
break;
}
- in6m = NULL;
- if ((error = in6_joingroup(ifp, &in6, NULL, &in6m, 0)) != 0) {
- in6_leavegroup(im6o->im6o_membership[0], NULL);
- free(im6o->im6o_membership, M_CARP);
+
+ if ((error = in6_joingroup(ifp, &in6, NULL, &im6f[1]->im6f_in6m, 0)) != 0) {
+ in6_leavegroup(im6f[0]->im6f_in6m, NULL);
+ ip6_mfilter_free(im6f[0]);
+ ip6_mfilter_free(im6f[1]);
break;
}
- in6m_acquire(in6m);
- im6o->im6o_membership[1] = in6m;
- im6o->im6o_num_memberships++;
+ ip6_mfilter_insert(&im6o->im6o_head, im6f[0]);
+ ip6_mfilter_insert(&im6o->im6o_head, im6f[1]);
break;
}
#endif
@@ -1464,35 +1461,38 @@ carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
static void
carp_multicast_cleanup(struct carp_if *cif, sa_family_t sa)
{
-
+#ifdef INET
+ struct ip_moptions *imo = &cif->cif_imo;
+ struct in_mfilter *imf;
+#endif
+#ifdef INET6
+ struct ip6_moptions *im6o = &cif->cif_im6o;
+ struct in6_mfilter *im6f;
+#endif
sx_assert(&carp_sx, SA_XLOCKED);
switch (sa) {
#ifdef INET
case AF_INET:
- if (cif->cif_naddrs == 0) {
- struct ip_moptions *imo = &cif->cif_imo;
-
- in_leavegroup(imo->imo_membership[0], NULL);
- KASSERT(imo->imo_mfilters == NULL,
- ("%s: imo_mfilters != NULL", __func__));
- free(imo->imo_membership, M_CARP);
- imo->imo_membership = NULL;
+ if (cif->cif_naddrs != 0)
+ break;
+ while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
+ ip_mfilter_remove(&imo->imo_head, imf);
+ in_leavegroup(imf->imf_inm, NULL);
+ ip_mfilter_free(imf);
}
break;
#endif
#ifdef INET6
case AF_INET6:
- if (cif->cif_naddrs6 == 0) {
- struct ip6_moptions *im6o = &cif->cif_im6o;
-
- in6_leavegroup(im6o->im6o_membership[0], NULL);
- in6_leavegroup(im6o->im6o_membership[1], NULL);
- KASSERT(im6o->im6o_mfilters == NULL,
- ("%s: im6o_mfilters != NULL", __func__));
- free(im6o->im6o_membership, M_CARP);
- im6o->im6o_membership = NULL;
+ if (cif->cif_naddrs6 != 0)
+ break;
+
+ while ((im6f = ip6_mfilter_first(&im6o->im6o_head)) != NULL) {
+ ip6_mfilter_remove(&im6o->im6o_head, im6f);
+ in6_leavegroup(im6f->im6f_in6m, NULL);
+ ip6_mfilter_free(im6f);
}
break;
#endif
diff --git a/freebsd/sys/netinet/ip_fw.h b/freebsd/sys/netinet/ip_fw.h
index de0cc29d..7a01c82b 100644
--- a/freebsd/sys/netinet/ip_fw.h
+++ b/freebsd/sys/netinet/ip_fw.h
@@ -293,6 +293,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_EXTERNAL_DATA, /* variable length data */
O_SKIP_ACTION, /* none */
+ O_TCPMSS, /* arg1=MSS value */
O_LAST_OPCODE /* not an opcode! */
};
diff --git a/freebsd/sys/netinet/ip_mroute.c b/freebsd/sys/netinet/ip_mroute.c
index 987549c6..3dd887f3 100644
--- a/freebsd/sys/netinet/ip_mroute.c
+++ b/freebsd/sys/netinet/ip_mroute.c
@@ -1680,7 +1680,6 @@ static void
send_packet(struct vif *vifp, struct mbuf *m)
{
struct ip_moptions imo;
- struct in_multi *imm[2];
int error __unused;
VIF_LOCK_ASSERT();
@@ -1689,9 +1688,7 @@ send_packet(struct vif *vifp, struct mbuf *m)
imo.imo_multicast_ttl = mtod(m, struct ip *)->ip_ttl - 1;
imo.imo_multicast_loop = 1;
imo.imo_multicast_vif = -1;
- imo.imo_num_memberships = 0;
- imo.imo_max_memberships = 2;
- imo.imo_membership = &imm[0];
+ STAILQ_INIT(&imo.imo_head);
/*
* Re-entrancy should not be a problem here, because
diff --git a/freebsd/sys/netinet/ip_var.h b/freebsd/sys/netinet/ip_var.h
index 86615a15..656c5732 100644
--- a/freebsd/sys/netinet/ip_var.h
+++ b/freebsd/sys/netinet/ip_var.h
@@ -82,6 +82,7 @@ struct ipoption {
char ipopt_list[MAX_IPOPTLEN]; /* options proper */
};
+#if defined(_NETINET_IN_VAR_H_) && defined(_KERNEL)
/*
* Structure attached to inpcb.ip_moptions and
* passed to ip_output when IP multicast options are in use.
@@ -93,12 +94,11 @@ struct ip_moptions {
u_long imo_multicast_vif; /* vif num outgoing multicasts */
u_char imo_multicast_ttl; /* TTL for outgoing multicasts */
u_char imo_multicast_loop; /* 1 => hear sends if a member */
- u_short imo_num_memberships; /* no. memberships this socket */
- u_short imo_max_memberships; /* max memberships this socket */
- struct in_multi **imo_membership; /* group memberships */
- struct in_mfilter *imo_mfilters; /* source filters */
- struct epoch_context imo_epoch_ctx;
+ struct ip_mfilter_head imo_head; /* group membership list */
};
+#else
+struct ip_moptions;
+#endif
struct ipstat {
uint64_t ips_total; /* total packets received */
diff --git a/freebsd/sys/netinet6/in6_ifattach.c b/freebsd/sys/netinet6/in6_ifattach.c
index 956b7290..b9adb19a 100644
--- a/freebsd/sys/netinet6/in6_ifattach.c
+++ b/freebsd/sys/netinet6/in6_ifattach.c
@@ -782,9 +782,11 @@ _in6_ifdetach(struct ifnet *ifp, int purgeulp)
in6_purgeaddr(ifa);
}
if (purgeulp) {
+ IN6_MULTI_LOCK();
in6_pcbpurgeif0(&V_udbinfo, ifp);
in6_pcbpurgeif0(&V_ulitecbinfo, ifp);
in6_pcbpurgeif0(&V_ripcbinfo, ifp);
+ IN6_MULTI_UNLOCK();
}
/* leave from all multicast groups joined */
in6_purgemaddrs(ifp);
diff --git a/freebsd/sys/netinet6/in6_mcast.c b/freebsd/sys/netinet6/in6_mcast.c
index e16de99d..1ac10633 100644
--- a/freebsd/sys/netinet6/in6_mcast.c
+++ b/freebsd/sys/netinet6/in6_mcast.c
@@ -104,7 +104,8 @@ RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp);
/*
* Locking:
- * - Lock order is: Giant, INP_WLOCK, IN6_MULTI_LOCK, MLD_LOCK, IF_ADDR_LOCK.
+ * - Lock order is: Giant, IN6_MULTI_LOCK, INP_WLOCK,
+ * IN6_MULTI_LIST_LOCK, MLD_LOCK, IF_ADDR_LOCK.
* - The IF_ADDR_LOCK is implicitly taken by in6m_lookup() earlier, however
* it can be taken by code in net/if.c also.
* - ip6_moptions and in6_mfilter are covered by the INP_WLOCK.
@@ -136,12 +137,11 @@ static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *);
static void im6f_purge(struct in6_mfilter *);
static void im6f_rollback(struct in6_mfilter *);
static void im6f_reap(struct in6_mfilter *);
-static int im6o_grow(struct ip6_moptions *);
-static size_t im6o_match_group(const struct ip6_moptions *,
+static struct in6_mfilter *
+ im6o_match_group(const struct ip6_moptions *,
const struct ifnet *, const struct sockaddr *);
static struct in6_msource *
- im6o_match_source(const struct ip6_moptions *, const size_t,
- const struct sockaddr *);
+ im6o_match_source(struct in6_mfilter *, const struct sockaddr *);
static void im6s_merge(struct ip6_msource *ims,
const struct in6_msource *lims, const int rollback);
static int in6_getmulti(struct ifnet *, const struct in6_addr *,
@@ -230,55 +230,25 @@ im6f_init(struct in6_mfilter *imf, const int st0, const int st1)
imf->im6f_st[1] = st1;
}
-/*
- * Resize the ip6_moptions vector to the next power-of-two minus 1.
- * May be called with locks held; do not sleep.
- */
-static int
-im6o_grow(struct ip6_moptions *imo)
+struct in6_mfilter *
+ip6_mfilter_alloc(const int mflags, const int st0, const int st1)
{
- struct in6_multi **nmships;
- struct in6_multi **omships;
- struct in6_mfilter *nmfilters;
- struct in6_mfilter *omfilters;
- size_t idx;
- size_t newmax;
- size_t oldmax;
-
- nmships = NULL;
- nmfilters = NULL;
- omships = imo->im6o_membership;
- omfilters = imo->im6o_mfilters;
- oldmax = imo->im6o_max_memberships;
- newmax = ((oldmax + 1) * 2) - 1;
-
- if (newmax <= IPV6_MAX_MEMBERSHIPS) {
- nmships = (struct in6_multi **)realloc(omships,
- sizeof(struct in6_multi *) * newmax, M_IP6MOPTS, M_NOWAIT);
- nmfilters = (struct in6_mfilter *)realloc(omfilters,
- sizeof(struct in6_mfilter) * newmax, M_IN6MFILTER,
- M_NOWAIT);
- if (nmships != NULL && nmfilters != NULL) {
- /* Initialize newly allocated source filter heads. */
- for (idx = oldmax; idx < newmax; idx++) {
- im6f_init(&nmfilters[idx], MCAST_UNDEFINED,
- MCAST_EXCLUDE);
- }
- imo->im6o_max_memberships = newmax;
- imo->im6o_membership = nmships;
- imo->im6o_mfilters = nmfilters;
- }
- }
+ struct in6_mfilter *imf;
- if (nmships == NULL || nmfilters == NULL) {
- if (nmships != NULL)
- free(nmships, M_IP6MOPTS);
- if (nmfilters != NULL)
- free(nmfilters, M_IN6MFILTER);
- return (ETOOMANYREFS);
- }
+ imf = malloc(sizeof(*imf), M_IN6MFILTER, mflags);
- return (0);
+ if (imf != NULL)
+ im6f_init(imf, st0, st1);
+
+ return (imf);
+}
+
+void
+ip6_mfilter_free(struct in6_mfilter *imf)
+{
+
+ im6f_purge(imf);
+ free(imf, M_IN6MFILTER);
}
/*
@@ -286,36 +256,27 @@ im6o_grow(struct ip6_moptions *imo)
* which matches the specified group, and optionally an interface.
* Return its index into the array, or -1 if not found.
*/
-static size_t
+static struct in6_mfilter *
im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
const struct sockaddr *group)
{
const struct sockaddr_in6 *gsin6;
- struct in6_multi **pinm;
- int idx;
- int nmships;
+ struct in6_mfilter *imf;
+ struct in6_multi *inm;
- gsin6 = (const struct sockaddr_in6 *)group;
+ gsin6 = (const struct sockaddr_in6 *)group;
- /* The im6o_membership array may be lazy allocated. */
- if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0)
- return (-1);
-
- nmships = imo->im6o_num_memberships;
- pinm = &imo->im6o_membership[0];
- for (idx = 0; idx < nmships; idx++, pinm++) {
- if (*pinm == NULL)
+ IP6_MFILTER_FOREACH(imf, &imo->im6o_head) {
+ inm = imf->im6f_in6m;
+ if (inm == NULL)
continue;
- if ((ifp == NULL || ((*pinm)->in6m_ifp == ifp)) &&
- IN6_ARE_ADDR_EQUAL(&(*pinm)->in6m_addr,
+ if ((ifp == NULL || (inm->in6m_ifp == ifp)) &&
+ IN6_ARE_ADDR_EQUAL(&inm->in6m_addr,
&gsin6->sin6_addr)) {
break;
}
}
- if (idx >= nmships)
- idx = -1;
-
- return (idx);
+ return (imf);
}
/*
@@ -330,22 +291,13 @@ im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
* it exists, which may not be the desired behaviour.
*/
static struct in6_msource *
-im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
- const struct sockaddr *src)
+im6o_match_source(struct in6_mfilter *imf, const struct sockaddr *src)
{
struct ip6_msource find;
- struct in6_mfilter *imf;
struct ip6_msource *ims;
const sockunion_t *psa;
KASSERT(src->sa_family == AF_INET6, ("%s: !AF_INET6", __func__));
- KASSERT(gidx != -1 && gidx < imo->im6o_num_memberships,
- ("%s: invalid index %d\n", __func__, (int)gidx));
-
- /* The im6o_mfilters array may be lazy allocated. */
- if (imo->im6o_mfilters == NULL)
- return (NULL);
- imf = &imo->im6o_mfilters[gidx];
psa = (const sockunion_t *)src;
find.im6s_addr = psa->sin6.sin6_addr;
@@ -365,14 +317,14 @@ int
im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp,
const struct sockaddr *group, const struct sockaddr *src)
{
- size_t gidx;
+ struct in6_mfilter *imf;
struct in6_msource *ims;
int mode;
KASSERT(ifp != NULL, ("%s: null ifp", __func__));
- gidx = im6o_match_group(imo, ifp, group);
- if (gidx == -1)
+ imf = im6o_match_group(imo, ifp, group);
+ if (imf == NULL)
return (MCAST_NOTGMEMBER);
/*
@@ -384,8 +336,8 @@ im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp,
* NOTE: We are comparing group state here at MLD t1 (now)
* with socket-layer t0 (since last downcall).
*/
- mode = imo->im6o_mfilters[gidx].im6f_st[1];
- ims = im6o_match_source(imo, gidx, src);
+ mode = imf->im6f_st[1];
+ ims = im6o_match_source(imf, src);
if ((ims == NULL && mode == MCAST_INCLUDE) ||
(ims != NULL && ims->im6sl_st[0] != mode))
@@ -1449,7 +1401,6 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
struct ip6_moptions *imo;
struct in6_msource *ims;
struct in6_multi *inm;
- size_t idx;
uint16_t fmode;
int error, doblock;
#ifdef KTR
@@ -1506,16 +1457,12 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
* Check if we are actually a member of this group.
*/
imo = in6p_findmoptions(inp);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
- if (idx == -1 || imo->im6o_mfilters == NULL) {
+ imf = im6o_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
error = EADDRNOTAVAIL;
goto out_in6p_locked;
}
-
- KASSERT(imo->im6o_mfilters != NULL,
- ("%s: im6o_mfilters not allocated", __func__));
- imf = &imo->im6o_mfilters[idx];
- inm = imo->im6o_membership[idx];
+ inm = imf->im6f_in6m;
/*
* Attempting to use the delta-based API on an
@@ -1533,7 +1480,7 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
* Asked to unblock, but nothing to unblock.
* If adding a new block entry, allocate it.
*/
- ims = im6o_match_source(imo, idx, &ssa->sa);
+ ims = im6o_match_source(imf, &ssa->sa);
if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
CTR3(KTR_MLD, "%s: source %s %spresent", __func__,
ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr),
@@ -1603,9 +1550,6 @@ static struct ip6_moptions *
in6p_findmoptions(struct inpcb *inp)
{
struct ip6_moptions *imo;
- struct in6_multi **immp;
- struct in6_mfilter *imfp;
- size_t idx;
INP_WLOCK(inp);
if (inp->in6p_moptions != NULL)
@@ -1614,27 +1558,14 @@ in6p_findmoptions(struct inpcb *inp)
INP_WUNLOCK(inp);
imo = malloc(sizeof(*imo), M_IP6MOPTS, M_WAITOK);
- immp = malloc(sizeof(*immp) * IPV6_MIN_MEMBERSHIPS, M_IP6MOPTS,
- M_WAITOK | M_ZERO);
- imfp = malloc(sizeof(struct in6_mfilter) * IPV6_MIN_MEMBERSHIPS,
- M_IN6MFILTER, M_WAITOK);
imo->im6o_multicast_ifp = NULL;
imo->im6o_multicast_hlim = V_ip6_defmcasthlim;
imo->im6o_multicast_loop = in6_mcast_loop;
- imo->im6o_num_memberships = 0;
- imo->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS;
- imo->im6o_membership = immp;
-
- /* Initialize per-group source filters. */
- for (idx = 0; idx < IPV6_MIN_MEMBERSHIPS; idx++)
- im6f_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
- imo->im6o_mfilters = imfp;
+ STAILQ_INIT(&imo->im6o_head);
INP_WLOCK(inp);
if (inp->in6p_moptions != NULL) {
- free(imfp, M_IN6MFILTER);
- free(immp, M_IP6MOPTS);
free(imo, M_IP6MOPTS);
return (inp->in6p_moptions);
}
@@ -1654,33 +1585,26 @@ in6p_findmoptions(struct inpcb *inp)
static void
inp_gcmoptions(struct ip6_moptions *imo)
{
- struct in6_mfilter *imf;
+ struct in6_mfilter *imf;
struct in6_multi *inm;
struct ifnet *ifp;
- size_t idx, nmships;
-
- nmships = imo->im6o_num_memberships;
- for (idx = 0; idx < nmships; ++idx) {
- imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL;
- if (imf)
- im6f_leave(imf);
- inm = imo->im6o_membership[idx];
- ifp = inm->in6m_ifp;
- if (ifp != NULL) {
- CURVNET_SET(ifp->if_vnet);
- (void)in6_leavegroup(inm, imf);
- CURVNET_RESTORE();
- } else {
- (void)in6_leavegroup(inm, imf);
- }
- if (imf)
- im6f_purge(imf);
- }
- if (imo->im6o_mfilters)
- free(imo->im6o_mfilters, M_IN6MFILTER);
- free(imo->im6o_membership, M_IP6MOPTS);
- free(imo, M_IP6MOPTS);
+ while ((imf = ip6_mfilter_first(&imo->im6o_head)) != NULL) {
+ ip6_mfilter_remove(&imo->im6o_head, imf);
+
+ im6f_leave(imf);
+ if ((inm = imf->im6f_in6m) != NULL) {
+ if ((ifp = inm->in6m_ifp) != NULL) {
+ CURVNET_SET(ifp->if_vnet);
+ (void)in6_leavegroup(inm, imf);
+ CURVNET_RESTORE();
+ } else {
+ (void)in6_leavegroup(inm, imf);
+ }
+ }
+ ip6_mfilter_free(imf);
+ }
+ free(imo, M_IP6MOPTS);
}
void
@@ -1709,7 +1633,7 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
struct sockaddr_storage *ptss;
struct sockaddr_storage *tss;
int error;
- size_t idx, nsrcs, ncsrcs;
+ size_t nsrcs, ncsrcs;
INP_WLOCK_ASSERT(inp);
@@ -1743,12 +1667,11 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
/*
* Lookup group on the socket.
*/
- idx = im6o_match_group(imo, ifp, &gsa->sa);
- if (idx == -1 || imo->im6o_mfilters == NULL) {
+ imf = im6o_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
INP_WUNLOCK(inp);
return (EADDRNOTAVAIL);
}
- imf = &imo->im6o_mfilters[idx];
/*
* Ignore memberships which are in limbo.
@@ -1945,15 +1868,12 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
struct ip6_moptions *imo;
struct in6_multi *inm;
struct in6_msource *lims;
- size_t idx;
int error, is_new;
SLIST_INIT(&inmh);
ifp = NULL;
- imf = NULL;
lims = NULL;
error = 0;
- is_new = 0;
memset(&gsr, 0, sizeof(struct group_source_req));
gsa = (sockunion_t *)&gsr.gsr_group;
@@ -2054,13 +1974,25 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
*/
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+ IN6_MULTI_LOCK();
+
+ /*
+ * Find the membership in the membership list.
+ */
imo = in6p_findmoptions(inp);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
- if (idx == -1) {
+ imf = im6o_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
is_new = 1;
+ inm = NULL;
+
+ if (ip6_mfilter_count(&imo->im6o_head) >= IPV6_MAX_MEMBERSHIPS) {
+ error = ENOMEM;
+ goto out_in6p_locked;
+ }
} else {
- inm = imo->im6o_membership[idx];
- imf = &imo->im6o_mfilters[idx];
+ is_new = 0;
+ inm = imf->im6f_in6m;
+
if (ssa->ss.ss_family != AF_UNSPEC) {
/*
* MCAST_JOIN_SOURCE_GROUP on an exclusive membership
@@ -2087,7 +2019,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
* full-state SSM API with the delta-based API,
* which is discouraged in the relevant RFCs.
*/
- lims = im6o_match_source(imo, idx, &ssa->sa);
+ lims = im6o_match_source(imf, &ssa->sa);
if (lims != NULL /*&&
lims->im6sl_st[1] == MCAST_INCLUDE*/) {
error = EADDRNOTAVAIL;
@@ -2115,27 +2047,6 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
*/
INP_WLOCK_ASSERT(inp);
- if (is_new) {
- if (imo->im6o_num_memberships == imo->im6o_max_memberships) {
- error = im6o_grow(imo);
- if (error)
- goto out_in6p_locked;
- }
- /*
- * Allocate the new slot upfront so we can deal with
- * grafting the new source filter in same code path
- * as for join-source on existing membership.
- */
- idx = imo->im6o_num_memberships;
- imo->im6o_membership[idx] = NULL;
- imo->im6o_num_memberships++;
- KASSERT(imo->im6o_mfilters != NULL,
- ("%s: im6f_mfilters vector was not allocated", __func__));
- imf = &imo->im6o_mfilters[idx];
- KASSERT(RB_EMPTY(&imf->im6f_sources),
- ("%s: im6f_sources not empty", __func__));
- }
-
/*
* Graft new source into filter list for this inpcb's
* membership of the group. The in6_multi may not have
@@ -2151,7 +2062,11 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
/* Membership starts in IN mode */
if (is_new) {
CTR1(KTR_MLD, "%s: new join w/source", __func__);
- im6f_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE);
+ imf = ip6_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_INCLUDE);
+ if (imf == NULL) {
+ error = ENOMEM;
+ goto out_in6p_locked;
+ }
} else {
CTR2(KTR_MLD, "%s: %s source", __func__, "allow");
}
@@ -2160,81 +2075,88 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
CTR1(KTR_MLD, "%s: merge imf state failed",
__func__);
error = ENOMEM;
- goto out_im6o_free;
+ goto out_in6p_locked;
}
} else {
/* No address specified; Membership starts in EX mode */
if (is_new) {
CTR1(KTR_MLD, "%s: new join w/o source", __func__);
- im6f_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE);
+ imf = ip6_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_EXCLUDE);
+ if (imf == NULL) {
+ error = ENOMEM;
+ goto out_in6p_locked;
+ }
}
}
/*
* Begin state merge transaction at MLD layer.
*/
- in_pcbref(inp);
- INP_WUNLOCK(inp);
- IN6_MULTI_LOCK();
-
if (is_new) {
+ in_pcbref(inp);
+ INP_WUNLOCK(inp);
+
error = in6_joingroup_locked(ifp, &gsa->sin6.sin6_addr, imf,
- &inm, 0);
+ &imf->im6f_in6m, 0);
+
+ INP_WLOCK(inp);
+ if (in_pcbrele_wlocked(inp)) {
+ error = ENXIO;
+ goto out_in6p_unlocked;
+ }
if (error) {
- IN6_MULTI_UNLOCK();
- goto out_im6o_free;
+ goto out_in6p_locked;
}
/*
* NOTE: Refcount from in6_joingroup_locked()
* is protecting membership.
*/
- imo->im6o_membership[idx] = inm;
} else {
CTR1(KTR_MLD, "%s: merge inm state", __func__);
IN6_MULTI_LIST_LOCK();
error = in6m_merge(inm, imf);
- if (error)
+ if (error) {
CTR1(KTR_MLD, "%s: failed to merge inm state",
__func__);
- else {
- CTR1(KTR_MLD, "%s: doing mld downcall", __func__);
- error = mld_change_state(inm, 0);
- if (error)
- CTR1(KTR_MLD, "%s: failed mld downcall",
- __func__);
+ IN6_MULTI_LIST_UNLOCK();
+ im6f_rollback(imf);
+ im6f_reap(imf);
+ goto out_in6p_locked;
}
+ CTR1(KTR_MLD, "%s: doing mld downcall", __func__);
+ error = mld_change_state(inm, 0);
IN6_MULTI_LIST_UNLOCK();
- }
- IN6_MULTI_UNLOCK();
- INP_WLOCK(inp);
- if (in_pcbrele_wlocked(inp))
- return (ENXIO);
- if (error) {
- im6f_rollback(imf);
- if (is_new)
- im6f_purge(imf);
- else
+ if (error) {
+ CTR1(KTR_MLD, "%s: failed mld downcall",
+ __func__);
+ im6f_rollback(imf);
im6f_reap(imf);
- } else {
- im6f_commit(imf);
- }
-
-out_im6o_free:
- if (error && is_new) {
- inm = imo->im6o_membership[idx];
- if (inm != NULL) {
- IN6_MULTI_LIST_LOCK();
- in6m_rele_locked(&inmh, inm);
- IN6_MULTI_LIST_UNLOCK();
+ goto out_in6p_locked;
}
- imo->im6o_membership[idx] = NULL;
- --imo->im6o_num_memberships;
}
+ if (is_new)
+ ip6_mfilter_insert(&imo->im6o_head, imf);
+
+ im6f_commit(imf);
+ imf = NULL;
+
out_in6p_locked:
INP_WUNLOCK(inp);
- in6m_release_list_deferred(&inmh);
+out_in6p_unlocked:
+ IN6_MULTI_UNLOCK();
+
+ if (is_new && imf) {
+ if (imf->im6f_in6m != NULL) {
+ struct in6_multi_head inmh;
+
+ SLIST_INIT(&inmh);
+ SLIST_INSERT_HEAD(&inmh, imf->im6f_in6m, in6m_defer);
+ in6m_release_list_deferred(&inmh);
+ }
+ ip6_mfilter_free(imf);
+ }
return (error);
}
@@ -2253,8 +2175,8 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
struct in6_msource *ims;
struct in6_multi *inm;
uint32_t ifindex;
- size_t idx;
- int error, is_final;
+ int error;
+ bool is_final;
#ifdef KTR
char ip6tbuf[INET6_ADDRSTRLEN];
#endif
@@ -2262,7 +2184,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
ifp = NULL;
ifindex = 0;
error = 0;
- is_final = 1;
+ is_final = true;
memset(&gsr, 0, sizeof(struct group_source_req));
gsa = (sockunion_t *)&gsr.gsr_group;
@@ -2380,20 +2302,21 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
CTR2(KTR_MLD, "%s: ifp = %p", __func__, ifp);
KASSERT(ifp != NULL, ("%s: ifp did not resolve", __func__));
+ IN6_MULTI_LOCK();
+
/*
- * Find the membership in the membership array.
+ * Find the membership in the membership list.
*/
imo = in6p_findmoptions(inp);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
- if (idx == -1) {
+ imf = im6o_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
error = EADDRNOTAVAIL;
goto out_in6p_locked;
}
- inm = imo->im6o_membership[idx];
- imf = &imo->im6o_mfilters[idx];
+ inm = imf->im6f_in6m;
if (ssa->ss.ss_family != AF_UNSPEC)
- is_final = 0;
+ is_final = false;
/*
* Begin state merge transaction at socket layer.
@@ -2405,13 +2328,14 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
* MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
*/
if (is_final) {
+ ip6_mfilter_remove(&imo->im6o_head, imf);
im6f_leave(imf);
} else {
if (imf->im6f_st[0] == MCAST_EXCLUDE) {
error = EADDRNOTAVAIL;
goto out_in6p_locked;
}
- ims = im6o_match_source(imo, idx, &ssa->sa);
+ ims = im6o_match_source(imf, &ssa->sa);
if (ims == NULL) {
CTR3(KTR_MLD, "%s: source %p %spresent", __func__,
ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr),
@@ -2431,60 +2355,47 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
/*
* Begin state merge transaction at MLD layer.
*/
- in_pcbref(inp);
- INP_WUNLOCK(inp);
- IN6_MULTI_LOCK();
-
- if (is_final) {
- /*
- * Give up the multicast address record to which
- * the membership points.
- */
- (void)in6_leavegroup_locked(inm, imf);
- } else {
+ if (!is_final) {
CTR1(KTR_MLD, "%s: merge inm state", __func__);
IN6_MULTI_LIST_LOCK();
error = in6m_merge(inm, imf);
- if (error)
+ if (error) {
CTR1(KTR_MLD, "%s: failed to merge inm state",
__func__);
- else {
- CTR1(KTR_MLD, "%s: doing mld downcall", __func__);
- error = mld_change_state(inm, 0);
- if (error)
- CTR1(KTR_MLD, "%s: failed mld downcall",
- __func__);
+ IN6_MULTI_LIST_UNLOCK();
+ im6f_rollback(imf);
+ im6f_reap(imf);
+ goto out_in6p_locked;
}
+
+ CTR1(KTR_MLD, "%s: doing mld downcall", __func__);
+ error = mld_change_state(inm, 0);
IN6_MULTI_LIST_UNLOCK();
+ if (error) {
+ CTR1(KTR_MLD, "%s: failed mld downcall",
+ __func__);
+ im6f_rollback(imf);
+ im6f_reap(imf);
+ goto out_in6p_locked;
+ }
}
- IN6_MULTI_UNLOCK();
- INP_WLOCK(inp);
- if (in_pcbrele_wlocked(inp))
- return (ENXIO);
-
- if (error)
- im6f_rollback(imf);
- else
- im6f_commit(imf);
-
+ im6f_commit(imf);
im6f_reap(imf);
- if (is_final) {
- /* Remove the gap in the membership array. */
- KASSERT(RB_EMPTY(&imf->im6f_sources),
- ("%s: im6f_sources not empty", __func__));
- for (++idx; idx < imo->im6o_num_memberships; ++idx) {
- imo->im6o_membership[idx - 1] = imo->im6o_membership[idx];
- imo->im6o_mfilters[idx - 1] = imo->im6o_mfilters[idx];
- }
- im6f_init(&imo->im6o_mfilters[idx - 1], MCAST_UNDEFINED,
- MCAST_EXCLUDE);
- imo->im6o_num_memberships--;
- }
-
out_in6p_locked:
INP_WUNLOCK(inp);
+
+ if (is_final && imf) {
+ /*
+ * Give up the multicast address record to which
+ * the membership points.
+ */
+ (void)in6_leavegroup_locked(inm, imf);
+ ip6_mfilter_free(imf);
+ }
+
+ IN6_MULTI_UNLOCK();
return (error);
}
@@ -2542,7 +2453,6 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
struct in6_mfilter *imf;
struct ip6_moptions *imo;
struct in6_multi *inm;
- size_t idx;
int error;
error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
@@ -2579,13 +2489,12 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
* Check if this socket is a member of this group.
*/
imo = in6p_findmoptions(inp);
- idx = im6o_match_group(imo, ifp, &gsa->sa);
- if (idx == -1 || imo->im6o_mfilters == NULL) {
+ imf = im6o_match_group(imo, ifp, &gsa->sa);
+ if (imf == NULL) {
error = EADDRNOTAVAIL;
goto out_in6p_locked;
}
- inm = imo->im6o_membership[idx];
- imf = &imo->im6o_mfilters[idx];
+ inm = imf->im6f_in6m;
/*
* Begin state merge transaction at socket layer.
diff --git a/freebsd/sys/netinet6/in6_pcb.c b/freebsd/sys/netinet6/in6_pcb.c
index b95bc708..3c89cdf4 100644
--- a/freebsd/sys/netinet6/in6_pcb.c
+++ b/freebsd/sys/netinet6/in6_pcb.c
@@ -817,8 +817,9 @@ void
in6_pcbpurgeif0(struct inpcbinfo *pcbinfo, struct ifnet *ifp)
{
struct inpcb *in6p;
+ struct in6_multi *inm;
+ struct in6_mfilter *imf;
struct ip6_moptions *im6o;
- int i, gap;
INP_INFO_WLOCK(pcbinfo);
CK_LIST_FOREACH(in6p, pcbinfo->ipi_listhead, inp_list) {
@@ -839,18 +840,18 @@ in6_pcbpurgeif0(struct inpcbinfo *pcbinfo, struct ifnet *ifp)
* Drop multicast group membership if we joined
* through the interface being detached.
*/
- gap = 0;
- for (i = 0; i < im6o->im6o_num_memberships; i++) {
- if (im6o->im6o_membership[i]->in6m_ifp ==
- ifp) {
- in6_leavegroup(im6o->im6o_membership[i], NULL);
- gap++;
- } else if (gap != 0) {
- im6o->im6o_membership[i - gap] =
- im6o->im6o_membership[i];
- }
+restart:
+ IP6_MFILTER_FOREACH(imf, &im6o->im6o_head) {
+ if ((inm = imf->im6f_in6m) == NULL)
+ continue;
+ if (inm->in6m_ifp != ifp)
+ continue;
+ ip6_mfilter_remove(&im6o->im6o_head, imf);
+ IN6_MULTI_LOCK_ASSERT();
+ in6_leavegroup_locked(inm, NULL);
+ ip6_mfilter_free(imf);
+ goto restart;
}
- im6o->im6o_num_memberships -= gap;
}
INP_WUNLOCK(in6p);
}
diff --git a/freebsd/sys/netinet6/in6_var.h b/freebsd/sys/netinet6/in6_var.h
index f80b34f2..6a54c043 100644
--- a/freebsd/sys/netinet6/in6_var.h
+++ b/freebsd/sys/netinet6/in6_var.h
@@ -602,9 +602,61 @@ struct in6_mfilter {
struct ip6_msource_tree im6f_sources; /* source list for (S,G) */
u_long im6f_nsrc; /* # of source entries */
uint8_t im6f_st[2]; /* state before/at commit */
+ struct in6_multi *im6f_in6m; /* associated multicast address */
+ STAILQ_ENTRY(in6_mfilter) im6f_entry; /* list entry */
};
/*
+ * Helper types and functions for IPv4 multicast filters.
+ */
+STAILQ_HEAD(ip6_mfilter_head, in6_mfilter);
+
+struct in6_mfilter *ip6_mfilter_alloc(int mflags, int st0, int st1);
+void ip6_mfilter_free(struct in6_mfilter *);
+
+static inline void
+ip6_mfilter_init(struct ip6_mfilter_head *head)
+{
+
+ STAILQ_INIT(head);
+}
+
+static inline struct in6_mfilter *
+ip6_mfilter_first(const struct ip6_mfilter_head *head)
+{
+
+ return (STAILQ_FIRST(head));
+}
+
+static inline void
+ip6_mfilter_insert(struct ip6_mfilter_head *head, struct in6_mfilter *imf)
+{
+
+ STAILQ_INSERT_TAIL(head, imf, im6f_entry);
+}
+
+static inline void
+ip6_mfilter_remove(struct ip6_mfilter_head *head, struct in6_mfilter *imf)
+{
+
+ STAILQ_REMOVE(head, imf, in6_mfilter, im6f_entry);
+}
+
+#define IP6_MFILTER_FOREACH(imf, head) \
+ STAILQ_FOREACH(imf, head, im6f_entry)
+
+static inline size_t
+ip6_mfilter_count(struct ip6_mfilter_head *head)
+{
+ struct in6_mfilter *imf;
+ size_t num = 0;
+
+ STAILQ_FOREACH(imf, head, im6f_entry)
+ num++;
+ return (num);
+}
+
+/*
* Legacy KAME IPv6 multicast membership descriptor.
*/
struct in6_multi_mship {
diff --git a/freebsd/sys/netinet6/ip6_var.h b/freebsd/sys/netinet6/ip6_var.h
index f235572d..b66f5cfb 100644
--- a/freebsd/sys/netinet6/ip6_var.h
+++ b/freebsd/sys/netinet6/ip6_var.h
@@ -110,6 +110,7 @@ struct ip6_direct_ctx {
uint32_t ip6dc_off; /* offset to next header */
};
+#if defined(_NETINET6_IN6_VAR_H_) && defined(_KERNEL)
/*
* Structure attached to inpcb.in6p_moptions and
* passed to ip6_output when IPv6 multicast options are in use.
@@ -119,13 +120,11 @@ struct ip6_moptions {
struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */
u_char im6o_multicast_hlim; /* hoplimit for outgoing multicasts */
u_char im6o_multicast_loop; /* 1 >= hear sends if a member */
- u_short im6o_num_memberships; /* no. memberships this socket */
- u_short im6o_max_memberships; /* max memberships this socket */
- struct in6_multi **im6o_membership; /* group memberships */
- struct in6_mfilter *im6o_mfilters; /* source filters */
- struct epoch_context imo6_epoch_ctx;
+ struct ip6_mfilter_head im6o_head; /* group membership list */
};
-
+#else
+struct ip6_moptions;
+#endif
/*
* Control options for outgoing packets
*/
diff --git a/freebsd/sys/netpfil/pf/if_pfsync.c b/freebsd/sys/netpfil/pf/if_pfsync.c
index 4eea494b..9d87cf67 100644
--- a/freebsd/sys/netpfil/pf/if_pfsync.c
+++ b/freebsd/sys/netpfil/pf/if_pfsync.c
@@ -266,7 +266,7 @@ static void pfsync_push(struct pfsync_bucket *);
static void pfsync_push_all(struct pfsync_softc *);
static void pfsyncintr(void *);
static int pfsync_multicast_setup(struct pfsync_softc *, struct ifnet *,
- void *);
+ struct in_mfilter *imf);
static void pfsync_multicast_cleanup(struct pfsync_softc *);
static void pfsync_pointers_init(void);
static void pfsync_pointers_uninit(void);
@@ -432,8 +432,7 @@ pfsync_clone_destroy(struct ifnet *ifp)
pfsync_drop(sc);
if_free(ifp);
- if (sc->sc_imo.imo_membership)
- pfsync_multicast_cleanup(sc);
+ pfsync_multicast_cleanup(sc);
mtx_destroy(&sc->sc_mtx);
mtx_destroy(&sc->sc_bulk_mtx);
@@ -1375,10 +1374,9 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCSETPFSYNC:
{
- struct ip_moptions *imo = &sc->sc_imo;
+ struct in_mfilter *imf = NULL;
struct ifnet *sifp;
struct ip *ip;
- void *mship = NULL;
if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0)
return (error);
@@ -1398,8 +1396,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
pfsyncr.pfsyncr_syncpeer.s_addr == 0 ||
pfsyncr.pfsyncr_syncpeer.s_addr ==
htonl(INADDR_PFSYNC_GROUP)))
- mship = malloc((sizeof(struct in_multi *) *
- IP_MIN_MEMBERSHIPS), M_PFSYNC, M_WAITOK | M_ZERO);
+ imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
PFSYNC_LOCK(sc);
if (pfsyncr.pfsyncr_syncpeer.s_addr == 0)
@@ -1421,8 +1418,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if (sc->sc_sync_if)
if_rele(sc->sc_sync_if);
sc->sc_sync_if = NULL;
- if (imo->imo_membership)
- pfsync_multicast_cleanup(sc);
+ pfsync_multicast_cleanup(sc);
PFSYNC_UNLOCK(sc);
break;
}
@@ -1438,14 +1434,13 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
PFSYNC_BUCKET_UNLOCK(&sc->sc_buckets[c]);
}
- if (imo->imo_membership)
- pfsync_multicast_cleanup(sc);
+ pfsync_multicast_cleanup(sc);
if (sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) {
- error = pfsync_multicast_setup(sc, sifp, mship);
+ error = pfsync_multicast_setup(sc, sifp, imf);
if (error) {
if_rele(sifp);
- free(mship, M_PFSYNC);
+ ip_mfilter_free(imf);
PFSYNC_UNLOCK(sc);
return (error);
}
@@ -2355,7 +2350,8 @@ pfsyncintr(void *arg)
}
static int
-pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp, void *mship)
+pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp,
+ struct in_mfilter *imf)
{
struct ip_moptions *imo = &sc->sc_imo;
int error;
@@ -2363,16 +2359,14 @@ pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp, void *mship)
if (!(ifp->if_flags & IFF_MULTICAST))
return (EADDRNOTAVAIL);
- imo->imo_membership = (struct in_multi **)mship;
- imo->imo_max_memberships = IP_MIN_MEMBERSHIPS;
imo->imo_multicast_vif = -1;
if ((error = in_joingroup(ifp, &sc->sc_sync_peer, NULL,
- &imo->imo_membership[0])) != 0) {
- imo->imo_membership = NULL;
+ &imf->imf_inm)) != 0)
return (error);
- }
- imo->imo_num_memberships++;
+
+ ip_mfilter_init(&imo->imo_head);
+ ip_mfilter_insert(&imo->imo_head, imf);
imo->imo_multicast_ifp = ifp;
imo->imo_multicast_ttl = PFSYNC_DFLTTL;
imo->imo_multicast_loop = 0;
@@ -2384,10 +2378,13 @@ static void
pfsync_multicast_cleanup(struct pfsync_softc *sc)
{
struct ip_moptions *imo = &sc->sc_imo;
+ struct in_mfilter *imf;
- in_leavegroup(imo->imo_membership[0], NULL);
- free(imo->imo_membership, M_PFSYNC);
- imo->imo_membership = NULL;
+ while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
+ ip_mfilter_remove(&imo->imo_head, imf);
+ in_leavegroup(imf->imf_inm, NULL);
+ ip_mfilter_free(imf);
+ }
imo->imo_multicast_ifp = NULL;
}
@@ -2406,7 +2403,7 @@ pfsync_detach_ifnet(struct ifnet *ifp)
* is going away. We do need to ensure we don't try to do
* cleanup later.
*/
- sc->sc_imo.imo_membership = NULL;
+ ip_mfilter_init(&sc->sc_imo.imo_head);
sc->sc_imo.imo_multicast_ifp = NULL;
sc->sc_sync_if = NULL;
}
diff --git a/freebsd/sys/powerpc/include/machine/spr.h b/freebsd/sys/powerpc/include/machine/spr.h
index 9b13c291..228e3955 100644
--- a/freebsd/sys/powerpc/include/machine/spr.h
+++ b/freebsd/sys/powerpc/include/machine/spr.h
@@ -188,6 +188,7 @@
#define IBMPOWERPCA2 0x0049
#define IBMPOWER7PLUS 0x004a
#define IBMPOWER8E 0x004b
+#define IBMPOWER8NVL 0x004c
#define IBMPOWER8 0x004d
#define IBMPOWER9 0x004e
#define MPC860 0x0050
diff --git a/freebsd/sys/sys/sbuf.h b/freebsd/sys/sys/sbuf.h
index 8e958cbe..a3b0a179 100644
--- a/freebsd/sys/sys/sbuf.h
+++ b/freebsd/sys/sys/sbuf.h
@@ -103,6 +103,7 @@ void sbuf_start_section(struct sbuf *, ssize_t *);
ssize_t sbuf_end_section(struct sbuf *, ssize_t, size_t, int);
void sbuf_hexdump(struct sbuf *, const void *, int, const char *,
int);
+int sbuf_count_drain(void *arg, const char *data, int len);
void sbuf_putbuf(struct sbuf *);
#ifdef _KERNEL
diff --git a/freebsd/sys/sys/sleepqueue.h b/freebsd/sys/sys/sleepqueue.h
index 07530e3b..fa16e414 100644
--- a/freebsd/sys/sys/sleepqueue.h
+++ b/freebsd/sys/sys/sleepqueue.h
@@ -85,6 +85,7 @@ struct thread;
#define SLEEPQ_SX 0x03 /* Used by an sx lock. */
#define SLEEPQ_LK 0x04 /* Used by a lockmgr. */
#define SLEEPQ_INTERRUPTIBLE 0x100 /* Sleep is interruptible. */
+#define SLEEPQ_UNFAIR 0x200 /* Unfair wakeup order. */
void init_sleepqueues(void);
int sleepq_abort(struct thread *td, int intrval);
diff --git a/freebsd/sys/sys/systm.h b/freebsd/sys/sys/systm.h
index ae25b843..a52cde01 100644
--- a/freebsd/sys/sys/systm.h
+++ b/freebsd/sys/sys/systm.h
@@ -613,6 +613,7 @@ int pause_sbt(const char *wmesg, sbintime_t sbt, sbintime_t pr,
_sleep((chan), NULL, (pri), (wmesg), (bt), (pr), (flags))
void wakeup(void * chan);
void wakeup_one(void * chan);
+void wakeup_any(void * chan);
/*
* Common `struct cdev *' stuff are declared here to avoid #include poisoning
diff --git a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
index bcf195af..cd38c7cf 100644
--- a/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
+++ b/rtemsbsd/include/machine/rtems-bsd-kernel-namespace.h
@@ -1128,7 +1128,10 @@
#define enc_xform_skipjack _bsd_enc_xform_skipjack
#define eopnotsupp _bsd_eopnotsupp
#define epoch_call _bsd_epoch_call
+#define epoch_drain_callbacks _bsd_epoch_drain_callbacks
+#define epoch_enter _bsd_epoch_enter
#define epoch_enter_preempt _bsd_epoch_enter_preempt
+#define epoch_exit _bsd_epoch_exit
#define epoch_exit_preempt _bsd_epoch_exit_preempt
#define epoch_init _bsd_epoch_init
#define epoch_wait _bsd_epoch_wait
@@ -2278,6 +2281,8 @@
#define ip6_maxfrags _bsd_ip6_maxfrags
#define ip6_maxfragsperpacket _bsd_ip6_maxfragsperpacket
#define ip6_mcast_pmtu _bsd_ip6_mcast_pmtu
+#define ip6_mfilter_alloc _bsd_ip6_mfilter_alloc
+#define ip6_mfilter_free _bsd_ip6_mfilter_free
#define ip6_mforward _bsd_ip6_mforward
#define ip6_mloopback _bsd_ip6_mloopback
#define ip6_mrouter _bsd_ip6_mrouter
@@ -2353,6 +2358,8 @@
#define ip_input _bsd_ip_input
#define ip_insertoptions _bsd_ip_insertoptions
#define ip_mcast_src _bsd_ip_mcast_src
+#define ip_mfilter_alloc _bsd_ip_mfilter_alloc
+#define ip_mfilter_free _bsd_ip_mfilter_free
#define ip_mforward _bsd_ip_mforward
#define ip_mrouter _bsd_ip_mrouter
#define ip_mrouter_done _bsd_ip_mrouter_done
@@ -3986,6 +3993,7 @@
#define sbuf_clear _bsd_sbuf_clear
#define sbuf_clear_flags _bsd_sbuf_clear_flags
#define sbuf_copyin _bsd_sbuf_copyin
+#define sbuf_count_drain _bsd_sbuf_count_drain
#define sbuf_cpy _bsd_sbuf_cpy
#define sbuf_data _bsd_sbuf_data
#define sbuf_delete _bsd_sbuf_delete
@@ -4498,6 +4506,8 @@
#define Skein_512_Output _bsd_Skein_512_Output
#define Skein_512_Process_Block _bsd_Skein_512_Process_Block
#define Skein_512_Update _bsd_Skein_512_Update
+#define Skein_Get64_LSB_First _bsd_Skein_Get64_LSB_First
+#define Skein_Put64_LSB_First _bsd_Skein_Put64_LSB_First
#define skipjack_backwards _bsd_skipjack_backwards
#define skipjack_forwards _bsd_skipjack_forwards
#define sl_compress_init _bsd_sl_compress_init
@@ -5440,6 +5450,7 @@
#define vsnrprintf _bsd_vsnrprintf
#define vsprintf _bsd_vsprintf
#define wakeup _bsd_wakeup
+#define wakeup_any _bsd_wakeup_any
#define wakeup_one _bsd_wakeup_one
#define window_deflate _bsd_window_deflate
#define window_inflate _bsd_window_inflate