diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2020-01-22 23:06:06 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2020-01-22 23:06:06 +0000 |
commit | 09ca2c2fff3a41263feb1130e002f81e36bb9fb8 (patch) | |
tree | 86b76c681f274f3e6e6134d1134d1a1745af1b93 | |
parent | 03ad2ae8fbe3a6243cee33b04a2f1a7287062ec1 (diff) |
add pppac(4) code for a dedicated PPP Access Concentrator interface.
this is so we can pull the PIPEX code out of tun(4), which in turn
will let us lock down tun(4) functionality and start moving the
code around.
ok claudio@ yasuoka@
-rw-r--r-- | sys/net/if_pppx.c | 507 | ||||
-rw-r--r-- | sys/sys/conf.h | 3 |
2 files changed, 508 insertions, 2 deletions
diff --git a/sys/net/if_pppx.c b/sys/net/if_pppx.c index 9bdbee78e6a..4d596249e4b 100644 --- a/sys/net/if_pppx.c +++ b/sys/net/if_pppx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_pppx.c,v 1.70 2019/12/31 13:48:32 visa Exp $ */ +/* $OpenBSD: if_pppx.c,v 1.71 2020/01/22 23:06:05 dlg Exp $ */ /* * Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org> @@ -1134,3 +1134,508 @@ pppx_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) } 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); + } +} diff --git a/sys/sys/conf.h b/sys/sys/conf.h index c4317cd1e44..b762fb5b2ac 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.h,v 1.147 2020/01/21 16:16:23 mpi Exp $ */ +/* $OpenBSD: conf.h,v 1.148 2020/01/22 23:06:05 dlg Exp $ */ /* $NetBSD: conf.h,v 1.33 1996/05/03 20:03:32 christos Exp $ */ /*- @@ -601,6 +601,7 @@ cdev_decl(tun); cdev_decl(tap); cdev_decl(switch); cdev_decl(pppx); +cdev_decl(pppac); cdev_decl(random); |