2 * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
36 #include <net/ethernet.h>
37 #include <netinet/in_systm.h>
38 #include <netinet/ip.h>
39 #include <netgraph/ng_ether.h>
40 #include <netgraph/ng_message.h>
41 #include <netgraph/ng_socket.h>
48 #include <sys/fcntl.h>
61 #include "throughput.h"
67 #include "descriptor.h"
76 #include "slcompress.h"
92 struct device dev
; /* What struct physical knows about */
93 int cs
; /* Control socket */
94 char hook
[NG_HOOKSIZ
]; /* Our socket node hook */
97 #define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL)
98 #define NG_MSGBUFSZ 4096
99 #define NETGRAPH_PREFIX "netgraph:"
104 return sizeof(struct ngdevice
);
108 ng_MessageOut(struct ngdevice
*dev
, const char *data
)
110 char path
[NG_PATHSIZ
];
116 * We expect a node path, one or more spaces, a command, one or more
117 * spaces and an ascii netgraph structure.
119 data
+= strspn(data
, " \t");
120 len
= strcspn(data
, " \t");
121 if (len
>= sizeof path
) {
122 log_Printf(LogWARN
, "%s: %.*s: Node path too long\n",
123 dev
->dev
.name
, len
, data
);
126 memcpy(path
, data
, len
);
130 data
+= strspn(data
, " \t");
131 len
= strcspn(data
, " \t");
132 for (pos
= len
; pos
>= 0; pos
--)
133 if (data
[pos
] == '%')
135 if ((fmt
= alloca(len
+ 4)) == NULL
) {
136 log_Printf(LogWARN
, "%s: alloca(%d) failure... %s\n",
137 dev
->dev
.name
, len
+ 4, strerror(errno
));
142 * This is probably a waste of time, but we really don't want to end
143 * up stuffing unexpected % escapes into the kernel....
145 for (pos
= dpos
= 0; pos
< (int)len
;) {
146 if (data
[dpos
] == '%')
148 fmt
[pos
++] = data
[dpos
++];
150 strcpy(fmt
+ pos
, " %s");
153 data
+= strspn(data
, " \t");
154 if (NgSendAsciiMsg(dev
->cs
, path
, fmt
, data
) < 0) {
155 log_Printf(LogDEBUG
, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n",
156 dev
->dev
.name
, path
, fmt
, data
, strerror(errno
));
164 * Get a netgraph message
167 ng_MessageIn(struct physical
*p
, char *buf
, size_t sz
)
169 char msgbuf
[sizeof(struct ng_mesg
) * 2 + NG_MSGBUFSZ
];
170 struct ngdevice
*dev
= device2ng(p
->handler
);
171 struct ng_mesg
*rep
= (struct ng_mesg
*)msgbuf
;
172 char path
[NG_PATHSIZ
];
183 if ((r
= mkfdset()) == NULL
) {
184 log_Printf(LogERROR
, "DoLoop: Cannot create fd_set\n");
189 t
.tv_sec
= t
.tv_usec
= 0;
190 ret
= select(dev
->cs
+ 1, r
, NULL
, NULL
, &t
);
197 if (NgRecvAsciiMsg(dev
->cs
, rep
, sizeof msgbuf
, path
)) {
198 log_Printf(LogWARN
, "%s: NgRecvAsciiMsg: %s\n",
199 dev
->dev
.name
, strerror(errno
));
203 /* XXX: Should we check rep->header.version ? */
206 log_Printf(LogWARN
, "%s: Unexpected message: %s\n", dev
->dev
.name
,
209 log_Printf(LogDEBUG
, "%s: Received message: %s\n", dev
->dev
.name
,
211 len
= strlen(rep
->header
.cmdstr
);
214 memcpy(buf
, rep
->header
.cmdstr
, sz
);
221 ng_Write(struct physical
*p
, const void *v
, size_t n
)
223 struct ngdevice
*dev
= device2ng(p
->handler
);
225 switch (p
->dl
->state
) {
228 return ng_MessageOut(dev
, v
) ? (ssize_t
)n
: -1;
230 return NgSendData(p
->fd
, dev
->hook
, v
, n
) == -1 ? -1 : (ssize_t
)n
;
234 ng_Read(struct physical
*p
, void *v
, size_t n
)
236 char hook
[NG_HOOKSIZ
];
238 log_Printf(LogDEBUG
, "ng_Read\n");
239 switch (p
->dl
->state
) {
242 return ng_MessageIn(p
, v
, n
);
245 return NgRecvData(p
->fd
, v
, n
, hook
);
249 ng_RemoveFromSet(struct physical
*p
, fd_set
*r
, fd_set
*w
, fd_set
*e
)
251 struct ngdevice
*dev
= device2ng(p
->handler
);
254 if (r
&& dev
->cs
>= 0 && FD_ISSET(dev
->cs
, r
)) {
256 log_Printf(LogTIMER
, "%s: fdunset(ctrl) %d\n", p
->link
.name
, dev
->cs
);
261 /* Careful... physical_RemoveFromSet() called us ! */
263 p
->handler
->removefromset
= NULL
;
264 result
+= physical_RemoveFromSet(p
, r
, w
, e
);
265 p
->handler
->removefromset
= ng_RemoveFromSet
;
271 ng_Free(struct physical
*p
)
273 struct ngdevice
*dev
= device2ng(p
->handler
);
275 physical_SetDescriptor(p
);
282 ng_device2iov(struct device
*d
, struct iovec
*iov
, int *niov
,
283 int maxiov __unused
, int *auxfd
, int *nauxfd
)
285 struct ngdevice
*dev
= device2ng(d
);
286 int sz
= physical_MaxDeviceSize();
288 iov
[*niov
].iov_base
= realloc(d
, sz
);
289 if (iov
[*niov
].iov_base
== NULL
) {
290 log_Printf(LogALERT
, "Failed to allocate memory: %d\n", sz
);
291 AbortProgram(EX_OSERR
);
293 iov
[*niov
].iov_len
= sz
;
300 static const struct device basengdevice
= {
304 { CD_REQUIRED
, DEF_NGCDDELAY
},
322 ng_iov2device(int type
, struct physical
*p
, struct iovec
*iov
, int *niov
,
323 int maxiov __unused
, int *auxfd
, int *nauxfd
)
325 if (type
== NG_DEVICE
) {
326 struct ngdevice
*dev
= (struct ngdevice
*)iov
[(*niov
)++].iov_base
;
328 dev
= realloc(dev
, sizeof *dev
); /* Reduce to the correct size */
330 log_Printf(LogALERT
, "Failed to allocate memory: %d\n",
332 AbortProgram(EX_OSERR
);
341 /* Refresh function pointers etc */
342 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
344 /* XXX: Are netgraph always synchronous ? */
345 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_FORCE_SYNCNOACF
);
353 ng_UpdateSet(struct fdescriptor
*d
, fd_set
*r
, fd_set
*w
, fd_set
*e
, int *n
)
355 struct physical
*p
= descriptor2physical(d
);
356 struct ngdevice
*dev
= device2ng(p
->handler
);
359 switch (p
->dl
->state
) {
364 log_Printf(LogTIMER
, "%s(ctrl): fdset(r) %d\n", p
->link
.name
, dev
->cs
);
371 result
= physical_doUpdateSet(d
, r
, w
, e
, n
, 0);
379 ng_IsSet(struct fdescriptor
*d
, const fd_set
*fdset
)
381 struct physical
*p
= descriptor2physical(d
);
382 struct ngdevice
*dev
= device2ng(p
->handler
);
385 result
= dev
->cs
>= 0 && FD_ISSET(dev
->cs
, fdset
);
386 result
+= physical_IsSet(d
, fdset
);
392 ng_DescriptorRead(struct fdescriptor
*d
, struct bundle
*bundle
,
395 struct physical
*p
= descriptor2physical(d
);
396 struct ngdevice
*dev
= device2ng(p
->handler
);
398 if (dev
->cs
>= 0 && FD_ISSET(dev
->cs
, fdset
))
399 ng_MessageIn(p
, NULL
, 0);
401 if (physical_IsSet(d
, fdset
))
402 physical_DescriptorRead(d
, bundle
, fdset
);
405 static struct device
*
406 ng_Abandon(struct ngdevice
*dev
, struct physical
*p
)
408 /* Abandon our node construction */
411 p
->fd
= -2; /* Nobody else need try.. */
419 * Populate the ``word'' (of size ``sz'') named ``what'' from ``from''
420 * ending with any character from ``sep''. Point ``endp'' at the next
424 #define GETSEGMENT(what, from, sep, endp) \
425 getsegment(#what, (what), sizeof(what), from, sep, endp)
428 getsegment(const char *what
, char *word
, size_t sz
, const char *from
,
429 const char *sep
, const char **endp
)
433 if ((len
= strcspn(from
, sep
)) == 0) {
434 log_Printf(LogWARN
, "%s name should not be empty !\n", what
);
439 log_Printf(LogWARN
, "%s name too long, max %d !\n", what
, sz
- 1);
443 strncpy(word
, from
, len
);
447 *endp
+= strspn(*endp
, sep
);
453 ng_Create(struct physical
*p
)
455 struct sockaddr_ng ngsock
;
457 struct sockaddr
*sock
= (struct sockaddr
*)&ngsock
;
458 const struct hooklist
*hlist
;
459 const struct nodeinfo
*ninfo
;
460 const struct linkinfo
*nlink
;
461 struct ngdevice
*dev
;
462 struct ng_mesg
*resp
;
463 struct ngm_mkpeer mkp
;
464 struct ngm_connect ngc
;
465 const char *devp
, *endp
;
466 char lasthook
[NG_HOOKSIZ
];
467 char hook
[NG_HOOKSIZ
];
468 char nodetype
[NG_TYPESIZ
+ NG_NODESIZ
];
469 char modname
[NG_TYPESIZ
+ 3];
470 char path
[NG_PATHSIZ
];
476 if (p
->fd
< 0 && !strncasecmp(p
->name
.full
, NETGRAPH_PREFIX
,
477 sizeof NETGRAPH_PREFIX
- 1)) {
478 p
->fd
--; /* We own the device - change fd */
480 if ((dev
= malloc(sizeof *dev
)) == NULL
)
483 loadmodules(LOAD_VERBOSLY
, "netgraph", "ng_socket", NULL
);
485 /* Create a socket node */
486 if (ID0NgMkSockNode(NULL
, &dev
->cs
, &p
->fd
) == -1) {
487 log_Printf(LogWARN
, "Cannot create netgraph socket node: %s\n",
494 devp
= p
->name
.full
+ sizeof NETGRAPH_PREFIX
- 1;
495 *lasthook
= *path
= '\0';
496 log_Printf(LogDEBUG
, "%s: Opening netgraph device \"%s\"\n",
500 while (*devp
!= '\0' && !done
) {
502 if (*lasthook
== '\0') {
503 log_Printf(LogWARN
, "%s: Netgraph devices must start with"
504 " [nodetype:nodename]\n", p
->link
.name
);
505 return ng_Abandon(dev
, p
);
508 /* Get the hook name of the new node */
509 if (!GETSEGMENT(hook
, devp
, ".[", &endp
))
510 return ng_Abandon(dev
, p
);
511 log_Printf(LogDEBUG
, "%s: Got hook \"%s\"\n", p
->link
.name
, hook
);
514 log_Printf(LogWARN
, "%s: Netgraph device must not end with a second"
515 " hook\n", p
->link
.name
);
516 return ng_Abandon(dev
, p
);
518 if (devp
[-1] != '[') {
519 log_Printf(LogWARN
, "%s: Expected a [nodetype:nodename] at device"
520 " pos %d\n", p
->link
.name
, devp
- p
->link
.name
- 1);
521 return ng_Abandon(dev
, p
);
524 /* Use lasthook as the hook name */
525 strcpy(hook
, lasthook
);
529 /* We've got ``lasthook'' and ``hook'', get the node type */
530 if (!GETSEGMENT(nodetype
, devp
, "]", &endp
))
531 return ng_Abandon(dev
, p
);
532 log_Printf(LogDEBUG
, "%s: Got node \"%s\"\n", p
->link
.name
, nodetype
);
534 if ((nodename
= strchr(nodetype
, ':')) != NULL
) {
536 if (*nodename
== '\0' && *nodetype
== '\0') {
537 log_Printf(LogWARN
, "%s: Empty [nodetype:nodename] at device"
538 " pos %d\n", p
->link
.name
, devp
- p
->link
.name
- 1);
539 return ng_Abandon(dev
, p
);
543 /* Ignore optional colons after nodes */
544 devp
= *endp
== ':' ? endp
+ 1 : endp
;
548 if (*lasthook
== '\0') {
549 /* This is the first node in the chain */
550 if (nodename
== NULL
|| *nodename
== '\0') {
551 log_Printf(LogWARN
, "%s: %s: No initial device nodename\n",
553 return ng_Abandon(dev
, p
);
556 if (*nodetype
!= '\0') {
557 /* Attempt to load the module */
558 snprintf(modname
, sizeof modname
, "ng_%s", nodetype
);
559 log_Printf(LogDEBUG
, "%s: Attempting to load %s.ko\n",
560 p
->link
.name
, modname
);
561 loadmodules(LOAD_QUIETLY
, modname
, NULL
);
564 snprintf(path
, sizeof path
, "%s:", nodename
);
565 /* XXX: If we have a node type, ensure it's correct */
568 * Ask for a list of hooks attached to the previous node. If we
569 * find the one we're interested in, and if it's connected to a
570 * node of the right type using the correct hook, use that.
571 * If we find the hook connected to something else, fail.
572 * If we find no match, mkpeer the new node.
574 if (*nodetype
== '\0') {
575 log_Printf(LogWARN
, "%s: Nodetype missing at device offset %d\n",
577 devp
- p
->name
.full
+ sizeof NETGRAPH_PREFIX
- 1);
578 return ng_Abandon(dev
, p
);
581 /* Get a list of node hooks */
582 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
, NGM_LISTHOOKS
,
584 log_Printf(LogWARN
, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
585 p
->link
.name
, path
, strerror(errno
));
586 return ng_Abandon(dev
, p
);
589 /* Get our list back */
590 resp
= (struct ng_mesg
*)rbuf
;
591 if (NgRecvMsg(dev
->cs
, resp
, sizeof rbuf
, NULL
) <= 0) {
592 log_Printf(LogWARN
, "%s: Cannot get netgraph response: %s\n",
593 p
->link
.name
, strerror(errno
));
594 return ng_Abandon(dev
, p
);
597 hlist
= (const struct hooklist
*)resp
->data
;
598 ninfo
= &hlist
->nodeinfo
;
600 log_Printf(LogDEBUG
, "List of netgraph node ``%s'' (id %x) hooks:\n",
603 /* look for a hook already attached. */
604 for (f
= 0; f
< ninfo
->hooks
; f
++) {
605 nlink
= &hlist
->link
[f
];
607 log_Printf(LogDEBUG
, " Found %s -> %s (type %s)\n", nlink
->ourhook
,
608 nlink
->peerhook
, nlink
->nodeinfo
.type
);
610 if (!strcmp(nlink
->ourhook
, lasthook
)) {
611 if (strcmp(nlink
->peerhook
, hook
) ||
612 strcmp(nlink
->nodeinfo
.type
, nodetype
)) {
613 log_Printf(LogWARN
, "%s: hook %s:%s is already in use\n",
614 p
->link
.name
, nlink
->ourhook
, path
);
615 return ng_Abandon(dev
, p
);
617 /* The node is already hooked up nicely.... reuse it */
622 if (f
== ninfo
->hooks
) {
623 /* Attempt to load the module */
624 snprintf(modname
, sizeof modname
, "ng_%s", nodetype
);
625 log_Printf(LogDEBUG
, "%s: Attempting to load %s.ko\n",
626 p
->link
.name
, modname
);
627 loadmodules(LOAD_QUIETLY
, modname
, NULL
);
629 /* Create (mkpeer) the new node */
631 snprintf(mkp
.type
, sizeof mkp
.type
, "%s", nodetype
);
632 snprintf(mkp
.ourhook
, sizeof mkp
.ourhook
, "%s", lasthook
);
633 snprintf(mkp
.peerhook
, sizeof mkp
.peerhook
, "%s", hook
);
635 log_Printf(LogDEBUG
, "%s: Doing MKPEER %s%s -> %s (type %s)\n",
636 p
->link
.name
, path
, mkp
.ourhook
, mkp
.peerhook
, nodetype
);
638 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
,
639 NGM_MKPEER
, &mkp
, sizeof mkp
) < 0) {
640 log_Printf(LogWARN
, "%s Cannot create %s netgraph node: %s\n",
641 path
, nodetype
, strerror(errno
));
642 return ng_Abandon(dev
, p
);
646 snprintf(path
+ len
, sizeof path
- len
, "%s%s",
647 path
[len
- 1] == ':' ? "" : ".", lasthook
);
650 /* Get a list of node hooks */
651 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
, NGM_LISTHOOKS
,
653 log_Printf(LogWARN
, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
654 p
->link
.name
, path
, strerror(errno
));
655 return ng_Abandon(dev
, p
);
658 /* Get our list back */
659 resp
= (struct ng_mesg
*)rbuf
;
660 if (NgRecvMsg(dev
->cs
, resp
, sizeof rbuf
, NULL
) <= 0) {
661 log_Printf(LogWARN
, "%s: Cannot get netgraph response: %s\n",
662 p
->link
.name
, strerror(errno
));
663 return ng_Abandon(dev
, p
);
666 hlist
= (const struct hooklist
*)resp
->data
;
667 ninfo
= &hlist
->nodeinfo
;
669 if (*lasthook
!= '\0' && nodename
!= NULL
&& *nodename
!= '\0' &&
670 strcmp(ninfo
->name
, nodename
) &&
671 NgNameNode(dev
->cs
, path
, "%s", nodename
) < 0) {
672 log_Printf(LogWARN
, "%s: %s: Cannot name netgraph node: %s\n",
673 p
->link
.name
, path
, strerror(errno
));
674 return ng_Abandon(dev
, p
);
677 if (!GETSEGMENT(lasthook
, devp
, " \t.[", &endp
))
678 return ng_Abandon(dev
, p
);
679 log_Printf(LogDEBUG
, "%s: Got hook \"%s\"\n", p
->link
.name
, lasthook
);
681 len
= strlen(lasthook
);
682 done
= strchr(" \t", devp
[len
]) ? 1 : 0;
688 } /* else should moan about devp[-1] being '[' ? */
691 snprintf(dev
->hook
, sizeof dev
->hook
, "%s", lasthook
);
693 /* Connect the node to our socket node */
694 snprintf(ngc
.path
, sizeof ngc
.path
, "%s", path
);
695 snprintf(ngc
.ourhook
, sizeof ngc
.ourhook
, "%s", dev
->hook
);
696 memcpy(ngc
.peerhook
, ngc
.ourhook
, sizeof ngc
.peerhook
);
698 log_Printf(LogDEBUG
, "Connecting netgraph socket .:%s -> %s.%s\n",
699 ngc
.ourhook
, ngc
.path
, ngc
.peerhook
);
700 if (NgSendMsg(dev
->cs
, ".:", NGM_GENERIC_COOKIE
,
701 NGM_CONNECT
, &ngc
, sizeof ngc
) < 0) {
702 log_Printf(LogWARN
, "Cannot connect %s and socket netgraph "
703 "nodes: %s\n", path
, strerror(errno
));
704 return ng_Abandon(dev
, p
);
707 /* Hook things up so that we monitor dev->cs */
708 p
->desc
.UpdateSet
= ng_UpdateSet
;
709 p
->desc
.IsSet
= ng_IsSet
;
710 p
->desc
.Read
= ng_DescriptorRead
;
712 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
715 /* See if we're a netgraph socket */
718 if (getsockname(p
->fd
, sock
, &sz
) != -1 && sock
->sa_family
== AF_NETGRAPH
) {
720 * It's a netgraph node... We can't determine hook names etc, so we
721 * stay pretty impartial....
723 log_Printf(LogPHASE
, "%s: Link is a netgraph node\n", p
->link
.name
);
725 if ((dev
= malloc(sizeof *dev
)) == NULL
) {
726 log_Printf(LogWARN
, "%s: Cannot allocate an ether device: %s\n",
727 p
->link
.name
, strerror(errno
));
731 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
738 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_FORCE_SYNCNOACF
);