1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2002 Mark Santcroos <marks@ripe.net> 5 * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * Netgraph "device" node 28 * 29 * This node presents a /dev/ngd%d device that interfaces to an other 30 * netgraph node. 31 * 32 * $FreeBSD$ 33 * 34 */ 35 36 #if 0 37 #define DBG do { printf("ng_device: %s\n", __func__ ); } while (0) 38 #else 39 #define DBG do {} while (0) 40 #endif 41 42 #include <sys/param.h> 43 #include <sys/conf.h> 44 #include <sys/ioccom.h> 45 #include <sys/kernel.h> 46 #include <sys/malloc.h> 47 #include <sys/mbuf.h> 48 #include <sys/poll.h> 49 #include <sys/proc.h> 50 #include <sys/epoch.h> 51 #include <sys/queue.h> 52 #include <sys/socket.h> 53 #include <sys/systm.h> 54 #include <sys/uio.h> 55 #include <sys/vnode.h> 56 57 #include <net/if.h> 58 #include <net/if_var.h> 59 #include <netinet/in.h> 60 #include <netinet/in_systm.h> 61 #include <netinet/ip.h> 62 63 #include <netgraph/ng_message.h> 64 #include <netgraph/netgraph.h> 65 #include <netgraph/ng_device.h> 66 67 #define ERROUT(x) do { error = (x); goto done; } while (0) 68 69 /* Netgraph methods */ 70 static int ng_device_mod_event(module_t, int, void *); 71 static ng_constructor_t ng_device_constructor; 72 static ng_rcvmsg_t ng_device_rcvmsg; 73 static ng_shutdown_t ng_device_shutdown; 74 static ng_newhook_t ng_device_newhook; 75 static ng_rcvdata_t ng_device_rcvdata; 76 static ng_disconnect_t ng_device_disconnect; 77 78 /* Netgraph type */ 79 static struct ng_type ngd_typestruct = { 80 .version = NG_ABI_VERSION, 81 .name = NG_DEVICE_NODE_TYPE, 82 .mod_event = ng_device_mod_event, 83 .constructor = ng_device_constructor, 84 .rcvmsg = ng_device_rcvmsg, 85 .shutdown = ng_device_shutdown, 86 .newhook = ng_device_newhook, 87 .rcvdata = ng_device_rcvdata, 88 .disconnect = ng_device_disconnect, 89 }; 90 NETGRAPH_INIT(device, &ngd_typestruct); 91 92 /* per node data */ 93 struct ngd_private { 94 struct ifqueue readq; 95 struct ng_node *node; 96 struct ng_hook *hook; 97 struct cdev *ngddev; 98 struct mtx ngd_mtx; 99 int unit; 100 uint16_t flags; 101 #define NGDF_OPEN 0x0001 102 #define NGDF_RWAIT 0x0002 103 }; 104 typedef struct ngd_private *priv_p; 105 106 /* unit number allocator entity */ 107 static struct unrhdr *ngd_unit; 108 109 /* Maximum number of NGD devices */ 110 #define MAX_NGD 999 111 112 static d_close_t ngdclose; 113 static d_open_t ngdopen; 114 static d_read_t ngdread; 115 static d_write_t ngdwrite; 116 #if 0 117 static d_ioctl_t ngdioctl; 118 #endif 119 static d_poll_t ngdpoll; 120 121 static struct cdevsw ngd_cdevsw = { 122 .d_version = D_VERSION, 123 .d_open = ngdopen, 124 .d_close = ngdclose, 125 .d_read = ngdread, 126 .d_write = ngdwrite, 127 #if 0 128 .d_ioctl = ngdioctl, 129 #endif 130 .d_poll = ngdpoll, 131 .d_name = NG_DEVICE_DEVNAME, 132 }; 133 134 /****************************************************************************** 135 * Netgraph methods 136 ******************************************************************************/ 137 138 /* 139 * Handle loading and unloading for this node type. 140 */ 141 static int 142 ng_device_mod_event(module_t mod, int event, void *data) 143 { 144 int error = 0; 145 146 switch (event) { 147 case MOD_LOAD: 148 ngd_unit = new_unrhdr(0, MAX_NGD, NULL); 149 break; 150 case MOD_UNLOAD: 151 delete_unrhdr(ngd_unit); 152 break; 153 default: 154 error = EOPNOTSUPP; 155 break; 156 } 157 return (error); 158 } 159 160 /* 161 * create new node 162 */ 163 static int 164 ng_device_constructor(node_p node) 165 { 166 priv_p priv; 167 168 DBG; 169 170 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 171 172 /* Allocate unit number */ 173 priv->unit = alloc_unr(ngd_unit); 174 175 /* Initialize mutexes and queue */ 176 mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF); 177 mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); 178 IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); 179 180 /* Link everything together */ 181 NG_NODE_SET_PRIVATE(node, priv); 182 priv->node = node; 183 184 priv->ngddev = make_dev(&ngd_cdevsw, priv->unit, UID_ROOT, 185 GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit); 186 if(priv->ngddev == NULL) { 187 printf("%s(): make_dev() failed\n",__func__); 188 mtx_destroy(&priv->ngd_mtx); 189 mtx_destroy(&priv->readq.ifq_mtx); 190 free_unr(ngd_unit, priv->unit); 191 free(priv, M_NETGRAPH); 192 return(EINVAL); 193 } 194 /* XXX: race here? */ 195 priv->ngddev->si_drv1 = priv; 196 197 return(0); 198 } 199 200 /* 201 * Process control message. 202 */ 203 204 static int 205 ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook) 206 { 207 const priv_p priv = NG_NODE_PRIVATE(node); 208 struct ng_mesg *msg; 209 struct ng_mesg *resp = NULL; 210 const char *dn; 211 int error = 0; 212 213 NGI_GET_MSG(item, msg); 214 215 if (msg->header.typecookie == NGM_DEVICE_COOKIE) { 216 switch (msg->header.cmd) { 217 case NGM_DEVICE_GET_DEVNAME: 218 /* XXX: Fix when MAX_NGD us bigger */ 219 NG_MKRESPONSE(resp, msg, 220 strlen(NG_DEVICE_DEVNAME) + 4, M_NOWAIT); 221 222 if (resp == NULL) 223 ERROUT(ENOMEM); 224 225 dn = devtoname(priv->ngddev); 226 strlcpy((char *)resp->data, dn, strlen(dn) + 1); 227 break; 228 229 default: 230 error = EINVAL; 231 break; 232 } 233 } else 234 error = EINVAL; 235 236 done: 237 NG_RESPOND_MSG(error, node, item, resp); 238 NG_FREE_MSG(msg); 239 return (error); 240 } 241 242 /* 243 * Accept incoming hook. We support only one hook per node. 244 */ 245 static int 246 ng_device_newhook(node_p node, hook_p hook, const char *name) 247 { 248 priv_p priv = NG_NODE_PRIVATE(node); 249 250 DBG; 251 252 /* We have only one hook per node */ 253 if (priv->hook != NULL) 254 return (EISCONN); 255 256 priv->hook = hook; 257 258 return(0); 259 } 260 261 /* 262 * Receive data from hook, write it to device. 263 */ 264 static int 265 ng_device_rcvdata(hook_p hook, item_p item) 266 { 267 priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 268 struct mbuf *m; 269 270 DBG; 271 272 NGI_GET_M(item, m); 273 NG_FREE_ITEM(item); 274 275 IF_LOCK(&priv->readq); 276 if (_IF_QFULL(&priv->readq)) { 277 IF_UNLOCK(&priv->readq); 278 NG_FREE_M(m); 279 return (ENOBUFS); 280 } 281 282 _IF_ENQUEUE(&priv->readq, m); 283 IF_UNLOCK(&priv->readq); 284 mtx_lock(&priv->ngd_mtx); 285 if (priv->flags & NGDF_RWAIT) { 286 priv->flags &= ~NGDF_RWAIT; 287 wakeup(priv); 288 } 289 mtx_unlock(&priv->ngd_mtx); 290 291 return(0); 292 } 293 294 /* 295 * Removal of the hook destroys the node. 296 */ 297 static int 298 ng_device_disconnect(hook_p hook) 299 { 300 priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 301 302 DBG; 303 304 destroy_dev(priv->ngddev); 305 mtx_destroy(&priv->ngd_mtx); 306 307 IF_DRAIN(&priv->readq); 308 mtx_destroy(&(priv)->readq.ifq_mtx); 309 310 free_unr(ngd_unit, priv->unit); 311 312 free(priv, M_NETGRAPH); 313 314 ng_rmnode_self(NG_HOOK_NODE(hook)); 315 316 return(0); 317 } 318 319 /* 320 * Node shutdown. Everything is already done in disconnect method. 321 */ 322 static int 323 ng_device_shutdown(node_p node) 324 { 325 NG_NODE_UNREF(node); 326 return (0); 327 } 328 329 /****************************************************************************** 330 * Device methods 331 ******************************************************************************/ 332 333 /* 334 * the device is opened 335 */ 336 static int 337 ngdopen(struct cdev *dev, int flag, int mode, struct thread *td) 338 { 339 priv_p priv = (priv_p )dev->si_drv1; 340 341 DBG; 342 343 mtx_lock(&priv->ngd_mtx); 344 priv->flags |= NGDF_OPEN; 345 mtx_unlock(&priv->ngd_mtx); 346 347 return(0); 348 } 349 350 /* 351 * the device is closed 352 */ 353 static int 354 ngdclose(struct cdev *dev, int flag, int mode, struct thread *td) 355 { 356 priv_p priv = (priv_p )dev->si_drv1; 357 358 DBG; 359 mtx_lock(&priv->ngd_mtx); 360 priv->flags &= ~NGDF_OPEN; 361 mtx_unlock(&priv->ngd_mtx); 362 363 return(0); 364 } 365 366 #if 0 /* 367 * The ioctl is transformed into netgraph control message. 368 * We do not process them, yet. 369 */ 370 /* 371 * process ioctl 372 * 373 * they are translated into netgraph messages and passed on 374 * 375 */ 376 static int 377 ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 378 { 379 struct ngd_softc *sc = &ngd_softc; 380 struct ngd_connection * connection = NULL; 381 struct ngd_connection * tmp; 382 int error = 0; 383 struct ng_mesg *msg; 384 struct ngd_param_s * datap; 385 386 DBG; 387 388 NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 389 M_NOWAIT); 390 if (msg == NULL) { 391 printf("%s(): msg == NULL\n",__func__); 392 goto nomsg; 393 } 394 395 /* pass the ioctl data into the ->data area */ 396 datap = (struct ngd_param_s *)msg->data; 397 datap->p = addr; 398 399 NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0); 400 if(error) 401 printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error); 402 403 nomsg: 404 405 return(0); 406 } 407 #endif /* if 0 */ 408 409 /* 410 * This function is called when a read(2) is done to our device. 411 * We process one mbuf from queue. 412 */ 413 static int 414 ngdread(struct cdev *dev, struct uio *uio, int flag) 415 { 416 priv_p priv = (priv_p )dev->si_drv1; 417 struct mbuf *m; 418 int len, error = 0; 419 420 DBG; 421 422 /* get an mbuf */ 423 do { 424 IF_DEQUEUE(&priv->readq, m); 425 if (m == NULL) { 426 if (flag & IO_NDELAY) 427 return (EWOULDBLOCK); 428 mtx_lock(&priv->ngd_mtx); 429 priv->flags |= NGDF_RWAIT; 430 if ((error = msleep(priv, &priv->ngd_mtx, 431 PDROP | PCATCH | (PZERO + 1), 432 "ngdread", 0)) != 0) 433 return (error); 434 } 435 } while (m == NULL); 436 437 while (m && uio->uio_resid > 0 && error == 0) { 438 len = MIN(uio->uio_resid, m->m_len); 439 if (len != 0) 440 error = uiomove(mtod(m, void *), len, uio); 441 m = m_free(m); 442 } 443 444 if (m) 445 m_freem(m); 446 447 return (error); 448 } 449 450 /* 451 * This function is called when our device is written to. 452 * We read the data from userland into mbuf chain and pass it to the remote hook. 453 * 454 */ 455 static int 456 ngdwrite(struct cdev *dev, struct uio *uio, int flag) 457 { 458 struct epoch_tracker et; 459 priv_p priv = (priv_p )dev->si_drv1; 460 struct mbuf *m; 461 int error = 0; 462 463 DBG; 464 465 if (uio->uio_resid == 0) 466 return (0); 467 468 if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET) 469 return (EIO); 470 471 if ((m = m_uiotombuf(uio, M_NOWAIT, 0, 0, M_PKTHDR)) == NULL) 472 return (ENOBUFS); 473 474 NET_EPOCH_ENTER(et); 475 NG_SEND_DATA_ONLY(error, priv->hook, m); 476 NET_EPOCH_EXIT(et); 477 478 return (error); 479 } 480 481 /* 482 * we are being polled/selected 483 * check if there is data available for read 484 */ 485 static int 486 ngdpoll(struct cdev *dev, int events, struct thread *td) 487 { 488 priv_p priv = (priv_p )dev->si_drv1; 489 int revents = 0; 490 491 if (events & (POLLIN | POLLRDNORM) && 492 !IFQ_IS_EMPTY(&priv->readq)) 493 revents |= events & (POLLIN | POLLRDNORM); 494 495 return (revents); 496 } 497