ng_socket.c revision 53913
152419Sjulian 252419Sjulian/* 352419Sjulian * ng_socket.c 452419Sjulian * 552419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 652419Sjulian * All rights reserved. 752419Sjulian * 852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 952419Sjulian * redistribution of this software, in source or object code forms, with or 1052419Sjulian * without modifications are expressly permitted by Whistle Communications; 1152419Sjulian * provided, however, that: 1252419Sjulian * 1. Any and all reproductions of the source or object code must include the 1352419Sjulian * copyright notice above and the following disclaimer of warranties; and 1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1552419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1652419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1752419Sjulian * such appears in the above copyright notice or in the software. 1852419Sjulian * 1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3552419Sjulian * OF SUCH DAMAGE. 3652419Sjulian * 3752419Sjulian * Author: Julian Elischer <julian@whistle.com> 3852419Sjulian * 3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_socket.c 53913 1999-11-30 02:45:32Z archie $ 4052752Sjulian * $Whistle: ng_socket.c,v 1.28 1999/11/01 09:24:52 julian Exp $ 4152419Sjulian */ 4252419Sjulian 4352419Sjulian/* 4452419Sjulian * Netgraph socket nodes 4552419Sjulian * 4652419Sjulian * There are two types of netgraph sockets, control and data. 4752419Sjulian * Control sockets have a netgraph node, but data sockets are 4852419Sjulian * parasitic on control sockets, and have no node of their own. 4952419Sjulian */ 5052419Sjulian 5152419Sjulian#include <sys/param.h> 5252419Sjulian#include <sys/systm.h> 5352419Sjulian#include <sys/proc.h> 5452419Sjulian#include <sys/domain.h> 5552419Sjulian#include <sys/errno.h> 5652419Sjulian#include <sys/kernel.h> 5752419Sjulian#include <sys/file.h> 5852419Sjulian#include <sys/filedesc.h> 5952419Sjulian#include <sys/malloc.h> 6052419Sjulian#include <sys/queue.h> 6152419Sjulian#include <sys/mbuf.h> 6252419Sjulian#include <sys/protosw.h> 6352419Sjulian#include <sys/socket.h> 6452419Sjulian#include <sys/socketvar.h> 6552419Sjulian#include <sys/sysctl.h> 6652419Sjulian#ifdef NOTYET 6752419Sjulian#include <sys/vnode.h> 6852419Sjulian#endif 6952419Sjulian#include <netgraph/ng_message.h> 7052419Sjulian#include <netgraph/netgraph.h> 7152919Sjulian#include <netgraph/ng_socketvar.h> 7252419Sjulian#include <netgraph/ng_socket.h> 7352419Sjulian 7452419Sjulian/* 7552419Sjulian * It's Ascii-art time! 7652419Sjulian * +-------------+ +-------------+ 7752419Sjulian * |socket (ctl)| |socket (data)| 7852419Sjulian * +-------------+ +-------------+ 7952419Sjulian * ^ ^ 8052419Sjulian * | | 8152419Sjulian * v v 8252419Sjulian * +-----------+ +-----------+ 8352419Sjulian * |pcb (ctl)| |pcb (data)| 8452419Sjulian * +-----------+ +-----------+ 8552419Sjulian * ^ ^ 8652419Sjulian * | | 8752419Sjulian * v v 8852419Sjulian * +--------------------------+ 8952419Sjulian * | Socket type private | 9052419Sjulian * | data | 9152419Sjulian * +--------------------------+ 9252419Sjulian * ^ 9352419Sjulian * | 9452419Sjulian * v 9552419Sjulian * +----------------+ 9652419Sjulian * | struct ng_node | 9752419Sjulian * +----------------+ 9852419Sjulian */ 9952419Sjulian 10052419Sjulian/* Netgraph node methods */ 10152752Sjulianstatic ng_constructor_t ngs_constructor; 10252752Sjulianstatic ng_rcvmsg_t ngs_rcvmsg; 10352752Sjulianstatic ng_shutdown_t ngs_rmnode; 10452752Sjulianstatic ng_newhook_t ngs_newhook; 10552752Sjulianstatic ng_rcvdata_t ngs_rcvdata; 10652885Sjulianstatic ng_disconnect_t ngs_disconnect; 10752419Sjulian 10852419Sjulian/* Internal methods */ 10952419Sjulianstatic int ng_attach_data(struct socket *so); 11052419Sjulianstatic int ng_attach_cntl(struct socket *so); 11152419Sjulianstatic int ng_attach_common(struct socket *so, int type); 11252419Sjulianstatic void ng_detach_common(struct ngpcb *pcbp, int type); 11352419Sjulian/*static int ng_internalize(struct mbuf *m, struct proc *p); */ 11452419Sjulian 11552419Sjulianstatic int ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp); 11652419Sjulianstatic int ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp); 11752419Sjulianstatic int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp); 11852419Sjulian 11952419Sjulianstatic int ngs_mod_event(module_t mod, int event, void *data); 12052419Sjulianstatic int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, 12152419Sjulian struct sockaddr_ng *addr); 12252419Sjulian 12352419Sjulian/* Netgraph type descriptor */ 12452419Sjulianstatic struct ng_type typestruct = { 12552419Sjulian NG_VERSION, 12652419Sjulian NG_SOCKET_NODE_TYPE, 12752419Sjulian ngs_mod_event, 12852419Sjulian ngs_constructor, 12952419Sjulian ngs_rcvmsg, 13052419Sjulian ngs_rmnode, 13152419Sjulian ngs_newhook, 13252419Sjulian NULL, 13352419Sjulian NULL, 13452419Sjulian ngs_rcvdata, 13552419Sjulian ngs_rcvdata, 13653913Sarchie ngs_disconnect, 13753913Sarchie NULL 13852419Sjulian}; 13952419SjulianNETGRAPH_INIT(socket, &typestruct); 14052419Sjulian 14152419Sjulian/* Buffer space */ 14252419Sjulianstatic u_long ngpdg_sendspace = 2 * 1024; /* really max datagram size */ 14352419Sjulianstatic u_long ngpdg_recvspace = 20 * 1024; 14452419Sjulian 14552419Sjulian/* List of all sockets */ 14652419SjulianLIST_HEAD(, ngpcb) ngsocklist; 14752419Sjulian 14853526Sjulian#define sotongpcb(so) ((struct ngpcb *)(so)->so_pcb) 14952419Sjulian 15052419Sjulian/* If getting unexplained errors returned, set this to "Debugger("X"); */ 15152419Sjulian#ifndef TRAP_ERROR 15252419Sjulian#define TRAP_ERROR 15352419Sjulian#endif 15452419Sjulian 15552419Sjulian/*************************************************************** 15652419Sjulian Control sockets 15752419Sjulian***************************************************************/ 15852419Sjulian 15952419Sjulianstatic int 16052419Sjulianngc_attach(struct socket *so, int proto, struct proc *p) 16152419Sjulian{ 16252419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 16352419Sjulian 16453532Sjulian if (suser(p)) 16553530Sjulian return (EPERM); 16652419Sjulian if (pcbp != NULL) 16752419Sjulian return (EISCONN); 16852419Sjulian return (ng_attach_cntl(so)); 16952419Sjulian} 17052419Sjulian 17152419Sjulianstatic int 17252419Sjulianngc_detach(struct socket *so) 17352419Sjulian{ 17452419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 17552419Sjulian 17652419Sjulian if (pcbp == NULL) 17752419Sjulian return (EINVAL); 17852419Sjulian ng_detach_common(pcbp, NG_CONTROL); 17952419Sjulian return (0); 18052419Sjulian} 18152419Sjulian 18252419Sjulianstatic int 18352419Sjulianngc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, 18452419Sjulian struct mbuf *control, struct proc *p) 18552419Sjulian{ 18652419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 18752419Sjulian struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; 18852419Sjulian struct ng_mesg *resp; 18952419Sjulian struct mbuf *m0; 19052419Sjulian char *msg, *path = NULL; 19152419Sjulian int len, error = 0; 19252419Sjulian 19352419Sjulian if (pcbp == NULL) { 19452419Sjulian error = EINVAL; 19552419Sjulian goto release; 19652419Sjulian } 19752419Sjulian#ifdef NOTYET 19852419Sjulian if (control && (error = ng_internalize(control, p))) { 19952419Sjulian if (pcbp->sockdata == NULL) { 20052419Sjulian error = ENOTCONN; 20152419Sjulian goto release; 20252419Sjulian } 20352419Sjulian } 20452419Sjulian#else /* NOTYET */ 20552419Sjulian if (control) { 20652419Sjulian error = EINVAL; 20752419Sjulian goto release; 20852419Sjulian } 20952419Sjulian#endif /* NOTYET */ 21052419Sjulian 21152419Sjulian /* Require destination as there may be >= 1 hooks on this node */ 21252419Sjulian if (addr == NULL) { 21352419Sjulian error = EDESTADDRREQ; 21452419Sjulian goto release; 21552419Sjulian } 21652419Sjulian 21752419Sjulian /* Allocate an expendable buffer for the path, chop off 21852419Sjulian * the sockaddr header, and make sure it's NUL terminated */ 21952419Sjulian len = sap->sg_len - 2; 22052419Sjulian MALLOC(path, char *, len + 1, M_NETGRAPH, M_WAITOK); 22152419Sjulian if (path == NULL) { 22252419Sjulian error = ENOMEM; 22352419Sjulian goto release; 22452419Sjulian } 22552419Sjulian bcopy(sap->sg_data, path, len); 22652419Sjulian path[len] = '\0'; 22752419Sjulian 22852419Sjulian /* Move the actual message out of mbufs into a linear buffer. 22952419Sjulian * Start by adding up the size of the data. (could use mh_len?) */ 23052419Sjulian for (len = 0, m0 = m; m0 != NULL; m0 = m0->m_next) 23152419Sjulian len += m0->m_len; 23252419Sjulian 23352419Sjulian /* Move the data into a linear buffer as well. Messages are not 23452419Sjulian * delivered in mbufs. */ 23552419Sjulian MALLOC(msg, char *, len + 1, M_NETGRAPH, M_WAITOK); 23652419Sjulian if (msg == NULL) { 23752419Sjulian error = ENOMEM; 23852419Sjulian goto release; 23952419Sjulian } 24052419Sjulian m_copydata(m, 0, len, msg); 24152419Sjulian 24252419Sjulian /* The callee will free the msg when done. The addr is our business. */ 24352419Sjulian error = ng_send_msg(pcbp->sockdata->node, 24452419Sjulian (struct ng_mesg *) msg, path, &resp); 24552419Sjulian 24652419Sjulian /* If the callee responded with a synchronous response, then put it 24752419Sjulian * back on the receive side of the socket; sap is source address. */ 24852419Sjulian if (error == 0 && resp != NULL) 24952419Sjulian error = ship_msg(pcbp, resp, sap); 25052419Sjulian 25152419Sjulianrelease: 25252419Sjulian if (path != NULL) 25352419Sjulian FREE(path, M_NETGRAPH); 25452419Sjulian if (control != NULL) 25552419Sjulian m_freem(control); 25652419Sjulian if (m != NULL) 25752419Sjulian m_freem(m); 25852419Sjulian return (error); 25952419Sjulian} 26052419Sjulian 26152419Sjulianstatic int 26252419Sjulianngc_bind(struct socket *so, struct sockaddr *nam, struct proc *p) 26352419Sjulian{ 26452419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 26552419Sjulian 26652419Sjulian if (pcbp == 0) 26752419Sjulian return (EINVAL); 26852419Sjulian return (ng_bind(nam, pcbp)); 26952419Sjulian} 27052419Sjulian 27152419Sjulianstatic int 27252419Sjulianngc_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 27352419Sjulian{ 27452419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 27552419Sjulian 27652419Sjulian if (pcbp == 0) 27752419Sjulian return (EINVAL); 27852419Sjulian return (ng_connect_cntl(nam, pcbp)); 27952419Sjulian} 28052419Sjulian 28152419Sjulian/*************************************************************** 28252419Sjulian Data sockets 28352419Sjulian***************************************************************/ 28452419Sjulian 28552419Sjulianstatic int 28652419Sjulianngd_attach(struct socket *so, int proto, struct proc *p) 28752419Sjulian{ 28852419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 28952419Sjulian 29052419Sjulian if (pcbp != NULL) 29152419Sjulian return (EISCONN); 29252419Sjulian return (ng_attach_data(so)); 29352419Sjulian} 29452419Sjulian 29552419Sjulianstatic int 29652419Sjulianngd_detach(struct socket *so) 29752419Sjulian{ 29852419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 29952419Sjulian 30052419Sjulian if (pcbp == NULL) 30152419Sjulian return (EINVAL); 30252419Sjulian ng_detach_common(pcbp, NG_DATA); 30352419Sjulian return (0); 30452419Sjulian} 30552419Sjulian 30652419Sjulianstatic int 30752419Sjulianngd_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, 30852419Sjulian struct mbuf *control, struct proc *p) 30952419Sjulian{ 31052419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 31152419Sjulian struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; 31252419Sjulian meta_p mp = NULL; 31352419Sjulian int len, error; 31453498Sjulian hook_p hook = NULL; 31553498Sjulian char hookname[NG_HOOKLEN + 1]; 31652419Sjulian 31752419Sjulian if ((pcbp == NULL) || (control != NULL)) { 31852419Sjulian error = EINVAL; 31952419Sjulian goto release; 32052419Sjulian } 32152419Sjulian if (pcbp->sockdata == NULL) { 32252419Sjulian error = ENOTCONN; 32352419Sjulian goto release; 32452419Sjulian } 32553498Sjulian /* 32653498Sjulian * If the user used any of these ways to not specify an address 32753498Sjulian * then handle specially. 32853498Sjulian */ 32953498Sjulian if ((sap == NULL) 33053498Sjulian || ((len = sap->sg_len) <= 2) 33153498Sjulian || (*sap->sg_data == '\0')) { 33253498Sjulian if (pcbp->sockdata->node->numhooks != 1) { 33353498Sjulian error = EDESTADDRREQ; 33453498Sjulian goto release; 33553498Sjulian } 33653498Sjulian /* 33753498Sjulian * if exactly one hook exists, just use it. 33853498Sjulian * Special case to allow write(2) to work on an ng_socket. 33953498Sjulian */ 34053498Sjulian hook = LIST_FIRST(&pcbp->sockdata->node->hooks); 34153498Sjulian } else { 34253498Sjulian if (len > NG_HOOKLEN) { 34353498Sjulian error = EINVAL; 34453498Sjulian goto release; 34553498Sjulian } 34652419Sjulian 34753498Sjulian /* 34853498Sjulian * chop off the sockaddr header, and make sure it's NUL 34953498Sjulian * terminated 35053498Sjulian */ 35153498Sjulian bcopy(sap->sg_data, hookname, len); 35253498Sjulian hookname[len] = '\0'; 35352419Sjulian 35453498Sjulian /* Find the correct hook from 'hookname' */ 35553498Sjulian LIST_FOREACH(hook, &pcbp->sockdata->node->hooks, hooks) { 35653498Sjulian if (strcmp(hookname, hook->name) == 0) 35753498Sjulian break; 35853498Sjulian } 35953498Sjulian if (hook == NULL) 36053498Sjulian error = EHOSTUNREACH; 36152419Sjulian } 36252419Sjulian 36352419Sjulian /* Send data (OK if hook is NULL) */ 36452419Sjulian NG_SEND_DATA(error, hook, m, mp); /* makes m NULL */ 36552419Sjulian 36652419Sjulianrelease: 36752419Sjulian if (control != NULL) 36852419Sjulian m_freem(control); 36952419Sjulian if (m != NULL) 37052419Sjulian m_freem(m); 37152419Sjulian return (error); 37252419Sjulian} 37352419Sjulian 37452419Sjulianstatic int 37552419Sjulianngd_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 37652419Sjulian{ 37752419Sjulian struct ngpcb *const pcbp = sotongpcb(so); 37852419Sjulian 37952419Sjulian if (pcbp == 0) 38052419Sjulian return (EINVAL); 38152419Sjulian return (ng_connect_data(nam, pcbp)); 38252419Sjulian} 38352419Sjulian 38452419Sjulian/* 38552419Sjulian * Used for both data and control sockets 38652419Sjulian */ 38752419Sjulianstatic int 38852419Sjulianng_setsockaddr(struct socket *so, struct sockaddr **addr) 38952419Sjulian{ 39053098Sbrian struct ngpcb *pcbp; 39153098Sbrian struct sockaddr_ng *sg; 39253098Sbrian int sg_len, namelen, s; 39352419Sjulian 39453098Sbrian /* Why isn't sg_data a `char[1]' ? :-( */ 39553098Sbrian sg_len = sizeof(struct sockaddr_ng) - sizeof(sg->sg_data) + 1; 39653098Sbrian 39753098Sbrian s = splnet(); 39853098Sbrian pcbp = sotongpcb(so); 39953098Sbrian if (pcbp == 0) { 40053098Sbrian splx(s); 40152419Sjulian return (EINVAL); 40253098Sbrian } 40353098Sbrian 40453098Sbrian namelen = 0; /* silence compiler ! */ 40553098Sbrian 40653098Sbrian if (pcbp->sockdata->node->name != NULL) 40753098Sbrian sg_len += namelen = strlen(pcbp->sockdata->node->name); 40853098Sbrian 40953098Sbrian MALLOC(sg, struct sockaddr_ng *, sg_len, M_SONAME, M_WAITOK); 41053098Sbrian bzero(sg, sg_len); 41153098Sbrian 41253098Sbrian if (pcbp->sockdata->node->name != NULL) 41353098Sbrian bcopy(pcbp->sockdata->node->name, sg->sg_data, namelen); 41453098Sbrian splx(s); 41553098Sbrian 41653098Sbrian sg->sg_len = sg_len; 41753098Sbrian sg->sg_family = AF_NETGRAPH; 41853098Sbrian *addr = (struct sockaddr *)sg; 41953098Sbrian 42052419Sjulian return (0); 42152419Sjulian} 42252419Sjulian 42352419Sjulian/* 42452419Sjulian * Attach a socket to it's protocol specific partner. 42552419Sjulian * For a control socket, actually create a netgraph node and attach 42652419Sjulian * to it as well. 42752419Sjulian */ 42852419Sjulian 42952419Sjulianstatic int 43052419Sjulianng_attach_cntl(struct socket *so) 43152419Sjulian{ 43252419Sjulian struct ngsock *privdata; 43352419Sjulian struct ngpcb *pcbp; 43452419Sjulian int error; 43552419Sjulian 43652419Sjulian /* Setup protocol control block */ 43752419Sjulian if ((error = ng_attach_common(so, NG_CONTROL)) != 0) 43852419Sjulian return (error); 43953526Sjulian pcbp = sotongpcb(so); 44052419Sjulian 44152419Sjulian /* Allocate node private info */ 44252419Sjulian MALLOC(privdata, struct ngsock *, 44352419Sjulian sizeof(*privdata), M_NETGRAPH, M_WAITOK); 44452419Sjulian if (privdata == NULL) { 44552419Sjulian ng_detach_common(pcbp, NG_CONTROL); 44652419Sjulian return (ENOMEM); 44752419Sjulian } 44852419Sjulian bzero(privdata, sizeof(*privdata)); 44952419Sjulian 45052419Sjulian /* Make the generic node components */ 45152419Sjulian if ((error = ng_make_node_common(&typestruct, &privdata->node)) != 0) { 45252419Sjulian FREE(privdata, M_NETGRAPH); 45352419Sjulian ng_detach_common(pcbp, NG_CONTROL); 45452419Sjulian return (error); 45552419Sjulian } 45652419Sjulian privdata->node->private = privdata; 45752419Sjulian 45852419Sjulian /* Link the pcb and the node private data */ 45952419Sjulian privdata->ctlsock = pcbp; 46052419Sjulian pcbp->sockdata = privdata; 46152419Sjulian privdata->refs++; 46252419Sjulian return (0); 46352419Sjulian} 46452419Sjulian 46552419Sjulianstatic int 46652419Sjulianng_attach_data(struct socket *so) 46752419Sjulian{ 46852419Sjulian return(ng_attach_common(so, NG_DATA)); 46952419Sjulian} 47052419Sjulian 47152419Sjulian/* 47252419Sjulian * Set up a socket protocol control block. 47352419Sjulian * This code is shared between control and data sockets. 47452419Sjulian */ 47552419Sjulianstatic int 47652419Sjulianng_attach_common(struct socket *so, int type) 47752419Sjulian{ 47852419Sjulian struct ngpcb *pcbp; 47952419Sjulian int error; 48052419Sjulian 48152419Sjulian /* Standard socket setup stuff */ 48252419Sjulian error = soreserve(so, ngpdg_sendspace, ngpdg_recvspace); 48352419Sjulian if (error) 48452419Sjulian return (error); 48552419Sjulian 48652419Sjulian /* Allocate the pcb */ 48752419Sjulian MALLOC(pcbp, struct ngpcb *, sizeof(*pcbp), M_PCB, M_WAITOK); 48852419Sjulian if (pcbp == NULL) 48952419Sjulian return (ENOMEM); 49052419Sjulian bzero(pcbp, sizeof(*pcbp)); 49152419Sjulian pcbp->type = type; 49252419Sjulian 49352419Sjulian /* Link the pcb and the socket */ 49452419Sjulian so->so_pcb = (caddr_t) pcbp; 49552419Sjulian pcbp->ng_socket = so; 49652419Sjulian 49752419Sjulian /* Add the socket to linked list */ 49852419Sjulian LIST_INSERT_HEAD(&ngsocklist, pcbp, socks); 49952419Sjulian return (0); 50052419Sjulian} 50152419Sjulian 50252419Sjulian/* 50352419Sjulian * Disassociate the socket from it's protocol specific 50452419Sjulian * partner. If it's attached to a node's private data structure, 50552419Sjulian * then unlink from that too. If we were the last socket attached to it, 50652419Sjulian * then shut down the entire node. Shared code for control and data sockets. 50752419Sjulian */ 50852419Sjulianstatic void 50952419Sjulianng_detach_common(struct ngpcb *pcbp, int which) 51052419Sjulian{ 51152419Sjulian struct ngsock *sockdata; 51252419Sjulian 51352419Sjulian if (pcbp->sockdata) { 51452419Sjulian sockdata = pcbp->sockdata; 51552419Sjulian pcbp->sockdata = NULL; 51652419Sjulian switch (which) { 51752419Sjulian case NG_CONTROL: 51852419Sjulian sockdata->ctlsock = NULL; 51952419Sjulian break; 52052419Sjulian case NG_DATA: 52152419Sjulian sockdata->datasock = NULL; 52252419Sjulian break; 52352419Sjulian default: 52452419Sjulian panic(__FUNCTION__); 52552419Sjulian } 52652419Sjulian if ((--sockdata->refs == 0) && (sockdata->node != NULL)) 52752419Sjulian ng_rmnode(sockdata->node); 52852419Sjulian } 52952419Sjulian pcbp->ng_socket->so_pcb = NULL; 53052419Sjulian pcbp->ng_socket = NULL; 53152419Sjulian LIST_REMOVE(pcbp, socks); 53252419Sjulian FREE(pcbp, M_PCB); 53352419Sjulian} 53452419Sjulian 53552419Sjulian#ifdef NOTYET 53652419Sjulian/* 53752419Sjulian * File descriptors can be passed into a AF_NETGRAPH socket. 53852419Sjulian * Note, that file descriptors cannot be passed OUT. 53952419Sjulian * Only character device descriptors are accepted. 54052419Sjulian * Character devices are useful to connect a graph to a device, 54152419Sjulian * which after all is the purpose of this whole system. 54252419Sjulian */ 54352419Sjulianstatic int 54452419Sjulianng_internalize(struct mbuf *control, struct proc *p) 54552419Sjulian{ 54652419Sjulian struct filedesc *fdp = p->p_fd; 54752419Sjulian struct cmsghdr *cm = mtod(control, struct cmsghdr *); 54852419Sjulian struct file *fp; 54952419Sjulian struct vnode *vn; 55052419Sjulian int oldfds; 55152419Sjulian int fd; 55252419Sjulian 55352419Sjulian if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET || 55452419Sjulian cm->cmsg_len != control->m_len) { 55552419Sjulian TRAP_ERROR; 55652419Sjulian return (EINVAL); 55752419Sjulian } 55852419Sjulian 55952419Sjulian /* Check there is only one FD. XXX what would more than one signify? */ 56052419Sjulian oldfds = (cm->cmsg_len - sizeof(*cm)) / sizeof(int); 56152419Sjulian if (oldfds != 1) { 56252419Sjulian TRAP_ERROR; 56352419Sjulian return (EINVAL); 56452419Sjulian } 56552419Sjulian 56652419Sjulian /* Check that the FD given is legit. and change it to a pointer to a 56752419Sjulian * struct file. */ 56852419Sjulian fd = *(int *) (cm + 1); 56952419Sjulian if ((unsigned) fd >= fdp->fd_nfiles 57052419Sjulian || (fp = fdp->fd_ofiles[fd]) == NULL) { 57152419Sjulian return (EBADF); 57252419Sjulian } 57352419Sjulian 57452419Sjulian /* Depending on what kind of resource it is, act differently. For 57552419Sjulian * devices, we treat it as a file. For a AF_NETGRAPH socket, 57652419Sjulian * shortcut straight to the node. */ 57752419Sjulian switch (fp->f_type) { 57852419Sjulian case DTYPE_VNODE: 57952419Sjulian vn = (struct vnode *) fp->f_data; 58052419Sjulian if (vn && (vn->v_type == VCHR)) { 58152419Sjulian /* for a VCHR, actually reference the FILE */ 58252419Sjulian fp->f_count++; 58352419Sjulian /* XXX then what :) */ 58452419Sjulian /* how to pass on to other modules? */ 58552419Sjulian } else { 58652419Sjulian TRAP_ERROR; 58752419Sjulian return (EINVAL); 58852419Sjulian } 58952419Sjulian break; 59052419Sjulian default: 59152419Sjulian TRAP_ERROR; 59252419Sjulian return (EINVAL); 59352419Sjulian } 59452419Sjulian return (0); 59552419Sjulian} 59652419Sjulian#endif /* NOTYET */ 59752419Sjulian 59852419Sjulian/* 59952419Sjulian * Connect the data socket to a named control socket node. 60052419Sjulian */ 60152419Sjulianstatic int 60252419Sjulianng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp) 60352419Sjulian{ 60452419Sjulian struct sockaddr_ng *sap; 60552419Sjulian node_p farnode; 60652419Sjulian struct ngsock *sockdata; 60752419Sjulian int error; 60852419Sjulian 60952419Sjulian /* If we are already connected, don't do it again */ 61052419Sjulian if (pcbp->sockdata != NULL) 61152419Sjulian return (EISCONN); 61252419Sjulian 61352419Sjulian /* Find the target (victim) and check it doesn't already have a data 61452419Sjulian * socket. Also check it is a 'socket' type node. */ 61552419Sjulian sap = (struct sockaddr_ng *) nam; 61652419Sjulian if ((error = ng_path2node(NULL, sap->sg_data, &farnode, NULL))) 61752419Sjulian return (error); 61852419Sjulian 61952419Sjulian if (strcmp(farnode->type->name, NG_SOCKET_NODE_TYPE) != 0) 62052419Sjulian return (EINVAL); 62152419Sjulian sockdata = farnode->private; 62252419Sjulian if (sockdata->datasock != NULL) 62352419Sjulian return (EADDRINUSE); 62452419Sjulian 62552419Sjulian /* Link the PCB and the private data struct. and note the extra 62652419Sjulian * reference */ 62752419Sjulian sockdata->datasock = pcbp; 62852419Sjulian pcbp->sockdata = sockdata; 62952419Sjulian sockdata->refs++; 63052419Sjulian return (0); 63152419Sjulian} 63252419Sjulian 63352419Sjulian/* 63452419Sjulian * Connect the existing control socket node to a named node:hook. 63552419Sjulian * The hook we use on this end is the same name as the remote node name. 63652419Sjulian */ 63752419Sjulianstatic int 63852419Sjulianng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp) 63952419Sjulian{ 64052419Sjulian struct ngsock *const sockdata = pcbp->sockdata; 64152419Sjulian struct sockaddr_ng *sap; 64252419Sjulian char *node, *hook; 64352419Sjulian node_p farnode; 64452419Sjulian int rtn, error; 64552419Sjulian 64652419Sjulian sap = (struct sockaddr_ng *) nam; 64752419Sjulian rtn = ng_path_parse(sap->sg_data, &node, NULL, &hook); 64852419Sjulian if (rtn < 0 || node == NULL || hook == NULL) { 64952419Sjulian TRAP_ERROR; 65052419Sjulian return (EINVAL); 65152419Sjulian } 65252419Sjulian farnode = ng_findname(sockdata->node, node); 65352419Sjulian if (farnode == NULL) { 65452419Sjulian TRAP_ERROR; 65552419Sjulian return (EADDRNOTAVAIL); 65652419Sjulian } 65752419Sjulian 65852419Sjulian /* Connect, using a hook name the same as the far node name. */ 65952419Sjulian error = ng_con_nodes(sockdata->node, node, farnode, hook); 66052419Sjulian return error; 66152419Sjulian} 66252419Sjulian 66352419Sjulian/* 66452419Sjulian * Binding a socket means giving the corresponding node a name 66552419Sjulian */ 66652419Sjulianstatic int 66752419Sjulianng_bind(struct sockaddr *nam, struct ngpcb *pcbp) 66852419Sjulian{ 66952419Sjulian struct ngsock *const sockdata = pcbp->sockdata; 67052419Sjulian struct sockaddr_ng *const sap = (struct sockaddr_ng *) nam; 67152419Sjulian 67252419Sjulian if (sockdata == NULL) { 67352419Sjulian TRAP_ERROR; 67452419Sjulian return (EINVAL); 67552419Sjulian } 67652419Sjulian if (sap->sg_len < 3 || sap->sg_data[sap->sg_len - 3] != '\0') { 67752419Sjulian TRAP_ERROR; 67852419Sjulian return (EINVAL); 67952419Sjulian } 68052419Sjulian return (ng_name_node(sockdata->node, sap->sg_data)); 68152419Sjulian} 68252419Sjulian 68352419Sjulian/* 68452419Sjulian * Take a message and pass it up to the control socket associated 68552419Sjulian * with the node. 68652419Sjulian */ 68752419Sjulianstatic int 68852419Sjulianship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr) 68952419Sjulian{ 69052419Sjulian struct socket *const so = pcbp->ng_socket; 69152419Sjulian struct mbuf *mdata; 69252419Sjulian int msglen; 69352419Sjulian 69452419Sjulian /* Copy the message itself into an mbuf chain */ 69552419Sjulian msglen = sizeof(struct ng_mesg) + msg->header.arglen; 69652419Sjulian mdata = m_devget((caddr_t) msg, msglen, 0, NULL, NULL); 69752419Sjulian 69852419Sjulian /* Here we free the message, as we are the end of the line. 69952419Sjulian * We need to do that regardless of whether we got mbufs. */ 70052419Sjulian FREE(msg, M_NETGRAPH); 70152419Sjulian 70252419Sjulian if (mdata == NULL) { 70352419Sjulian TRAP_ERROR; 70452419Sjulian return (ENOBUFS); 70552419Sjulian } 70652419Sjulian 70752419Sjulian /* Send it up to the socket */ 70852419Sjulian if (sbappendaddr(&so->so_rcv, 70952419Sjulian (struct sockaddr *) addr, mdata, NULL) == 0) { 71052419Sjulian TRAP_ERROR; 71152419Sjulian m_freem(mdata); 71252419Sjulian return (ENOBUFS); 71352419Sjulian } 71452419Sjulian sorwakeup(so); 71552419Sjulian return (0); 71652419Sjulian} 71752419Sjulian 71852419Sjulian/* 71952419Sjulian * You can only create new nodes from the socket end of things. 72052419Sjulian */ 72152419Sjulianstatic int 72252419Sjulianngs_constructor(node_p *nodep) 72352419Sjulian{ 72452419Sjulian return (EINVAL); 72552419Sjulian} 72652419Sjulian 72752419Sjulian/* 72852419Sjulian * We allow any hook to be connected to the node. 72952419Sjulian * There is no per-hook private information though. 73052419Sjulian */ 73152419Sjulianstatic int 73252419Sjulianngs_newhook(node_p node, hook_p hook, const char *name) 73352419Sjulian{ 73452419Sjulian hook->private = node->private; 73552419Sjulian return (0); 73652419Sjulian} 73752419Sjulian 73852419Sjulian/* 73952419Sjulian * Incoming messages get passed up to the control socket. 74052885Sjulian * Unless they are for us specifically (socket_type) 74152419Sjulian */ 74252419Sjulianstatic int 74352419Sjulianngs_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, 74452419Sjulian struct ng_mesg **resp) 74552419Sjulian{ 74652419Sjulian struct ngsock *const sockdata = node->private; 74752419Sjulian struct ngpcb *const pcbp = sockdata->ctlsock; 74852419Sjulian struct sockaddr_ng *addr; 74952419Sjulian int addrlen; 75052419Sjulian int error = 0; 75152419Sjulian 75252419Sjulian /* Only allow mesgs to be passed if we have the control socket. 75352419Sjulian * Data sockets can only support the generic messages. */ 75452419Sjulian if (pcbp == NULL) { 75552419Sjulian TRAP_ERROR; 75652419Sjulian return (EINVAL); 75752419Sjulian } 75852419Sjulian 75952885Sjulian if (msg->header.typecookie == NGM_SOCKET_COOKIE) { 76052885Sjulian switch (msg->header.cmd) { 76152885Sjulian case NGM_SOCK_CMD_NOLINGER: 76252885Sjulian sockdata->flags |= NGS_FLAG_NOLINGER; 76352885Sjulian break; 76452885Sjulian case NGM_SOCK_CMD_LINGER: 76552885Sjulian sockdata->flags &= ~NGS_FLAG_NOLINGER; 76652885Sjulian break; 76752885Sjulian default: 76852885Sjulian error = EINVAL; /* unknown command */ 76952885Sjulian } 77052885Sjulian /* Free the message and return */ 77152885Sjulian FREE(msg, M_NETGRAPH); 77252885Sjulian return(error); 77352885Sjulian 77452885Sjulian } 77552419Sjulian /* Get the return address into a sockaddr */ 77652419Sjulian if ((retaddr == NULL) || (*retaddr == '\0')) 77752419Sjulian retaddr = ""; 77852419Sjulian addrlen = strlen(retaddr); 77952419Sjulian MALLOC(addr, struct sockaddr_ng *, addrlen + 4, M_NETGRAPH, M_NOWAIT); 78052419Sjulian if (addr == NULL) { 78152419Sjulian TRAP_ERROR; 78252419Sjulian return (ENOMEM); 78352419Sjulian } 78452419Sjulian addr->sg_len = addrlen + 3; 78552419Sjulian addr->sg_family = AF_NETGRAPH; 78652419Sjulian bcopy(retaddr, addr->sg_data, addrlen); 78752419Sjulian addr->sg_data[addrlen] = '\0'; 78852419Sjulian 78952419Sjulian /* Send it up */ 79052419Sjulian error = ship_msg(pcbp, msg, addr); 79152419Sjulian FREE(addr, M_NETGRAPH); 79252419Sjulian return (error); 79352419Sjulian} 79452419Sjulian 79552419Sjulian/* 79652419Sjulian * Receive data on a hook 79752419Sjulian */ 79852419Sjulianstatic int 79952419Sjulianngs_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 80052419Sjulian{ 80152419Sjulian struct ngsock *const sockdata = hook->node->private; 80252419Sjulian struct ngpcb *const pcbp = sockdata->datasock; 80352419Sjulian struct socket *so; 80452419Sjulian struct sockaddr_ng *addr; 80552419Sjulian char *addrbuf[NG_HOOKLEN + 1 + 4]; 80652419Sjulian int addrlen; 80752419Sjulian 80852419Sjulian /* If there is no data socket, black-hole it */ 80952419Sjulian if (pcbp == NULL) { 81052419Sjulian NG_FREE_DATA(m, meta); 81152419Sjulian return (0); 81252419Sjulian } 81352419Sjulian so = pcbp->ng_socket; 81452419Sjulian 81552419Sjulian /* Get the return address into a sockaddr. */ 81652419Sjulian addrlen = strlen(hook->name); /* <= NG_HOOKLEN */ 81752419Sjulian addr = (struct sockaddr_ng *) addrbuf; 81852419Sjulian addr->sg_len = addrlen + 3; 81952419Sjulian addr->sg_family = AF_NETGRAPH; 82052419Sjulian bcopy(hook->name, addr->sg_data, addrlen); 82152419Sjulian addr->sg_data[addrlen] = '\0'; 82252419Sjulian 82352419Sjulian /* We have no use for the meta data, free/clear it now. */ 82452419Sjulian NG_FREE_META(meta); 82552419Sjulian 82652419Sjulian /* Try to tell the socket which hook it came in on */ 82752419Sjulian if (sbappendaddr(&so->so_rcv, (struct sockaddr *) addr, m, NULL) == 0) { 82852419Sjulian m_freem(m); 82952419Sjulian TRAP_ERROR; 83052419Sjulian return (ENOBUFS); 83152419Sjulian } 83252419Sjulian sorwakeup(so); 83352419Sjulian return (0); 83452419Sjulian} 83552419Sjulian 83652419Sjulian/* 83753498Sjulian * Hook disconnection 83852885Sjulian * 83952885Sjulian * For this type, removal of the last link destroys the node 84052885Sjulian * if the NOLINGER flag is set. 84152885Sjulian */ 84252885Sjulianstatic int 84352885Sjulianngs_disconnect(hook_p hook) 84452885Sjulian{ 84552885Sjulian struct ngsock *const sockdata = hook->node->private; 84652885Sjulian 84752885Sjulian if ((sockdata->flags & NGS_FLAG_NOLINGER ) 84852885Sjulian && (hook->node->numhooks == 0)) { 84952885Sjulian ng_rmnode(hook->node); 85052885Sjulian } 85152885Sjulian return (0); 85252885Sjulian} 85352885Sjulian 85452885Sjulian/* 85552419Sjulian * Do local shutdown processing. 85652419Sjulian * In this case, that involves making sure the socket 85752419Sjulian * knows we should be shutting down. 85852419Sjulian */ 85952419Sjulianstatic int 86052419Sjulianngs_rmnode(node_p node) 86152419Sjulian{ 86252419Sjulian struct ngsock *const sockdata = node->private; 86352419Sjulian struct ngpcb *const dpcbp = sockdata->datasock; 86452419Sjulian struct ngpcb *const pcbp = sockdata->ctlsock; 86552419Sjulian 86652419Sjulian ng_cutlinks(node); 86752419Sjulian ng_unname(node); 86852419Sjulian 86952419Sjulian if (dpcbp != NULL) { 87052419Sjulian soisdisconnected(dpcbp->ng_socket); 87152419Sjulian dpcbp->sockdata = NULL; 87252419Sjulian sockdata->datasock = NULL; 87352419Sjulian sockdata->refs--; 87452419Sjulian } 87552419Sjulian if (pcbp != NULL) { 87652419Sjulian soisdisconnected(pcbp->ng_socket); 87752419Sjulian pcbp->sockdata = NULL; 87852419Sjulian sockdata->ctlsock = NULL; 87952419Sjulian sockdata->refs--; 88052419Sjulian } 88152419Sjulian node->private = NULL; 88252419Sjulian ng_unref(node); 88352419Sjulian FREE(sockdata, M_NETGRAPH); 88452419Sjulian return (0); 88552419Sjulian} 88652419Sjulian 88752419Sjulian/* 88852419Sjulian * Control and data socket type descriptors 88952419Sjulian */ 89052419Sjulian 89152419Sjulianstatic struct pr_usrreqs ngc_usrreqs = { 89252419Sjulian NULL, /* abort */ 89352419Sjulian pru_accept_notsupp, 89452419Sjulian ngc_attach, 89552419Sjulian ngc_bind, 89652419Sjulian ngc_connect, 89752419Sjulian pru_connect2_notsupp, 89852419Sjulian pru_control_notsupp, 89952419Sjulian ngc_detach, 90052419Sjulian NULL, /* disconnect */ 90152419Sjulian pru_listen_notsupp, 90252419Sjulian NULL, /* setpeeraddr */ 90352419Sjulian pru_rcvd_notsupp, 90452419Sjulian pru_rcvoob_notsupp, 90552419Sjulian ngc_send, 90652419Sjulian pru_sense_null, 90752419Sjulian NULL, /* shutdown */ 90852419Sjulian ng_setsockaddr, 90952419Sjulian sosend, 91052419Sjulian soreceive, 91152419Sjulian sopoll 91252419Sjulian}; 91352419Sjulian 91452419Sjulianstatic struct pr_usrreqs ngd_usrreqs = { 91552419Sjulian NULL, /* abort */ 91652419Sjulian pru_accept_notsupp, 91752419Sjulian ngd_attach, 91852419Sjulian NULL, /* bind */ 91952419Sjulian ngd_connect, 92052419Sjulian pru_connect2_notsupp, 92152419Sjulian pru_control_notsupp, 92252419Sjulian ngd_detach, 92352419Sjulian NULL, /* disconnect */ 92452419Sjulian pru_listen_notsupp, 92552419Sjulian NULL, /* setpeeraddr */ 92652419Sjulian pru_rcvd_notsupp, 92752419Sjulian pru_rcvoob_notsupp, 92852419Sjulian ngd_send, 92952419Sjulian pru_sense_null, 93052419Sjulian NULL, /* shutdown */ 93152419Sjulian ng_setsockaddr, 93252419Sjulian sosend, 93352419Sjulian soreceive, 93452419Sjulian sopoll 93552419Sjulian}; 93652419Sjulian 93752419Sjulian/* 93852419Sjulian * Definitions of protocols supported in the NETGRAPH domain. 93952419Sjulian */ 94052419Sjulian 94152419Sjulianextern struct domain ngdomain; /* stop compiler warnings */ 94252419Sjulian 94352419Sjulianstatic struct protosw ngsw[] = { 94452419Sjulian { 94552419Sjulian SOCK_DGRAM, 94652419Sjulian &ngdomain, 94752419Sjulian NG_CONTROL, 94852419Sjulian PR_ATOMIC | PR_ADDR /* | PR_RIGHTS */, 94952419Sjulian 0, 0, 0, 0, 95052419Sjulian NULL, 95152419Sjulian 0, 0, 0, 0, 95252419Sjulian &ngc_usrreqs 95352419Sjulian }, 95452419Sjulian { 95552419Sjulian SOCK_DGRAM, 95652419Sjulian &ngdomain, 95752419Sjulian NG_DATA, 95852419Sjulian PR_ATOMIC | PR_ADDR, 95952419Sjulian 0, 0, 0, 0, 96052419Sjulian NULL, 96152419Sjulian 0, 0, 0, 0, 96252419Sjulian &ngd_usrreqs 96352419Sjulian } 96452419Sjulian}; 96552419Sjulian 96652419Sjulianstruct domain ngdomain = { 96752419Sjulian AF_NETGRAPH, 96852419Sjulian "netgraph", 96952419Sjulian 0, 97052419Sjulian NULL, 97152419Sjulian NULL, 97252419Sjulian ngsw, 97352419Sjulian &ngsw[sizeof(ngsw) / sizeof(ngsw[0])], 97452419Sjulian 0, 97552419Sjulian NULL, 97652419Sjulian 0, 97752419Sjulian 0 97852419Sjulian}; 97952419Sjulian 98052419Sjulian/* 98152419Sjulian * Handle loading and unloading for this node type 98252419Sjulian * This is to handle auxiliary linkages (e.g protocol domain addition). 98352419Sjulian */ 98452419Sjulianstatic int 98552419Sjulianngs_mod_event(module_t mod, int event, void *data) 98652419Sjulian{ 98752419Sjulian int error = 0; 98852419Sjulian 98952419Sjulian switch (event) { 99052419Sjulian case MOD_LOAD: 99152419Sjulian /* Register protocol domain */ 99252419Sjulian net_add_domain(&ngdomain); 99352419Sjulian break; 99452419Sjulian case MOD_UNLOAD: 99552419Sjulian /* Insure there are no open netgraph sockets */ 99652419Sjulian if (!LIST_EMPTY(&ngsocklist)) { 99752419Sjulian error = EBUSY; 99852419Sjulian break; 99952419Sjulian } 100052419Sjulian 100152419Sjulian#ifdef NOTYET 100252419Sjulian /* Unregister protocol domain XXX can't do this yet.. */ 100352419Sjulian if ((error = net_rm_domain(&ngdomain)) != 0) 100452419Sjulian break; 100552419Sjulian#else 100652419Sjulian error = EBUSY; 100752419Sjulian#endif 100852419Sjulian break; 100952419Sjulian default: 101052419Sjulian error = EOPNOTSUPP; 101152419Sjulian break; 101252419Sjulian } 101352419Sjulian return (error); 101452419Sjulian} 101552419Sjulian 101652419SjulianSYSCTL_NODE(_net, AF_NETGRAPH, graph, CTLFLAG_RW, 0, "netgraph Family"); 101752419SjulianSYSCTL_INT(_net_graph, OID_AUTO, family, CTLFLAG_RD, 0, AF_NETGRAPH, ""); 101852419SjulianSYSCTL_NODE(_net_graph, OID_AUTO, data, CTLFLAG_RW, 0, "DATA"); 101952419SjulianSYSCTL_INT(_net_graph_data, OID_AUTO, proto, CTLFLAG_RD, 0, NG_DATA, ""); 102052419SjulianSYSCTL_NODE(_net_graph, OID_AUTO, control, CTLFLAG_RW, 0, "CONTROL"); 102152419SjulianSYSCTL_INT(_net_graph_control, OID_AUTO, proto, CTLFLAG_RD, 0, NG_CONTROL, ""); 102252419Sjulian 1023