claudio and i have been looking at some tun(4) semantics, and want to
move stuff around, but feel constrained because PIPEX has been very
carefully added to tun(4). the PIPEX bits make it hard to rearrange
tun(4), so this moves that functionality out into a separate driver
called pppac(4).

there's not much to say about it except that it does exactly enough for
npppd to work with. the major missing bits are a manpage (which will be
boring), and all the MAKEDEV bits.

so would anyone object if we move forward with this at the hackathon
so we can then pull PIPEX out of tun(4), and in turn start making tun(4)
less complicated?

Index: sys/arch/amd64/amd64/conf.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/conf.c,v
retrieving revision 1.65
diff -u -p -r1.65 conf.c
--- sys/arch/amd64/amd64/conf.c 17 Dec 2019 13:08:54 -0000      1.65
+++ sys/arch/amd64/amd64/conf.c 21 Jan 2020 08:38:59 -0000
@@ -299,6 +299,7 @@ struct cdevsw       cdevsw[] =
        cdev_ipmi_init(NIPMI,ipmi),     /* 96: ipmi */
        cdev_switch_init(NSWITCH,switch), /* 97: switch(4) control interface */
        cdev_fido_init(NFIDO,fido),     /* 98: FIDO/U2F security keys */
+       cdev_pppx_init(NPPPX,pppac),    /* 99: PPP Access Concentrator */
 };
 int    nchrdev = nitems(cdevsw);
 
Index: sys/net/if_pppx.c
===================================================================
RCS file: /cvs/src/sys/net/if_pppx.c,v
retrieving revision 1.70
diff -u -p -r1.70 if_pppx.c
--- sys/net/if_pppx.c   31 Dec 2019 13:48:32 -0000      1.70
+++ sys/net/if_pppx.c   21 Jan 2020 08:38:59 -0000
@@ -1134,3 +1134,508 @@ pppx_if_ioctl(struct ifnet *ifp, u_long 
 }
 
 RBT_GENERATE(pppx_ifs, pppx_if, pxi_entry, pppx_if_cmp);
