1139823Simp/*- 298402Sjulian * Copyright (c) 2002 Mark Santcroos <marks@ripe.net> 3143593Sglebius * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 498402Sjulian * 598402Sjulian * Redistribution and use in source and binary forms, with or without 698402Sjulian * modification, are permitted provided that the following conditions 798402Sjulian * are met: 898402Sjulian * 1. Redistributions of source code must retain the above copyright 998402Sjulian * notice, this list of conditions and the following disclaimer. 1098402Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1198402Sjulian * notice, this list of conditions and the following disclaimer in the 1298402Sjulian * documentation and/or other materials provided with the distribution. 1398402Sjulian * 1498402Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1598402Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1698402Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1798402Sjulian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1898402Sjulian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1998402Sjulian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2098402Sjulian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2198402Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2298402Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2398402Sjulian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2498402Sjulian * 2598402Sjulian * Netgraph "device" node 2698402Sjulian * 27136673Sglebius * This node presents a /dev/ngd%d device that interfaces to an other 2898402Sjulian * netgraph node. 2998402Sjulian * 3098402Sjulian * $FreeBSD$ 3198402Sjulian * 3298402Sjulian */ 3398402Sjulian 34136673Sglebius#if 0 35137100Sglebius#define DBG do { printf("ng_device: %s\n", __func__ ); } while (0) 36136673Sglebius#else 37137100Sglebius#define DBG do {} while (0) 38136673Sglebius#endif 39136673Sglebius 4098402Sjulian#include <sys/param.h> 41132446Sglebius#include <sys/conf.h> 42132446Sglebius#include <sys/ioccom.h> 4398402Sjulian#include <sys/kernel.h> 44132446Sglebius#include <sys/malloc.h> 4598402Sjulian#include <sys/mbuf.h> 46132446Sglebius#include <sys/poll.h> 47132446Sglebius#include <sys/queue.h> 48136673Sglebius#include <sys/socket.h> 49132446Sglebius#include <sys/systm.h> 5098402Sjulian#include <sys/uio.h> 51136673Sglebius#include <sys/vnode.h> 5298402Sjulian 53136673Sglebius#include <net/if.h> 54136673Sglebius#include <net/if_var.h> 55136673Sglebius#include <netinet/in.h> 56136673Sglebius#include <netinet/in_systm.h> 57136673Sglebius#include <netinet/ip.h> 58136673Sglebius 5998402Sjulian#include <netgraph/ng_message.h> 6098402Sjulian#include <netgraph/netgraph.h> 61132446Sglebius#include <netgraph/ng_device.h> 6298402Sjulian 63132448Sglebius#define ERROUT(x) do { error = (x); goto done; } while (0) 64132448Sglebius 6598402Sjulian/* Netgraph methods */ 66141341Srustatic int ng_device_mod_event(module_t, int, void *); 67136673Sglebiusstatic ng_constructor_t ng_device_constructor; 6898402Sjulianstatic ng_rcvmsg_t ng_device_rcvmsg; 69136673Sglebiusstatic ng_shutdown_t ng_device_shutdown; 7098402Sjulianstatic ng_newhook_t ng_device_newhook; 7198402Sjulianstatic ng_rcvdata_t ng_device_rcvdata; 7298402Sjulianstatic ng_disconnect_t ng_device_disconnect; 7398402Sjulian 7498402Sjulian/* Netgraph type */ 75136673Sglebiusstatic struct ng_type ngd_typestruct = { 76129823Sjulian .version = NG_ABI_VERSION, 77129823Sjulian .name = NG_DEVICE_NODE_TYPE, 78141341Sru .mod_event = ng_device_mod_event, 79136673Sglebius .constructor = ng_device_constructor, 80136673Sglebius .rcvmsg = ng_device_rcvmsg, 81136673Sglebius .shutdown = ng_device_shutdown, 82129823Sjulian .newhook = ng_device_newhook, 83129823Sjulian .rcvdata = ng_device_rcvdata, 84129823Sjulian .disconnect = ng_device_disconnect, 8598402Sjulian}; 86136673SglebiusNETGRAPH_INIT(device, &ngd_typestruct); 8798402Sjulian 88136673Sglebius/* per node data */ 89136673Sglebiusstruct ngd_private { 90136673Sglebius struct ifqueue readq; 91136673Sglebius struct ng_node *node; 92136673Sglebius struct ng_hook *hook; 93136673Sglebius struct cdev *ngddev; 94136673Sglebius struct mtx ngd_mtx; 95136673Sglebius int unit; 96136673Sglebius uint16_t flags; 97136673Sglebius#define NGDF_OPEN 0x0001 98136673Sglebius#define NGDF_RWAIT 0x0002 9998402Sjulian}; 100136673Sglebiustypedef struct ngd_private *priv_p; 10198402Sjulian 102143593Sglebius/* unit number allocator entity */ 103143593Sglebiusstatic struct unrhdr *ngd_unit; 10498402Sjulian 10598402Sjulian/* Maximum number of NGD devices */ 106143593Sglebius#define MAX_NGD 999 10798402Sjulian 10898402Sjulianstatic d_close_t ngdclose; 10998402Sjulianstatic d_open_t ngdopen; 11098402Sjulianstatic d_read_t ngdread; 11198402Sjulianstatic d_write_t ngdwrite; 112136673Sglebius#if 0 11398402Sjulianstatic d_ioctl_t ngdioctl; 114136673Sglebius#endif 11598402Sjulianstatic d_poll_t ngdpoll; 11698402Sjulian 11798402Sjulianstatic struct cdevsw ngd_cdevsw = { 118126080Sphk .d_version = D_VERSION, 119111815Sphk .d_open = ngdopen, 120111815Sphk .d_close = ngdclose, 121111815Sphk .d_read = ngdread, 122111815Sphk .d_write = ngdwrite, 123136673Sglebius#if 0 124111815Sphk .d_ioctl = ngdioctl, 125136673Sglebius#endif 126111815Sphk .d_poll = ngdpoll, 127136673Sglebius .d_name = NG_DEVICE_DEVNAME, 12898402Sjulian}; 12998402Sjulian 130136673Sglebius/****************************************************************************** 131136673Sglebius * Netgraph methods 132136673Sglebius ******************************************************************************/ 133136673Sglebius 134136673Sglebius/* 135143593Sglebius * Handle loading and unloading for this node type. 136143593Sglebius */ 137143593Sglebiusstatic int 138143593Sglebiusng_device_mod_event(module_t mod, int event, void *data) 139143593Sglebius{ 140143593Sglebius int error = 0; 141143593Sglebius 142143593Sglebius switch (event) { 143143593Sglebius case MOD_LOAD: 144143593Sglebius ngd_unit = new_unrhdr(0, MAX_NGD, NULL); 145143593Sglebius break; 146143593Sglebius case MOD_UNLOAD: 147143593Sglebius delete_unrhdr(ngd_unit); 148143593Sglebius break; 149143593Sglebius default: 150143593Sglebius error = EOPNOTSUPP; 151143593Sglebius break; 152143593Sglebius } 153143593Sglebius return (error); 154143593Sglebius} 155143593Sglebius 156143593Sglebius/* 157136673Sglebius * create new node 15898402Sjulian */ 15998402Sjulianstatic int 160136673Sglebiusng_device_constructor(node_p node) 16198402Sjulian{ 162136673Sglebius priv_p priv; 16398402Sjulian 164137022Sglebius DBG; 16598402Sjulian 166220768Sglebius priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 16798402Sjulian 168143593Sglebius /* Allocate unit number */ 169143593Sglebius priv->unit = alloc_unr(ngd_unit); 17098402Sjulian 171141914Sglebius /* Initialize mutexes and queue */ 172141914Sglebius mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF); 173141914Sglebius mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); 174141914Sglebius IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); 175141914Sglebius 176141914Sglebius /* Link everything together */ 177141914Sglebius NG_NODE_SET_PRIVATE(node, priv); 178141914Sglebius priv->node = node; 179141914Sglebius 180183381Sed priv->ngddev = make_dev(&ngd_cdevsw, priv->unit, UID_ROOT, 181136673Sglebius GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit); 182136673Sglebius if(priv->ngddev == NULL) { 183136673Sglebius printf("%s(): make_dev() failed\n",__func__); 184136673Sglebius mtx_destroy(&priv->ngd_mtx); 185141914Sglebius mtx_destroy(&priv->readq.ifq_mtx); 186143593Sglebius free_unr(ngd_unit, priv->unit); 187184205Sdes free(priv, M_NETGRAPH); 188136673Sglebius return(EINVAL); 189136673Sglebius } 190141914Sglebius /* XXX: race here? */ 191136673Sglebius priv->ngddev->si_drv1 = priv; 19298402Sjulian 19398402Sjulian return(0); 19498402Sjulian} 19598402Sjulian 196136673Sglebius/* 197136673Sglebius * Process control message. 19898402Sjulian */ 19998402Sjulian 20098402Sjulianstatic int 20198402Sjulianng_device_rcvmsg(node_p node, item_p item, hook_p lasthook) 20298402Sjulian{ 203136673Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 20498402Sjulian struct ng_mesg *msg; 205136673Sglebius struct ng_mesg *resp = NULL; 206231378Sed const char *dn; 20798402Sjulian int error = 0; 20898402Sjulian 209136673Sglebius NGI_GET_MSG(item, msg); 21098402Sjulian 211136673Sglebius if (msg->header.typecookie == NGM_DEVICE_COOKIE) { 212136673Sglebius switch (msg->header.cmd) { 213136673Sglebius case NGM_DEVICE_GET_DEVNAME: 214143593Sglebius /* XXX: Fix when MAX_NGD us bigger */ 215136673Sglebius NG_MKRESPONSE(resp, msg, 216143593Sglebius strlen(NG_DEVICE_DEVNAME) + 4, M_NOWAIT); 21798402Sjulian 218136673Sglebius if (resp == NULL) 219136673Sglebius ERROUT(ENOMEM); 22098402Sjulian 221231378Sed dn = devtoname(priv->ngddev); 222231378Sed strlcpy((char *)resp->data, dn, strlen(dn) + 1); 223136673Sglebius break; 22498402Sjulian 225136673Sglebius default: 226136673Sglebius error = EINVAL; 227136673Sglebius break; 22898402Sjulian } 229136673Sglebius } else 230136673Sglebius error = EINVAL; 23198402Sjulian 232136673Sglebiusdone: 233136673Sglebius NG_RESPOND_MSG(error, node, item, resp); 234136673Sglebius NG_FREE_MSG(msg); 235136673Sglebius return (error); 23698402Sjulian} 23798402Sjulian 23898402Sjulian/* 239136673Sglebius * Accept incoming hook. We support only one hook per node. 24098402Sjulian */ 24198402Sjulianstatic int 24298402Sjulianng_device_newhook(node_p node, hook_p hook, const char *name) 24398402Sjulian{ 244136673Sglebius priv_p priv = NG_NODE_PRIVATE(node); 24598402Sjulian 246137022Sglebius DBG; 24798402Sjulian 248136673Sglebius /* We have only one hook per node */ 249136673Sglebius if (priv->hook != NULL) 250136673Sglebius return (EISCONN); 25198402Sjulian 252136673Sglebius priv->hook = hook; 25398402Sjulian 25498402Sjulian return(0); 25598402Sjulian} 25698402Sjulian 25798402Sjulian/* 258136673Sglebius * Receive data from hook, write it to device. 25998402Sjulian */ 26098402Sjulianstatic int 26198402Sjulianng_device_rcvdata(hook_p hook, item_p item) 26298402Sjulian{ 263136673Sglebius priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 26498402Sjulian struct mbuf *m; 26598402Sjulian 266137022Sglebius DBG; 26798402Sjulian 268132448Sglebius NGI_GET_M(item, m); 269132448Sglebius NG_FREE_ITEM(item); 270132448Sglebius 271136673Sglebius IF_LOCK(&priv->readq); 272136673Sglebius if (_IF_QFULL(&priv->readq)) { 273136673Sglebius _IF_DROP(&priv->readq); 274136673Sglebius IF_UNLOCK(&priv->readq); 275136673Sglebius NG_FREE_M(m); 276136673Sglebius return (ENOBUFS); 27798402Sjulian } 27898402Sjulian 279136673Sglebius _IF_ENQUEUE(&priv->readq, m); 280136673Sglebius IF_UNLOCK(&priv->readq); 281136673Sglebius mtx_lock(&priv->ngd_mtx); 282136673Sglebius if (priv->flags & NGDF_RWAIT) { 283136673Sglebius priv->flags &= ~NGDF_RWAIT; 284136673Sglebius wakeup(priv); 28598402Sjulian } 286136673Sglebius mtx_unlock(&priv->ngd_mtx); 28798402Sjulian 288136673Sglebius return(0); 28998402Sjulian} 29098402Sjulian 29198402Sjulian/* 292136673Sglebius * Removal of the hook destroys the node. 29398402Sjulian */ 29498402Sjulianstatic int 29598402Sjulianng_device_disconnect(hook_p hook) 29698402Sjulian{ 297136673Sglebius priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 29898402Sjulian 299137022Sglebius DBG; 30098402Sjulian 301136673Sglebius destroy_dev(priv->ngddev); 302136673Sglebius mtx_destroy(&priv->ngd_mtx); 303132448Sglebius 304136673Sglebius IF_DRAIN(&priv->readq); 305136673Sglebius mtx_destroy(&(priv)->readq.ifq_mtx); 30698402Sjulian 307143593Sglebius free_unr(ngd_unit, priv->unit); 308143593Sglebius 309184205Sdes free(priv, M_NETGRAPH); 31098402Sjulian 311136673Sglebius ng_rmnode_self(NG_HOOK_NODE(hook)); 31298402Sjulian 31398402Sjulian return(0); 31498402Sjulian} 315136673Sglebius 31698402Sjulian/* 317136673Sglebius * Node shutdown. Everything is already done in disconnect method. 31898402Sjulian */ 31998402Sjulianstatic int 320136673Sglebiusng_device_shutdown(node_p node) 321136673Sglebius{ 322136673Sglebius NG_NODE_UNREF(node); 323136673Sglebius return (0); 324136673Sglebius} 325136673Sglebius 326136673Sglebius/****************************************************************************** 327136673Sglebius * Device methods 328136673Sglebius ******************************************************************************/ 329136673Sglebius 330136673Sglebius/* 331136673Sglebius * the device is opened 332136673Sglebius */ 333136673Sglebiusstatic int 334130585Sphkngdopen(struct cdev *dev, int flag, int mode, struct thread *td) 33598402Sjulian{ 336136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 33798402Sjulian 338137022Sglebius DBG; 339137022Sglebius 340136673Sglebius mtx_lock(&priv->ngd_mtx); 341136673Sglebius priv->flags |= NGDF_OPEN; 342136673Sglebius mtx_unlock(&priv->ngd_mtx); 34398402Sjulian 34498402Sjulian return(0); 34598402Sjulian} 34698402Sjulian 34798402Sjulian/* 348136673Sglebius * the device is closed 34998402Sjulian */ 35098402Sjulianstatic int 351130585Sphkngdclose(struct cdev *dev, int flag, int mode, struct thread *td) 35298402Sjulian{ 353136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 35498402Sjulian 355137022Sglebius DBG; 356136673Sglebius mtx_lock(&priv->ngd_mtx); 357136673Sglebius priv->flags &= ~NGDF_OPEN; 358136673Sglebius mtx_unlock(&priv->ngd_mtx); 35998402Sjulian 36098402Sjulian return(0); 36198402Sjulian} 36298402Sjulian 363136673Sglebius#if 0 /* 364136673Sglebius * The ioctl is transformed into netgraph control message. 365136673Sglebius * We do not process them, yet. 366136673Sglebius */ 36798402Sjulian/* 36898402Sjulian * process ioctl 36998402Sjulian * 37098402Sjulian * they are translated into netgraph messages and passed on 371136673Sglebius * 37298402Sjulian */ 37398402Sjulianstatic int 374130585Sphkngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 37598402Sjulian{ 37698402Sjulian struct ngd_softc *sc = &ngd_softc; 37798402Sjulian struct ngd_connection * connection = NULL; 37898402Sjulian struct ngd_connection * tmp; 37998402Sjulian int error = 0; 38098402Sjulian struct ng_mesg *msg; 381136673Sglebius struct ngd_param_s * datap; 38298402Sjulian 383137022Sglebius DBG; 38498402Sjulian 385136673Sglebius NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 38698402Sjulian M_NOWAIT); 38798402Sjulian if (msg == NULL) { 38898402Sjulian printf("%s(): msg == NULL\n",__func__); 38998402Sjulian goto nomsg; 39098402Sjulian } 39198402Sjulian 39298402Sjulian /* pass the ioctl data into the ->data area */ 39398402Sjulian datap = (struct ngd_param_s *)msg->data; 394136673Sglebius datap->p = addr; 39598402Sjulian 396132446Sglebius NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0); 39798402Sjulian if(error) 39898402Sjulian printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error); 39998402Sjulian 40098402Sjuliannomsg: 40198402Sjulian 40298402Sjulian return(0); 40398402Sjulian} 404136673Sglebius#endif /* if 0 */ 40598402Sjulian 40698402Sjulian/* 40798402Sjulian * This function is called when a read(2) is done to our device. 408136673Sglebius * We process one mbuf from queue. 40998402Sjulian */ 41098402Sjulianstatic int 411130585Sphkngdread(struct cdev *dev, struct uio *uio, int flag) 41298402Sjulian{ 413136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 414136673Sglebius struct mbuf *m; 415136673Sglebius int len, error = 0; 41698402Sjulian 417137022Sglebius DBG; 41898402Sjulian 419136673Sglebius /* get an mbuf */ 420136673Sglebius do { 421136673Sglebius IF_DEQUEUE(&priv->readq, m); 422136673Sglebius if (m == NULL) { 423136673Sglebius if (flag & IO_NDELAY) 424136673Sglebius return (EWOULDBLOCK); 425136673Sglebius mtx_lock(&priv->ngd_mtx); 426136673Sglebius priv->flags |= NGDF_RWAIT; 427139331Srik if ((error = msleep(priv, &priv->ngd_mtx, 428139331Srik PDROP | PCATCH | (PZERO + 1), 429136673Sglebius "ngdread", 0)) != 0) 430136673Sglebius return (error); 43198402Sjulian } 432136673Sglebius } while (m == NULL); 433136673Sglebius 434136673Sglebius while (m && uio->uio_resid > 0 && error == 0) { 435136673Sglebius len = MIN(uio->uio_resid, m->m_len); 436136673Sglebius if (len != 0) 437136673Sglebius error = uiomove(mtod(m, void *), len, uio); 438136673Sglebius m = m_free(m); 43998402Sjulian } 44098402Sjulian 441136673Sglebius if (m) 442136673Sglebius m_freem(m); 44398402Sjulian 444136673Sglebius return (error); 44598402Sjulian} 44698402Sjulian 44798402Sjulian 448136673Sglebius/* 44998402Sjulian * This function is called when our device is written to. 450136673Sglebius * We read the data from userland into mbuf chain and pass it to the remote hook. 45198402Sjulian * 45298402Sjulian */ 45398402Sjulianstatic int 454130585Sphkngdwrite(struct cdev *dev, struct uio *uio, int flag) 45598402Sjulian{ 456136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 457136673Sglebius struct mbuf *m; 45898402Sjulian int error = 0; 45998402Sjulian 460137022Sglebius DBG; 46198402Sjulian 462136673Sglebius if (uio->uio_resid == 0) 463136673Sglebius return (0); 46498402Sjulian 465136673Sglebius if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET) 466136673Sglebius return (EIO); 46798402Sjulian 468243882Sglebius if ((m = m_uiotombuf(uio, M_NOWAIT, 0, 0, M_PKTHDR)) == NULL) 469136673Sglebius return (ENOBUFS); 47098402Sjulian 471136673Sglebius NG_SEND_DATA_ONLY(error, priv->hook, m); 47298402Sjulian 473136673Sglebius return (error); 47498402Sjulian} 47598402Sjulian 47698402Sjulian/* 47798402Sjulian * we are being polled/selected 47898402Sjulian * check if there is data available for read 47998402Sjulian */ 48098402Sjulianstatic int 481130585Sphkngdpoll(struct cdev *dev, int events, struct thread *td) 48298402Sjulian{ 483136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 48498402Sjulian int revents = 0; 48598402Sjulian 486136673Sglebius if (events & (POLLIN | POLLRDNORM) && 487136673Sglebius !IFQ_IS_EMPTY(&priv->readq)) 488136673Sglebius revents |= events & (POLLIN | POLLRDNORM); 48998402Sjulian 490136673Sglebius return (revents); 491136673Sglebius} 492