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 + +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)); }