summaryrefslogtreecommitdiff
path: root/sys/net/if_pfsync.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net/if_pfsync.c')
-rw-r--r--sys/net/if_pfsync.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c
new file mode 100644
index 00000000000..a2b8b25c8cf
--- /dev/null
+++ b/sys/net/if_pfsync.c
@@ -0,0 +1,359 @@
+/* $OpenBSD: if_pfsync.c,v 1.1 2002/11/29 18:25:22 mickey Exp $ */
+
+/*
+ * Copyright (c) 2002 Michael Shalayeff
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bpfilter.h"
+#include "pfsync.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/timeout.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#endif
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/nd6.h>
+#endif /* INET6 */
+
+#include <net/pfvar.h>
+#include <net/if_pfsync.h>
+
+#define PFSYNCMTU \
+ (sizeof(struct pfsync_header) + sizeof(struct pf_state) * 4)
+#define PFSYNC_MINMTU \
+ (sizeof(struct pfsync_header) + sizeof(struct pf_state))
+
+#ifdef PFSYNCDEBUG
+#define DPRINTF(x) do { if (pfsyncdebug) printf x ; } while (0)
+int pfsyncdebug;
+#else
+#define DPRINTF(x)
+#endif
+
+struct pfsync_softc pfsyncif;
+
+void pfsyncattach(int);
+int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+int pfsyncioctl(struct ifnet *, u_long, caddr_t);
+void pfsyncrtrequest(int, struct rtentry *, struct sockaddr *);
+void pfsyncstart(struct ifnet *);
+
+struct mbuf *pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action);
+int pfsync_sendout(struct pfsync_softc *sc);
+void pfsync_timeout(void *v);
+
+extern int ifqmaxlen;
+
+void
+pfsyncattach(int npfsync)
+{
+ struct ifnet *ifp;
+
+ pfsyncif.sc_mbuf = NULL;
+ pfsyncif.sc_ptr = NULL;
+ pfsyncif.sc_count = 8;
+ ifp = &pfsyncif.sc_if;
+ strcpy(ifp->if_xname, "pfsync0");
+ ifp->if_softc = &pfsyncif;
+ ifp->if_mtu = PFSYNCMTU;
+ ifp->if_ioctl = pfsyncioctl;
+ ifp->if_output = pfsyncoutput;
+ ifp->if_start = pfsyncstart;
+ ifp->if_type = IFT_PFSYNC;
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_hdrlen = PFSYNC_HDRLEN;
+ timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
+ if_attach(ifp);
+ if_alloc_sadl(ifp);
+
+#if NBPFILTER > 0
+ bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
+#endif
+}
+
+/*
+ * Start output on the pfsync interface.
+ */
+void
+pfsyncstart(struct ifnet *ifp)
+{
+ struct mbuf *m;
+ int s;
+
+ for (;;) {
+ s = splimp();
+ IF_DROP(&ifp->if_snd);
+ IF_DEQUEUE(&ifp->if_snd, m);
+ splx(s);
+
+ if (m == NULL)
+ return;
+ else
+ m_freem(m);
+ }
+}
+
+int
+pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt)
+{
+ m_freem(m);
+ return (0);
+}
+
+/* ARGSUSED */
+void
+pfsyncrtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa)
+{
+ if (rt)
+ rt->rt_rmx.rmx_mtu = PFSYNCMTU;
+}
+
+/* ARGSUSED */
+int
+pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct pfsync_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ case SIOCAIFADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP)
+ ifp->if_flags |= IFF_RUNNING;
+ else
+ ifp->if_flags &= ~IFF_RUNNING;
+ break;
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < PFSYNC_MINMTU)
+ return (EINVAL);
+ if (ifr->ifr_mtu > MCLBYTES)
+ ifr->ifr_mtu = MCLBYTES;
+ s = splnet();
+ if (ifr->ifr_mtu < ifp->if_mtu)
+ pfsync_sendout(sc);
+ sc->sc_count = (ifr->ifr_mtu - sizeof(struct pfsync_header)) /
+ sizeof(struct pf_state);
+ ifp->if_mtu = sizeof(struct pfsync_header) +
+ sc->sc_count * sizeof(struct pf_state);
+ splx(s);
+ break;
+ default:
+ return (ENOTTY);
+ }
+
+ return (0);
+}
+
+struct mbuf *
+pfsync_get_mbuf(sc, action)
+ struct pfsync_softc *sc;
+ u_int8_t action;
+{
+ extern int hz;
+ struct pfsync_header *h;
+ struct mbuf *m;
+ int len;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ sc->sc_if.if_oerrors++;
+ return (NULL);
+ }
+
+ len = sc->sc_if.if_mtu;
+ if (len > MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ sc->sc_if.if_oerrors++;
+ return (NULL);
+ }
+ }
+ m->m_pkthdr.rcvif = NULL;
+ m->m_pkthdr.len = m->m_len = len;
+ MH_ALIGN(m, m->m_len);
+
+ h = mtod(m, struct pfsync_header *);
+ h->version = PFSYNC_VERSION;
+ h->af = 0;
+ h->count = 0;
+ h->action = action;
+
+ sc->sc_mbuf = m;
+ sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN);
+ timeout_add(&sc->sc_tmo, hz);
+
+ return (m);
+}
+
+int
+pfsync_pack_state(action, st)
+ u_int8_t action;
+ struct pf_state *st;
+{
+ extern struct timeval time;
+ struct ifnet *ifp = &pfsyncif.sc_if;
+ struct pfsync_softc *sc = ifp->if_softc;
+ struct pfsync_header *h;
+ struct pf_state *sp;
+ struct pf_rule *r = st->rule.ptr;
+ struct mbuf *m;
+ u_long secs;
+ int s, ret;
+
+ if (action >= PFSYNC_ACT_MAX)
+ return (EINVAL);
+
+ s = splnet();
+ m = sc->sc_mbuf;
+ if (m == NULL) {
+ if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
+ splx(s);
+ return (ENOMEM);
+ }
+ h = mtod(m, struct pfsync_header *);
+ } else {
+ h = mtod(m, struct pfsync_header *);
+ if (h->action != action) {
+ pfsync_sendout(sc);
+ if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
+ splx(s);
+ return (ENOMEM);
+ }
+ h = mtod(m, struct pfsync_header *);
+ }
+ }
+
+ sp = sc->sc_ptr++;
+ h->count++;
+ bzero(sp, sizeof(*sp));
+
+ sp->lan = st->lan; HTONS(sp->lan.port);
+ sp->gwy = st->gwy; HTONS(sp->gwy.port);
+ sp->ext = st->ext; HTONS(sp->ext.port);
+
+ pf_state_peer_hton(&st->src, &sp->src);
+ pf_state_peer_hton(&st->dst, &sp->dst);
+
+ sp->rt_addr = st->rt_addr;
+ secs = time.tv_sec;
+ sp->creation = htonl(secs - st->creation);
+ if (st->expire <= secs)
+ sp->expire = htonl(0);
+ else
+ sp->expire = htonl(st->expire - secs);
+ sp->packets = htonl(st->packets);
+ sp->bytes = htonl(st->bytes);
+ if (r == NULL)
+ sp->rule.nr = htonl(-1);
+ else
+ sp->rule.nr = htonl(r->nr);
+ sp->af = st->af;
+ sp->proto = st->proto;
+ sp->direction = st->direction;
+ sp->log = st->log;
+ sp->allow_opts = st->allow_opts;
+
+ ret = 0;
+ if (h->count == sc->sc_count)
+ ret = pfsync_sendout(sc);
+
+ splx(s);
+ return (0);
+}
+
+int
+pfsync_clear_state(st)
+ struct pf_state *st;
+{
+ struct ifnet *ifp = &pfsyncif.sc_if;
+ struct pfsync_softc *sc = ifp->if_softc;
+ struct mbuf *m = sc->sc_mbuf;
+ int s, ret;
+
+ s = splnet();
+ if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) {
+ splx(s);
+ return (ENOMEM);
+ }
+
+ ret = (pfsync_sendout(sc));
+ splx(s);
+ return (ret);
+}
+
+void
+pfsync_timeout(void *v)
+{
+ struct pfsync_softc *sc = v;
+ int s;
+
+ s = splnet();
+ pfsync_sendout(sc);
+ splx(s);
+}
+
+int
+pfsync_sendout(sc)
+ struct pfsync_softc *sc;
+{
+ struct ifnet *ifp = &sc->sc_if;
+ struct mbuf *m = sc->sc_mbuf;
+
+ timeout_del(&sc->sc_tmo);
+ sc->sc_mbuf = NULL;
+ sc->sc_ptr = NULL;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+
+ m_freem(m);
+
+ return (0);
+}