FreeBSD kernel netgraph code
ng_device.c
Go to the documentation of this file.
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/syslog.h>
54#include <sys/systm.h>
55#include <sys/uio.h>
56#include <sys/vnode.h>
57
58#include <net/ethernet.h>
59#include <net/if.h>
60#include <net/if_var.h>
61#include <netinet/in.h>
62#include <netinet/in_systm.h>
63#include <netinet/ip.h>
64
65#include <netgraph/ng_message.h>
66#include <netgraph/netgraph.h>
67#include <netgraph/ng_device.h>
68#include <netgraph/ng_parse.h>
69
70#define ERROUT(x) do { error = (x); goto done; } while (0)
71
72/* Netgraph methods */
73static int ng_device_mod_event(module_t, int, void *);
80
81/* List of commands and how to convert arguments to/from ASCII. */
82static const struct ng_cmdlist ng_device_cmds[] = {
83 {
86 "getdevname",
87 NULL,
89 },
90 {
93 "etheralign",
94 NULL,
95 NULL
96 },
97 { 0 }
98};
99
100/* Netgraph type */
101static struct ng_type ngd_typestruct = {
103 .name = NG_DEVICE_NODE_TYPE,
104 .mod_event = ng_device_mod_event,
105 .constructor = ng_device_constructor,
106 .rcvmsg = ng_device_rcvmsg,
107 .shutdown = ng_device_shutdown,
108 .newhook = ng_device_newhook,
109 .rcvdata = ng_device_rcvdata,
110 .disconnect = ng_device_disconnect,
111 .cmdlist = ng_device_cmds,
112};
114
115/* per node data */
117 struct ifqueue readq;
118 struct ng_node *node;
119 struct ng_hook *hook;
120 struct cdev *ngddev;
121 struct mtx ngd_mtx;
122 int unit;
124 uint16_t flags;
125#define NGDF_OPEN 0x0001
126#define NGDF_RWAIT 0x0002
127};
128typedef struct ngd_private *priv_p;
129
130/* unit number allocator entity */
131static struct unrhdr *ngd_unit;
132
133/* Maximum number of NGD devices */
134#define MAX_NGD 999
135
136static d_close_t ngdclose;
137static d_open_t ngdopen;
138static d_read_t ngdread;
139static d_write_t ngdwrite;
140#if 0
141static d_ioctl_t ngdioctl;
142#endif
143static d_poll_t ngdpoll;
144
145static struct cdevsw ngd_cdevsw = {
146 .d_version = D_VERSION,
147 .d_open = ngdopen,
148 .d_close = ngdclose,
149 .d_read = ngdread,
150 .d_write = ngdwrite,
151#if 0
152 .d_ioctl = ngdioctl,
153#endif
154 .d_poll = ngdpoll,
155 .d_name = NG_DEVICE_DEVNAME,
156};
157
158/******************************************************************************
159 * Netgraph methods
160 ******************************************************************************/
161
162/*
163 * Handle loading and unloading for this node type.
164 */
165static int
166ng_device_mod_event(module_t mod, int event, void *data)
167{
168 int error = 0;
169
170 switch (event) {
171 case MOD_LOAD:
172 ngd_unit = new_unrhdr(0, MAX_NGD, NULL);
173 break;
174 case MOD_UNLOAD:
175 delete_unrhdr(ngd_unit);
176 break;
177 default:
178 error = EOPNOTSUPP;
179 break;
180 }
181 return (error);
182}
183
184/*
185 * create new node
186 */
187static int
189{
190 priv_p priv;
191
192 DBG;
193
194 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
195
196 /* Allocate unit number */
197 priv->unit = alloc_unr(ngd_unit);
198
199 /* Initialize mutexes and queue */
200 mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF);
201 mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF);
202 IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen);
203
204 /* Link everything together */
206 priv->node = node;
207
208 priv->ngddev = make_dev(&ngd_cdevsw, priv->unit, UID_ROOT,
209 GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit);
210 if(priv->ngddev == NULL) {
211 printf("%s(): make_dev() failed\n",__func__);
212 mtx_destroy(&priv->ngd_mtx);
213 mtx_destroy(&priv->readq.ifq_mtx);
214 free_unr(ngd_unit, priv->unit);
215 free(priv, M_NETGRAPH);
216 return(EINVAL);
217 }
218 /* XXX: race here? */
219 priv->ngddev->si_drv1 = priv;
220
221 /* Give this node the same name as the device (if possible). */
222 if (ng_name_node(node, devtoname(priv->ngddev)) != 0)
223 log(LOG_WARNING, "%s: can't acquire netgraph name\n",
224 devtoname(priv->ngddev));
225
226 return(0);
227}
228
229/*
230 * Process control message.
231 */
232
233static int
235{
236 const priv_p priv = NG_NODE_PRIVATE(node);
237 struct ng_mesg *msg;
238 struct ng_mesg *resp = NULL;
239 const char *dn;
240 int error = 0;
241
242 NGI_GET_MSG(item, msg);
243
244 if (msg->header.typecookie == NGM_DEVICE_COOKIE) {
245 switch (msg->header.cmd) {
247 /* XXX: Fix when MAX_NGD us bigger */
248 NG_MKRESPONSE(resp, msg,
249 strlen(NG_DEVICE_DEVNAME) + 4, M_NOWAIT);
250
251 if (resp == NULL)
252 ERROUT(ENOMEM);
253
254 dn = devtoname(priv->ngddev);
255 strlcpy((char *)resp->data, dn, strlen(dn) + 1);
256 break;
257
259 /* Use ETHER_ALIGN on arches that require it. */
260#ifndef __NO_STRICT_ALIGNMENT
261 priv->ether_align = ETHER_ALIGN;
262#endif
263 break;
264
265 default:
266 error = EINVAL;
267 break;
268 }
269 } else
270 error = EINVAL;
271
272done:
273 NG_RESPOND_MSG(error, node, item, resp);
274 NG_FREE_MSG(msg);
275 return (error);
276}
277
278/*
279 * Accept incoming hook. We support only one hook per node.
280 */
281static int
282ng_device_newhook(node_p node, hook_p hook, const char *name)
283{
285
286 DBG;
287
288 /* We have only one hook per node */
289 if (priv->hook != NULL)
290 return (EISCONN);
291
292 priv->hook = hook;
293
294 return(0);
295}
296
297/*
298 * Receive data from hook, write it to device.
299 */
300static int
302{
304 struct mbuf *m;
305
306 DBG;
307
308 NGI_GET_M(item, m);
309 NG_FREE_ITEM(item);
310
311 IF_LOCK(&priv->readq);
312 if (_IF_QFULL(&priv->readq)) {
313 IF_UNLOCK(&priv->readq);
314 NG_FREE_M(m);
315 return (ENOBUFS);
316 }
317
318 _IF_ENQUEUE(&priv->readq, m);
319 IF_UNLOCK(&priv->readq);
320 mtx_lock(&priv->ngd_mtx);
321 if (priv->flags & NGDF_RWAIT) {
322 priv->flags &= ~NGDF_RWAIT;
323 wakeup(priv);
324 }
325 mtx_unlock(&priv->ngd_mtx);
326
327 return(0);
328}
329
330/*
331 * Removal of the hook destroys the node.
332 */
333static int
335{
337
338 DBG;
339
340 destroy_dev(priv->ngddev);
341 mtx_destroy(&priv->ngd_mtx);
342
343 IF_DRAIN(&priv->readq);
344 mtx_destroy(&(priv)->readq.ifq_mtx);
345
346 free_unr(ngd_unit, priv->unit);
347
348 free(priv, M_NETGRAPH);
349
351
352 return(0);
353}
354
355/*
356 * Node shutdown. Everything is already done in disconnect method.
357 */
358static int
360{
361 NG_NODE_UNREF(node);
362 return (0);
363}
364
365/******************************************************************************
366 * Device methods
367 ******************************************************************************/
368
369/*
370 * the device is opened
371 */
372static int
373ngdopen(struct cdev *dev, int flag, int mode, struct thread *td)
374{
375 priv_p priv = (priv_p )dev->si_drv1;
376
377 DBG;
378
379 mtx_lock(&priv->ngd_mtx);
380 priv->flags |= NGDF_OPEN;
381 mtx_unlock(&priv->ngd_mtx);
382
383 return(0);
384}
385
386/*
387 * the device is closed
388 */
389static int
390ngdclose(struct cdev *dev, int flag, int mode, struct thread *td)
391{
392 priv_p priv = (priv_p )dev->si_drv1;
393
394 DBG;
395 mtx_lock(&priv->ngd_mtx);
396 priv->flags &= ~NGDF_OPEN;
397 mtx_unlock(&priv->ngd_mtx);
398
399 return(0);
400}
401
402#if 0 /*
403 * The ioctl is transformed into netgraph control message.
404 * We do not process them, yet.
405 */
406/*
407 * process ioctl
408 *
409 * they are translated into netgraph messages and passed on
410 *
411 */
412static int
413ngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
414{
415 struct ngd_softc *sc = &ngd_softc;
416 struct ngd_connection * connection = NULL;
417 struct ngd_connection * tmp;
418 int error = 0;
419 struct ng_mesg *msg;
420 struct ngd_param_s * datap;
421
422 DBG;
423
424 NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s),
425 M_NOWAIT);
426 if (msg == NULL) {
427 printf("%s(): msg == NULL\n",__func__);
428 goto nomsg;
429 }
430
431 /* pass the ioctl data into the ->data area */
432 datap = (struct ngd_param_s *)msg->data;
433 datap->p = addr;
434
435 NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0);
436 if(error)
437 printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
438
439nomsg:
440
441 return(0);
442}
443#endif /* if 0 */
444
445/*
446 * This function is called when a read(2) is done to our device.
447 * We process one mbuf from queue.
448 */
449static int
450ngdread(struct cdev *dev, struct uio *uio, int flag)
451{
452 priv_p priv = (priv_p )dev->si_drv1;
453 struct mbuf *m;
454 int len, error = 0;
455
456 DBG;
457
458 /* get an mbuf */
459 do {
460 IF_DEQUEUE(&priv->readq, m);
461 if (m == NULL) {
462 if (flag & IO_NDELAY)
463 return (EWOULDBLOCK);
464 mtx_lock(&priv->ngd_mtx);
465 priv->flags |= NGDF_RWAIT;
466 if ((error = msleep(priv, &priv->ngd_mtx,
467 PDROP | PCATCH | (PZERO + 1),
468 "ngdread", 0)) != 0)
469 return (error);
470 }
471 } while (m == NULL);
472
473 while (m && uio->uio_resid > 0 && error == 0) {
474 len = MIN(uio->uio_resid, m->m_len);
475 if (len != 0)
476 error = uiomove(mtod(m, void *), len, uio);
477 m = m_free(m);
478 }
479
480 if (m)
481 m_freem(m);
482
483 return (error);
484}
485
486/*
487 * This function is called when our device is written to.
488 * We read the data from userland into mbuf chain and pass it to the remote hook.
489 *
490 */
491static int
492ngdwrite(struct cdev *dev, struct uio *uio, int flag)
493{
494 struct epoch_tracker et;
495 priv_p priv = (priv_p )dev->si_drv1;
496 struct mbuf *m;
497 int error = 0;
498
499 DBG;
500
501 if (uio->uio_resid == 0)
502 return (0);
503
504 if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET)
505 return (EIO);
506
507 m = m_uiotombuf(uio, M_NOWAIT, 0, priv->ether_align, M_PKTHDR);
508 if (m == NULL)
509 return (ENOBUFS);
510
511 NET_EPOCH_ENTER(et);
512 NG_SEND_DATA_ONLY(error, priv->hook, m);
513 NET_EPOCH_EXIT(et);
514
515 return (error);
516}
517
518/*
519 * we are being polled/selected
520 * check if there is data available for read
521 */
522static int
523ngdpoll(struct cdev *dev, int events, struct thread *td)
524{
525 priv_p priv = (priv_p )dev->si_drv1;
526 int revents = 0;
527
528 if (events & (POLLIN | POLLRDNORM) &&
529 !IFQ_IS_EMPTY(&priv->readq))
530 revents |= events & (POLLIN | POLLRDNORM);
531
532 return (revents);
533}
#define NG_HOOK_NODE(hook)
Definition: netgraph.h:339
#define NG_FREE_M(m)
Definition: netgraph.h:946
int ng_rcvmsg_t(node_p node, item_p item, hook_p lasthook)
Definition: netgraph.h:105
int ng_disconnect_t(hook_p hook)
Definition: netgraph.h:107
#define NG_NODE_SET_PRIVATE(node, val)
Definition: netgraph.h:608
#define NG_RESPOND_MSG(error, here, item, resp)
Definition: netgraph.h:1025
#define NG_NODE_UNREF(node)
Definition: netgraph.h:607
int ng_rmnode_self(node_p here)
Definition: ng_base.c:1609
#define NG_SEND_DATA_ONLY(error, hook, m)
Definition: netgraph.h:932
int ng_rcvdata_t(hook_p hook, item_p item)
Definition: netgraph.h:106
int ng_shutdown_t(node_p node)
Definition: netgraph.h:101
#define NG_FREE_ITEM(item)
Definition: netgraph.h:847
int ng_name_node(node_p node, const char *name)
Definition: ng_base.c:852
int ng_constructor_t(node_p node)
Definition: netgraph.h:99
#define NGI_GET_M(i, m)
Definition: netgraph.h:852
#define NG_FREE_MSG(msg)
Definition: netgraph.h:938
#define NG_ABI_VERSION
Definition: netgraph.h:77
#define NGI_GET_MSG(i, m)
Definition: netgraph.h:858
#define NG_NODE_PRIVATE(node)
Definition: netgraph.h:609
#define NG_SEND_MSG_HOOK(error, here, msg, hook, retaddr)
Definition: netgraph.h:958
int ng_newhook_t(node_p node, hook_p hook, const char *name)
Definition: netgraph.h:102
int ifqmaxlen
static struct ng_type ngd_typestruct
Definition: ng_device.c:101
static d_read_t ngdread
Definition: ng_device.c:138
static d_poll_t ngdpoll
Definition: ng_device.c:143
static ng_shutdown_t ng_device_shutdown
Definition: ng_device.c:76
#define DBG
Definition: ng_device.c:39
static ng_newhook_t ng_device_newhook
Definition: ng_device.c:77
struct ngd_private * priv_p
Definition: ng_device.c:128
static int ng_device_mod_event(module_t, int, void *)
Definition: ng_device.c:166
#define ERROUT(x)
Definition: ng_device.c:70
static struct unrhdr * ngd_unit
Definition: ng_device.c:131
static struct cdevsw ngd_cdevsw
Definition: ng_device.c:145
static ng_rcvmsg_t ng_device_rcvmsg
Definition: ng_device.c:75
static d_open_t ngdopen
Definition: ng_device.c:137
#define NGDF_OPEN
Definition: ng_device.c:125
static ng_disconnect_t ng_device_disconnect
Definition: ng_device.c:79
static const struct ng_cmdlist ng_device_cmds[]
Definition: ng_device.c:82
static d_close_t ngdclose
Definition: ng_device.c:136
NETGRAPH_INIT(device, &ngd_typestruct)
#define NGDF_RWAIT
Definition: ng_device.c:126
static d_write_t ngdwrite
Definition: ng_device.c:139
#define MAX_NGD
Definition: ng_device.c:134
static ng_rcvdata_t ng_device_rcvdata
Definition: ng_device.c:78
static ng_constructor_t ng_device_constructor
Definition: ng_device.c:74
@ NGM_DEVICE_ETHERALIGN
Definition: ng_device.h:42
@ NGM_DEVICE_GET_DEVNAME
Definition: ng_device.h:41
#define NGM_DEVICE_COOKIE
Definition: ng_device.h:36
#define NG_DEVICE_NODE_TYPE
Definition: ng_device.h:35
#define NG_DEVICE_DEVNAME
Definition: ng_device.h:37
#define NG_MKRESPONSE(rsp, msg, len, how)
Definition: ng_message.h:396
#define NG_MKMESSAGE(msg, cookie, cmdid, len, how)
Definition: ng_message.h:378
const struct ng_parse_type ng_parse_string_type
Definition: ng_parse.c:766
char *const name
Definition: ng_ppp.c:261
cmd
Definition: ng_pppoe.h:74
uint8_t data[]
Definition: ng_ubt_var.h:2
uint8_t event
Definition: ng_ubt_var.h:0
u_int32_t typecookie
Definition: ng_message.h:66
struct ng_mesg::ng_msghdr header
char data[]
Definition: ng_message.h:69
u_int32_t version
Definition: netgraph.h:1077
struct cdev * ngddev
Definition: ng_device.c:120
struct ifqueue readq
Definition: ng_device.c:117
struct mtx ngd_mtx
Definition: ng_device.c:121
uint16_t flags
Definition: ng_device.c:124
int ether_align
Definition: ng_device.c:123
struct ng_hook * hook
Definition: ng_device.c:119
struct ng_node * node
Definition: ng_device.c:118
Definition: ng_sscfu.c:66