+
+/*
+ * pppac(4) - PPP Access Concentrator interface
+ */
+
+#include <net/if_tun.h>
+
+struct pppac_softc {
+       struct ifnet    sc_if;
+       unsigned int    sc_dead;
+       dev_t           sc_dev;
+       LIST_ENTRY(pppac_softc)
+                       sc_entry;
+
+       struct mutex    sc_rsel_mtx;
+       struct selinfo  sc_rsel;
+       struct mutex    sc_wsel_mtx;
+       struct selinfo  sc_wsel;
+
+       struct pipex_iface_context
+                       sc_pipex_iface;
+
+       struct mbuf_queue
+                       sc_mq;
+};
+
+LIST_HEAD(pppac_list, pppac_softc);
+
+static void    filt_pppac_rdetach(struct knote *);
+static int     filt_pppac_read(struct knote *, long);
+
+static const struct filterops pppac_rd_filtops = {
+       1,
+       NULL,
+       filt_pppac_rdetach,
+       filt_pppac_read
+};
+
+static void    filt_pppac_wdetach(struct knote *);
+static int     filt_pppac_write(struct knote *, long);
+
+static const struct filterops pppac_wr_filtops = {
+       1,
+       NULL,
+       filt_pppac_wdetach,
+       filt_pppac_write
+};
+
+static struct pppac_list pppac_devs = LIST_HEAD_INITIALIZER(pppac_devs);
+
+static int     pppac_ioctl(struct ifnet *, u_long, caddr_t);
+
+static int     pppac_output(struct ifnet *, struct mbuf *, struct sockaddr *,
+                   struct rtentry *);
+static void    pppac_start(struct ifnet *);
+
+static inline struct pppac_softc *
+pppac_lookup(dev_t dev)
+{
+       struct pppac_softc *sc;
+
+       LIST_FOREACH(sc, &pppac_devs, sc_entry) {
+               if (sc->sc_dev == dev)
+                       return (sc);
+       }
+
+       return (NULL);
+}
+
+void
+pppacattach(int n)
+{
+       pipex_init(); /* to be sure, to be sure */
+}
+
+int
+pppacopen(dev_t dev, int flags, int mode, struct proc *p)
+{
+       struct pppac_softc *sc;
+       struct ifnet *ifp;
+
+       sc = pppac_lookup(dev);
+       if (sc != NULL)
+               return (EBUSY);
+
+       sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
+       sc->sc_dev = dev;
+
+       mtx_init(&sc->sc_rsel_mtx, IPL_SOFTNET);
+       mtx_init(&sc->sc_wsel_mtx, IPL_SOFTNET);
+       mq_init(&sc->sc_mq, IFQ_MAXLEN, IPL_SOFTNET);
+
+       LIST_INSERT_HEAD(&pppac_devs, sc, sc_entry);
+
+       ifp = &sc->sc_if;
+       snprintf(ifp->if_xname, sizeof(ifp->if_xname), "pppac%u", minor(dev));
+
+       ifp->if_softc = sc;
+       ifp->if_type = IFT_L3IPVLAN;
+       ifp->if_hdrlen = sizeof(uint32_t); /* for BPF */;
+       ifp->if_mtu = MAXMCLBYTES - sizeof(uint32_t);
+       ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST;
+       ifp->if_xflags = IFXF_CLONED;
+       ifp->if_rtrequest = p2p_rtrequest; /* XXX */
+       ifp->if_output = pppac_output;
+       ifp->if_start = pppac_start;
+       ifp->if_ioctl = pppac_ioctl;
+
+       if_counters_alloc(ifp);
+       if_attach(ifp);
+       if_alloc_sadl(ifp);
+
+#if NBPFILTER > 0
+       bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
+#endif
+
+       pipex_iface_init(&sc->sc_pipex_iface, ifp);
+
+       return (0);
+}
+
+int
+pppacread(dev_t dev, struct uio *uio, int ioflag)
+{
+       struct pppac_softc *sc = pppac_lookup(dev);
+       struct ifnet *ifp = &sc->sc_if;
+       struct mbuf *m0, *m;
+       int error = 0;
+       size_t len;
+
+       if (!ISSET(ifp->if_flags, IFF_RUNNING))
+               return (EHOSTDOWN);
+
+       m0 = mq_dequeue(&sc->sc_mq);
+       if (m0 == NULL) {
+               if (ISSET(ioflag, IO_NDELAY))
+                       return (EWOULDBLOCK);
+
+               do {
+                       error = tsleep_nsec(sc, (PZERO + 1)|PCATCH,
+                           "pppacrd", INFSLP);
+                       if (error != 0)
+                               return (error);
+
+                       m0 = mq_dequeue(&sc->sc_mq);
+               } while (m0 == NULL);
+       }
+
+       m = m0;
+       while (uio->uio_resid > 0) {
+               len = ulmin(uio->uio_resid, m->m_len);
+               if (len != 0) {
+                       error = uiomove(mtod(m, caddr_t), len, uio);
+                       if (error != 0)
+                               break;
+               }
+
+               m = m->m_next;
+               if (m == NULL)
+                       break;
+       }
+       m_freem(m0);
+
+       return (error);
+}
+
+int
+pppacwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+       struct pppac_softc *sc = pppac_lookup(dev);
+       struct ifnet *ifp = &sc->sc_if;
+       uint32_t proto;
+       int error;
+       struct mbuf *m;
+
+       if (!ISSET(ifp->if_flags, IFF_RUNNING))
+               return (EHOSTDOWN);
+
+       if (uio->uio_resid < ifp->if_hdrlen || uio->uio_resid > MAXMCLBYTES)
+               return (EMSGSIZE);
+
+       m = m_gethdr(M_DONTWAIT, MT_DATA);
+       if (m == NULL)
+               return (ENOMEM);
+
+       if (uio->uio_resid > MINCLSIZE) {
+               m_clget(m, M_WAITOK, uio->uio_resid);
+               if (!ISSET(m->m_flags, M_EXT)) {
+                       m_free(m);
+                       return (ENOMEM);
+               }
+       }
+
+       m->m_pkthdr.len = m->m_len = uio->uio_resid;
+
+       error = uiomove(mtod(m, void *), m->m_len, uio);
+       if (error != 0) {
+               m_freem(m);
+               return (error);
+       }
+
+#if NBPFILTER > 0
+       if (ifp->if_bpf)
+               bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
+#endif
+
+       /* strip the tunnel header */
+       proto = ntohl(*mtod(m, uint32_t *));
+       m_adj(m, sizeof(uint32_t));
+
+       m->m_flags &= ~(M_MCAST|M_BCAST);
+       m->m_pkthdr.ph_ifidx = ifp->if_index;
+       m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
+
+#if NPF > 0
+       pf_pkt_addr_changed(m);
+#endif
+
+       counters_pkt(ifp->if_counters,
+           ifc_ipackets, ifc_ibytes, m->m_pkthdr.len);
+
+       NET_LOCK();
+
+       switch (proto) {
+       case AF_INET:
+               ipv4_input(ifp, m);
+               break;
+#ifdef INET6
+       case AF_INET6:
+               ipv6_input(ifp, m);
+               break;
+#endif
+       default:
+               m_freem(m);
+               error = EAFNOSUPPORT;
+               break;
+       }
+
+       NET_UNLOCK();
+
+       return (error);
+}
+
+int
+pppacioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
+{
+       struct pppac_softc *sc = pppac_lookup(dev);
+       int error = 0;
+
+       switch (cmd) {
+       case TUNSIFMODE: /* make npppd happy */
+               break;
+
+       case FIONBIO:
+               break;
+       case FIONREAD:
+               *(int *)data = mq_hdatalen(&sc->sc_mq);
+               break;
+
+       default:
+               error = pipex_ioctl(&sc->sc_pipex_iface, cmd, data);
+               break;
+       }
+
+       return (error);
+}
+
+int
+pppacpoll(dev_t dev, int events, struct proc *p)
+{
+       struct pppac_softc *sc = pppac_lookup(dev);
+       int revents = 0;
+
+       if (events & (POLLIN | POLLRDNORM)) {
+               if (!mq_empty(&sc->sc_mq))
+                       revents |= events & (POLLIN | POLLRDNORM);
+       }
+       if (events & (POLLOUT | POLLWRNORM))
+               revents |= events & (POLLOUT | POLLWRNORM);
+
+       if (revents == 0) {
+               if (events & (POLLIN | POLLRDNORM))
+                       selrecord(p, &sc->sc_rsel);
+       }
+
+       return (revents);
+}
+
+int
+pppackqfilter(dev_t dev, struct knote *kn)
+{
+       struct pppac_softc *sc = pppac_lookup(dev);
+       struct mutex *mtx;
+       struct klist *klist;
+
+       switch (kn->kn_filter) {
+       case EVFILT_READ:
+               mtx = &sc->sc_rsel_mtx;
+               klist = &sc->sc_rsel.si_note;
+               kn->kn_fop = &pppac_rd_filtops;
+               break;
+       case EVFILT_WRITE:
+               mtx = &sc->sc_wsel_mtx;
+               klist = &sc->sc_wsel.si_note;
+               kn->kn_fop = &pppac_wr_filtops;
+               break;
+       default:
+               return (EINVAL);
+       }
+
+       kn->kn_hook = sc;
+
+       mtx_enter(mtx);
+       SLIST_INSERT_HEAD(klist, kn, kn_selnext);
+       mtx_leave(mtx);
+
+       return (0);
+}
+
+static void
+filt_pppac_rdetach(struct knote *kn)
+{
+       struct pppac_softc *sc;
+       struct klist *klist;
+
+       if (ISSET(kn->kn_status, KN_DETACHED))
+               return;
+
+       sc = kn->kn_hook;
+       klist = &sc->sc_rsel.si_note;
+
+       mtx_enter(&sc->sc_rsel_mtx);
+       SLIST_REMOVE(klist, kn, knote, kn_selnext);
+       mtx_leave(&sc->sc_rsel_mtx);
+}
+
+static int
+filt_pppac_read(struct knote *kn, long hint)
+{
+       struct pppac_softc *sc;
+
+       if (ISSET(kn->kn_status, KN_DETACHED)) {
+               kn->kn_data = 0;
+               return (1);
+       }
+
+       sc = kn->kn_hook;
+
+       kn->kn_data = mq_hdatalen(&sc->sc_mq);
+
+       return (kn->kn_data > 0);
+}
+
+static void
+filt_pppac_wdetach(struct knote *kn)
+{
+       struct pppac_softc *sc;
+       struct klist *klist;
+
+       if (ISSET(kn->kn_status, KN_DETACHED))
+               return;
+
+       sc = kn->kn_hook;
+       klist = &sc->sc_wsel.si_note;
+
+       mtx_enter(&sc->sc_wsel_mtx);
+       SLIST_REMOVE(klist, kn, knote, kn_selnext);
+       mtx_leave(&sc->sc_wsel_mtx);
+}
+
+static int
+filt_pppac_write(struct knote *kn, long hint)
+{
+       /* We're always ready to accept a write. */
+       return (1);
+}
+
+int
+pppacclose(dev_t dev, int flags, int mode, struct proc *p)
+{
+       struct pppac_softc *sc = pppac_lookup(dev);
+       struct ifnet *ifp = &sc->sc_if;
+       int s;
+
+       NET_LOCK();
+       sc->sc_dead = 1;
+       CLR(ifp->if_flags, IFF_RUNNING);
+       NET_UNLOCK();
+
+       s = splhigh();
+       klist_invalidate(&sc->sc_rsel.si_note);
+       klist_invalidate(&sc->sc_wsel.si_note);
+       splx(s);
+
+       pipex_iface_fini(&sc->sc_pipex_iface);
+
+       if_detach(ifp);
+
+       LIST_REMOVE(sc, sc_entry);
+       free(sc, M_DEVBUF, sizeof(*sc));
+
+       return (0);
+}
+
+static int
+pppac_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct pppac_softc *sc = ifp->if_softc;
+       /* struct ifreq *ifr = (struct ifreq *)data; */
+       int error = 0;
+
+       if (sc->sc_dead)
+               return (ENXIO);
+
+       switch (cmd) {
+       case SIOCSIFADDR:
+               SET(ifp->if_flags, IFF_UP); /* XXX cry cry */
+               /* FALLTHROUGH */
+       case SIOCSIFFLAGS:
+               if (ISSET(ifp->if_flags, IFF_UP))
+                       SET(ifp->if_flags, IFF_RUNNING);
+               else
+                       CLR(ifp->if_flags, IFF_RUNNING);
+               break;
+       case SIOCSIFMTU:
+               break;
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               /* XXX */
+               break;
+
+       default:
+               error = ENOTTY;
+               break;
+       }
+
+       return (error);
+}
+
+static int
+pppac_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+    struct rtentry *rt)
+{
+       int error;
+
+       if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
+               error = EHOSTDOWN;
+               goto drop;
+       }
+
+       switch (dst->sa_family) {
+       case AF_INET:
+#ifdef INET6
+       case AF_INET6:
+#endif
+               break;
+       default:
+               error = EAFNOSUPPORT;
+               goto drop;
+       }
+
+       m->m_pkthdr.ph_family = dst->sa_family;
+
+       return (if_enqueue(ifp, m));
+
+drop:
+       m_freem(m);
+       return (error);
+}
+
+static void
+pppac_start(struct ifnet *ifp)
+{
+       struct pppac_softc *sc = ifp->if_softc;
+       struct mbuf *m;
+
+       while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
+#if NBPFILTER > 0
+               if (ifp->if_bpf) {
+                       bpf_mtap_af(ifp->if_bpf, m->m_pkthdr.ph_family, m,
+                           BPF_DIRECTION_OUT);
+               }
+#endif
+
+               m = pipex_output(m, m->m_pkthdr.ph_family, 0,
+                   &sc->sc_pipex_iface);
+               if (m == NULL)
+                       continue;
+
+               m = m_prepend(m, sizeof(uint32_t), M_DONTWAIT);
+               if (m == NULL) {
+                       /* oh well */
+                       continue;
+               }
+               *mtod(m, uint32_t *) = htonl(m->m_pkthdr.ph_family);
+
+               mq_enqueue(&sc->sc_mq, m); /* qdrop? */
+       }
+
+       if (!mq_empty(&sc->sc_mq)) {
+               KERNEL_ASSERT_LOCKED();
+               wakeup(sc);
+               selwakeup(&sc->sc_rsel);
+       }
+}
Index: sys/sys/conf.h
===================================================================
RCS file: /cvs/src/sys/sys/conf.h,v
retrieving revision 1.146
diff -u -p -r1.146 conf.h
--- sys/sys/conf.h      17 Dec 2019 13:08:54 -0000      1.146
+++ sys/sys/conf.h      21 Jan 2020 08:38:59 -0000
@@ -592,6 +592,7 @@ cdev_decl(tun);
 cdev_decl(tap);
 cdev_decl(switch);
 cdev_decl(pppx);
