pfsync: support deferring IPv6 packets

When we send out a deferred packet we must make sure to call
ip6_output() for IPv6 packets. If not we might end up attempting to
ip_fragment() an IPv6 packet, which could lead to us reading outside of
the mbuf.

PR:		268246
Reviewed by:	melifaro, zlei
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D38586
This commit is contained in:
Kristof Provost 2023-02-14 07:11:38 +01:00
parent 889a9acc54
commit 9a1cab6d79

View File

@ -103,9 +103,13 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netpfil/pf/pfsync_nv.h>
struct pfsync_bucket;
struct pfsync_softc;
union inet_template {
struct ip ipv4;
@ -171,6 +175,7 @@ static void pfsync_q_ins(struct pf_kstate *, int, bool);
static void pfsync_q_del(struct pf_kstate *, bool, struct pfsync_bucket *);
static void pfsync_update_state(struct pf_kstate *);
static void pfsync_tx(struct pfsync_softc *, struct mbuf *);
struct pfsync_upd_req_item {
TAILQ_ENTRY(pfsync_upd_req_item) ur_entry;
@ -187,8 +192,6 @@ struct pfsync_deferral {
struct mbuf *pd_m;
};
struct pfsync_sofct;
struct pfsync_bucket
{
int b_id;
@ -1837,13 +1840,7 @@ pfsync_defer_tmo(void *arg)
free(pd, M_PFSYNC);
PFSYNC_BUCKET_UNLOCK(b);
switch (sc->sc_sync_peer.ss_family) {
#ifdef INET
case AF_INET:
ip_output(m, NULL, NULL, 0, NULL, NULL);
break;
#endif
}
pfsync_tx(sc, m);
pf_release_state(st);
@ -2326,6 +2323,56 @@ pfsync_push_all(struct pfsync_softc *sc)
}
}
static void
pfsync_tx(struct pfsync_softc *sc, struct mbuf *m)
{
struct ip *ip;
int error, af;
ip = mtod(m, struct ip *);
MPASS(ip->ip_v == IPVERSION || ip->ip_v == (IPV6_VERSION >> 4));
af = ip->ip_v == IPVERSION ? AF_INET : AF_INET6;
/*
* We distinguish between a deferral packet and our
* own pfsync packet based on M_SKIP_FIREWALL
* flag. This is XXX.
*/
switch (af) {
#ifdef INET
case AF_INET:
if (m->m_flags & M_SKIP_FIREWALL) {
error = ip_output(m, NULL, NULL, 0,
NULL, NULL);
} else {
error = ip_output(m, NULL, NULL,
IP_RAWOUTPUT, &sc->sc_imo, NULL);
}
break;
#endif
#ifdef INET6
case AF_INET6:
if (m->m_flags & M_SKIP_FIREWALL) {
error = ip6_output(m, NULL, NULL, 0,
NULL, NULL, NULL);
} else {
MPASS(false);
/* We don't support pfsync over IPv6. */
/*error = ip6_output(m, NULL, NULL,
IP_RAWOUTPUT, &sc->sc_imo6, NULL);*/
}
break;
#endif
}
if (error == 0)
V_pfsyncstats.pfsyncs_opackets++;
else
V_pfsyncstats.pfsyncs_oerrors++;
}
static void
pfsyncintr(void *arg)
{
@ -2333,7 +2380,7 @@ pfsyncintr(void *arg)
struct pfsync_softc *sc = arg;
struct pfsync_bucket *b;
struct mbuf *m, *n;
int c, error;
int c;
NET_EPOCH_ENTER(et);
CURVNET_SET(sc->sc_ifp->if_vnet);
@ -2353,29 +2400,7 @@ pfsyncintr(void *arg)
n = m->m_nextpkt;
m->m_nextpkt = NULL;
/*
* We distinguish between a deferral packet and our
* own pfsync packet based on M_SKIP_FIREWALL
* flag. This is XXX.
*/
switch (sc->sc_sync_peer.ss_family) {
#ifdef INET
case AF_INET:
if (m->m_flags & M_SKIP_FIREWALL) {
error = ip_output(m, NULL, NULL, 0,
NULL, NULL);
} else {
error = ip_output(m, NULL, NULL,
IP_RAWOUTPUT, &sc->sc_imo, NULL);
}
break;
#endif
}
if (error == 0)
V_pfsyncstats.pfsyncs_opackets++;
else
V_pfsyncstats.pfsyncs_oerrors++;
pfsync_tx(sc, m);
}
}
CURVNET_RESTORE();