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.3 2007/06/04 00:40:32 swildner 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>
41 #include <netgraph7/ng_ether.h>
42 #include <netgraph7/ng_message.h>
43 #include <netgraph7/ng_socket.h>
45 #include <netgraph/ng_ether.h>
46 #include <netgraph/ng_message.h>
47 #include <netgraph/ng_socket.h>
55 #include <sys/fcntl.h>
68 #include "throughput.h"
74 #include "descriptor.h"
83 #include "slcompress.h"
99 struct device dev
; /* What struct physical knows about */
100 int cs
; /* Control socket */
101 char hook
[NG_HOOKSIZ
]; /* Our socket node hook */
104 #define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL)
105 #define NG_MSGBUFSZ 4096
106 #define NETGRAPH_PREFIX "netgraph:"
111 return sizeof(struct ngdevice
);
115 ng_MessageOut(struct ngdevice
*dev
, const char *data
)
117 char path
[NG_PATHSIZ
];
123 * We expect a node path, one or more spaces, a command, one or more
124 * spaces and an ascii netgraph structure.
126 data
+= strspn(data
, " \t");
127 len
= strcspn(data
, " \t");
128 if (len
>= sizeof path
) {
129 log_Printf(LogWARN
, "%s: %.*s: Node path too long\n",
130 dev
->dev
.name
, len
, data
);
133 memcpy(path
, data
, len
);
137 data
+= strspn(data
, " \t");
138 len
= strcspn(data
, " \t");
139 for (pos
= len
; pos
>= 0; pos
--)
140 if (data
[pos
] == '%')
142 if ((fmt
= alloca(len
+ 4)) == NULL
) {
143 log_Printf(LogWARN
, "%s: alloca(%d) failure... %s\n",
144 dev
->dev
.name
, len
+ 4, strerror(errno
));
149 * This is probably a waste of time, but we really don't want to end
150 * up stuffing unexpected % escapes into the kernel....
152 for (pos
= dpos
= 0; pos
< (int)len
;) {
153 if (data
[dpos
] == '%')
155 fmt
[pos
++] = data
[dpos
++];
157 strcpy(fmt
+ pos
, " %s");
160 data
+= strspn(data
, " \t");
161 if (NgSendAsciiMsg(dev
->cs
, path
, fmt
, data
) < 0) {
162 log_Printf(LogDEBUG
, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n",
163 dev
->dev
.name
, path
, fmt
, data
, strerror(errno
));
171 * Get a netgraph message
174 ng_MessageIn(struct physical
*p
, char *buf
, size_t sz
)
176 char msgbuf
[sizeof(struct ng_mesg
) * 2 + NG_MSGBUFSZ
];
177 struct ngdevice
*dev
= device2ng(p
->handler
);
178 struct ng_mesg
*rep
= (struct ng_mesg
*)msgbuf
;
179 char path
[NG_PATHSIZ
];
190 if ((r
= mkfdset()) == NULL
) {
191 log_Printf(LogERROR
, "DoLoop: Cannot create fd_set\n");
196 t
.tv_sec
= t
.tv_usec
= 0;
197 ret
= select(dev
->cs
+ 1, r
, NULL
, NULL
, &t
);
204 if (NgRecvAsciiMsg(dev
->cs
, rep
, sizeof msgbuf
, path
)) {
205 log_Printf(LogWARN
, "%s: NgRecvAsciiMsg: %s\n",
206 dev
->dev
.name
, strerror(errno
));
210 /* XXX: Should we check rep->header.version ? */
213 log_Printf(LogWARN
, "%s: Unexpected message: %s\n", dev
->dev
.name
,
216 log_Printf(LogDEBUG
, "%s: Received message: %s\n", dev
->dev
.name
,
218 len
= strlen(rep
->header
.cmdstr
);
221 memcpy(buf
, rep
->header
.cmdstr
, sz
);
228 ng_Write(struct physical
*p
, const void *v
, size_t n
)
230 struct ngdevice
*dev
= device2ng(p
->handler
);
232 switch (p
->dl
->state
) {
235 return ng_MessageOut(dev
, v
) ? (ssize_t
)n
: -1;
237 return NgSendData(p
->fd
, dev
->hook
, v
, n
) == -1 ? -1 : (ssize_t
)n
;
241 ng_Read(struct physical
*p
, void *v
, size_t n
)
243 char hook
[NG_HOOKSIZ
];
245 log_Printf(LogDEBUG
, "ng_Read\n");
246 switch (p
->dl
->state
) {
249 return ng_MessageIn(p
, v
, n
);
252 return NgRecvData(p
->fd
, v
, n
, hook
);
256 ng_RemoveFromSet(struct physical
*p
, fd_set
*r
, fd_set
*w
, fd_set
*e
)
258 struct ngdevice
*dev
= device2ng(p
->handler
);
261 if (r
&& dev
->cs
>= 0 && FD_ISSET(dev
->cs
, r
)) {
263 log_Printf(LogTIMER
, "%s: fdunset(ctrl) %d\n", p
->link
.name
, dev
->cs
);
268 /* Careful... physical_RemoveFromSet() called us ! */
270 p
->handler
->removefromset
= NULL
;
271 result
+= physical_RemoveFromSet(p
, r
, w
, e
);
272 p
->handler
->removefromset
= ng_RemoveFromSet
;
278 ng_Free(struct physical
*p
)
280 struct ngdevice
*dev
= device2ng(p
->handler
);
282 physical_SetDescriptor(p
);
289 ng_device2iov(struct device
*d
, struct iovec
*iov
, int *niov
,
290 int maxiov __unused
, int *auxfd
, int *nauxfd
)
292 struct ngdevice
*dev
= device2ng(d
);
293 int sz
= physical_MaxDeviceSize();
295 iov
[*niov
].iov_base
= realloc(d
, sz
);
296 if (iov
[*niov
].iov_base
== NULL
) {
297 log_Printf(LogALERT
, "Failed to allocate memory: %d\n", sz
);
298 AbortProgram(EX_OSERR
);
300 iov
[*niov
].iov_len
= sz
;
307 static const struct device basengdevice
= {
311 { CD_REQUIRED
, DEF_NGCDDELAY
},
329 ng_iov2device(int type
, struct physical
*p
, struct iovec
*iov
, int *niov
,
330 int maxiov __unused
, int *auxfd
, int *nauxfd
)
332 if (type
== NG_DEVICE
) {
333 struct ngdevice
*dev
= (struct ngdevice
*)iov
[(*niov
)++].iov_base
;
335 dev
= realloc(dev
, sizeof *dev
); /* Reduce to the correct size */
337 log_Printf(LogALERT
, "Failed to allocate memory: %d\n",
339 AbortProgram(EX_OSERR
);
348 /* Refresh function pointers etc */
349 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
351 /* XXX: Are netgraph always synchronous ? */
352 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_FORCE_SYNCNOACF
);
360 ng_UpdateSet(struct fdescriptor
*d
, fd_set
*r
, fd_set
*w
, fd_set
*e
, int *n
)
362 struct physical
*p
= descriptor2physical(d
);
363 struct ngdevice
*dev
= device2ng(p
->handler
);
366 switch (p
->dl
->state
) {
371 log_Printf(LogTIMER
, "%s(ctrl): fdset(r) %d\n", p
->link
.name
, dev
->cs
);
378 result
= physical_doUpdateSet(d
, r
, w
, e
, n
, 0);
386 ng_IsSet(struct fdescriptor
*d
, const fd_set
*fdset
)
388 struct physical
*p
= descriptor2physical(d
);
389 struct ngdevice
*dev
= device2ng(p
->handler
);
392 result
= dev
->cs
>= 0 && FD_ISSET(dev
->cs
, fdset
);
393 result
+= physical_IsSet(d
, fdset
);
399 ng_DescriptorRead(struct fdescriptor
*d
, struct bundle
*bundle
,
402 struct physical
*p
= descriptor2physical(d
);
403 struct ngdevice
*dev
= device2ng(p
->handler
);
405 if (dev
->cs
>= 0 && FD_ISSET(dev
->cs
, fdset
))
406 ng_MessageIn(p
, NULL
, 0);
408 if (physical_IsSet(d
, fdset
))
409 physical_DescriptorRead(d
, bundle
, fdset
);
412 static struct device
*
413 ng_Abandon(struct ngdevice
*dev
, struct physical
*p
)
415 /* Abandon our node construction */
418 p
->fd
= -2; /* Nobody else need try.. */
426 * Populate the ``word'' (of size ``sz'') named ``what'' from ``from''
427 * ending with any character from ``sep''. Point ``endp'' at the next
431 #define GETSEGMENT(what, from, sep, endp) \
432 getsegment(#what, (what), sizeof(what), from, sep, endp)
435 getsegment(const char *what
, char *word
, size_t sz
, const char *from
,
436 const char *sep
, const char **endp
)
440 if ((len
= strcspn(from
, sep
)) == 0) {
441 log_Printf(LogWARN
, "%s name should not be empty !\n", what
);
446 log_Printf(LogWARN
, "%s name too long, max %d !\n", what
, sz
- 1);
450 strncpy(word
, from
, len
);
454 *endp
+= strspn(*endp
, sep
);
460 ng_Create(struct physical
*p
)
462 struct sockaddr_ng ngsock
;
464 struct sockaddr
*sock
= (struct sockaddr
*)&ngsock
;
465 const struct hooklist
*hlist
;
466 const struct nodeinfo
*ninfo
;
467 const struct linkinfo
*nlink
;
468 struct ngdevice
*dev
;
469 struct ng_mesg
*resp
;
470 struct ngm_mkpeer mkp
;
471 struct ngm_connect ngc
;
472 const char *devp
, *endp
;
473 char lasthook
[NG_HOOKSIZ
];
474 char hook
[NG_HOOKSIZ
];
475 char nodetype
[NG_TYPESIZ
+ NG_NODESIZ
];
476 char modname
[NG_TYPESIZ
+ 3];
477 char path
[NG_PATHSIZ
];
483 if (p
->fd
< 0 && !strncasecmp(p
->name
.full
, NETGRAPH_PREFIX
,
484 sizeof NETGRAPH_PREFIX
- 1)) {
485 p
->fd
--; /* We own the device - change fd */
487 if ((dev
= malloc(sizeof *dev
)) == NULL
)
490 loadmodules(LOAD_VERBOSLY
, "netgraph", "ng_socket", NULL
);
492 /* Create a socket node */
493 if (ID0NgMkSockNode(NULL
, &dev
->cs
, &p
->fd
) == -1) {
494 log_Printf(LogWARN
, "Cannot create netgraph socket node: %s\n",
501 devp
= p
->name
.full
+ sizeof NETGRAPH_PREFIX
- 1;
502 *lasthook
= *path
= '\0';
503 log_Printf(LogDEBUG
, "%s: Opening netgraph device \"%s\"\n",
507 while (*devp
!= '\0' && !done
) {
509 if (*lasthook
== '\0') {
510 log_Printf(LogWARN
, "%s: Netgraph devices must start with"
511 " [nodetype:nodename]\n", p
->link
.name
);
512 return ng_Abandon(dev
, p
);
515 /* Get the hook name of the new node */
516 if (!GETSEGMENT(hook
, devp
, ".[", &endp
))
517 return ng_Abandon(dev
, p
);
518 log_Printf(LogDEBUG
, "%s: Got hook \"%s\"\n", p
->link
.name
, hook
);
521 log_Printf(LogWARN
, "%s: Netgraph device must not end with a second"
522 " hook\n", p
->link
.name
);
523 return ng_Abandon(dev
, p
);
525 if (devp
[-1] != '[') {
526 log_Printf(LogWARN
, "%s: Expected a [nodetype:nodename] at device"
527 " pos %d\n", p
->link
.name
, devp
- p
->link
.name
- 1);
528 return ng_Abandon(dev
, p
);
531 /* Use lasthook as the hook name */
532 strcpy(hook
, lasthook
);
536 /* We've got ``lasthook'' and ``hook'', get the node type */
537 if (!GETSEGMENT(nodetype
, devp
, "]", &endp
))
538 return ng_Abandon(dev
, p
);
539 log_Printf(LogDEBUG
, "%s: Got node \"%s\"\n", p
->link
.name
, nodetype
);
541 if ((nodename
= strchr(nodetype
, ':')) != NULL
) {
543 if (*nodename
== '\0' && *nodetype
== '\0') {
544 log_Printf(LogWARN
, "%s: Empty [nodetype:nodename] at device"
545 " pos %d\n", p
->link
.name
, devp
- p
->link
.name
- 1);
546 return ng_Abandon(dev
, p
);
550 /* Ignore optional colons after nodes */
551 devp
= *endp
== ':' ? endp
+ 1 : endp
;
555 if (*lasthook
== '\0') {
556 /* This is the first node in the chain */
557 if (nodename
== NULL
|| *nodename
== '\0') {
558 log_Printf(LogWARN
, "%s: %s: No initial device nodename\n",
560 return ng_Abandon(dev
, p
);
563 if (*nodetype
!= '\0') {
564 /* Attempt to load the module */
565 snprintf(modname
, sizeof modname
, "ng_%s", nodetype
);
566 log_Printf(LogDEBUG
, "%s: Attempting to load %s.ko\n",
567 p
->link
.name
, modname
);
568 loadmodules(LOAD_QUIETLY
, modname
, NULL
);
571 snprintf(path
, sizeof path
, "%s:", nodename
);
572 /* XXX: If we have a node type, ensure it's correct */
575 * Ask for a list of hooks attached to the previous node. If we
576 * find the one we're interested in, and if it's connected to a
577 * node of the right type using the correct hook, use that.
578 * If we find the hook connected to something else, fail.
579 * If we find no match, mkpeer the new node.
581 if (*nodetype
== '\0') {
582 log_Printf(LogWARN
, "%s: Nodetype missing at device offset %d\n",
584 devp
- p
->name
.full
+ sizeof NETGRAPH_PREFIX
- 1);
585 return ng_Abandon(dev
, p
);
588 /* Get a list of node hooks */
589 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
, NGM_LISTHOOKS
,
591 log_Printf(LogWARN
, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
592 p
->link
.name
, path
, strerror(errno
));
593 return ng_Abandon(dev
, p
);
596 /* Get our list back */
597 resp
= (struct ng_mesg
*)rbuf
;
598 if (NgRecvMsg(dev
->cs
, resp
, sizeof rbuf
, NULL
) <= 0) {
599 log_Printf(LogWARN
, "%s: Cannot get netgraph response: %s\n",
600 p
->link
.name
, strerror(errno
));
601 return ng_Abandon(dev
, p
);
604 hlist
= (const struct hooklist
*)resp
->data
;
605 ninfo
= &hlist
->nodeinfo
;
607 log_Printf(LogDEBUG
, "List of netgraph node ``%s'' (id %x) hooks:\n",
610 /* look for a hook already attached. */
611 for (f
= 0; f
< ninfo
->hooks
; f
++) {
612 nlink
= &hlist
->link
[f
];
614 log_Printf(LogDEBUG
, " Found %s -> %s (type %s)\n", nlink
->ourhook
,
615 nlink
->peerhook
, nlink
->nodeinfo
.type
);
617 if (!strcmp(nlink
->ourhook
, lasthook
)) {
618 if (strcmp(nlink
->peerhook
, hook
) ||
619 strcmp(nlink
->nodeinfo
.type
, nodetype
)) {
620 log_Printf(LogWARN
, "%s: hook %s:%s is already in use\n",
621 p
->link
.name
, nlink
->ourhook
, path
);
622 return ng_Abandon(dev
, p
);
624 /* The node is already hooked up nicely.... reuse it */
629 if (f
== ninfo
->hooks
) {
630 /* Attempt to load the module */
631 snprintf(modname
, sizeof modname
, "ng_%s", nodetype
);
632 log_Printf(LogDEBUG
, "%s: Attempting to load %s.ko\n",
633 p
->link
.name
, modname
);
634 loadmodules(LOAD_QUIETLY
, modname
, NULL
);
636 /* Create (mkpeer) the new node */
638 snprintf(mkp
.type
, sizeof mkp
.type
, "%s", nodetype
);
639 snprintf(mkp
.ourhook
, sizeof mkp
.ourhook
, "%s", lasthook
);
640 snprintf(mkp
.peerhook
, sizeof mkp
.peerhook
, "%s", hook
);
642 log_Printf(LogDEBUG
, "%s: Doing MKPEER %s%s -> %s (type %s)\n",
643 p
->link
.name
, path
, mkp
.ourhook
, mkp
.peerhook
, nodetype
);
645 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
,
646 NGM_MKPEER
, &mkp
, sizeof mkp
) < 0) {
647 log_Printf(LogWARN
, "%s Cannot create %s netgraph node: %s\n",
648 path
, nodetype
, strerror(errno
));
649 return ng_Abandon(dev
, p
);
653 snprintf(path
+ len
, sizeof path
- len
, "%s%s",
654 path
[len
- 1] == ':' ? "" : ".", lasthook
);
657 /* Get a list of node hooks */
658 if (NgSendMsg(dev
->cs
, path
, NGM_GENERIC_COOKIE
, NGM_LISTHOOKS
,
660 log_Printf(LogWARN
, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
661 p
->link
.name
, path
, strerror(errno
));
662 return ng_Abandon(dev
, p
);
665 /* Get our list back */
666 resp
= (struct ng_mesg
*)rbuf
;
667 if (NgRecvMsg(dev
->cs
, resp
, sizeof rbuf
, NULL
) <= 0) {
668 log_Printf(LogWARN
, "%s: Cannot get netgraph response: %s\n",
669 p
->link
.name
, strerror(errno
));
670 return ng_Abandon(dev
, p
);
673 hlist
= (const struct hooklist
*)resp
->data
;
674 ninfo
= &hlist
->nodeinfo
;
676 if (*lasthook
!= '\0' && nodename
!= NULL
&& *nodename
!= '\0' &&
677 strcmp(ninfo
->name
, nodename
) &&
678 NgNameNode(dev
->cs
, path
, "%s", nodename
) < 0) {
679 log_Printf(LogWARN
, "%s: %s: Cannot name netgraph node: %s\n",
680 p
->link
.name
, path
, strerror(errno
));
681 return ng_Abandon(dev
, p
);
684 if (!GETSEGMENT(lasthook
, devp
, " \t.[", &endp
))
685 return ng_Abandon(dev
, p
);
686 log_Printf(LogDEBUG
, "%s: Got hook \"%s\"\n", p
->link
.name
, lasthook
);
688 len
= strlen(lasthook
);
689 done
= strchr(" \t", devp
[len
]) ? 1 : 0;
695 } /* else should moan about devp[-1] being '[' ? */
698 snprintf(dev
->hook
, sizeof dev
->hook
, "%s", lasthook
);
700 /* Connect the node to our socket node */
701 snprintf(ngc
.path
, sizeof ngc
.path
, "%s", path
);
702 snprintf(ngc
.ourhook
, sizeof ngc
.ourhook
, "%s", dev
->hook
);
703 memcpy(ngc
.peerhook
, ngc
.ourhook
, sizeof ngc
.peerhook
);
705 log_Printf(LogDEBUG
, "Connecting netgraph socket .:%s -> %s.%s\n",
706 ngc
.ourhook
, ngc
.path
, ngc
.peerhook
);
707 if (NgSendMsg(dev
->cs
, ".:", NGM_GENERIC_COOKIE
,
708 NGM_CONNECT
, &ngc
, sizeof ngc
) < 0) {
709 log_Printf(LogWARN
, "Cannot connect %s and socket netgraph "
710 "nodes: %s\n", path
, strerror(errno
));
711 return ng_Abandon(dev
, p
);
714 /* Hook things up so that we monitor dev->cs */
715 p
->desc
.UpdateSet
= ng_UpdateSet
;
716 p
->desc
.IsSet
= ng_IsSet
;
717 p
->desc
.Read
= ng_DescriptorRead
;
719 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
722 /* See if we're a netgraph socket */
725 if (getsockname(p
->fd
, sock
, &sz
) != -1 && sock
->sa_family
== AF_NETGRAPH
) {
727 * It's a netgraph node... We can't determine hook names etc, so we
728 * stay pretty impartial....
730 log_Printf(LogPHASE
, "%s: Link is a netgraph node\n", p
->link
.name
);
732 if ((dev
= malloc(sizeof *dev
)) == NULL
) {
733 log_Printf(LogWARN
, "%s: Cannot allocate an ether device: %s\n",
734 p
->link
.name
, strerror(errno
));
738 memcpy(&dev
->dev
, &basengdevice
, sizeof dev
->dev
);
745 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_FORCE_SYNCNOACF
);