+cdev_decl(pppac);
 
 cdev_decl(random);
 
Index: usr.sbin/npppd/npppd/privsep.c
===================================================================
RCS file: /cvs/src/usr.sbin/npppd/npppd/privsep.c,v
retrieving revision 1.23
diff -u -p -r1.23 privsep.c
--- usr.sbin/npppd/npppd/privsep.c      19 Apr 2017 05:36:13 -0000      1.23
+++ usr.sbin/npppd/npppd/privsep.c      21 Jan 2020 08:38:59 -0000
@@ -986,6 +986,7 @@ privsep_npppd_check_open(struct PRIVSEP_
                { "/dev/bpf",           0,      0 },
                { "/etc/resolv.conf",   0,      1 },
                { "/dev/tun",           1,      0 },
+               { "/dev/pppac",         1,      0 },
                { "/dev/pppx",          1,      0 }
        };
 
@@ -1069,46 +1070,42 @@ privsep_npppd_check_get_user_info(struct
 }
 
 static int
-privsep_npppd_check_get_if_addr(struct PRIVSEP_GET_IF_ADDR_ARG *arg)
+privsep_npppd_check_ifname(const char *ifname)
 {
-       if (startswith(arg->ifname, "tun") || startswith(arg->ifname, "pppx"))
+       if (startswith(ifname, "tun") ||
+           startswith(ifname, "pppac") ||
+           startswith(ifname, "pppx"))
                return (0);
 
-       return (1);
+       return (0);
 }
 
 static int
