summaryrefslogtreecommitdiff
path: root/sys/net/if_pppx.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2010-09-22 13:03:49 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2010-09-22 13:03:49 +0000
commit7bf133becb3b3e6caff767958a5bcba5af5e926f (patch)
tree38b6cc220c9939806ed849e8ec6964b2dec92c79 /sys/net/if_pppx.c
parentdd7ff3035ba24dde9fad643ba832183988a4f9f7 (diff)
Add a new interface pppx(4) -- the ppp multiplexer to be used with npppd
and pipex. pppx(4) creates an interface whenever a session is created so that altq and pf can work on these. Started by dlg@ debugged and made usable by myself OK dlg@ yasuoka@ deraadt@
Diffstat (limited to 'sys/net/if_pppx.c')
-rw-r--r--sys/net/if_pppx.c1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/sys/net/if_pppx.c b/sys/net/if_pppx.c
new file mode 100644
index 00000000000..033f878435b
--- /dev/null
+++ b/sys/net/if_pppx.c
@@ -0,0 +1,1005 @@
+/* $OpenBSD: if_pppx.c,v 1.1 2010/09/22 13:03:48 claudio Exp $ */
+
+/*
+ * Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2010 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (c) 2009 Internet Initiative Japan Inc.
+ * 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 AND CONTRIBUTORS ``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 CONTRIBUTORS 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 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/queue.h>
+#include <sys/rwlock.h>
+#include <sys/pool.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#ifdef INET
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#endif
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/nd6.h>
+#endif /* INET6 */
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <net/ppp_defs.h>
+#include <net/ppp-comp.h>
+#include <crypto/arc4.h>
+
+#ifdef PIPEX
+#include <net/pipex.h>
+#include <net/pipex_local.h>
+#else
+#error PIPEX option not enabled
+#endif
+
+#ifdef PPPX_DEBUG
+#define PPPX_D_INIT (1<<0)
+
+int pppxdebug = 0;
+
+#define DPRINTF(_m, _p...) do { \
+ if (ISSET(pppxdebug, (_m))) \
+ printf(_p); \
+ } while (0)
+#else
+#define DPRINTF(_m, _p...) /* _m, _p */
+#endif
+
+
+struct pppx_if;
+
+struct pppx_dev {
+ LIST_ENTRY(pppx_dev) pxd_entry;
+ int pxd_unit;
+
+ /* kq shizz */
+ struct selinfo pxd_rsel;
+ struct mutex pxd_rsel_mtx;
+ struct selinfo pxd_wsel;
+ struct mutex pxd_wsel_mtx;
+
+ /* queue of packets for userland to service - protected by splnet */
+ struct ifqueue pxd_svcq;
+ int pxd_waiting;
+ LIST_HEAD(,pppx_if) pxd_pxis;
+};
+
+struct rwlock pppx_devs_lk = RWLOCK_INITIALIZER("pppxdevs");
+LIST_HEAD(, pppx_dev) pppx_devs = LIST_HEAD_INITIALIZER(&pppx_devs);
+struct pool *pppx_if_pl;
+
+struct pppx_dev *pppx_dev_lookup(int);
+struct pppx_dev *pppx_dev2pxd(int);
+
+struct pppx_if_key {
+ int pxik_session_id;
+ int pxik_protocol;
+};
+
+int pppx_if_cmp(struct pppx_if *, struct pppx_if *);
+
+struct pppx_if {
+ struct pppx_if_key pxi_key; /* must be first in the struct */
+
+ RB_ENTRY(pppx_if) pxi_entry;
+ LIST_ENTRY(pppx_if) pxi_list;
+
+ int pxi_unit;
+ struct ifnet pxi_if;
+ struct pipex_session pxi_session;
+ struct pipex_iface_context pxi_ifcontext;
+};
+
+struct rwlock pppx_ifs_lk = RWLOCK_INITIALIZER("pppxifs");
+RB_HEAD(pppx_ifs, pppx_if) pppx_ifs = RB_INITIALIZER(&pppx_ifs);
+RB_PROTOTYPE(pppx_ifs, pppx_if, pxi_entry, pppx_if_cmp);
+
+int pppx_if_next_unit(void);
+struct pppx_if *pppx_if_find(struct pppx_dev *, int, int);
+int pppx_add_session(struct pppx_dev *,
+ struct pipex_session_req *);
+int pppx_del_session(struct pppx_dev *,
+ struct pipex_session_close_req *);
+
+void pppx_if_destroy(struct pppx_dev *, struct pppx_if *);
+void pppx_if_start(struct ifnet *);
+int pppx_if_output(struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *);
+int pppx_if_ioctl(struct ifnet *, u_long, caddr_t);
+
+
+void pppxattach(int);
+
+void filt_pppx_rdetach(struct knote *);
+int filt_pppx_read(struct knote *, long);
+
+struct filterops pppx_rd_filtops = {
+ 1,
+ NULL,
+ filt_pppx_rdetach,
+ filt_pppx_read
+};
+
+void filt_pppx_wdetach(struct knote *);
+int filt_pppx_write(struct knote *, long);
+
+struct filterops pppx_wr_filtops = {
+ 1,
+ NULL,
+ filt_pppx_wdetach,
+ filt_pppx_write
+};
+
+struct pppx_dev *
+pppx_dev_lookup(dev_t dev)
+{
+ struct pppx_dev *pxd;
+ int unit = minor(dev);
+
+ /* must hold pppx_devs_lk */
+
+ LIST_FOREACH(pxd, &pppx_devs, pxd_entry) {
+ if (pxd->pxd_unit == unit)
+ return (pxd);
+ }
+
+ return (NULL);
+}
+
+struct pppx_dev *
+pppx_dev2pxd(dev_t dev)
+{
+ struct pppx_dev *pxd;
+
+ rw_enter_read(&pppx_devs_lk);
+ pxd = pppx_dev_lookup(dev);
+ rw_exit_read(&pppx_devs_lk);
+
+ return (pxd);
+}
+
+void
+pppxattach(int n)
+{
+ pipex_init();
+}
+
+int
+pppxopen(dev_t dev, int flags, int mode, struct proc *p)
+{
+ struct pppx_dev *pxd;
+ int rv = 0;
+
+ rv = rw_enter(&pppx_devs_lk, RW_WRITE | RW_INTR);
+ if (rv != 0)
+ return (rv);
+
+ pxd = pppx_dev_lookup(dev);
+ if (pxd != NULL) {
+ rv = EBUSY;
+ goto out;
+ }
+
+ if (LIST_EMPTY(&pppx_devs) && pppx_if_pl == NULL) {
+ pppx_if_pl = malloc(sizeof(*pppx_if_pl), M_DEVBUF, M_WAITOK);
+ pool_init(pppx_if_pl, sizeof(struct pppx_if), 0, 0, 0,
+ "pppxif", &pool_allocator_nointr);
+ }
+
+ pxd = malloc(sizeof(*pxd), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ mtx_init(&pxd->pxd_rsel_mtx, IPL_NET);
+ mtx_init(&pxd->pxd_wsel_mtx, IPL_NET);
+ LIST_INIT(&pxd->pxd_pxis);
+
+ IFQ_SET_MAXLEN(&pxd->pxd_svcq, 128);
+ LIST_INSERT_HEAD(&pppx_devs, pxd, pxd_entry);
+
+out:
+ rw_exit(&pppx_devs_lk);
+ return (rv);
+}
+
+int
+pppxread(dev_t dev, struct uio *uio, int ioflag)
+{
+ struct pppx_dev *pxd = pppx_dev2pxd(dev);
+ struct mbuf *m, *m0;
+ int error = 0;
+ int len, s;
+
+ if (!pxd)
+ return (ENXIO);
+
+ s = splnet();
+ for (;;) {
+ IF_DEQUEUE(&pxd->pxd_svcq, m);
+ if (m != NULL)
+ break;
+
+ if (ISSET(ioflag, IO_NDELAY)) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+
+ pxd->pxd_waiting = 1;
+ error = tsleep(pxd, (PZERO + 1)|PCATCH, "pppxread", 0);
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ }
+ splx(s);
+
+ while (m0 != NULL && uio->uio_resid > 0 && error == 0) {
+ len = min(uio->uio_resid, m0->m_len);
+ if (len != 0)
+ error = uiomove(mtod(m0, caddr_t), len, uio);
+ MFREE(m0, m);
+ m0 = m;
+ }
+
+ if (m0 != NULL)
+ m_freem(m0);
+
+ return (error);
+}
+
+int
+pppxwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+/* struct pppx_dev *pxd = pppx_dev2pxd(dev); */
+ struct pppx_hdr *th;
+ struct mbuf *top, **mp, *m;
+ struct ifqueue *ifq;
+ int tlen, mlen;
+ int isr, s, error = 0;
+
+ if (uio->uio_resid < sizeof(*th) || uio->uio_resid > MCLBYTES)
+ return (EMSGSIZE);
+
+ tlen = uio->uio_resid;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (ENOBUFS);
+ mlen = MHLEN;
+ if (uio->uio_resid >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ m_free(m);
+ return (ENOBUFS);
+ }
+ mlen = MCLBYTES;
+ }
+
+ top = NULL;
+ mp = &top;
+
+ while (error == 0 && uio->uio_resid > 0) {
+ m->m_len = min(mlen, uio->uio_resid);
+ error = uiomove(mtod (m, caddr_t), m->m_len, uio);
+ *mp = m;
+ mp = &m->m_next;
+ if (error == 0 && uio->uio_resid > 0) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ mlen = MLEN;
+ if (uio->uio_resid >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ error = ENOBUFS;
+ m_free(m);
+ break;
+ }
+ mlen = MCLBYTES;
+ }
+ }
+ }
+
+ if (error) {
+ if (top != NULL)
+ m_freem(top);
+ return (error);
+ }
+
+ top->m_pkthdr.len = tlen;
+
+ /* strip the tunnel header */
+ th = mtod(top, struct pppx_hdr *);
+ m_adj(top, sizeof(struct pppx_hdr));
+
+ switch (ntohl(th->pppx_proto)) {
+#ifdef INET
+ case AF_INET:
+ ifq = &ipintrq;
+ isr = NETISR_IP;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ ifq = &ip6intrq;
+ isr = NETISR_IPV6;
+ break;
+#endif
+ default:
+ m_freem(top);
+ return (EAFNOSUPPORT);
+ }
+
+ s = splnet();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ splx(s);
+ m_freem(top);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(ifq, top);
+ schednetisr(isr);
+ splx(s);
+
+ return (error);
+}
+
+int
+pppxioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
+{
+ struct pppx_dev *pxd = pppx_dev2pxd(dev);
+ int error = 0;
+
+ switch (cmd) {
+ case PIPEXSMODE:
+ /*
+ * npppd always enables on open, and only disables before
+ * closing. we cheat and let open and close do that, so lie
+ * to npppd.
+ */
+ break;
+ case PIPEXGMODE:
+ *(int *)addr = 1;
+ break;
+
+ case PIPEXASESSION:
+ error = pppx_add_session(pxd,
+ (struct pipex_session_req *)addr);
+ break;
+ case PIPEXDSESSION:
+ error = pppx_del_session(pxd,
+ (struct pipex_session_close_req *)addr);
+ break;
+
+ case PIPEXCSESSION:
+ error = pipex_config_session(
+ (struct pipex_session_config_req *)addr);
+ return (error);
+
+ case PIPEXGSTAT:
+ error = pipex_get_stat((struct pipex_session_stat_req *)addr);
+ return (error);
+
+ case PIPEXGCLOSED:
+ error = pipex_get_closed((struct pipex_session_list_req *)addr);
+ return (error);
+ case FIONBIO:
+ case FIOASYNC:
+ case FIONREAD:
+ return (0);
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+
+int
+pppxpoll(dev_t dev, int events, struct proc *p)
+{
+ struct pppx_dev *pxd = pppx_dev2pxd(dev);
+ int s, revents = 0;
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ s = splnet();
+ if (!IF_IS_EMPTY(&pxd->pxd_svcq))
+ revents |= events & (POLLIN | POLLRDNORM);
+ splx(s);
+ }
+ if (events & (POLLOUT | POLLWRNORM))
+ revents |= events & (POLLOUT | POLLWRNORM);
+
+ if (revents == 0) {
+ if (events & (POLLIN | POLLRDNORM))
+ selrecord(p, &pxd->pxd_rsel);
+ }
+
+ return (revents);
+}
+
+int
+pppxkqfilter(dev_t dev, struct knote *kn)
+{
+ struct pppx_dev *pxd = pppx_dev2pxd(dev);
+ struct mutex *mtx;
+ struct klist *klist;
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ mtx = &pxd->pxd_rsel_mtx;
+ klist = &pxd->pxd_rsel.si_note;
+ kn->kn_fop = &pppx_rd_filtops;
+ break;
+ case EVFILT_WRITE:
+ mtx = &pxd->pxd_wsel_mtx;
+ klist = &pxd->pxd_wsel.si_note;
+ kn->kn_fop = &pppx_wr_filtops;
+ break;
+ default:
+ return (1);
+ }
+
+ kn->kn_hook = (caddr_t)pxd;
+
+ mtx_enter(mtx);
+ SLIST_INSERT_HEAD(klist, kn, kn_selnext);
+ mtx_leave(mtx);
+
+ return (0);
+}
+
+void
+filt_pppx_rdetach(struct knote *kn)
+{
+ struct pppx_dev *pxd = (struct pppx_dev *)kn->kn_hook;
+ struct klist *klist = &pxd->pxd_rsel.si_note;
+
+ if (ISSET(kn->kn_status, KN_DETACHED))
+ return;
+
+ mtx_enter(&pxd->pxd_rsel_mtx);
+ SLIST_REMOVE(klist, kn, knote, kn_selnext);
+ mtx_leave(&pxd->pxd_rsel_mtx);
+}
+
+int
+filt_pppx_read(struct knote *kn, long hint)
+{
+ struct pppx_dev *pxd = (struct pppx_dev *)kn->kn_hook;
+ int s, event = 0;
+
+ if (ISSET(kn->kn_status, KN_DETACHED)) {
+ kn->kn_data = 0;
+ return (1);
+ }
+
+ s = splnet();
+ if (!IF_IS_EMPTY(&pxd->pxd_svcq)) {
+ event = 1;
+ kn->kn_data = pxd->pxd_svcq.ifq_len;
+ }
+ splx(s);
+
+ return (event);
+}
+
+void
+filt_pppx_wdetach(struct knote *kn)
+{
+ struct pppx_dev *pxd = (struct pppx_dev *)kn->kn_hook;
+ struct klist *klist = &pxd->pxd_wsel.si_note;
+
+ if (ISSET(kn->kn_status, KN_DETACHED))
+ return;
+
+ mtx_enter(&pxd->pxd_wsel_mtx);
+ SLIST_REMOVE(klist, kn, knote, kn_selnext);
+ mtx_leave(&pxd->pxd_wsel_mtx);
+}
+
+int
+filt_pppx_write(struct knote *kn, long hint)
+{
+ /* We're always ready to accept a write. */
+ return (1);
+}
+
+int
+pppxclose(dev_t dev, int flags, int mode, struct proc *p)
+{
+ struct pppx_dev *pxd;
+ struct pppx_if *pxi;
+ int s;
+
+ rw_enter_write(&pppx_devs_lk);
+
+ pxd = pppx_dev_lookup(dev);
+
+ /* XXX */
+ while ((pxi = LIST_FIRST(&pxd->pxd_pxis)))
+ pppx_if_destroy(pxd, pxi);
+
+ LIST_REMOVE(pxd, pxd_entry);
+
+ s = splnet();
+ IF_PURGE(&pxd->pxd_svcq);
+ splx(s);
+
+ free(pxd, M_DEVBUF);
+
+ if (LIST_EMPTY(&pppx_devs)) {
+ pool_destroy(pppx_if_pl);
+ free(pppx_if_pl, M_DEVBUF);
+ pppx_if_pl = NULL;
+ }
+
+ rw_exit_write(&pppx_devs_lk);
+ return (0);
+}
+
+int
+pppx_if_cmp(struct pppx_if *a, struct pppx_if *b)
+{
+ return memcmp(&a->pxi_key, &b->pxi_key, sizeof(a->pxi_key));
+}
+
+int
+pppx_if_next_unit(void)
+{
+ struct pppx_if *pxi;
+ int unit = 0;
+
+ rw_assert_wrlock(&pppx_ifs_lk);
+
+ /* this is safe without splnet since we're not modifying it */
+ RB_FOREACH(pxi, pppx_ifs, &pppx_ifs) {
+ if (pxi->pxi_unit >= unit)
+ unit = pxi->pxi_unit + 1;
+ }
+
+ return (unit);
+}
+
+struct pppx_if *
+pppx_if_find(struct pppx_dev *pxd, int session_id, int protocol)
+{
+ struct pppx_if s, *p;
+
+ s.pxi_key.pxik_session_id = session_id;
+ s.pxi_key.pxik_protocol = protocol;
+
+ rw_enter_read(&pppx_ifs_lk);
+ p = RB_FIND(pppx_ifs, &pppx_ifs, &s);
+ rw_exit_read(&pppx_ifs_lk);
+
+ return (p);
+}
+
+int
+pppx_add_session(struct pppx_dev *pxd, struct pipex_session_req *req)
+{
+ struct pppx_if *pxi;
+ struct pipex_session *session;
+ struct pipex_hash_head *chain;
+ struct ifnet *ifp;
+ int unit, s, error = 0;
+#ifdef PIPEX_PPPOE
+ struct ifnet *over_ifp = NULL;
+ struct ether_header *eh;
+ struct sockaddr peer_addr;
+#endif
+
+ switch (req->pr_protocol) {
+#ifdef PIPEX_PPTP
+ case PIPEX_PROTO_PPTP:
+ break;
+#endif
+#ifdef PIPEX_PPPOE
+ case PIPEX_PROTO_PPPOE:
+ over_ifp = ifunit(req->pr_proto.pppoe.over_ifname);
+ if (over_ifp == NULL)
+ return (EINVAL);
+ bzero(&peer_addr, sizeof(peer_addr));
+ peer_addr.sa_family = AF_UNSPEC;
+ eh = (struct ether_header *)&peer_addr.sa_data;
+ eh->ether_type = htons(ETHERTYPE_PPPOE);
+ memcpy(&eh->ether_dhost, &req->pr_proto.pppoe.peer_address,
+ sizeof(eh->ether_dhost));
+ break;
+#endif
+ default:
+ return (EPROTONOSUPPORT);
+ }
+
+ pxi = pool_get(pppx_if_pl, PR_WAITOK | PR_ZERO);
+ if (pxi == NULL)
+ return (ENOMEM);
+
+ session = &pxi->pxi_session;
+ ifp = &pxi->pxi_if;
+
+ /* fake a pipex interface context */
+ session->pipex_iface = &pxi->pxi_ifcontext;
+ session->pipex_iface->ifnet_this = ifp;
+ session->pipex_iface->pipexmode = PIPEX_ENABLED;
+
+ /* setup session */
+ session->state = PIPEX_STATE_OPENED;
+ session->protocol = req->pr_protocol;
+ session->session_id = req->pr_session_id;
+ session->peer_session_id = req->pr_peer_session_id;
+ session->peer_mru = req->pr_peer_mru;
+ session->timeout_sec = req->pr_timeout_sec;
+ session->ppp_flags = req->pr_ppp_flags;
+ session->ppp_id = req->pr_ppp_id;
+
+ session->ip_forward = 1;
+
+ session->ip_address.sin_family = AF_INET;
+ session->ip_address.sin_len = sizeof(struct sockaddr_in);
+ session->ip_address.sin_addr = req->pr_ip_address;
+
+ session->ip_netmask.sin_family = AF_INET;
+ session->ip_netmask.sin_len = sizeof(struct sockaddr_in);
+ session->ip_netmask.sin_addr = req->pr_ip_netmask;
+
+ if (session->ip_netmask.sin_addr.s_addr == 0L)
+ session->ip_netmask.sin_addr.s_addr = 0xffffffffL;
+ session->ip_address.sin_addr.s_addr &=
+ session->ip_netmask.sin_addr.s_addr;
+
+ switch (req->pr_protocol) {
+#ifdef PIPEX_PPPOE
+ case PIPEX_PROTO_PPPOE:
+ session->proto.pppoe.peer_addr = peer_addr;
+ session->proto.pppoe.over_ifp = over_ifp;
+ break;
+#endif
+#ifdef PIPEX_PPTP
+ case PIPEX_PROTO_PPTP:
+ session->proto.pptp.snd_gap = 0;
+ session->proto.pptp.rcv_gap = 0;
+ session->proto.pptp.snd_una = req->pr_proto.pptp.snd_una;
+ session->proto.pptp.snd_nxt = req->pr_proto.pptp.snd_nxt;
+ session->proto.pptp.rcv_nxt = req->pr_proto.pptp.rcv_nxt;
+ session->proto.pptp.rcv_acked = req->pr_proto.pptp.rcv_acked;
+ session->proto.pptp.winsz = req->pr_proto.pptp.winsz;
+ session->proto.pptp.maxwinsz = req->pr_proto.pptp.maxwinsz;
+ session->proto.pptp.peer_maxwinsz =
+ req->pr_proto.pptp.peer_maxwinsz;
+ session->proto.pptp.peer_address =
+ req->pr_proto.pptp.peer_address;
+ session->proto.pptp.our_address =
+ req->pr_proto.pptp.our_address;
+ break;
+#endif
+ }
+
+#ifdef PIPEX_MPPE
+ if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ACCEPTED) != 0)
+ pipex_mppe_req_init(&req->pr_mppe_recv, &session->mppe_recv);
+ if ((req->pr_ppp_flags & PIPEX_PPP_MPPE_ENABLED) != 0)
+ pipex_mppe_req_init(&req->pr_mppe_send, &session->mppe_send);
+
+ if (pipex_session_is_mppe_required(session)) {
+ if (!pipex_session_is_mppe_enabled(session) ||
+ !pipex_session_is_mppe_accepted(session)) {
+ pool_put(pppx_if_pl, pxi);
+ return (EINVAL);
+ }
+ }
+#endif
+
+ /* try to set the interface up */
+ rw_enter_write(&pppx_ifs_lk);
+ unit = pppx_if_next_unit();
+
+ pxi->pxi_unit = unit;
+ pxi->pxi_key.pxik_session_id = req->pr_session_id;
+ pxi->pxi_key.pxik_protocol = req->pr_protocol;
+
+ /* this is safe without splnet since we're not modifying it */
+ if (RB_FIND(pppx_ifs, &pppx_ifs, pxi) != NULL) {
+ pool_put(pppx_if_pl, pxi);
+ error = EADDRINUSE;
+ goto out;
+ }
+
+ snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d", "pppx", unit);
+ ifp->if_mtu = req->pr_peer_mru; /* XXX */
+ ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
+ ifp->if_start = pppx_if_start;
+ ifp->if_output = pppx_if_output;
+ ifp->if_ioctl = pppx_if_ioctl;
+ ifp->if_type = IFT_PPP;
+ IFQ_SET_MAXLEN(&ifp->if_snd, 1);
+ IFQ_SET_READY(&ifp->if_snd);
+ ifp->if_softc = pxi;
+ /* ifp->if_rdomain = req->pr_rdomain; */
+
+ s = splnet();
+
+ /* hook up pipex context */
+ chain = PIPEX_ID_HASHTABLE(session->session_id);
+ LIST_INSERT_HEAD(chain, session, id_chain);
+ LIST_INSERT_HEAD(&pipex_session_list, session, session_list);
+#ifdef PIPEX_PPTP
+ if (req->pr_protocol == PIPEX_PROTO_PPTP) {
+ chain = PIPEX_PEER_ADDR_HASHTABLE(
+ session->proto.pptp.peer_address.s_addr);
+ LIST_INSERT_HEAD(chain, session, peer_addr_chain);
+ }
+#endif
+
+ /* if first session is added, start timer */
+ if (LIST_NEXT(session, session_list) == NULL)
+ pipex_timer_start();
+
+ if_attach(ifp);
+ if_alloc_sadl(ifp);
+
+#if NBPFILTER > 0
+ bpfattach(&ifp->if_bpf, ifp, DLT_NULL, 0);
+#endif
+ SET(ifp->if_flags, IFF_RUNNING);
+
+ if (RB_INSERT(pppx_ifs, &pppx_ifs, pxi) != NULL)
+ panic("pppx_ifs modified while lock was held");
+ LIST_INSERT_HEAD(&pxd->pxd_pxis, pxi, pxi_list);
+ splx(s);
+
+out:
+ rw_exit_write(&pppx_ifs_lk);
+
+ return (error);
+}
+
+int
+pppx_del_session(struct pppx_dev *pxd, struct pipex_session_close_req *req)
+{
+ struct pppx_if *pxi;
+
+ pxi = pppx_if_find(pxd, req->pcr_session_id, req->pcr_protocol);
+ if (pxi == NULL)
+ return (EINVAL);
+
+ req->pcr_stat = pxi->pxi_session.stat;
+
+ pppx_if_destroy(pxd, pxi);
+ return (0);
+}
+
+void
+pppx_if_destroy(struct pppx_dev *pxd, struct pppx_if *pxi)
+{
+ struct ifnet *ifp;
+ struct pipex_session *session;
+ int s;
+
+ session = &pxi->pxi_session;
+ ifp = &pxi->pxi_if;
+
+ s = splnet();
+ LIST_REMOVE(session, id_chain);
+ LIST_REMOVE(session, session_list);
+#ifdef PIPEX_PPTP
+ if (session->protocol == PIPEX_PROTO_PPTP)
+ LIST_REMOVE((struct pipex_session *)session,
+ peer_addr_chain);
+#endif
+
+ /* if final session is destroyed, stop timer */
+ if (LIST_EMPTY(&pipex_session_list))
+ pipex_timer_stop();
+ splx(s);
+
+ if_detach(ifp);
+
+ rw_enter_write(&pppx_ifs_lk);
+ if (RB_REMOVE(pppx_ifs, &pppx_ifs, pxi) == NULL)
+ panic("pppx_ifs modified while lock was held");
+ LIST_REMOVE(pxi, pxi_list);
+ rw_exit_write(&pppx_ifs_lk);
+
+ pool_put(pppx_if_pl, pxi);
+}
+
+void
+pppx_if_start(struct ifnet *ifp)
+{
+ struct pppx_if *pxi = (struct pppx_if *)ifp->if_softc;
+ struct mbuf *m;
+ int proto, s;
+
+ if (ISSET(ifp->if_flags, IFF_OACTIVE))
+ return;
+ if (!ISSET(ifp->if_flags, IFF_RUNNING))
+ return;
+
+ for (;;) {
+ s = splnet();
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ splx(s);
+
+ if (m == NULL)
+ break;
+
+ proto = *mtod(m, int *);
+ m_adj(m, sizeof(proto));
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ switch (proto) {
+ case PPP_IP:
+ bpf_mtap_af(ifp->if_bpf, AF_INET, m,
+ BPF_DIRECTION_OUT);
+ break;
+ case PPP_IPV6:
+ bpf_mtap_af(ifp->if_bpf, AF_INET6, m,
+ BPF_DIRECTION_OUT);
+ break;
+ }
+ }
+#endif
+
+ ifp->if_obytes += m->m_pkthdr.len;
+ ifp->if_opackets++;
+
+ pipex_ppp_output(m, &pxi->pxi_session, proto);
+ }
+}
+
+int
+pppx_if_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt)
+{
+ int error = 0;
+ int proto, s;
+
+ if (!ISSET(ifp->if_flags, IFF_UP)) {
+ m_freem(m);
+ error = ENETDOWN;
+ goto out;
+ }
+
+ switch (dst->sa_family) {
+ case AF_INET:
+ proto = PPP_IP;
+ break;
+ default:
+ m_freem(m);
+ error = EPFNOSUPPORT;
+ goto out;
+ }
+
+ M_PREPEND(m, sizeof(int), M_DONTWAIT);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ *mtod(m, int *) = proto;
+
+ s = splnet();
+ IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
+ if_start(ifp);
+ splx(s);
+
+out:
+ if (error)
+ ifp->if_oerrors++;
+ return (error);
+}
+
+int
+pppx_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
+{
+ struct pppx_if *pxi = (struct pppx_if *)ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)addr;
+ int error = 0;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ case SIOCSIFFLAGS:
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ switch (ifr->ifr_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < 512 ||
+ ifr->ifr_mtu > pxi->pxi_session.peer_mru)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+
+RB_GENERATE(pppx_ifs, pppx_if, pxi_entry, pppx_if_cmp);