193418Sbrian/*- 293418Sbrian * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org> 393418Sbrian * All rights reserved. 493418Sbrian * 593418Sbrian * Redistribution and use in source and binary forms, with or without 693418Sbrian * modification, are permitted provided that the following conditions 793418Sbrian * are met: 893418Sbrian * 1. Redistributions of source code must retain the above copyright 993418Sbrian * notice, this list of conditions and the following disclaimer. 1093418Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1193418Sbrian * notice, this list of conditions and the following disclaimer in the 1293418Sbrian * documentation and/or other materials provided with the distribution. 1393418Sbrian * 1493418Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1593418Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1693418Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1793418Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1893418Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1993418Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2093418Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2193418Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2293418Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2393418Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2493418Sbrian * SUCH DAMAGE. 2593418Sbrian * 2693418Sbrian * $FreeBSD: releng/11.0/usr.sbin/ppp/netgraph.c 196513 2009-08-24 17:18:17Z brian $ 2793418Sbrian */ 2893418Sbrian 2993418Sbrian#include <sys/param.h> 3093418Sbrian#include <sys/socket.h> 3193418Sbrian#include <sys/un.h> 3293418Sbrian#include <netinet/in.h> 3393418Sbrian#include <arpa/inet.h> 3493418Sbrian#include <netdb.h> 3593418Sbrian#include <netgraph.h> 3693418Sbrian#include <net/ethernet.h> 3793418Sbrian#include <netinet/in_systm.h> 3893418Sbrian#include <netinet/ip.h> 3993418Sbrian#include <netgraph/ng_ether.h> 4093418Sbrian#include <netgraph/ng_message.h> 4193418Sbrian#include <netgraph/ng_socket.h> 4293418Sbrian 4393418Sbrian#include <errno.h> 4493418Sbrian#include <stdio.h> 4593418Sbrian#include <stdlib.h> 4693418Sbrian#include <string.h> 4793418Sbrian#include <sysexits.h> 4893418Sbrian#include <sys/fcntl.h> 4993418Sbrian#include <sys/uio.h> 5093418Sbrian#include <termios.h> 5193418Sbrian#include <sys/time.h> 5293418Sbrian#include <unistd.h> 5393418Sbrian 5493418Sbrian#include "layer.h" 5593418Sbrian#include "defs.h" 5693418Sbrian#include "mbuf.h" 5793418Sbrian#include "log.h" 5893418Sbrian#include "timer.h" 5993418Sbrian#include "lqr.h" 6093418Sbrian#include "hdlc.h" 6193418Sbrian#include "throughput.h" 6293418Sbrian#include "fsm.h" 6393418Sbrian#include "lcp.h" 6493418Sbrian#include "ccp.h" 6593418Sbrian#include "link.h" 6693418Sbrian#include "async.h" 6793418Sbrian#include "descriptor.h" 6893418Sbrian#include "physical.h" 6993418Sbrian#include "main.h" 7093418Sbrian#include "mp.h" 7193418Sbrian#include "chat.h" 7293418Sbrian#include "auth.h" 7393418Sbrian#include "chap.h" 7493418Sbrian#include "cbcp.h" 7593418Sbrian#include "datalink.h" 7693418Sbrian#include "slcompress.h" 7793418Sbrian#include "iplist.h" 7893418Sbrian#include "ncpaddr.h" 7993418Sbrian#include "ipcp.h" 8093418Sbrian#include "ipv6cp.h" 8193418Sbrian#include "ncp.h" 8293418Sbrian#include "filter.h" 8393418Sbrian#ifndef NORADIUS 8493418Sbrian#include "radius.h" 8593418Sbrian#endif 8693418Sbrian#include "bundle.h" 8793418Sbrian#include "id.h" 8893418Sbrian#include "netgraph.h" 8993418Sbrian 9093418Sbrian 9193418Sbrianstruct ngdevice { 9293418Sbrian struct device dev; /* What struct physical knows about */ 9393418Sbrian int cs; /* Control socket */ 94122758Sharti char hook[NG_HOOKSIZ]; /* Our socket node hook */ 9593418Sbrian}; 9693418Sbrian 9793418Sbrian#define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL) 9893418Sbrian#define NG_MSGBUFSZ 4096 9993418Sbrian#define NETGRAPH_PREFIX "netgraph:" 10093418Sbrian 101134789Sbrianunsigned 10293418Sbrianng_DeviceSize(void) 10393418Sbrian{ 10493418Sbrian return sizeof(struct ngdevice); 10593418Sbrian} 10693418Sbrian 10793418Sbrianstatic int 108134789Sbrianng_MessageOut(struct ngdevice *dev, const char *data) 10993418Sbrian{ 110122758Sharti char path[NG_PATHSIZ]; 11193418Sbrian char *fmt; 112134789Sbrian size_t len; 113134789Sbrian int pos, dpos; 11493418Sbrian 11593418Sbrian /* 11693418Sbrian * We expect a node path, one or more spaces, a command, one or more 11793418Sbrian * spaces and an ascii netgraph structure. 11893418Sbrian */ 11993418Sbrian data += strspn(data, " \t"); 12093418Sbrian len = strcspn(data, " \t"); 12193418Sbrian if (len >= sizeof path) { 12293418Sbrian log_Printf(LogWARN, "%s: %.*s: Node path too long\n", 12393418Sbrian dev->dev.name, len, data); 12493418Sbrian return 0; 12593418Sbrian } 12693418Sbrian memcpy(path, data, len); 12793418Sbrian path[len] = '\0'; 12893418Sbrian data += len; 12993418Sbrian 13093418Sbrian data += strspn(data, " \t"); 13193418Sbrian len = strcspn(data, " \t"); 13293418Sbrian for (pos = len; pos >= 0; pos--) 13393418Sbrian if (data[pos] == '%') 13493418Sbrian len++; 13593418Sbrian if ((fmt = alloca(len + 4)) == NULL) { 13693418Sbrian log_Printf(LogWARN, "%s: alloca(%d) failure... %s\n", 13793418Sbrian dev->dev.name, len + 4, strerror(errno)); 13893418Sbrian return 0; 13993418Sbrian } 14093418Sbrian 14193418Sbrian /* 14293418Sbrian * This is probably a waste of time, but we really don't want to end 14393418Sbrian * up stuffing unexpected % escapes into the kernel.... 14493418Sbrian */ 145134789Sbrian for (pos = dpos = 0; pos < (int)len;) { 14693418Sbrian if (data[dpos] == '%') 14793418Sbrian fmt[pos++] = '%'; 14893418Sbrian fmt[pos++] = data[dpos++]; 14993418Sbrian } 15093418Sbrian strcpy(fmt + pos, " %s"); 15193418Sbrian data += dpos; 15293418Sbrian 15393418Sbrian data += strspn(data, " \t"); 15493418Sbrian if (NgSendAsciiMsg(dev->cs, path, fmt, data) < 0) { 15593418Sbrian log_Printf(LogDEBUG, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n", 15693418Sbrian dev->dev.name, path, fmt, data, strerror(errno)); 15793418Sbrian return 0; 15893418Sbrian } 15993418Sbrian 16093418Sbrian return 1; 16193418Sbrian} 16293418Sbrian 16393418Sbrian/* 16493418Sbrian * Get a netgraph message 16593418Sbrian */ 16693418Sbrianstatic ssize_t 16793418Sbrianng_MessageIn(struct physical *p, char *buf, size_t sz) 16893418Sbrian{ 16993418Sbrian char msgbuf[sizeof(struct ng_mesg) * 2 + NG_MSGBUFSZ]; 17093418Sbrian struct ngdevice *dev = device2ng(p->handler); 17193418Sbrian struct ng_mesg *rep = (struct ng_mesg *)msgbuf; 172122758Sharti char path[NG_PATHSIZ]; 173134789Sbrian size_t len; 17493418Sbrian 17593418Sbrian#ifdef BROKEN_SELECT 17693418Sbrian struct timeval t; 17793418Sbrian fd_set *r; 17893418Sbrian int ret; 17993418Sbrian 18093418Sbrian if (dev->cs < 0) 18193418Sbrian return 0; 18293418Sbrian 18393418Sbrian if ((r = mkfdset()) == NULL) { 18493418Sbrian log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n"); 18593418Sbrian return -1; 18693418Sbrian } 18793418Sbrian zerofdset(r); 18893418Sbrian FD_SET(dev->cs, r); 18993418Sbrian t.tv_sec = t.tv_usec = 0; 19093418Sbrian ret = select(dev->cs + 1, r, NULL, NULL, &t); 19193418Sbrian free(r); 19293418Sbrian 19393418Sbrian if (ret <= 0) 19493418Sbrian return 0; 19593418Sbrian#endif 19693418Sbrian 19793418Sbrian if (NgRecvAsciiMsg(dev->cs, rep, sizeof msgbuf, path)) { 19893418Sbrian log_Printf(LogWARN, "%s: NgRecvAsciiMsg: %s\n", 19993418Sbrian dev->dev.name, strerror(errno)); 20093418Sbrian return -1; 20193418Sbrian } 20293418Sbrian 20393418Sbrian /* XXX: Should we check rep->header.version ? */ 20493418Sbrian 20593418Sbrian if (sz == 0) 20693418Sbrian log_Printf(LogWARN, "%s: Unexpected message: %s\n", dev->dev.name, 20793418Sbrian rep->header.cmdstr); 20893418Sbrian else { 20993418Sbrian log_Printf(LogDEBUG, "%s: Received message: %s\n", dev->dev.name, 21093418Sbrian rep->header.cmdstr); 21193418Sbrian len = strlen(rep->header.cmdstr); 21293418Sbrian if (sz > len) 21393418Sbrian sz = len; 21493418Sbrian memcpy(buf, rep->header.cmdstr, sz); 21593418Sbrian } 21693418Sbrian 21793418Sbrian return sz; 21893418Sbrian} 21993418Sbrian 22093418Sbrianstatic ssize_t 22193418Sbrianng_Write(struct physical *p, const void *v, size_t n) 22293418Sbrian{ 22393418Sbrian struct ngdevice *dev = device2ng(p->handler); 22493418Sbrian 22593418Sbrian switch (p->dl->state) { 22693418Sbrian case DATALINK_DIAL: 22793418Sbrian case DATALINK_LOGIN: 228134789Sbrian return ng_MessageOut(dev, v) ? (ssize_t)n : -1; 22993418Sbrian } 230134789Sbrian return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n; 23193418Sbrian} 23293418Sbrian 23393418Sbrianstatic ssize_t 23493418Sbrianng_Read(struct physical *p, void *v, size_t n) 23593418Sbrian{ 236122758Sharti char hook[NG_HOOKSIZ]; 23793418Sbrian 23893418Sbrian switch (p->dl->state) { 23993418Sbrian case DATALINK_DIAL: 24093418Sbrian case DATALINK_LOGIN: 24193418Sbrian return ng_MessageIn(p, v, n); 24293418Sbrian } 24393418Sbrian 24493418Sbrian return NgRecvData(p->fd, v, n, hook); 24593418Sbrian} 24693418Sbrian 24793418Sbrianstatic int 24893418Sbrianng_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e) 24993418Sbrian{ 25093418Sbrian struct ngdevice *dev = device2ng(p->handler); 25193418Sbrian int result; 25293418Sbrian 25393418Sbrian if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) { 25493418Sbrian FD_CLR(dev->cs, r); 25593418Sbrian log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs); 25693418Sbrian result = 1; 25793418Sbrian } else 25893418Sbrian result = 0; 25993418Sbrian 26093418Sbrian /* Careful... physical_RemoveFromSet() called us ! */ 26193418Sbrian 26293418Sbrian p->handler->removefromset = NULL; 26393418Sbrian result += physical_RemoveFromSet(p, r, w, e); 26493418Sbrian p->handler->removefromset = ng_RemoveFromSet; 26593418Sbrian 26693418Sbrian return result; 26793418Sbrian} 26893418Sbrian 26993418Sbrianstatic void 27093418Sbrianng_Free(struct physical *p) 27193418Sbrian{ 27293418Sbrian struct ngdevice *dev = device2ng(p->handler); 27393418Sbrian 27493418Sbrian physical_SetDescriptor(p); 27593418Sbrian if (dev->cs != -1) 27693418Sbrian close(dev->cs); 27793418Sbrian free(dev); 27893418Sbrian} 27993418Sbrian 28093418Sbrianstatic void 28193418Sbrianng_device2iov(struct device *d, struct iovec *iov, int *niov, 282134789Sbrian int maxiov __unused, int *auxfd, int *nauxfd) 28393418Sbrian{ 284196513Sbrian struct ngdevice *dev; 28593418Sbrian int sz = physical_MaxDeviceSize(); 28693418Sbrian 287196513Sbrian iov[*niov].iov_base = d = realloc(d, sz); 288196513Sbrian if (d == NULL) { 28993418Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz); 29093418Sbrian AbortProgram(EX_OSERR); 29193418Sbrian } 29293418Sbrian iov[*niov].iov_len = sz; 29393418Sbrian (*niov)++; 29493418Sbrian 295196513Sbrian dev = device2ng(d); 29693418Sbrian *auxfd = dev->cs; 29793418Sbrian (*nauxfd)++; 29893418Sbrian} 29993418Sbrian 30093418Sbrianstatic const struct device basengdevice = { 30193418Sbrian NG_DEVICE, 30293418Sbrian "netgraph", 30393418Sbrian 0, 30493418Sbrian { CD_REQUIRED, DEF_NGCDDELAY }, 30593418Sbrian NULL, 30693418Sbrian ng_RemoveFromSet, 30793418Sbrian NULL, 30893418Sbrian NULL, 30993418Sbrian NULL, 31093418Sbrian NULL, 31193418Sbrian NULL, 31293418Sbrian ng_Free, 31393418Sbrian ng_Read, 31493418Sbrian ng_Write, 31593418Sbrian ng_device2iov, 31693418Sbrian NULL, 31796582Sbrian NULL, 31893418Sbrian NULL 31993418Sbrian}; 32093418Sbrian 32193418Sbrianstruct device * 32293418Sbrianng_iov2device(int type, struct physical *p, struct iovec *iov, int *niov, 323134789Sbrian int maxiov __unused, int *auxfd, int *nauxfd) 32493418Sbrian{ 32593418Sbrian if (type == NG_DEVICE) { 32693418Sbrian struct ngdevice *dev = (struct ngdevice *)iov[(*niov)++].iov_base; 32793418Sbrian 32893418Sbrian dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ 32993418Sbrian if (dev == NULL) { 33093418Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", 33193418Sbrian (int)(sizeof *dev)); 33293418Sbrian AbortProgram(EX_OSERR); 33393418Sbrian } 33493418Sbrian 33593418Sbrian if (*nauxfd) { 33693418Sbrian dev->cs = *auxfd; 33793418Sbrian (*nauxfd)--; 33893418Sbrian } else 33993418Sbrian dev->cs = -1; 34093418Sbrian 34193418Sbrian /* Refresh function pointers etc */ 34293418Sbrian memcpy(&dev->dev, &basengdevice, sizeof dev->dev); 34393418Sbrian 34493418Sbrian /* XXX: Are netgraph always synchronous ? */ 34593418Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); 34693418Sbrian return &dev->dev; 34793418Sbrian } 34893418Sbrian 34993418Sbrian return NULL; 35093418Sbrian} 35193418Sbrian 35293418Sbrianstatic int 35393418Sbrianng_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 35493418Sbrian{ 35593418Sbrian struct physical *p = descriptor2physical(d); 35693418Sbrian struct ngdevice *dev = device2ng(p->handler); 35793418Sbrian int result; 35893418Sbrian 35993418Sbrian switch (p->dl->state) { 36093418Sbrian case DATALINK_DIAL: 36193418Sbrian case DATALINK_LOGIN: 36293418Sbrian if (r) { 36393418Sbrian FD_SET(dev->cs, r); 36493418Sbrian log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs); 36593418Sbrian result = 1; 36693418Sbrian } else 36793418Sbrian result = 0; 36893418Sbrian break; 36993418Sbrian 37093418Sbrian default: 37193418Sbrian result = physical_doUpdateSet(d, r, w, e, n, 0); 37293418Sbrian break; 37393418Sbrian } 37493418Sbrian 37593418Sbrian return result; 37693418Sbrian} 37793418Sbrian 37893418Sbrianstatic int 37993418Sbrianng_IsSet(struct fdescriptor *d, const fd_set *fdset) 38093418Sbrian{ 38193418Sbrian struct physical *p = descriptor2physical(d); 38293418Sbrian struct ngdevice *dev = device2ng(p->handler); 38393418Sbrian int result; 38493418Sbrian 38593418Sbrian result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset); 38693418Sbrian result += physical_IsSet(d, fdset); 38793418Sbrian 38893418Sbrian return result; 38993418Sbrian} 39093418Sbrian 39193418Sbrianstatic void 39293418Sbrianng_DescriptorRead(struct fdescriptor *d, struct bundle *bundle, 39393418Sbrian const fd_set *fdset) 39493418Sbrian{ 39593418Sbrian struct physical *p = descriptor2physical(d); 39693418Sbrian struct ngdevice *dev = device2ng(p->handler); 39793418Sbrian 39893418Sbrian if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) 39993418Sbrian ng_MessageIn(p, NULL, 0); 40093418Sbrian 40193418Sbrian if (physical_IsSet(d, fdset)) 40293418Sbrian physical_DescriptorRead(d, bundle, fdset); 40393418Sbrian} 40493418Sbrian 40593418Sbrianstatic struct device * 40693418Sbrianng_Abandon(struct ngdevice *dev, struct physical *p) 40793418Sbrian{ 40893418Sbrian /* Abandon our node construction */ 40993418Sbrian close(dev->cs); 41093418Sbrian close(p->fd); 41193418Sbrian p->fd = -2; /* Nobody else need try.. */ 41293418Sbrian free(dev); 41393418Sbrian 41493418Sbrian return NULL; 41593418Sbrian} 41693418Sbrian 41793418Sbrian 41893418Sbrian/* 41993418Sbrian * Populate the ``word'' (of size ``sz'') named ``what'' from ``from'' 42093418Sbrian * ending with any character from ``sep''. Point ``endp'' at the next 42193418Sbrian * word. 42293418Sbrian */ 42393418Sbrian 42493418Sbrian#define GETSEGMENT(what, from, sep, endp) \ 42593418Sbrian getsegment(#what, (what), sizeof(what), from, sep, endp) 42693418Sbrian 42793418Sbrianstatic int 42893418Sbriangetsegment(const char *what, char *word, size_t sz, const char *from, 42993418Sbrian const char *sep, const char **endp) 43093418Sbrian{ 431134789Sbrian size_t len; 43293418Sbrian 43393418Sbrian if ((len = strcspn(from, sep)) == 0) { 43493418Sbrian log_Printf(LogWARN, "%s name should not be empty !\n", what); 43593418Sbrian return 0; 43693418Sbrian } 43793418Sbrian 43893418Sbrian if (len >= sz) { 43993418Sbrian log_Printf(LogWARN, "%s name too long, max %d !\n", what, sz - 1); 44093418Sbrian return 0; 44193418Sbrian } 44293418Sbrian 44393418Sbrian strncpy(word, from, len); 44493418Sbrian word[len] = '\0'; 44593418Sbrian 44693418Sbrian *endp = from + len; 44793418Sbrian *endp += strspn(*endp, sep); 44893418Sbrian 44993418Sbrian return 1; 45093418Sbrian} 45193418Sbrian 45293418Sbrianstruct device * 45393418Sbrianng_Create(struct physical *p) 45493418Sbrian{ 45593418Sbrian struct sockaddr_ng ngsock; 45693418Sbrian u_char rbuf[2048]; 45793418Sbrian struct sockaddr *sock = (struct sockaddr *)&ngsock; 45893418Sbrian const struct hooklist *hlist; 45993418Sbrian const struct nodeinfo *ninfo; 46093418Sbrian const struct linkinfo *nlink; 46193418Sbrian struct ngdevice *dev; 46293418Sbrian struct ng_mesg *resp; 46393418Sbrian struct ngm_mkpeer mkp; 46493418Sbrian struct ngm_connect ngc; 46593418Sbrian const char *devp, *endp; 466122758Sharti char lasthook[NG_HOOKSIZ]; 467122758Sharti char hook[NG_HOOKSIZ]; 468122758Sharti char nodetype[NG_TYPESIZ + NG_NODESIZ]; 469122758Sharti char modname[NG_TYPESIZ + 3]; 470122758Sharti char path[NG_PATHSIZ]; 47193418Sbrian char *nodename; 472134789Sbrian int len, sz, done; 473134789Sbrian unsigned f; 47493418Sbrian 47593418Sbrian dev = NULL; 47693418Sbrian if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX, 47793418Sbrian sizeof NETGRAPH_PREFIX - 1)) { 47893418Sbrian p->fd--; /* We own the device - change fd */ 47993418Sbrian 48093418Sbrian if ((dev = malloc(sizeof *dev)) == NULL) 48193418Sbrian return NULL; 48293418Sbrian 48393418Sbrian loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL); 48493418Sbrian 48593418Sbrian /* Create a socket node */ 48693418Sbrian if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) { 48793418Sbrian log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n", 48893418Sbrian strerror(errno)); 48993418Sbrian free(dev); 49093418Sbrian p->fd = -2; 49193418Sbrian return NULL; 49293418Sbrian } 49393418Sbrian 49493418Sbrian devp = p->name.full + sizeof NETGRAPH_PREFIX - 1; 49593418Sbrian *lasthook = *path = '\0'; 49693418Sbrian log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n", 49793418Sbrian p->link.name, devp); 49893418Sbrian done = 0; 49993418Sbrian 50093418Sbrian while (*devp != '\0' && !done) { 50193418Sbrian if (*devp != '[') { 50293418Sbrian if (*lasthook == '\0') { 50393418Sbrian log_Printf(LogWARN, "%s: Netgraph devices must start with" 50493418Sbrian " [nodetype:nodename]\n", p->link.name); 50593418Sbrian return ng_Abandon(dev, p); 50693418Sbrian } 50793418Sbrian 50893418Sbrian /* Get the hook name of the new node */ 50993418Sbrian if (!GETSEGMENT(hook, devp, ".[", &endp)) 51093418Sbrian return ng_Abandon(dev, p); 51193418Sbrian log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook); 51293418Sbrian devp = endp; 51393418Sbrian if (*devp == '\0') { 51493418Sbrian log_Printf(LogWARN, "%s: Netgraph device must not end with a second" 51593418Sbrian " hook\n", p->link.name); 51693418Sbrian return ng_Abandon(dev, p); 51793418Sbrian } 51893418Sbrian if (devp[-1] != '[') { 51993418Sbrian log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device" 52093418Sbrian " pos %d\n", p->link.name, devp - p->link.name - 1); 52193418Sbrian return ng_Abandon(dev, p); 52293418Sbrian } 52393418Sbrian } else { 52493418Sbrian /* Use lasthook as the hook name */ 52593418Sbrian strcpy(hook, lasthook); 52693418Sbrian devp++; 52793418Sbrian } 52893418Sbrian 52993418Sbrian /* We've got ``lasthook'' and ``hook'', get the node type */ 53093418Sbrian if (!GETSEGMENT(nodetype, devp, "]", &endp)) 53193418Sbrian return ng_Abandon(dev, p); 53293418Sbrian log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype); 53393418Sbrian 53493418Sbrian if ((nodename = strchr(nodetype, ':')) != NULL) { 53593418Sbrian *nodename++ = '\0'; 53693418Sbrian if (*nodename == '\0' && *nodetype == '\0') { 53793418Sbrian log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device" 53893418Sbrian " pos %d\n", p->link.name, devp - p->link.name - 1); 53993418Sbrian return ng_Abandon(dev, p); 54093418Sbrian } 54193418Sbrian } 54293418Sbrian 54393418Sbrian /* Ignore optional colons after nodes */ 54493418Sbrian devp = *endp == ':' ? endp + 1 : endp; 54593418Sbrian if (*devp == '.') 54693418Sbrian devp++; 54793418Sbrian 54893418Sbrian if (*lasthook == '\0') { 54993418Sbrian /* This is the first node in the chain */ 55093418Sbrian if (nodename == NULL || *nodename == '\0') { 55193418Sbrian log_Printf(LogWARN, "%s: %s: No initial device nodename\n", 55293418Sbrian p->link.name, devp); 55393418Sbrian return ng_Abandon(dev, p); 55493418Sbrian } 55593418Sbrian 55693418Sbrian if (*nodetype != '\0') { 55793418Sbrian /* Attempt to load the module */ 55893418Sbrian snprintf(modname, sizeof modname, "ng_%s", nodetype); 55993418Sbrian log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", 56093418Sbrian p->link.name, modname); 56193418Sbrian loadmodules(LOAD_QUIETLY, modname, NULL); 56293418Sbrian } 56393418Sbrian 56493418Sbrian snprintf(path, sizeof path, "%s:", nodename); 56593418Sbrian /* XXX: If we have a node type, ensure it's correct */ 56693418Sbrian } else { 56793418Sbrian /* 56893418Sbrian * Ask for a list of hooks attached to the previous node. If we 56993418Sbrian * find the one we're interested in, and if it's connected to a 57093418Sbrian * node of the right type using the correct hook, use that. 57193418Sbrian * If we find the hook connected to something else, fail. 57293418Sbrian * If we find no match, mkpeer the new node. 57393418Sbrian */ 57493418Sbrian if (*nodetype == '\0') { 57593418Sbrian log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n", 57693418Sbrian p->link.name, 57793418Sbrian devp - p->name.full + sizeof NETGRAPH_PREFIX - 1); 57893418Sbrian return ng_Abandon(dev, p); 57993418Sbrian } 58093418Sbrian 58193418Sbrian /* Get a list of node hooks */ 58293418Sbrian if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, 58393418Sbrian NULL, 0) < 0) { 58493418Sbrian log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", 58593418Sbrian p->link.name, path, strerror(errno)); 58693418Sbrian return ng_Abandon(dev, p); 58793418Sbrian } 58893418Sbrian 58993418Sbrian /* Get our list back */ 59093418Sbrian resp = (struct ng_mesg *)rbuf; 59193418Sbrian if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { 59293418Sbrian log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", 59393418Sbrian p->link.name, strerror(errno)); 59493418Sbrian return ng_Abandon(dev, p); 59593418Sbrian } 59693418Sbrian 59793418Sbrian hlist = (const struct hooklist *)resp->data; 59893418Sbrian ninfo = &hlist->nodeinfo; 59993418Sbrian 60093418Sbrian log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n", 60193418Sbrian path, ninfo->id); 60293418Sbrian 60393418Sbrian /* look for a hook already attached. */ 60493418Sbrian for (f = 0; f < ninfo->hooks; f++) { 60593418Sbrian nlink = &hlist->link[f]; 60693418Sbrian 60793418Sbrian log_Printf(LogDEBUG, " Found %s -> %s (type %s)\n", nlink->ourhook, 60893418Sbrian nlink->peerhook, nlink->nodeinfo.type); 60993418Sbrian 61093418Sbrian if (!strcmp(nlink->ourhook, lasthook)) { 61193418Sbrian if (strcmp(nlink->peerhook, hook) || 61293418Sbrian strcmp(nlink->nodeinfo.type, nodetype)) { 61393418Sbrian log_Printf(LogWARN, "%s: hook %s:%s is already in use\n", 61493418Sbrian p->link.name, nlink->ourhook, path); 61593418Sbrian return ng_Abandon(dev, p); 61693418Sbrian } 61793418Sbrian /* The node is already hooked up nicely.... reuse it */ 61893418Sbrian break; 61993418Sbrian } 62093418Sbrian } 62198243Sbrian 62293418Sbrian if (f == ninfo->hooks) { 62393418Sbrian /* Attempt to load the module */ 62493418Sbrian snprintf(modname, sizeof modname, "ng_%s", nodetype); 62593418Sbrian log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n", 62693418Sbrian p->link.name, modname); 62793418Sbrian loadmodules(LOAD_QUIETLY, modname, NULL); 62893418Sbrian 62993418Sbrian /* Create (mkpeer) the new node */ 63093418Sbrian 63193418Sbrian snprintf(mkp.type, sizeof mkp.type, "%s", nodetype); 63293418Sbrian snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook); 63393418Sbrian snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook); 63493418Sbrian 63593418Sbrian log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n", 63693418Sbrian p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype); 63793418Sbrian 63893418Sbrian if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, 63993418Sbrian NGM_MKPEER, &mkp, sizeof mkp) < 0) { 64093418Sbrian log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n", 64193418Sbrian path, nodetype, strerror(errno)); 64293418Sbrian return ng_Abandon(dev, p); 64393418Sbrian } 64493418Sbrian } 64593418Sbrian len = strlen(path); 64693418Sbrian snprintf(path + len, sizeof path - len, "%s%s", 64793418Sbrian path[len - 1] == ':' ? "" : ".", lasthook); 64893418Sbrian } 64993418Sbrian 65093418Sbrian /* Get a list of node hooks */ 65193418Sbrian if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, 65293418Sbrian NULL, 0) < 0) { 65393418Sbrian log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n", 65493418Sbrian p->link.name, path, strerror(errno)); 65593418Sbrian return ng_Abandon(dev, p); 65693418Sbrian } 65793418Sbrian 65893418Sbrian /* Get our list back */ 65993418Sbrian resp = (struct ng_mesg *)rbuf; 66093418Sbrian if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) { 66193418Sbrian log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n", 66293418Sbrian p->link.name, strerror(errno)); 66393418Sbrian return ng_Abandon(dev, p); 66493418Sbrian } 66593418Sbrian 66693418Sbrian hlist = (const struct hooklist *)resp->data; 66793418Sbrian ninfo = &hlist->nodeinfo; 66893418Sbrian 66993418Sbrian if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' && 67093418Sbrian strcmp(ninfo->name, nodename) && 67193418Sbrian NgNameNode(dev->cs, path, "%s", nodename) < 0) { 67293418Sbrian log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n", 67393418Sbrian p->link.name, path, strerror(errno)); 67493418Sbrian return ng_Abandon(dev, p); 67593418Sbrian } 67693418Sbrian 67793418Sbrian if (!GETSEGMENT(lasthook, devp, " \t.[", &endp)) 67893418Sbrian return ng_Abandon(dev, p); 67993418Sbrian log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook); 68093418Sbrian 68193418Sbrian len = strlen(lasthook); 68293418Sbrian done = strchr(" \t", devp[len]) ? 1 : 0; 68393418Sbrian devp = endp; 68493418Sbrian 68593418Sbrian if (*devp != '\0') { 68693418Sbrian if (devp[-1] == '[') 68793418Sbrian devp--; 68893418Sbrian } /* else should moan about devp[-1] being '[' ? */ 68993418Sbrian } 69093418Sbrian 69193418Sbrian snprintf(dev->hook, sizeof dev->hook, "%s", lasthook); 69293418Sbrian 69393418Sbrian /* Connect the node to our socket node */ 69493418Sbrian snprintf(ngc.path, sizeof ngc.path, "%s", path); 69593418Sbrian snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook); 69693418Sbrian memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); 69793418Sbrian 69893418Sbrian log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n", 69993418Sbrian ngc.ourhook, ngc.path, ngc.peerhook); 70093418Sbrian if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE, 70193418Sbrian NGM_CONNECT, &ngc, sizeof ngc) < 0) { 70293418Sbrian log_Printf(LogWARN, "Cannot connect %s and socket netgraph " 70393418Sbrian "nodes: %s\n", path, strerror(errno)); 70493418Sbrian return ng_Abandon(dev, p); 70593418Sbrian } 70693418Sbrian 70793418Sbrian /* Hook things up so that we monitor dev->cs */ 70893418Sbrian p->desc.UpdateSet = ng_UpdateSet; 70993418Sbrian p->desc.IsSet = ng_IsSet; 71093418Sbrian p->desc.Read = ng_DescriptorRead; 71193418Sbrian 71293418Sbrian memcpy(&dev->dev, &basengdevice, sizeof dev->dev); 71393418Sbrian 71493418Sbrian } else { 71593418Sbrian /* See if we're a netgraph socket */ 71693418Sbrian 71793418Sbrian sz = sizeof ngsock; 71893418Sbrian if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) { 71993418Sbrian /* 72093418Sbrian * It's a netgraph node... We can't determine hook names etc, so we 72193418Sbrian * stay pretty impartial.... 72293418Sbrian */ 72393418Sbrian log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name); 72493418Sbrian 72593418Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 72693418Sbrian log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n", 72793418Sbrian p->link.name, strerror(errno)); 72893418Sbrian return NULL; 72993418Sbrian } 73093418Sbrian 73193418Sbrian memcpy(&dev->dev, &basengdevice, sizeof dev->dev); 73293418Sbrian dev->cs = -1; 73393418Sbrian *dev->hook = '\0'; 73493418Sbrian } 73593418Sbrian } 73693418Sbrian 73793418Sbrian if (dev) { 73893418Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF); 73993418Sbrian return &dev->dev; 74093418Sbrian } 74193418Sbrian 74293418Sbrian return NULL; 74393418Sbrian} 744