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/10.3/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