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
26 * $FreeBSD: src/usr.sbin/ppp/netgraph.c,v 1.4.2.1 2002/09/01 02:12:29 brian Exp $
27 * $DragonFly: src/usr.sbin/ppp/netgraph.c,v 1.2 2003/06/17 06:30:00 dillon Exp $
30 #include <sys/param.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
37 #include <net/ethernet.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip.h>
40 #include <netgraph/ng_ether.h>
41 #include <netgraph/ng_message.h>
42 #include <netgraph/ng_socket.h>
49 #include <sys/fcntl.h>
62 #include "throughput.h"
68 #include "descriptor.h"
77 #include "slcompress.h"
93 struct device dev
; /* What struct physical knows about */
94 int cs
; /* Control socket */
95 char hook
[NG_HOOKLEN
+ 1]; /* Our socket node hook */
98 #define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL)
99 #define NG_MSGBUFSZ 4096
100 #define NETGRAPH_PREFIX "netgraph:"
105 return sizeof(struct ngdevice
);
109 ng_MessageOut(struct ngdevice
*dev
, struct physical
*p
, const char *data
)
111 char path
[NG_PATHLEN
+ 1];
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
< 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_PATHLEN
+ 1];
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
, p
, v
) ? n
: -1;
230 return NgSendData(p
->fd
, dev
->hook
, v
, n
) == -1 ? -1 : n
;
234 ng_Read(struct physical
*p
, void *v
, size_t n
)
236 char hook
[NG_HOOKLEN
+ 1];
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
, 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
, 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_HOOKLEN
+ 1];
467 char hook
[NG_HOOKLEN
+ 1];
468 char nodetype
[NG_TYPELEN
+ NG_NODELEN
+ 2];
469 char modname
[NG_TYPELEN
+ 4];
470 char path
[NG_PATHLEN
+ 1];
472 int len
, sz
, done
, f
;
475 if (p
->fd
< 0 && !strncasecmp(p
->name
.full
, NETGRAPH_PREFIX
,
476 sizeof NETGRAPH_PREFIX
- 1)) {
477 p
->fd
--; /* We own the device - change fd */
479 if ((dev
= malloc(sizeof *dev
)) == NULL
)
482 loadmodules(LOAD_VERBOSLY
, "netgraph", "ng_socket", NULL
);
484 /* Create a socket node */
485 if (ID0NgMkSockNode(NULL
, &dev
->cs
, &p
->fd
) == -1) {
486 log_Printf(LogWARN
, "Cannot create netgraph socket node: %s\n",
493 devp
= p
->name
.full
+ sizeof NETGRAPH_PREFIX
- 1;
494 *lasthook
= *path
= '\0';
495 log_Printf(LogDEBUG
, "%s: Opening netgraph device \"%s\"\n",
499 while (*devp
!= '\0' && !done
) {
501 if (*lasthook
== '\0') {
502 log_Printf(LogWARN
, "%s: Netgraph devices must start with"
503 " [nodetype:nodename]\n", p
->link
.name
);
504 return ng_Abandon(dev
, p
);
507 /* Get the hook name of the new node */
508 if (!GETSEGMENT(hook
, devp
, ".[", &endp
))
509 return ng_Abandon(dev
, p
);
510 log_Printf(LogDEBUG
, "%s: Got hook \"%s\"\n", p
->link
.name
, hook
);
513 log_Printf(LogWARN
, "%s: Netgraph device must not end with a second"
514 " hook\n", p
->link
.name
);
515 return ng_Abandon(dev
, p
);
517 if (devp
[-1] != '[') {
518 log_Printf(LogWARN
, "%s: Expected a [nodetype:nodename] at device"
519 " pos %d\n", p
->link
.name
, devp
- p
->link
.name
- 1);
520 return ng_Abandon(dev
, p
);
523 /* Use lasthook as the hook name */
524 strcpy(hook
, lasthook
);
528 /* We've got ``lasthook'' and ``hook'', get the node type */
529 if (!GETSEGMENT(nodetype
, devp
, "]", &endp
))
530 return ng_Abandon(dev
, p
);
531 log_Printf(LogDEBUG
, "%s: Got node \"%s\"\n", p
->link
.name
, nodetype
);
533 if ((nodename
= strchr(nodetype
, ':')) != NULL
) {
535 if (*nodename
== '\0' && *nodetype
== '\0') {
536 log_Printf(LogWARN
, "%s: Empty [nodetype:nodename] at device"
537 " pos %d\n", p
->link
.name
, devp
- p
->link
.name
- 1);
538 return ng_Abandon(dev
, p
);
542 /* Ignore optional colons after nodes */
543 devp
= *endp
== ':' ? endp
+ 1 : endp
;
547 if (*lasthook
== '\0') {
548 /* This is the first node in the chain */
549 if (nodename
== NULL
|| *nodename
== '\0') {
550 log_Printf(LogWARN
, "%s: %s: No initial device nodename\n",
552 return ng_Abandon(dev
, p
);
555 if (*nodetype
!= '\0') {
556 /* Attempt to load the module */
557 snprintf(modname
, sizeof modname
, "ng_%s", nodetype
);
558 log_Printf(LogDEBUG
, "%s: Attempting to load %s.ko\n",
559 p
->link
.name
, modname
);
560 loadmodules(LOAD_QUIETLY
, modname
, NULL
);
563 snprintf(path
, sizeof path
, "%s:", nodename
);
564 /* XXX: If we have a node type, ensure it's correct */
567 * Ask for a list of hooks attached to the previous node. If we
568 * find the one we're interested in, and if it's connected to a
569 * node of the right type using the correct hook, use that.
570 * If we find the hook connected to something else, fail.
571 * If we find no match, mkpeer the new node.
573 if (*nodetype
== '\0') {
574 log_Printf(LogWARN
, "%s: Nodetype missing at device offset %d\n",
576 devp
- p
->name
.full
+ sizeof NETGRAPH_PREFIX
- 1);
577 return ng_Abandon(dev
, p
);
580 /* Get a list of node hooks */
581 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
, NGM_LISTHOOKS
,
583 log_Printf(LogWARN
, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
584 p
->link
.name
, path
, strerror(errno
));
585 return ng_Abandon(dev
, p
);
588 /* Get our list back */
589 resp
= (struct ng_mesg
*)rbuf
;
590 if (NgRecvMsg(dev
->cs
, resp
, sizeof rbuf
, NULL
) <= 0) {
591 log_Printf(LogWARN
, "%s: Cannot get netgraph response: %s\n",
592 p
->link
.name
, strerror(errno
));
593 return ng_Abandon(dev
, p
);
596 hlist
= (const struct hooklist
*)resp
->data
;
597 ninfo
= &hlist
->nodeinfo
;
599 log_Printf(LogDEBUG
, "List of netgraph node ``%s'' (id %x) hooks:\n",
602 /* look for a hook already attached. */
603 for (f
= 0; f
< ninfo
->hooks
; f
++) {
604 nlink
= &hlist
->link
[f
];
606 log_Printf(LogDEBUG
, " Found %s -> %s (type %s)\n", nlink
->ourhook
,
607 nlink
->peerhook
, nlink
->nodeinfo
.type
);
609 if (!strcmp(nlink
->ourhook
, lasthook
)) {
610 if (strcmp(nlink
->peerhook
, hook
) ||
611 strcmp(nlink
->nodeinfo
.type
, nodetype
)) {
612 log_Printf(LogWARN
, "%s: hook %s:%s is already in use\n",
613 p
->link
.name
, nlink
->ourhook
, path
);
614 return ng_Abandon(dev
, p
);
616 /* The node is already hooked up nicely.... reuse it */
621 if (f
== ninfo
->hooks
) {
622 /* Attempt to load the module */
623 snprintf(modname
, sizeof modname
, "ng_%s", nodetype
);
624 log_Printf(LogDEBUG
, "%s: Attempting to load %s.ko\n",
625 p
->link
.name
, modname
);
626 loadmodules(LOAD_QUIETLY
, modname
, NULL
);
628 /* Create (mkpeer) the new node */
630 snprintf(mkp
.type
, sizeof mkp
.type
, "%s", nodetype
);
631 snprintf(mkp
.ourhook
, sizeof mkp
.ourhook
, "%s", lasthook
);
632 snprintf(mkp
.peerhook
, sizeof mkp
.peerhook
, "%s", hook
);
634 log_Printf(LogDEBUG
, "%s: Doing MKPEER %s%s -> %s (type %s)\n",
635 p
->link
.name
, path
, mkp
.ourhook
, mkp
.peerhook
, nodetype
);
637 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
,
638 NGM_MKPEER
, &mkp
, sizeof mkp
) < 0) {
639 log_Printf(LogWARN
, "%s Cannot create %s netgraph node: %s\n",
640 path
, nodetype
, strerror(errno
));
641 return ng_Abandon(dev
, p
);
645 snprintf(path
+ len
, sizeof path
- len
, "%s%s",
646 path
[len
- 1] == ':' ? "" : ".", lasthook
);
649 /* Get a list of node hooks */
650 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
, NGM_LISTHOOKS
,
652 log_Printf(LogWARN
, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
653 p
->link
.name
, path
, strerror(errno
));
654 return ng_Abandon(dev
, p
);
657 /* Get our list back */
658 resp
= (struct ng_mesg
*)rbuf
;
659 if (NgRecvMsg(dev
->cs
, resp
, sizeof rbuf
, NULL
) <= 0) {
660 log_Printf(LogWARN
, "%s: Cannot get netgraph response: %s\n",
661 p
->link
.name
, strerror(errno
));
662 return ng_Abandon(dev
, p
);
665 hlist
= (const struct hooklist
*)resp
->data
;
666 ninfo
= &hlist
->nodeinfo
;
668 if (*lasthook
!= '\0' && nodename
!= NULL
&& *nodename
!= '\0' &&
669 strcmp(ninfo
->name
, nodename
) &&
670 NgNameNode(dev
->cs
, path
, "%s", nodename
) < 0) {
671 log_Printf(LogWARN
, "%s: %s: Cannot name netgraph node: %s\n",
672 p
->link
.name
, path
, strerror(errno
));
673 return ng_Abandon(dev
, p
);
676 if (!GETSEGMENT(lasthook
, devp
, " \t.[", &endp
))
677 return ng_Abandon(dev
, p
);
678 log_Printf(LogDEBUG
, "%s: Got hook \"%s\"\n", p
->link
.name
, lasthook
);
680 len
= strlen(lasthook
);
681 done
= strchr(" \t", devp
[len
]) ? 1 : 0;
687 } /* else should moan about devp[-1] being '[' ? */
690 snprintf(dev
->hook
, sizeof dev
->hook
, "%s", lasthook
);
692 /* Connect the node to our socket node */
693 snprintf(ngc
.path
, sizeof ngc
.path
, "%s", path
);
694 snprintf(ngc
.ourhook
, sizeof ngc
.ourhook
, "%s", dev
->hook
);
695 memcpy(ngc
.peerhook
, ngc
.ourhook
, sizeof ngc
.peerhook
);
697 log_Printf(LogDEBUG
, "Connecting netgraph socket .:%s -> %s.%s\n",
698 ngc
.ourhook
, ngc
.path
, ngc
.peerhook
);
699 if (NgSendMsg(dev
->cs
, ".:", NGM_GENERIC_COOKIE
,
700 NGM_CONNECT
, &ngc
, sizeof ngc
) < 0) {
701 log_Printf(LogWARN
, "Cannot connect %s and socket netgraph "
702 "nodes: %s\n", path
, strerror(errno
));
703 return ng_Abandon(dev
, p
);
706 /* Hook things up so that we monitor dev->cs */
707 p
->desc
.UpdateSet
= ng_UpdateSet
;
708 p
->desc
.IsSet
= ng_IsSet
;
709 p
->desc
.Read
= ng_DescriptorRead
;
711 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
714 /* See if we're a netgraph socket */
717 if (getsockname(p
->fd
, sock
, &sz
) != -1 && sock
->sa_family
== AF_NETGRAPH
) {
719 * It's a netgraph node... We can't determine hook names etc, so we
720 * stay pretty impartial....
722 log_Printf(LogPHASE
, "%s: Link is a netgraph node\n", p
->link
.name
);
724 if ((dev
= malloc(sizeof *dev
)) == NULL
) {
725 log_Printf(LogWARN
, "%s: Cannot allocate an ether device: %s\n",
726 p
->link
.name
, strerror(errno
));
730 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
737 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_FORCE_SYNCNOACF
);