Page MenuHomeFreeBSD

D51800.id.diff
No OneTemporary

D51800.id.diff

diff --git a/sys/net/pfil.h b/sys/net/pfil.h
--- a/sys/net/pfil.h
+++ b/sys/net/pfil.h
@@ -136,6 +136,7 @@
/* Public functions for pfil hook management by packet filters. */
pfil_hook_t pfil_add_hook(struct pfil_hook_args *);
void pfil_remove_hook(pfil_hook_t);
+void pfil_mbuf_skip_hook(struct mbuf *, pfil_hook_t);
/* Argument structure used by ioctl() and packet filters to set filters. */
struct pfil_link_args {
@@ -192,5 +193,8 @@
#define PFIL_HOOKED_IN(p) (((struct _pfil_head *)(p))->head_nhooksin > 0)
#define PFIL_HOOKED_OUT(p) (((struct _pfil_head *)(p))->head_nhooksout > 0)
+#define MTAG_PFIL 0x7066696c /* hex for ascii pfil */
+#define MTAG_PFIL_NEXT_HOOK 0
+
#endif /* _KERNEL */
#endif /* _NET_PFIL_H_ */
diff --git a/sys/net/pfil.c b/sys/net/pfil.c
--- a/sys/net/pfil.c
+++ b/sys/net/pfil.c
@@ -193,12 +193,33 @@
return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m));
}
+static __always_inline struct pfil_hook**
+pfil_mbuf_get_skip_mtag(struct mbuf **m) {
+ struct m_tag *mtag;
+
+ mtag = m_tag_locate(*m, MTAG_PFIL, MTAG_PFIL_NEXT_HOOK, NULL);
+ if (mtag != NULL)
+ return (struct pfil_hook**)(mtag + 1);
+
+ mtag = m_tag_alloc(MTAG_PFIL, MTAG_PFIL_NEXT_HOOK, sizeof(void*),
+ M_ZERO | M_NOWAIT);
+ if (mtag == NULL) {
+ /* Drop the packet to prevent looping */
+ m_freem(*m);
+ *m = NULL;
+ return NULL;
+ }
+ m_tag_prepend(*m, mtag);
+ return (struct pfil_hook**)(mtag + 1);
+}
+
static __always_inline int
pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp,
int flags, struct inpcb *inp)
{
- struct pfil_link *link;
- pfil_return_t rv;
+ struct pfil_hook **last_hook;
+ struct pfil_link *link;
+ pfil_return_t rv;
NET_EPOCH_ASSERT();
KASSERT((flags & ~(PFIL_IN|PFIL_OUT|PFIL_FWD)) == 0,
@@ -207,17 +228,45 @@
(flags & ~PFIL_FWD) == PFIL_OUT,
("%s: conflicting directions %#x", __func__, flags));
+ if ((last_hook = pfil_mbuf_get_skip_mtag(m)) == NULL)
+ return PFIL_DROPPED;
+
rv = PFIL_PASS;
CK_STAILQ_FOREACH(link, pch, link_chain) {
+ /*
+ * We can't use CK_STAILQ_FOREACH_FROM because list of pfil
+ * hooks could have been modified, so we can't trust that the
+ * pointer is still valid.
+ */
+ if (*last_hook != NULL) {
+ if (*last_hook == link->link_hook)
+ *last_hook = NULL;
+ continue;
+ }
+ /*
+ * Set the last_hook link before link_mbuf_chk() in case
+ * the hook consumes and then re-injects the packet, like
+ * for example dummynet does.
+ */
+ *last_hook = link->link_hook;
rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset,
inp);
if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) {
MPASS(*m == NULL);
- break;
+ return (rv);
} else {
MPASS(*m != NULL);
}
+ /*
+ * Find the last_hook tag again, in case the pfil hook
+ * has changed *m, like for example happens for mbufs
+ * reassempled by pf.
+ */
+ if ((last_hook = pfil_mbuf_get_skip_mtag(m)) == NULL)
+ return PFIL_DROPPED;
+ *last_hook = NULL;
}
+ *last_hook = NULL;
return (rv);
}
@@ -508,6 +557,18 @@
free(hook, M_PFIL);
}
+/*
+ * Modify last hook tracking of pfil_mbuf_common to skip to after the given hook
+ */
+void pfil_mbuf_skip_hook(struct mbuf *m, pfil_hook_t hook)
+{
+ struct m_tag *pfil_mtag;
+
+ pfil_mtag = m_tag_locate(m, MTAG_PFIL, MTAG_PFIL_NEXT_HOOK, NULL);
+ MPASS(pfil_mtag != NULL);
+ *(pfil_hook_t *)(pfil_mtag + 1) = hook;
+}
+
/*
* Internal: Remove a pfil hook from a hook chain.
*/

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 16, 10:30 AM (14 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31596226
Default Alt Text
D51800.id.diff (3 KB)

Event Timeline