-privsep_npppd_check_set_if_addr(struct PRIVSEP_SET_IF_ADDR_ARG *arg)
+privsep_npppd_check_get_if_addr(struct PRIVSEP_GET_IF_ADDR_ARG *arg)
 {
-       if (startswith(arg->ifname, "tun") || startswith(arg->ifname, "pppx"))
-               return (0);
+       return (privsep_npppd_check_ifname(arg->ifname));
+}
 
-       return (1);
+static int
+privsep_npppd_check_set_if_addr(struct PRIVSEP_SET_IF_ADDR_ARG *arg)
+{
+       return (privsep_npppd_check_ifname(arg->ifname));
 }
 
 static int
 privsep_npppd_check_del_if_addr(struct PRIVSEP_DEL_IF_ADDR_ARG *arg)
 {
-       if (startswith(arg->ifname, "tun") || startswith(arg->ifname, "pppx"))
-               return (0);
-
-       return (1);
+       return (privsep_npppd_check_ifname(arg->ifname));
 }
 
 static int
 privsep_npppd_check_get_if_flags(struct PRIVSEP_GET_IF_FLAGS_ARG *arg)
 {
-       if (startswith(arg->ifname, "tun") || startswith(arg->ifname, "pppx"))
-               return (0);
-
-       return (1);
+       return (privsep_npppd_check_ifname(arg->ifname));
 }
 
 static int
 privsep_npppd_check_set_if_flags(struct PRIVSEP_SET_IF_FLAGS_ARG *arg)
 {
-       if (startswith(arg->ifname, "tun") || startswith(arg->ifname, "pppx"))
-               return (0);
-
-       return (1);
+       return (privsep_npppd_check_ifname(arg->ifname));
 }

Reply via email to