| /*- |
| * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * $FreeBSD: src/usr.sbin/ppp/netgraph.c,v 1.6.10.1.4.1 2010/12/21 17:10:29 kensmith Exp $ |
| */ |
| |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #include <netgraph.h> |
| #include <net/ethernet.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| #include <netgraph/ng_ether.h> |
| #include <netgraph/ng_message.h> |
| #include <netgraph/ng_socket.h> |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sysexits.h> |
| #include <sys/fcntl.h> |
| #include <sys/uio.h> |
| #include <termios.h> |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include "layer.h" |
| #include "defs.h" |
| #include "mbuf.h" |
| #include "log.h" |
| #include "timer.h" |
| #include "lqr.h" |
| #include "hdlc.h" |
| #include "throughput.h" |
| #include "fsm.h" |
| #include "lcp.h" |
| #include "ccp.h" |
| #include "link.h" |
| #include "async.h" |
| #include "descriptor.h" |
| #include "physical.h" |
| #include "main.h" |
| #include "mp.h" |
| #include "chat.h" |
| #include "auth.h" |
| #include "chap.h" |
| #include "cbcp.h" |
| #include "datalink.h" |
| #include "slcompress.h" |
| #include "iplist.h" |
| #include "ncpaddr.h" |
| #include "ipcp.h" |
| #include "ipv6cp.h" |
| #include "ncp.h" |
| #include "filter.h" |
| #ifndef NORADIUS |
| #include "radius.h" |
| #endif |
| #include "bundle.h" |
| #include "id.h" |
| #include "netgraph.h" |
| |
| |
| struct ngdevice { |
| struct device dev; /* What struct physical knows about */ |
| int cs; /* Control socket */ |
| char hook[NG_HOOKSIZ]; /* Our socket node hook */ |
| }; |
| |
| #define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL) |
| #define NG_MSGBUFSZ 4096 |
| #define NETGRAPH_PREFIX "netgraph:" |
| |
| unsigned |
| ng_DeviceSize(void) |
| { |
| return sizeof(struct ngdevice); |
| } |
| |
| static int |
| ng_MessageOut(struct ngdevice *dev, const char *data) |
| { |
| char path[NG_PATHSIZ]; |
| char *fmt; |
| size_t len; |
| int pos, dpos; |
| |
| /* |
| * We expect a node path, one or more spaces, a command, one or more |
| * spaces and an ascii netgraph structure. |
| */ |
| data += strspn(data, " \t"); |
| len = strcspn(data, " \t"); |
| if (len >= sizeof path) { |
| log_Printf(LogWARN, "%s: %.*s: Node path too long\n", |
| dev->dev.name, len, data); |
| return 0; |
| } |
| memcpy(path, data, len); |
| path[len] = '\0'; |
| data += len; |
| |
| data += strspn(data, " \t"); |
| len = strcspn(data, " \t"); |
| for (pos = len; pos >= 0; pos--) |
| if (data[pos] == '%') |
| len++; |
| if ((fmt = alloca(len + 4)) == NULL) { |
| log_Printf(LogWARN, "%s: alloca(%d) failure... %s\n", |
| dev->dev.name, len + 4, strerror(errno)); |
| return 0; |
| } |
| |
| /* |
| * This is probably a waste of time, but we really don't want to end |
| * up stuffing unexpected % escapes into the kernel.... |
| */ |
| for (pos = dpos = 0; pos < (int)len;) { |
| if (data[dpos] == '%') |
| fmt[pos++] = '%'; |
| fmt[pos++] = data[dpos++]; |
| } |
| strcpy(fmt + pos, " %s"); |
| data += dpos; |
| |
| data += strspn(data, " \t"); |
| if (NgSendAsciiMsg(dev->cs, path, fmt, data) < 0) { |
| log_Printf(LogDEBUG, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n", |
| dev->dev.name, path, fmt, data, strerror(errno)); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * Get a netgraph message |
| */ |
| static ssize_t |
| ng_MessageIn(struct physical *p, char *buf, size_t sz) |
| { |
| char msgbuf[sizeof(struct ng_mesg) * 2 + NG_MSGBUFSZ]; |
| struct ngdevice *dev = device2ng(p->handler); |
| struct ng_mesg *rep = (struct ng_mesg *)msgbuf; |
| char path[NG_PATHSIZ]; |
| size_t len; |
| |
| #ifdef BROKEN_SELECT |
| struct timeval t; |
| fd_set *r; |
| int ret; |
| |
| if (dev->cs < 0) |
| return 0; |
| |
| if ((r = mkfdset()) == NULL) { |
| log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n"); |
| return -1; |
| } |
| zerofdset(r); |
| FD_SET(dev->cs, r); |
| t.tv_sec = t.tv_usec = 0; |
| ret = select(dev->cs + 1, r, NULL, NULL, &t); |
| free(r); |
| |
| if (ret <= 0) |
| return 0; |
| #endif |
| |
| if (NgRecvAsciiMsg(dev->cs, rep, sizeof msgbuf, path)) { |
| log_Printf(LogWARN, "%s: NgRecvAsciiMsg: %s\n", |
| dev->dev.name, strerror(errno)); |
| return -1; |
| } |
| |
| /* XXX: Should we check rep->header.version ? */ |
| |
| if (sz == 0) |
| log_Printf(LogWARN, "%s: Unexpected message: %s\n", dev->dev.name, |
| rep->header.cmdstr); |
| else { |
| log_Printf(LogDEBUG, "%s: Received message: %s\n", dev->dev.name, |
| rep->header.cmdstr); |
| len = strlen(rep->header.cmdstr); |
| if (sz > len) |
| sz = len; |
| memcpy(buf, rep->header.cmdstr, sz); |
| } |
| |
| return sz; |
| } |
| |
| static ssize_t |
| ng_Write(struct physical *p, const void *v, size_t n) |
| { |
| struct ngdevice *dev = device2ng(p->handler); |
| |
| switch (p->dl->state) { |
| case DATALINK_DIAL: |
| case DATALINK_LOGIN: |
| return ng_MessageOut(dev, v) ? (ssize_t)n : -1; |
| } |
| return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n; |
| } |
| |
| static ssize_t |
| ng_Read(struct physical *p, void *v, size_t n) |
| { |
| char hook[NG_HOOKSIZ]; |
| |
| switch (p->dl->state) { |
| case DATALINK_DIAL: |
| case DATALINK_LOGIN: |
| return ng_MessageIn(p, v, n); |
| } |
| |
| return NgRecvData(p->fd, v, n, hook); |
| } |
| |
| static int |
| ng_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e) |
| { |
| struct ngdevice *dev = device2ng(p->handler); |
| int result; |
| |
| if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) { |
| FD_CLR(dev->cs, r); |
| log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs); |
| result = 1; |
| } else |
| result = 0; |
| |
| /* Careful... physical_RemoveFromSet() called us ! */ |
| |
| p->handler->removefromset = NULL; |
| result += physical_RemoveFromSet(p, r, w, e); |
| p->handler->removefromset = ng_RemoveFromSet; |
| |
| return result; |
| } |
| |
| static void |
| ng_Free(struct physical *p) |
| { |
| struct ngdevice *dev = device2ng(p->handler); |
| |
| physical_SetDescriptor(p); |
| if (dev->cs != -1) |
| close(dev->cs); |
| free(dev); |
| } |
| |
| static void |
| ng_device2iov(struct device *d, struct iovec *iov, int *niov, |
| int maxiov __unused, int *auxfd, int *nauxfd) |
| { |
| struct ngdevice *dev; |
| int sz = physical_MaxDeviceSize(); |
| |
| iov[*niov].iov_base = d = realloc(d, sz); |
| if (d == NULL) { |
| log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz); |
| AbortProgram(EX_OSERR); |
| } |
| iov[*niov].iov_len = sz; |
| (*niov)++; |
| |
| dev = device2ng(d); |
| *auxfd = dev->cs; |
| (*nauxfd)++; |
| } |
| |
| static const struct device basengdevice = { |
| NG_DEVICE, |
| "netgraph", |
| 0, |
| { CD_REQUIRED, DEF_NGCDDELAY }, |
| NULL, |
| ng_RemoveFromSet, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| ng_Free, |
| ng_Read, |
| ng_Write, |
| ng_device2iov, |
| NULL, |
| NULL, |
| NULL |
| }; |
| |
| struct device * |
| ng_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, |
| int maxiov __unused, int *auxfd, int *nauxfd) |
| { |
| if (type == NG_DEVICE) { |
| struct ngdevice *dev = (struct ngdevice *)iov[(*niov)++].iov_base; |
| |
| dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ |
| if (dev == NULL) { |
| log_Printf(LogALERT, "Failed to allocate memory: %d\n", |
| (int)(sizeof *dev)); |
| AbortProgram(EX_OSERR); |
| } |
| |
| if (*nauxfd) { |
| dev->cs = *auxfd; |
| (*nauxfd)--; |
| } else |
| dev->cs = -1; |
| |
| /* Refresh function pointers etc */ |
| memcpy(&dev->dev, &basengdevice, sizeof dev->dev); |
| |
| /* XXX: Are netgraph always synchronous ? */ |
| physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); |
| return &dev->dev; |
| } |
| |
| return NULL; |
| } |
| |
| static int |
| ng_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) |
| { |
| struct physical *p = descriptor2physical(d); |
| struct ngdevice *dev = device2ng(p->handler); |
| int result; |
| |
| switch (p->dl->state) { |
| case DATALINK_DIAL: |
| case DATALINK_LOGIN: |
| if (r) { |
| FD_SET(dev->cs, r); |
| log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs); |
| result = 1; |
| } else |
| result = 0; |
| break; |
| |
| default: |
| result = physical_doUpdateSet(d, r, w, e, n, 0); |
| break; |
| } |
| |
| return result; |
| } |
| |
| static int |
| ng_IsSet(struct fdescriptor *d, const fd_set *fdset) |
| { |
| struct physical *p = descriptor2physical(d); |
| struct ngdevice *dev = device2ng(p->handler); |
| int result; |
| |
| result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset); |
| result += physical_IsSet(d, fdset); |
| |
| return result; |
| } |
| |
| static void |
| ng_DescriptorRead(struct fdescriptor *d, struct bundle *bundle, |
| const fd_set *fdset) |
| { |
| struct physical *p = descriptor2physical(d); |
| struct ngdevice *dev = device2ng(p->handler); |
| |
| if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) |
| ng_MessageIn(p, NULL, 0); |
| |
| if (physical_IsSet(d, fdset)) |
| physical_DescriptorRead(d, bundle, fdset); |
| } |
| |
| static struct device * |
| ng_Abandon(struct ngdevice *dev, struct physical *p) |
| { |
| /* Abandon our node construction */ |
| close(dev->cs); |
| close(p->fd); |
| p->fd = -2; /* Nobody else need try.. */ |
| free(dev); |
| |
| return NULL; |
| } |
| |
| |
| /* |
| * Populate the ``word'' (of size ``sz'') named ``what'' from ``from'' |
| * ending with any character from ``sep''. Point ``endp'' at the next |
| * word. |
| */ |
| |
| #define GETSEGMENT(what, from, sep, endp) \ |
| getsegment(#what, (what), sizeof(what), from, sep, endp) |
| |
| static int |
| getsegment(const char *what, char *word, size_t sz, const char *from, |
| const char *sep, const char **endp) |
| { |
| size_t len; |
| |
| if ((len = strcspn(from, sep)) == 0) { |
| log_Printf(LogWARN, "%s name should not be empty !\n", what); |
| return 0; |
| } |
| |
| if (len >= sz) { |
| log_Printf(LogWARN, "%s name too long, max %d !\n", what, sz - 1); |
| return 0; |
| } |
| |
| strncpy(word, from, len); |
| word[len] = '\0'; |
| |
| *endp = from + len; |
| *endp += strspn(*endp, sep); |
| |
| return 1; |
| } |
| |
| struct device * |
| ng_Create(struct physical *p) |
| { |
| struct sockaddr_ng ngsock; |
| u_char rbuf[2048]; |
| struct sockaddr *sock = (struct sockaddr *)&ngsock; |
| const struct hooklist *hlist; |
| const struct nodeinfo *ninfo; |
| const struct linkinfo *nlink; |
| struct ngdevice *dev; |
| struct ng_mesg *resp; |
| struct ngm_mkpeer mkp; |
| struct ngm_connect ngc; |
| const char *devp, *endp; |
| char lasthook[NG_HOOKSIZ]; |
| char hook[NG_HOOKSIZ]; |
| char nodetype[NG_TYPESIZ + NG_NODESIZ]; |
| char modname[NG_TYPESIZ + 3]; |
| char path[NG_PATHSIZ]; |
| char *nodename; |
| int len, sz, done; |
| unsigned f; |
| |
| dev = NULL; |
| if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX, |
| sizeof NETGRAPH_PREFIX - 1)) { |
| p->fd--; /* We own the device - change fd */ |
| |
| if ((dev = malloc(sizeof *dev)) == NULL) |
| return NULL; |
| |
| loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL); |
| |
| /* Create a socket node */ |
| if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) { |
| log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n", |
| strerror(errno)); |
| free(dev); |
| p->fd = -2; |
| return NULL; |
| } |
| |
| devp = p->name.full + sizeof NETGRAPH_PREFIX - 1; |
| *lasthook = *path = '\0'; |
| log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n", |
| p->link.name, devp); |
| done = 0; |
| |
| while (*devp != '\0' && !done) { |
| if (*devp != '[') { |
| if (*lasthook == '\0') { |
| log_Printf(LogWARN, "%s: Netgraph devices must start with" |
| " [nodetype:nodename]\n", p->link.name); |
| return ng_Abandon(dev, p); |
| } |
| |
| /* Get the hook name of the new node */ |
| if (!GETSEGMENT(hook, devp, ".[", &endp)) |
| return ng_Abandon(dev, p); |
| log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook); |
| devp = endp; |
| if (*devp == '\0') { |
| log_Printf(LogWARN, "%s: Netgraph device must not end with a second" |
| " hook\n", p->link.name); |
| return ng_Abandon(dev, p); |
| } |
| if (devp[-1] != '[') { |
| log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device" |
| " pos %d\n", p->link.name, devp - p->link.name - 1); |
| return ng_Abandon(dev, p); |
| } |
| } else { |
| /* Use lasthook as the hook name */ |
| strcpy(hook, lasthook); |
| devp++; |
| } |
| |
| /* We've got ``lasthook'' and ``hook'', get the node type */ |
| if (!GETSEGMENT(nodetype, devp, "]", &endp)) |
| return ng_Abandon(dev, p); |
| log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype); |
| |
| if ((nodename = strchr(nodetype, ':')) != NULL) { |
| *nodename++ = '\0'; |
| if (*nodename == '\0' && *nodetype == '\0') { |
| log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device" |
| " pos %d\n", p->link.name, devp - p->link.name - 1); |
| return ng_Abandon(dev, p); |
| } |
| } |
| |
| /* Ignore optional colons after nodes */ |
| devp = *endp == ':' ? endp + 1 : endp; |
| if (*devp == '.') |
| devp++; |
| |
| if (*lasthook == '\0') { |
| /* This is the first node in the chain */ |
| if (nodename == NULL || *nodename == '\0') { |
| log_Printf(LogWARN, "%s: %s: No initial device nodename\n", |
| p->link.name, devp); |
| return ng_Abandon(dev, p); |
| } |
| |
| if (*nodetype != '\0') { |
| /* Attempt to load the module */ |
| snprintf(modname, sizeof modname, "ng_%s", nodetype); |
| log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", |
| p->link.name, modname); |
| loadmodules(LOAD_QUIETLY, modname, NULL); |
| } |
| |
| snprintf(path, sizeof path, "%s:", nodename); |
| /* XXX: If we have a node type, ensure it's correct */ |
| } else { |
| /* |
| * Ask for a list of hooks attached to the previous node. If we |
| * find the one we're interested in, and if it's connected to a |
| * node of the right type using the correct hook, use that. |
| * If we find the hook connected to something else, fail. |
| * If we find no match, mkpeer the new node. |
| */ |
| if (*nodetype == '\0') { |
| log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n", |
| p->link.name, |
| devp - p->name.full + sizeof NETGRAPH_PREFIX - 1); |
| return ng_Abandon(dev, p); |
| } |
| |
| /* Get a list of node hooks */ |
| if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, |
| NULL, 0) < 0) { |
| log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", |
| p->link.name, path, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| |
| /* Get our list back */ |
| resp = (struct ng_mesg *)rbuf; |
| if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { |
| log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", |
| p->link.name, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| |
| hlist = (const struct hooklist *)resp->data; |
| ninfo = &hlist->nodeinfo; |
| |
| log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n", |
| path, ninfo->id); |
| |
| /* look for a hook already attached. */ |
| for (f = 0; f < ninfo->hooks; f++) { |
| nlink = &hlist->link[f]; |
| |
| log_Printf(LogDEBUG, " Found %s -> %s (type %s)\n", nlink->ourhook, |
| nlink->peerhook, nlink->nodeinfo.type); |
| |
| if (!strcmp(nlink->ourhook, lasthook)) { |
| if (strcmp(nlink->peerhook, hook) || |
| strcmp(nlink->nodeinfo.type, nodetype)) { |
| log_Printf(LogWARN, "%s: hook %s:%s is already in use\n", |
| p->link.name, nlink->ourhook, path); |
| return ng_Abandon(dev, p); |
| } |
| /* The node is already hooked up nicely.... reuse it */ |
| break; |
| } |
| } |
| |
| if (f == ninfo->hooks) { |
| /* Attempt to load the module */ |
| snprintf(modname, sizeof modname, "ng_%s", nodetype); |
| log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", |
| p->link.name, modname); |
| loadmodules(LOAD_QUIETLY, modname, NULL); |
| |
| /* Create (mkpeer) the new node */ |
| |
| snprintf(mkp.type, sizeof mkp.type, "%s", nodetype); |
| snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook); |
| snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook); |
| |
| log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n", |
| p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype); |
| |
| if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, |
| NGM_MKPEER, &mkp, sizeof mkp) < 0) { |
| log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n", |
| path, nodetype, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| } |
| len = strlen(path); |
| snprintf(path + len, sizeof path - len, "%s%s", |
| path[len - 1] == ':' ? "" : ".", lasthook); |
| } |
| |
| /* Get a list of node hooks */ |
| if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, |
| NULL, 0) < 0) { |
| log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", |
| p->link.name, path, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| |
| /* Get our list back */ |
| resp = (struct ng_mesg *)rbuf; |
| if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { |
| log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", |
| p->link.name, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| |
| hlist = (const struct hooklist *)resp->data; |
| ninfo = &hlist->nodeinfo; |
| |
| if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' && |
| strcmp(ninfo->name, nodename) && |
| NgNameNode(dev->cs, path, "%s", nodename) < 0) { |
| log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n", |
| p->link.name, path, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| |
| if (!GETSEGMENT(lasthook, devp, " \t.[", &endp)) |
| return ng_Abandon(dev, p); |
| log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook); |
| |
| len = strlen(lasthook); |
| done = strchr(" \t", devp[len]) ? 1 : 0; |
| devp = endp; |
| |
| if (*devp != '\0') { |
| if (devp[-1] == '[') |
| devp--; |
| } /* else should moan about devp[-1] being '[' ? */ |
| } |
| |
| snprintf(dev->hook, sizeof dev->hook, "%s", lasthook); |
| |
| /* Connect the node to our socket node */ |
| snprintf(ngc.path, sizeof ngc.path, "%s", path); |
| snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook); |
| memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); |
| |
| log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n", |
| ngc.ourhook, ngc.path, ngc.peerhook); |
| if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE, |
| NGM_CONNECT, &ngc, sizeof ngc) < 0) { |
| log_Printf(LogWARN, "Cannot connect %s and socket netgraph " |
| "nodes: %s\n", path, strerror(errno)); |
| return ng_Abandon(dev, p); |
| } |
| |
| /* Hook things up so that we monitor dev->cs */ |
| p->desc.UpdateSet = ng_UpdateSet; |
| p->desc.IsSet = ng_IsSet; |
| p->desc.Read = ng_DescriptorRead; |
| |
| memcpy(&dev->dev, &basengdevice, sizeof dev->dev); |
| |
| } else { |
| /* See if we're a netgraph socket */ |
| |
| sz = sizeof ngsock; |
| if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) { |
| /* |
| * It's a netgraph node... We can't determine hook names etc, so we |
| * stay pretty impartial.... |
| */ |
| log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name); |
| |
| if ((dev = malloc(sizeof *dev)) == NULL) { |
| log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n", |
| p->link.name, strerror(errno)); |
| return NULL; |
| } |
| |
| memcpy(&dev->dev, &basengdevice, sizeof dev->dev); |
| dev->cs = -1; |
| *dev->hook = '\0'; |
| } |
| } |
| |
| if (dev) { |
| physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); |
| return &dev->dev; |
| } |
| |
| return NULL; |
| } |