Beispiel #1
0
static int
StatusCmd(int ac, char **av)
{
	u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE];
	struct ng_mesg *const resp = (struct ng_mesg *) sbuf;
	char *const status = (char *) resp->data;
	char *path;
	int nostat = 0;

	/* Get arguments */
	switch (ac) {
	case 2:
		path = av[1];
		break;
	default:
		return (CMDRTN_USAGE);
	}

	/* Get node status summary */
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	    NGM_TEXT_STATUS, NULL, 0) < 0) {
		switch (errno) {
		case EINVAL:
			nostat = 1;
			break;
		default:
			warn("send msg");
			return (CMDRTN_ERROR);
		}
	} else {
		if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0
		    || (resp->header.flags & NGF_RESP) == 0)
			nostat = 1;
	}

	/* Show it */
	if (nostat)
		printf("No status available for \"%s\"\n", path);
	else
		printf("Status for \"%s\":\n%s\n", path, status);
	return (CMDRTN_OK);
}
Beispiel #2
0
static void
do_show(int version, void (*func)(struct ngnf_show_header *))
{
	struct ng_mesg *ng_mesg;
	struct ngnf_show_header req, *resp;
	int token, nread;

	ng_mesg = alloca(SORCVBUF_SIZE);

	req.version = version;
	req.hash_id = req.list_id = 0;

	for (;;) {
		/* request set of accounting records */
		token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE,
		    NGM_NETFLOW_SHOW, (void *)&req, sizeof(req));
		if (token == -1)
			err(1, "NgSendMsg(NGM_NETFLOW_SHOW)");

		/* read reply */
		nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL);
		if (nread == -1)
			err(1, "NgRecvMsg() failed");

		if (ng_mesg->header.token != token)
			err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch");

		resp = (struct ngnf_show_header *)ng_mesg->data;
		if ((ng_mesg->header.arglen < (sizeof(*resp))) ||
		    (ng_mesg->header.arglen < (sizeof(*resp) +
		    (resp->nentries * sizeof(struct flow_entry_data)))))
			err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small");

		(*func)(resp);

		if (resp->hash_id != 0)
			req.hash_id = resp->hash_id;
		else
			break;
		req.list_id = resp->list_id;
	}
}
Beispiel #3
0
/*
 * Receive a control message and convert the arguments to ASCII
 */
int
NgRecvAsciiMsg(int cs, struct ng_mesg *reply, size_t replen, char *path)
{
	struct ng_mesg *msg, *ascii;
	int bufSize, errnosv;
	u_char *buf;

	/* Allocate buffer */
	bufSize = 2 * sizeof(*reply) + replen;
	if ((buf = malloc(bufSize)) == NULL)
		return (-1);
	msg = (struct ng_mesg *)buf;
	ascii = (struct ng_mesg *)msg->data;

	/* Get binary message */
	if (NgRecvMsg(cs, msg, bufSize, path) < 0)
		goto fail;
	memcpy(reply, msg, sizeof(*msg));

	/* Ask originating node to convert the arguments to ASCII */
	if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE,
	    NGM_BINARY2ASCII, msg, sizeof(*msg) + msg->header.arglen) < 0)
		goto fail;
	if (NgRecvMsg(cs, msg, bufSize, NULL) < 0)
		goto fail;

	/* Copy result to client buffer */
	if (sizeof(*ascii) + ascii->header.arglen > replen) {
		errno = ERANGE;
fail:
		errnosv = errno;
		free(buf);
		errno = errnosv;
		return (-1);
	}
	strncpy(reply->data, ascii->data, ascii->header.arglen);

	/* Done */
	free(buf);
	return (0);
}
Beispiel #4
0
/*
 * Read and display the next incoming control message
 */
void
MsgRead(void)
{
	struct ng_mesg *m, *m2;
	struct ng_mesg *ascii;
	char path[NG_PATHSIZ];

	/* Get incoming message (in binary form) */
	if (NgAllocRecvMsg(csock, &m, path) < 0) {
		warn("recv incoming message");
		return;
	}

	/* Ask originating node to convert message to ASCII */
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	      NGM_BINARY2ASCII, m, sizeof(*m) + m->header.arglen) < 0
	    || NgAllocRecvMsg(csock, &m2, NULL) < 0) {
		printf("Rec'd %s %d from \"%s\":\n",
		    (m->header.flags & NGF_RESP) != 0 ? "response" : "command",
		    m->header.cmd, path);
		if (m->header.arglen == 0)
			printf("No arguments\n");
		else
			DumpAscii((const u_char *)m->data, m->header.arglen);
		free(m);
		return;
	}

	/* Display message in ASCII form */
	free(m);
	ascii = (struct ng_mesg *)m2->data;
	printf("Rec'd %s \"%s\" (%d) from \"%s\":\n",
	    (ascii->header.flags & NGF_RESP) != 0 ? "response" : "command",
	    ascii->header.cmdstr, ascii->header.cmd, path);
	if (*ascii->data != '\0')
		printf("Args:\t%s\n", ascii->data);
	else
		printf("No arguments\n");
	free(m2);
}
Beispiel #5
0
static int
ShutdownCmd(int ac, char **av)
{
	char *path;

	/* Get arguments */
	switch (ac) {
	case 2:
		path = av[1];
		break;
	default:
		return (CMDRTN_USAGE);
	}

	/* Shutdown node */
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	    NGM_SHUTDOWN, NULL, 0) < 0) {
		warn("shutdown");
		return (CMDRTN_ERROR);
	}
	return (CMDRTN_OK);
}
Beispiel #6
0
/*
 * Assign a globally unique name to a node
 * Returns -1 if error and sets errno.
 */
int
NgNameNode(int cs, const char *path, const char *fmt, ...)
{
	struct ngm_name ngn;
	va_list args;

	/* Build message arg */
	va_start(args, fmt);
	vsnprintf(ngn.name, sizeof(ngn.name), fmt, args);
	va_end(args);

	/* Send message */
	if (NgSendMsg(cs, path,
	    NGM_GENERIC_COOKIE, NGM_NAME, &ngn, sizeof(ngn)) < 0) {
		if (_gNgDebugLevel >= 1)
			NGLOGX("%s: failed", __func__);
		return (-1);
	}

	/* Done */
	return (0);
}
Beispiel #7
0
int SetKsocketKeepAlive (char path[NG_PATHSIZ]) {
	union {
		u_char buf[sizeof(struct ng_ksocket_sockopt) + sizeof(int)];
		struct ng_ksocket_sockopt sockopt;
	} sockopt_buf;
	struct ng_ksocket_sockopt * const sockopt = &sockopt_buf.sockopt;
	int one = 1;

	// setsockopt resolve TIME_WAIT problem
	// setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,&one,sizeof(int)) < 0)
	memset(&sockopt_buf, 0, sizeof(sockopt_buf));

	sockopt->level = SOL_SOCKET;
	sockopt->name = SO_KEEPALIVE;
	memcpy(sockopt->value, &one, sizeof(int));
	if (NgSendMsg(csock, path, NGM_KSOCKET_COOKIE, NGM_KSOCKET_SETOPT,
			sockopt, sizeof(sockopt_buf)) == -1) {
		fprintf(stderr, "%s(): Sockopt set failed : %s\n", __FUNCTION__,
				strerror(errno));
		return -1;
	}
	return 1;
}
Beispiel #8
0
int GetKsocketReusePort (char path[NG_PATHSIZ]) {
    struct ng_ksocket_sockopt *sockopt_resp = malloc(sizeof(struct ng_ksocket_sockopt) + sizeof(int)); 
    struct ng_mesg *resp;
    memset(sockopt_resp, 0, (sizeof(struct ng_ksocket_sockopt) + sizeof(int)));
    sockopt_resp->level = SOL_SOCKET;
    sockopt_resp->name = SO_REUSEPORT;
    if ( NgSendMsg(csock, path, NGM_KSOCKET_COOKIE, NGM_KSOCKET_GETOPT, 
                            sockopt_resp, sizeof(*sockopt_resp)) == -1 ) {
        printf("Error while trying to get sockopt from %s - %s\n", 
                        path, strerror(errno));
        return 1;
    }
    if ( NgAllocRecvMsg(csock, &resp, 0 ) < 0 ) {
        fprintf(stderr, "Error while trying to get message from getsockopt: %s\n", strerror(errno));
        return 1;
    }
    
    int *option = (int *)(((struct ng_ksocket_sockopt *)resp->data)->value); 
    printf("REUSEPORT = %d\n", htons(*option));
    free(sockopt_resp);
    free(resp);
    return 1;

}
Beispiel #9
0
static int
NameCmd(int ac, char **av)
{
	struct ngm_name name;
	char *path;

	/* Get arguments */
	switch (ac) {
	case 3:
		path = av[1];
		snprintf(name.name, sizeof(name.name), "%s", av[2]);
		break;
	default:
		return (CMDRTN_USAGE);
	}

	/* Send message */
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	    NGM_NAME, &name, sizeof(name)) < 0) {
		warn("send msg");
		return (CMDRTN_ERROR);
	}
	return (CMDRTN_OK);
}
Beispiel #10
0
void
netgraphprotopr(u_long off, const char *name, int af1 __unused,
    int proto __unused)
{
	struct ngpcb *this, *next;
	struct ngpcb ngpcb;
	struct socket sockb;
	int debug = 1;

	/* If symbol not found, try looking in the KLD module */
	if (off == 0) {
		if (debug)
			fprintf(stderr,
			    "Error reading symbols from ng_socket.ko");
		return;
	}

	/* Get pointer to first socket */
	kread(off, (char *)&this, sizeof(this));

	/* Get my own socket node */
	if (csock == -1)
		NgMkSockNode(NULL, &csock, NULL);

	for (; this != NULL; this = next) {
		u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
		struct ng_mesg *resp = (struct ng_mesg *) rbuf;
		struct nodeinfo *ni = (struct nodeinfo *) resp->data;
		char path[64];

		/* Read in ngpcb structure */
		kread((u_long)this, (char *)&ngpcb, sizeof(ngpcb));
		next = LIST_NEXT(&ngpcb, socks);

		/* Read in socket structure */
		kread((u_long)ngpcb.ng_socket, (char *)&sockb, sizeof(sockb));

		/* Check type of socket */
		if (strcmp(name, "ctrl") == 0 && ngpcb.type != NG_CONTROL)
			continue;
		if (strcmp(name, "data") == 0 && ngpcb.type != NG_DATA)
			continue;

		/* Do headline */
		if (first) {
			printf("Netgraph sockets\n");
			if (Aflag)
				printf("%-8.8s ", "PCB");
			printf("%-5.5s %-6.6s %-6.6s %-14.14s %s\n",
			    "Type", "Recv-Q", "Send-Q",
			    "Node Address", "#Hooks");
			first = 0;
		}

		/* Show socket */
		if (Aflag)
			printf("%8lx ", (u_long) this);
		printf("%-5.5s %6u %6u ",
		    name, sockb.so_rcv.sb_cc, sockb.so_snd.sb_cc);

		/* Get info on associated node */
		if (ngpcb.node_id == 0 || csock == -1)
			goto finish;
		snprintf(path, sizeof(path), "[%x]:", ngpcb.node_id);
		if (NgSendMsg(csock, path,
		    NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0)
			goto finish;
		if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0)
			goto finish;

		/* Display associated node info */
		if (*ni->name != '\0')
			snprintf(path, sizeof(path), "%s:", ni->name);
		printf("%-14.14s %4d", path, ni->hooks);
finish:
		putchar('\n');
	}
}
Beispiel #11
0
static int
ConfigureNode(const char *prog, const char *iface, const char *provider,
              int cs, int ds, int debug, struct ngm_connect *ngc)
{
  /*
   * We're going to do this with the passed `ds' & `cs' descriptors:
   *
   * .---------.
   * |  ether  |
   * | <iface> |
   * `---------'
   *  (orphan)                                     ds    cs
   *     |                                         |     |
   *     |                                         |     |
   * (ethernet)                                    |     |
   * .---------.                                .-----------.
   * |  pppoe  |                                |  socket   |
   * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> |
   * `---------                                 `-----------'
   * (exec-<pid>)
   *     ^                .-----------.      .-------------.
   *     |                |   socket  |      | ppp -direct |
   *     `--->(exec-<pid>)| <unnamed> |--fd--|  provider   |
   *                      `-----------'      `-------------'
   *
   * where there are potentially many ppp processes running off of the
   * same PPPoE node.
   * The exec-<pid> hook isn't made 'till we Spawn().
   */

  char *epath, *spath;
  struct ngpppoe_init_data *data;
  const struct hooklist *hlist;
  const struct nodeinfo *ninfo;
  const struct linkinfo *nlink;
  struct ngm_mkpeer mkp;
  struct ng_mesg *resp;
  u_char rbuf[2048];
  int f, plen;

  /*
   * Ask for a list of hooks attached to the "ether" node.  This node should
   * magically exist as a way of hooking stuff onto an ethernet device
   */
  epath = (char *)alloca(strlen(iface) + 2);
  sprintf(epath, "%s:", iface);

  if (debug)
    fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath);

  if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) {
    if (errno == ENOENT)
      fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n",
              epath);
    else
      fprintf(stderr, "%s Cannot send a netgraph message: %s\n",
              epath, strerror(errno));
    return EX_UNAVAILABLE;
  }

  /* Get our list back */
  resp = (struct ng_mesg *)rbuf;
  if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) {
    perror("Cannot get netgraph response");
    return EX_UNAVAILABLE;
  }

  hlist = (const struct hooklist *)resp->data;
  ninfo = &hlist->nodeinfo;

  if (debug)
    fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n",
            ninfo->id, ninfo->type, ninfo->hooks);

  /* Make sure we've got the right type of node */
  if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) {
    fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``"
            NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type);
    return EX_DATAERR;
  }

  /* look for a hook already attached.  */
  for (f = 0; f < ninfo->hooks; f++) {
    nlink = &hlist->link[f];

    if (debug)
      fprintf(stderr, "  Got [%x]:%s -> [%x]:%s\n", ninfo->id,
              nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook);

    if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
        !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
      /*
       * Something is using the data coming out of this `ether' node.
       * If it's a PPPoE node, we use that node, otherwise we complain that
       * someone else is using the node.
       */
      if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) {
        fprintf(stderr, "%s Node type %s is currently active\n",
                epath, nlink->nodeinfo.type);
        return EX_UNAVAILABLE;
      }
      break;
    }
  }

  if (f == ninfo->hooks) {
    /*
     * Create a new PPPoE node connected to the `ether' node using
     * the magic `orphan' and `ethernet' hooks
     */
    snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
    snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
    snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);

    if (debug)
      fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath,
              mkp.ourhook, mkp.type, mkp.peerhook);

    if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE,
                  NGM_MKPEER, &mkp, sizeof mkp) < 0) {
      fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n",
              epath, strerror(errno));
      return EX_OSERR;
    }
  }

  /* Connect the PPPoE node to our socket node.  */
  snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN);
  snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid());
  memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook);

  if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
                NGM_CONNECT, ngc, sizeof *ngc) < 0) {
    perror("Cannot CONNECT PPPoE and socket nodes");
    return EX_OSERR;
  }

  plen = strlen(provider);

  data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen);
  snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook);
  memcpy(data->data, provider, plen);
  data->data_len = plen;

  spath = (char *)alloca(strlen(ngc->peerhook) + 3);
  strcpy(spath, ".:");
  strcpy(spath + 2, ngc->ourhook);

  if (debug) {
    if (provider)
      fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n",
              spath, provider);
    else
      fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath);
  }

  if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
                data, sizeof *data + plen) == -1) {
    fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n",
            spath, strerror(errno));
    return EX_OSERR;
  }

  return 0;
}
Beispiel #12
0
static void
Spawn(const char *prog, const char *acname, const char *provider,
      const char *exec, struct ngm_connect ngc, int cs, int ds, void *request,
      int sz, int debug)
{
  char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
  struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
  struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
  struct ngpppoe_init_data *data;
  char env[18], unknown[14], sessionid[5], *path;
  unsigned char *macaddr;
  const char *msg;
  int ret, slen;

  switch ((ret = fork())) {
    case -1:
      syslog(LOG_ERR, "fork: %m");
      break;

    case 0:
      switch (fork()) {
        case 0:
          break;
        case -1:
          _exit(errno);
        default:
          _exit(0);
      }
      close(cs);
      close(ds);

      /* Create a new socket node */
      if (debug)
        syslog(LOG_INFO, "Creating a new socket node");

      if (NgMkSockNode(NULL, &cs, &ds) == -1) {
        syslog(LOG_ERR, "Cannot create netgraph socket node: %m");
        _exit(EX_CANTCREAT);
      }

      /* Connect the PPPoE node to our new socket node.  */
      snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid());
      memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);

      if (debug)
        syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s",
               ngc.ourhook, ngc.path, ngc.peerhook);
      if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
                    NGM_CONNECT, &ngc, sizeof ngc) < 0) {
        syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m");
        _exit(EX_OSERR);
      }

      /*
       * If we tell the socket node not to LINGER, it will go away when
       * the last hook is removed.
       */
      if (debug)
        syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket");
      if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE,
                    NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) {
        syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m");
        _exit(EX_OSERR);
      }

      /* Put the PPPoE node into OFFER mode */
      slen = strlen(acname);
      data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
      snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
      memcpy(data->data, acname, slen);
      data->data_len = slen;

      path = (char *)alloca(strlen(ngc.ourhook) + 3);
      strcpy(path, ".:");
      strcpy(path + 2, ngc.ourhook);

      syslog(LOG_INFO, "Offering to %s as access concentrator %s",
             path, acname);
      if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
                    data, sizeof *data + slen) == -1) {
        syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path);
        _exit(EX_OSERR);
      }
      /* If we have a provider code, set it */
      if (provider) {
        slen = strlen(provider);
        data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
        snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
        memcpy(data->data, provider, slen);
        data->data_len = slen;

        syslog(LOG_INFO, "adding to %s as offered service %s",
             path, acname);
        if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE,
                    data, sizeof *data + slen) == -1) {
          syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path);
          _exit(EX_OSERR);
        }
      }

      /* Put the peer's MAC address in the environment */
      if (sz >= sizeof(struct ether_header)) {
        macaddr = ((struct ether_header *)request)->ether_shost;
        snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x",
                 macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4],
                 macaddr[5]);
        if (setenv(HISMACADDR, env, 1) != 0)
          syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR);
      }

      /* And send our request data to the waiting node */
      if (debug)
        syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz);
      if (NgSendData(ds, ngc.ourhook, request, sz) == -1) {
        syslog(LOG_ERR, "Cannot send original request to %s: %m", path);
        _exit(EX_OSERR);
      }

      /* Then wait for a success indication */

      if (debug)
        syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path);

      do {
        if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) {
          syslog(LOG_ERR, "%s: Cannot receive a message: %m", path);
          _exit(EX_OSERR);
        }

        if (ret == 0) {
          /* The socket has been closed */
          syslog(LOG_INFO, "%s: Client timed out", path);
          _exit(EX_TEMPFAIL);
        }

        if (rep->header.version != NG_VERSION) {
          syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld",
                 (long)rep->header.version, (long)NG_VERSION);
          _exit(EX_PROTOCOL);
        }

        if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
          syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld",
                 (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
          continue;
        }

        switch (rep->header.cmd) {
          case NGM_PPPOE_SET_FLAG:	msg = "SET_FLAG";	break;
          case NGM_PPPOE_CONNECT:	msg = "CONNECT";	break;
          case NGM_PPPOE_LISTEN:	msg = "LISTEN";		break;
          case NGM_PPPOE_OFFER:		msg = "OFFER";		break;
          case NGM_PPPOE_SUCCESS:	msg = "SUCCESS";	break;
          case NGM_PPPOE_FAIL:		msg = "FAIL";		break;
          case NGM_PPPOE_CLOSE:		msg = "CLOSE";		break;
          case NGM_PPPOE_GET_STATUS:	msg = "GET_STATUS";	break;
          case NGM_PPPOE_ACNAME:
            msg = "ACNAME";
            if (setenv("ACNAME", sts->hook, 1) != 0)
              syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m",
                     sts->hook);
            break;
          case NGM_PPPOE_SESSIONID:
            msg = "SESSIONID";
            snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts);
            if (setenv("SESSIONID", sessionid, 1) != 0)
              syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m",
                     sessionid);
            break;
          default:
            snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
            msg = unknown;
            break;
        }

        switch (rep->header.cmd) {
          case NGM_PPPOE_FAIL:
          case NGM_PPPOE_CLOSE:
            syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")",
                   msg, sts->hook);
            _exit(0);
        }

        syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook);
      } while (rep->header.cmd != NGM_PPPOE_SUCCESS);

      dup2(ds, STDIN_FILENO);
      dup2(ds, STDOUT_FILENO);
      close(ds);
      close(cs);

      setsid();
      syslog(LOG_INFO, "Executing: %s", exec);
      execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL);
      syslog(LOG_ERR, "execlp failed: %m");
      _exit(EX_OSFILE);

    default:
      wait(&ret);
      errno = ret;
      if (errno)
        syslog(LOG_ERR, "Second fork failed: %m");
      break;
  }
}
Beispiel #13
0
/*
 * Display a negraph message
 */
void
_NgDebugMsg(const struct ng_mesg *msg, const char *path)
{
	u_char buf[2 * sizeof(struct ng_mesg) + ARGS_BUFSIZE];
	struct ng_mesg *const req = (struct ng_mesg *)buf;
	struct ng_mesg *const bin = (struct ng_mesg *)req->data;
	int arglen, csock = -1;

	/* Display header stuff */
	NGLOGX("NG_MESG :");
	NGLOGX("  vers   %d", msg->header.version);
	NGLOGX("  arglen %d", msg->header.arglen);
	NGLOGX("  flags  %u", msg->header.flags);
	NGLOGX("  token  %u", msg->header.token);
	NGLOGX("  cookie %s (%d)",
	    NgCookie(msg->header.typecookie), msg->header.typecookie);

	/* At lower debugging levels, skip ASCII translation */
	if (_gNgDebugLevel <= 2)
		goto fail2;

	/* If path is not absolute, don't bother trying to use relative
	   address on a different socket for the ASCII translation */
	if (strchr(path, ':') == NULL)
		goto fail2;

	/* Get a temporary socket */
	if (NgMkSockNode(NULL, &csock, NULL) < 0)
		goto fail;

	/* Copy binary message into request message payload */
	arglen = msg->header.arglen;
	if (arglen > ARGS_BUFSIZE)
		arglen = ARGS_BUFSIZE;
	memcpy(bin, msg, sizeof(*msg) + arglen);
	bin->header.arglen = arglen;

	/* Lower debugging to avoid infinite recursion */
	_gNgDebugLevel -= RECURSIVE_DEBUG_ADJUST;

	/* Ask the node to translate the binary message to ASCII for us */
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	    NGM_BINARY2ASCII, bin, sizeof(*bin) + bin->header.arglen) < 0) {
		_gNgDebugLevel += RECURSIVE_DEBUG_ADJUST;
		goto fail;
	}
	if (NgRecvMsg(csock, req, sizeof(buf), NULL) < 0) {
		_gNgDebugLevel += RECURSIVE_DEBUG_ADJUST;
		goto fail;
	}

	/* Restore debugging level */
	_gNgDebugLevel += RECURSIVE_DEBUG_ADJUST;

	/* Display command string and arguments */
	NGLOGX("  cmd    %s (%d)", bin->header.cmdstr, bin->header.cmd);
	NGLOGX("  args   %s", bin->data);
	goto done;

fail:
	/* Just display binary version */
	NGLOGX("  [error decoding message: %s]", strerror(errno));
fail2:
	NGLOGX("  cmd    %d", msg->header.cmd);
	NGLOGX("  args (%d bytes)", msg->header.arglen);
	_NgDebugBytes(msg->data, msg->header.arglen);

done:
	if (csock != -1)
		(void)close(csock);
}
Beispiel #14
0
/*
 * Send a message given in ASCII format. We first ask the node to translate
 * the command into binary, and then we send the binary.
 */
int
NgSendAsciiMsg(int cs, const char *path, const char *fmt, ...)
{
	struct ng_mesg *reply, *binary, *ascii;
	char *buf, *cmd, *args;
	va_list fmtargs;
	int token;

	/* Parse out command and arguments */
	va_start(fmtargs, fmt);
	vasprintf(&buf, fmt, fmtargs);
	va_end(fmtargs);
	if (buf == NULL)
		return (-1);

	/* Parse out command, arguments */
	for (cmd = buf; isspace(*cmd); cmd++)
		;
	for (args = cmd; *args != '\0' && !isspace(*args); args++)
		;
	if (*args != '\0') {
		while (isspace(*args))
			*args++ = '\0';
	}

	/* Get a bigger buffer to hold inner message header plus arg string */
	if ((ascii = malloc(sizeof(struct ng_mesg)
	    + strlen(args) + 1)) == NULL) {
		free(buf);
		return (-1);
	}
	memset(ascii, 0, sizeof(*ascii));

	/* Build inner header (only need cmdstr, arglen, and data fields) */
	strncpy((char *)ascii->header.cmdstr, cmd,
	    sizeof(ascii->header.cmdstr) - 1);
	strcpy(ascii->data, args);
	ascii->header.arglen = strlen(ascii->data) + 1;
	free(buf);

	/* Send node a request to convert ASCII to binary */
	if (NgSendMsg(cs, path, NGM_GENERIC_COOKIE, NGM_ASCII2BINARY,
	    (u_char *)ascii, sizeof(*ascii) + ascii->header.arglen) < 0) {
		free(ascii);
		return (-1);
	}
	free(ascii);

	/* Get reply */
	if (NgAllocRecvMsg(cs, &reply, NULL) < 0)
		return (-1);

	/* Now send binary version */
	binary = (struct ng_mesg *)reply->data;
	if (++gMsgId < 0)
		gMsgId = 1;
	binary->header.token = gMsgId;
	binary->header.version = NG_VERSION;
	if (NgDeliverMsg(cs,
	    path, binary, binary->data, binary->header.arglen) < 0) {
		free(reply);
		return (-1);
	}
	token = binary->header.token;
	free(reply);
	return (token);
}
Beispiel #15
0
static void
CcpLayerUp(Fsm fp)
{
    Bund 	b = (Bund)fp->arg;
  CcpState	const ccp = &b->ccp;
  struct ngm_connect    cn;
  char		buf[64];

  /* If nothing was negotiated in either direction, close CCP */
  if ((!ccp->recv || !(*ccp->recv->Negotiated)(b, COMP_DIR_RECV))
      && (!ccp->xmit || !(*ccp->xmit->Negotiated)(b, COMP_DIR_XMIT))) {
    Log(LG_CCP, ("[%s] %s: No compression negotiated", Pref(fp), Fsm(fp)));
    FsmFailure(fp, FAIL_NEGOT_FAILURE);
    return;
  }

  /* Check for required encryption */
  if (CcpCheckEncryption(b) < 0) {
    return;
  }

  /* Initialize each direction */
  if (ccp->xmit != NULL && ccp->xmit->Init != NULL
      && (*ccp->xmit->Init)(b, COMP_DIR_XMIT) < 0) {
    Log(LG_CCP, ("[%s] %s: compression init failed", Pref(fp), Fsm(fp)));
    FsmFailure(fp, FAIL_NEGOT_FAILURE);		/* XXX */
    return;
  }
  if (ccp->recv != NULL && ccp->recv->Init != NULL
      && (*ccp->recv->Init)(b, COMP_DIR_RECV) < 0) {
    Log(LG_CCP, ("[%s] %s: decompression init failed", Pref(fp), Fsm(fp)));
    FsmFailure(fp, FAIL_NEGOT_FAILURE);		/* XXX */
    return;
  }

  if (ccp->xmit != NULL && ccp->xmit->Compress != NULL) {
    /* Connect a hook from the ppp node to our socket node */
    snprintf(cn.path, sizeof(cn.path), "[%x]:", b->nodeID);
    snprintf(cn.ourhook, sizeof(cn.ourhook), "c%d", b->id);
    strcpy(cn.peerhook, NG_PPP_HOOK_COMPRESS);
    if (NgSendMsg(gCcpCsock, ".:",
	    NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
	Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
    	    b->name, ".:", cn.ourhook, cn.path, cn.peerhook);
    }
  }

  if (ccp->recv != NULL && ccp->recv->Decompress != NULL) {
    /* Connect a hook from the ppp node to our socket node */
    snprintf(cn.path, sizeof(cn.path), "[%x]:", b->nodeID);
    snprintf(cn.ourhook, sizeof(cn.ourhook), "d%d", b->id);
    strcpy(cn.peerhook, NG_PPP_HOOK_DECOMPRESS);
    if (NgSendMsg(gCcpCsock, ".:",
	    NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
	Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
    	    b->name, ".:", cn.ourhook, cn.path, cn.peerhook);
    }
  }

  /* Report what we're doing */
  Log(LG_CCP, ("[%s] CCP: Compress using: %s (%s)", b->name, !ccp->xmit ? "none" : ccp->xmit->name,
    (ccp->xmit && ccp->xmit->Describe) ? (*ccp->xmit->Describe)(b, COMP_DIR_XMIT, buf, sizeof(buf)) : ""));
  Log(LG_CCP, ("[%s] CCP: Decompress using: %s (%s)", b->name, !ccp->recv ? "none" : ccp->recv->name,
    (ccp->recv && ccp->recv->Describe) ? (*ccp->recv->Describe)(b, COMP_DIR_RECV, buf, sizeof(buf)) : ""));

  /* Update PPP node config */
  b->pppConfig.bund.enableCompression = (ccp->xmit != NULL)?ccp->xmit->mode:0;
  b->pppConfig.bund.enableDecompression = (ccp->recv != NULL)?ccp->recv->mode:0;
  NgFuncSetConfig(b);

  /* Update interface MTU */
  BundUpdateParams(b);
}
Beispiel #16
0
static int
ip_account_show(int v, int outgoing)
{
	struct ng_ipacct_ainfo ci;
	struct ng_ipacct_hinfo hi;
	int token, nread;
	struct ng_mesg *ng_mesg;
	struct ng_ipacct_mesg *ng_cmesg;
	struct ip_acct_chunk *data;
	char path[NG_PATHLEN + 1];

	ng_cmesg = alloca(sizeof(*ng_cmesg));
	snprintf(ng_cmesg->hname, sizeof(ng_cmesg->hname), 
	    "%s_%s", ng_hookprefix, outgoing ? "out" : "in");

	ng_mesg = alloca(REPL_SIZE);

	if (ip_account_get_info(NGM_IPACCT_CINFO, &ci, sizeof(ci), outgoing)) {
		return (-1);
	}
	if (ip_account_get_info(NGM_IPACCT_HINFO, &hi, sizeof(hi), outgoing)) {
		return (-1);
	}
	for (;;) {
		/* request set of accounting records */
		token = NgSendMsg(ng_cs, ng_nodename, NGM_IPACCT_COOKIE, 
		    NGM_IPACCT_SHOW, ng_cmesg, sizeof(*ng_cmesg));
		if (token == -1) {
			warn("NgSendMsg(NGM_IPACCT_SHOW)");
			return (-1);
		}

        /* read reply */
        if (ng_ready_for_read() != 1)
            return (-1);
        nread = NgRecvMsg(ng_cs, ng_mesg, REPL_SIZE, path);
        if (nread == -1) {
            warn("NgRecvMsg(NG_IPACCT_SHOW)");
            return (-1);
        }

		if (ng_mesg->header.token != token) {
			warnx("NgRecvMsg(NGM_IPACCT_SHOW): token mismatch");
			return (-1);
		}

		data = (struct ip_acct_chunk*)ng_mesg->data;
		if (ng_mesg->header.arglen != sizeof(*data)) {
			warnx("NgRecvMsg(NGM_IPACCT_SHOW): arglen too small");
			return (-1);
		}

		if (data->nrecs == 0)
			break; /* no more data available */
		ip_account_print(data, hi.hi_flags);
	}
	if (ci.ai_th_packets)
		printf(" Accounting exceed threshold by %u packets (%" PRIu64 " bytes)\n",
		    ci.ai_th_packets, ci.ai_th_bytes);
	
	return (0);
}
Beispiel #17
0
static void
IpcpLayerUp(Fsm fp)
{
    Bund 			b = (Bund)fp->arg;
    IpcpState			const ipcp = &b->ipcp;
    char			ipbuf[20];
#ifdef USE_NG_VJC
    char			path[NG_PATHSIZ];
    struct ngm_vjc_config	vjc;
#endif
    struct u_addr		tmp;

    /* Determine actual address we'll use for ourselves */
    in_addrtou_addr(&ipcp->want_addr, &tmp);
    if (!IpAddrInRange(&ipcp->self_allow, &tmp)) {
	Log(LG_IPCP, ("[%s]   Note: ignoring negotiated %s IP %s,",
    	    b->name, "self", inet_ntoa(ipcp->want_addr)));
	u_addrtoin_addr(&ipcp->self_allow.addr, &ipcp->want_addr);
	Log(LG_IPCP, ("[%s]        using %s instead.",
    	    b->name, inet_ntoa(ipcp->want_addr)));
    }

    /* Determine actual address we'll use for peer */
    in_addrtou_addr(&ipcp->peer_addr, &tmp);
    if (!IpAddrInRange(&ipcp->peer_allow, &tmp)
    	    && !u_addrempty(&ipcp->peer_allow.addr)) {
	Log(LG_IPCP, ("[%s]   Note: ignoring negotiated %s IP %s,",
    	    b->name, "peer", inet_ntoa(ipcp->peer_addr)));
	u_addrtoin_addr(&ipcp->peer_allow.addr, &ipcp->peer_addr);
	Log(LG_IPCP, ("[%s]        using %s instead.",
    	    b->name, inet_ntoa(ipcp->peer_addr)));
    }

    /* Report */
    strlcpy(ipbuf, inet_ntoa(ipcp->peer_addr), sizeof(ipbuf));
    Log(LG_IPCP, ("[%s]   %s -> %s", b->name, inet_ntoa(ipcp->want_addr), ipbuf));

#ifdef USE_NG_VJC
    memset(&vjc, 0, sizeof(vjc));
    if (ntohs(ipcp->peer_comp.proto) == PROTO_VJCOMP || 
	    ntohs(ipcp->want_comp.proto) == PROTO_VJCOMP) {
  
	IpcpNgInitVJ(b);

	/* Configure VJ compression node */
	vjc.enableComp = ntohs(ipcp->peer_comp.proto) == PROTO_VJCOMP;
	vjc.enableDecomp = ntohs(ipcp->want_comp.proto) == PROTO_VJCOMP;
	vjc.maxChannel = ipcp->peer_comp.maxchan;
	vjc.compressCID = ipcp->peer_comp.compcid;
        snprintf(path, sizeof(path), "[%x]:%s", b->nodeID, NG_PPP_HOOK_VJC_IP);
	if (NgSendMsg(gLinksCsock, path,
    		NGM_VJC_COOKIE, NGM_VJC_SET_CONFIG, &vjc, sizeof(vjc)) < 0) {
	    Perror("[%s] can't config %s node", b->name, NG_VJC_NODE_TYPE);
	}
    }
#endif /* USE_NG_VJC */

    /* Enable IP packets in the PPP node */
    b->pppConfig.bund.enableIP = 1;
#ifdef USE_NG_VJC
    b->pppConfig.bund.enableVJCompression = vjc.enableComp;
    b->pppConfig.bund.enableVJDecompression = vjc.enableDecomp;
#endif
    NgFuncSetConfig(b);

    BundNcpsJoin(b, NCP_IPCP);
}
Beispiel #18
0
/*
 * main()
 */
int
main(int ac, char *av[])
{
	struct ngm_connect ngc;
	const char *path = NULL;
	const char *hook = DEFAULT_HOOKNAME;
	int     csock, dsock;
	int     asciiFlag = 0;
	int     loopFlag = 0;
	int	noInput = 0;
	int	execFlag = 0;
	int	ch;

	if ((msgs = sl_init()) == NULL)
		err(EX_OSERR, NULL);

	/* Parse flags */
	while ((ch = getopt(ac, av, "aedlm:nsS")) != -1) {
		switch (ch) {
		case 'a':
			asciiFlag = 1;
			break;
		case 'd':
			NgSetDebug(NgSetDebug(-1) + 1);
			break;
		case 'e':
			execFlag = 1;
			break;
		case 'l':
			loopFlag = 1;
			break;
		case 'n':
			noInput = 1;
			break;
		case 'm':
			if (sl_add(msgs, optarg) == -1)
				err(EX_OSERR, NULL);
			break;
		case 's':
			outfd = STDIN_FILENO;
			break;
		case 'S':
			infd = STDOUT_FILENO;
			break;
		case '?':
		default:
			Usage();
		}
	}
	ac -= optind;
	av += optind;

	if (execFlag) {
		if (asciiFlag || loopFlag) {
			fprintf(stderr, "conflicting options\n");
			Usage();
		}
		if (ac < 3)
			Usage();
		path = av[0];
		hook = av[1];
		av += 2;
		ac -= 2;
	} else {
		/* Get params */
		switch (ac) {
		case 2:
			hook = av[1];
			/* FALLTHROUGH */
		case 1:
			path = av[0];
			break;
		default:
			Usage();
		}
	}

	/* Get sockets */
	if (NgMkSockNode(NULL, &csock, &dsock) < 0)
		errx(EX_OSERR, "can't get sockets");

	/* Connect socket node to specified node */
	snprintf(ngc.path, sizeof(ngc.path), "%s", path);
	snprintf(ngc.ourhook, sizeof(ngc.ourhook), NG_SOCK_HOOK_NAME);
	snprintf(ngc.peerhook, sizeof(ngc.peerhook), "%s", hook);

	if (NgSendMsg(csock, ".",
	    NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof(ngc)) < 0)
		errx(EX_OSERR, "can't connect to node");

	if (execFlag) {
		/* move dsock to fd 0 and 1 */
		(void)close(0);
		(void)close(1);
		if (!noInput)
			(void)dup2(dsock, 0);
		(void)dup2(dsock, 1);

		send_msgs(csock, path);

		/* try executing the program */
		(void)execv(av[0], av);
		err(EX_OSERR, "%s", av[0]);

	} else
		send_msgs(csock, path);

	/* Close standard input if not reading from it */
	if (noInput)
		fclose(stdin);

	/* Relay data */
	while (1) {
		fd_set  rfds;

		/* Setup bits */
		FD_ZERO(&rfds);
		if (!noInput)
			FD_SET(infd, &rfds);
		FD_SET(dsock, &rfds);

		/* Wait for something to happen */
		if (select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0)
			err(EX_OSERR, "select");

		/* Check data from socket */
		if (FD_ISSET(dsock, &rfds)) {
			char    buf[BUF_SIZE];
			int     rl, wl;

			/* Read packet from socket */
			if ((rl = NgRecvData(dsock,
			    buf, sizeof(buf), NULL)) < 0)
				err(EX_OSERR, "read(hook)");
			if (rl == 0)
				errx(EX_OSERR, "read EOF from hook?!");

			/* Write packet to stdout */
			if (asciiFlag)
				WriteAscii((u_char *) buf, rl);
			else if ((wl = write(outfd, buf, rl)) != rl) {
				if (wl < 0) {
					err(EX_OSERR, "write(stdout)");
				} else {
					errx(EX_OSERR,
					    "stdout: read %d, wrote %d",
					    rl, wl);
				}
			}
			/* Loopback */
			if (loopFlag) {
				if (NgSendData(dsock, NG_SOCK_HOOK_NAME, buf, rl) < 0)
					err(EX_OSERR, "write(hook)");
			}
		}

		/* Check data from stdin */
		if (FD_ISSET(infd, &rfds)) {
			char    buf[BUF_SIZE];
			int     rl;

			/* Read packet from stdin */
			if ((rl = read(infd, buf, sizeof(buf))) < 0)
				err(EX_OSERR, "read(stdin)");
			if (rl == 0)
				errx(EX_OSERR, "EOF(stdin)");

			/* Write packet to socket */
			if (NgSendData(dsock, NG_SOCK_HOOK_NAME, buf, rl) < 0)
				err(EX_OSERR, "write(hook)");
		}
	}
}
Beispiel #19
0
static int
IpcpNgInitVJ(Bund b)
{
  struct ngm_mkpeer	mp;
  struct ngm_connect	cn;
  char path[NG_PATHSIZ];
  struct ngm_name	nm;

  /* Add a VJ compression node */
  snprintf(path, sizeof(path), "[%x]:", b->nodeID);
  strcpy(mp.type, NG_VJC_NODE_TYPE);
  strcpy(mp.ourhook, NG_PPP_HOOK_VJC_IP);
  strcpy(mp.peerhook, NG_VJC_HOOK_IP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
    Perror("[%s] can't create %s node at \"%s\"->\"%s\"",
      b->name, NG_VJC_NODE_TYPE, path, mp.ourhook);
    goto fail;
  }

  /* Give it a name */
  strlcat(path, NG_PPP_HOOK_VJC_IP, sizeof(path));
  snprintf(nm.name, sizeof(nm.name), "mpd%d-%s-vjc", gPid, b->name);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_NAME, &nm, sizeof(nm)) < 0) {
    Perror("[%s] can't name %s node", b->name, NG_VJC_NODE_TYPE);
    goto fail;
  }

  /* Connect the other three hooks between the ppp and vjc nodes */
  snprintf(path, sizeof(path), "[%x]:", b->nodeID);
  strcpy(cn.path, NG_PPP_HOOK_VJC_IP);
  strcpy(cn.ourhook, NG_PPP_HOOK_VJC_COMP);
  strcpy(cn.peerhook, NG_VJC_HOOK_VJCOMP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
    Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
      b->name, path, cn.ourhook, cn.path, cn.peerhook);
    goto fail;
  }
  strcpy(cn.ourhook, NG_PPP_HOOK_VJC_UNCOMP);
  strcpy(cn.peerhook, NG_VJC_HOOK_VJUNCOMP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
    Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
      b->name, path, cn.ourhook, cn.path, cn.peerhook);
    goto fail;
  }
  strcpy(cn.ourhook, NG_PPP_HOOK_VJC_VJIP);
  strcpy(cn.peerhook, NG_VJC_HOOK_VJIP);
  if (NgSendMsg(gLinksCsock, path,
      NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
    Perror("[%s] can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
      b->name, path, cn.ourhook, cn.path, cn.peerhook);
    goto fail;
  }

    return 0;
fail:
    return -1;
}
Beispiel #20
0
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;
}
Beispiel #21
0
/*
 * Create a socket type node and give it the supplied name.
 * Return data and control sockets corresponding to the node.
 * Returns -1 if error and sets errno.
 */
int
NgMkSockNode(const char *name, int *csp, int *dsp)
{
	char namebuf[NG_NODESIZ];
	int cs = -1;		/* control socket */
	int ds = -1;		/* data socket */
	int errnosv;

	/* Empty name means no name */
	if (name && *name == 0)
		name = NULL;

	/* Create control socket; this also creates the netgraph node.
	   If we get an EPROTONOSUPPORT then the socket node type is
	   not loaded, so load it and try again. */
	if ((cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL)) < 0) {
		if (errno == EPROTONOSUPPORT) {
			if (kldload(NG_SOCKET_KLD) < 0) {
				errnosv = errno;
				if (_gNgDebugLevel >= 1)
					NGLOG("can't load %s", NG_SOCKET_KLD);
				goto errout;
			}
			cs = socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL);
			if (cs >= 0)
				goto gotNode;
		}
		errnosv = errno;
		if (_gNgDebugLevel >= 1)
			NGLOG("socket");
		goto errout;
	}

gotNode:
	/* Assign the node the desired name, if any */
	if (name != NULL) {
		u_char sbuf[NG_NODESIZ + NGSA_OVERHEAD];
		struct sockaddr_ng *const sg = (struct sockaddr_ng *) sbuf;

		/* Assign name */
		strlcpy(sg->sg_data, name, NG_NODESIZ);
		sg->sg_family = AF_NETGRAPH;
		sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD;
		if (bind(cs, (struct sockaddr *) sg, sg->sg_len) < 0) {
			errnosv = errno;
			if (_gNgDebugLevel >= 1)
				NGLOG("bind(%s)", sg->sg_data);
			goto errout;
		}

		/* Save node name */
		strlcpy(namebuf, name, sizeof(namebuf));
	} else if (dsp != NULL) {
		u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
		struct ng_mesg *const resp = (struct ng_mesg *) rbuf;
		struct nodeinfo *const ni = (struct nodeinfo *) resp->data;

		/* Find out the node ID */
		if (NgSendMsg(cs, ".", NGM_GENERIC_COOKIE,
		    NGM_NODEINFO, NULL, 0) < 0) {
			errnosv = errno;
			if (_gNgDebugLevel >= 1)
				NGLOG("send nodeinfo");
			goto errout;
		}
		if (NgRecvMsg(cs, resp, sizeof(rbuf), NULL) < 0) {
			errnosv = errno;
			if (_gNgDebugLevel >= 1)
				NGLOG("recv nodeinfo");
			goto errout;
		}

		/* Save node "name" */
		snprintf(namebuf, sizeof(namebuf), "[%lx]", (u_long) ni->id);
	}

	/* Create data socket if desired */
	if (dsp != NULL) {
		u_char sbuf[NG_NODESIZ + 1 + NGSA_OVERHEAD];
		struct sockaddr_ng *const sg = (struct sockaddr_ng *) sbuf;

		/* Create data socket, initially just "floating" */
		if ((ds = socket(AF_NETGRAPH, SOCK_DGRAM, NG_DATA)) < 0) {
			errnosv = errno;
			if (_gNgDebugLevel >= 1)
				NGLOG("socket");
			goto errout;
		}

		/* Associate the data socket with the node */
		snprintf(sg->sg_data, NG_NODESIZ + 1, "%s:", namebuf);
		sg->sg_family = AF_NETGRAPH;
		sg->sg_len = strlen(sg->sg_data) + 1 + NGSA_OVERHEAD;
		if (connect(ds, (struct sockaddr *) sg, sg->sg_len) < 0) {
			errnosv = errno;
			if (_gNgDebugLevel >= 1)
				NGLOG("connect(%s)", sg->sg_data);
			goto errout;
		}
	}

	/* Return the socket(s) */
	if (csp)
		*csp = cs;
	else
		close(cs);
	if (dsp)
		*dsp = ds;
	return (0);

errout:
	/* Failed */
	if (cs >= 0)
		close(cs);
	if (ds >= 0)
		close(ds);
	errno = errnosv;
	return (-1);
}
Beispiel #22
0
static int
ShowCmd(int ac, char **av)
{
	char *path;
	struct ng_mesg *resp;
	struct hooklist *hlist;
	struct nodeinfo *ninfo;
	int ch, no_hooks = 0;

	/* Get options */
	optind = 1;
	while ((ch = getopt(ac, av, "n")) != -1) {
		switch (ch) {
		case 'n':
			no_hooks = 1;
			break;
		case '?':
		default:
			return (CMDRTN_USAGE);
			break;
		}
	}
	ac -= optind;
	av += optind;

	/* Get arguments */
	switch (ac) {
	case 1:
		path = av[0];
		break;
	default:
		return (CMDRTN_USAGE);
	}

	/* Get node info and hook list */
	if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
	    NGM_LISTHOOKS, NULL, 0) < 0) {
		warn("send msg");
		return (CMDRTN_ERROR);
	}
	if (NgAllocRecvMsg(csock, &resp, NULL) < 0) {
		warn("recv msg");
		return (CMDRTN_ERROR);
	}

	/* Show node information */
	hlist = (struct hooklist *) resp->data;
	ninfo = &hlist->nodeinfo;
	if (!*ninfo->name)
		snprintf(ninfo->name, sizeof(ninfo->name), "%s", UNNAMED);
	printf("  Name: %-15s Type: %-15s ID: %08x   Num hooks: %d\n",
	    ninfo->name, ninfo->type, ninfo->id, ninfo->hooks);
	if (!no_hooks && ninfo->hooks > 0) {
		u_int k;

		printf(FMT, "Local hook", "Peer name",
		    "Peer type", "Peer ID", "Peer hook");
		printf(FMT, "----------", "---------",
		    "---------", "-------", "---------");
		for (k = 0; k < ninfo->hooks; k++) {
			struct linkinfo *const link = &hlist->link[k];
			struct nodeinfo *const peer = &hlist->link[k].nodeinfo;
			char idbuf[20];

			if (!*peer->name) {
				snprintf(peer->name, sizeof(peer->name),
				  "%s", UNNAMED);
			}
			snprintf(idbuf, sizeof(idbuf), "%08x", peer->id);
			printf(FMT, link->ourhook, peer->name,
			    peer->type, idbuf, link->peerhook);
		}
	}
	free(resp);
	return (CMDRTN_OK);
}
Beispiel #23
0
static int
DotCmd(int ac, char **av)
{
	struct ng_mesg *nlresp;
	struct namelist *nlist;
	FILE *f = stdout;
	int ch;
	u_int i;

	/* Get options */
	optind = 1;
	while ((ch = getopt(ac, av, "")) != -1) {
		switch (ch) {
		case '?':
		default:
			return (CMDRTN_USAGE);
			break;
		}
	}
	ac -= optind;
	av += optind;

	/* Get arguments */
	switch (ac) {
	case 1:
		f = fopen(av[0], "w");
		if (f == NULL) {
			warn("Could not open %s for writing", av[0]);
			return (CMDRTN_ERROR);
		}
	case 0:
		break;
	default:
		if (f != stdout)
			(void)fclose(f);
		return (CMDRTN_USAGE);
	}

	/* Get list of nodes */
	if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL,
	    0) < 0) {
		warn("send listnodes msg");
		goto error;
	}
	if (NgAllocRecvMsg(csock, &nlresp, NULL) < 0) {
		warn("recv listnodes msg");
		goto error;
	}

	nlist = (struct namelist *)nlresp->data;
	fprintf(f, "graph netgraph {\n");
	/* TODO: implement rank = same or subgraphs at some point */
	fprintf(f, "\tedge [ weight = 1.0 ];\n");
	fprintf(f, "\tnode [ shape = record, fontsize = 12 ] {\n");
	for (i = 0; i < nlist->numnames; i++)
		fprintf(f, "\t\t\"%jx\" [ label = \"{%s:|{%s|[%jx]:}}\" ];\n",
		    (uintmax_t)nlist->nodeinfo[i].id,
		    nlist->nodeinfo[i].name[0] != '\0' ?
		    nlist->nodeinfo[i].name : UNNAMED,
		    nlist->nodeinfo[i].type, (uintmax_t)nlist->nodeinfo[i].id);
	fprintf(f, "\t};\n");

	fprintf(f, "\tsubgraph cluster_disconnected {\n");
	fprintf(f, "\t\tbgcolor = pink;\n");
	for (i = 0; i < nlist->numnames; i++)
		if (nlist->nodeinfo[i].hooks == 0)
			fprintf(f, "\t\t\"%jx\";\n",
			    (uintmax_t)nlist->nodeinfo[i].id);
	fprintf(f, "\t};\n");

	for (i = 0; i < nlist->numnames; i++) {
		struct ng_mesg *hlresp;
		struct hooklist *hlist;
		struct nodeinfo *ninfo;
		char path[NG_PATHSIZ];
		u_int j;

		(void)snprintf(path, sizeof(path), "[%jx]:",
		    (uintmax_t)nlist->nodeinfo[i].id);

		/* Get node info and hook list */
		if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
		    NULL, 0) < 0) {
			free(nlresp);
			warn("send listhooks msg");
			goto error;
		}
		if (NgAllocRecvMsg(csock, &hlresp, NULL) < 0) {
			free(nlresp);
			warn("recv listhooks msg");
			goto error;
		}

		hlist = (struct hooklist *)hlresp->data;
		ninfo = &hlist->nodeinfo;
		if (ninfo->hooks == 0) {
			free(hlresp);
			continue;
		}

		fprintf(f, "\tnode [ shape = octagon, fontsize = 10 ] {\n");
		for (j = 0; j < ninfo->hooks; j++)
			fprintf(f, "\t\t\"%jx.%s\" [ label = \"%s\" ];\n",
			    (uintmax_t)nlist->nodeinfo[i].id,
			    hlist->link[j].ourhook, hlist->link[j].ourhook);
		fprintf(f, "\t};\n");

		fprintf(f, "\t{\n\t\tedge [ weight = 2.0, style = bold ];\n");
		for (j = 0; j < ninfo->hooks; j++)
			fprintf(f, "\t\t\"%jx\" -- \"%jx.%s\";\n",
			    (uintmax_t)nlist->nodeinfo[i].id,
			    (uintmax_t)nlist->nodeinfo[i].id,
			    hlist->link[j].ourhook);
		fprintf(f, "\t};\n");

		for (j = 0; j < ninfo->hooks; j++) {
			/* Only print the edges going in one direction. */
			if (hlist->link[j].nodeinfo.id > nlist->nodeinfo[i].id)
				continue;
			fprintf(f, "\t\"%jx.%s\" -- \"%jx.%s\";\n",
			    (uintmax_t)nlist->nodeinfo[i].id,
			    hlist->link[j].ourhook,
			    (uintmax_t)hlist->link[j].nodeinfo.id,
			    hlist->link[j].peerhook);
		}
		free(hlresp);
	}

	fprintf(f, "};\n");

	free(nlresp);
	if (f != stdout)
		(void)fclose(f);
	return (CMDRTN_OK);
error:
	if (f != stdout)
		(void)fclose(f);
	return (CMDRTN_ERROR);
}
Beispiel #24
0
struct device *
ether_Create(struct physical *p)
{
  u_char rbuf[2048];
  struct etherdevice *dev;
  struct ng_mesg *resp;
  const struct hooklist *hlist;
  const struct nodeinfo *ninfo;
  char *path, *sessionid;
  const char *mode;
  size_t ifacelen;
  unsigned f;

  dev = NULL;
  path = NULL;
  ifacelen = 0;
  if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE,
                                PPPOE_NODE_TYPE_LEN) &&
      p->name.full[PPPOE_NODE_TYPE_LEN] == ':') {
    const struct linkinfo *nlink;
    struct ngpppoe_init_data *data;
    struct ngm_mkpeer mkp;
    struct ngm_connect ngc;
    const char *iface, *provider;
    char etherid[12];
    int providerlen;
    char connectpath[sizeof dev->hook + 2];	/* .:<hook> */

    p->fd--;				/* We own the device - change fd */

    loadmodules(LOAD_VERBOSLY, "netgraph", "ng_ether", "ng_pppoe", "ng_socket",
                NULL);

    if ((dev = malloc(sizeof *dev)) == NULL)
      return NULL;

    iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1;

    provider = strchr(iface, ':');
    if (provider) {
      ifacelen = provider - iface;
      provider++;
      providerlen = strlen(provider);
    } else {
      ifacelen = strlen(iface);
      provider = "";
      providerlen = 0;
    }

    /*
     * We're going to do this (where tunN is our tunnel device):
     *
     * .---------.
     * |  ether  |
     * | <iface> |                         dev->cs
     * `---------'                           |
     *  (orphan)                     p->fd   |
     *     |                           |     |
     *     |                           |     |
     * (ethernet)                      |     |
     * .---------.                  .-----------.
     * |  pppoe  |                  |  socket   |
     * | <iface> |(tunN)<---->(tunN)| <unnamed> |
     * `---------                   `-----------'
     *   (tunX)
     *     ^
     *     |
     *     `--->(tunX)
     */

    /* 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;
    }

    /*
     * Ask for a list of hooks attached to the "ether" node.  This node should
     * magically exist as a way of hooking stuff onto an ethernet device
     */
    path = (char *)alloca(ifacelen + 2);
    sprintf(path, "%.*s:", (int)ifacelen, iface);
    if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
                  NULL, 0) < 0) {
      log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n",
                 path, strerror(errno));
      return ether_Abandon(dev, p);
    }

    /* Get our list back */
    resp = (struct ng_mesg *)rbuf;
    if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
      log_Printf(LogWARN, "Cannot get netgraph response: %s\n",
                 strerror(errno));
      return ether_Abandon(dev, p);
    }

    hlist = (const struct hooklist *)resp->data;
    ninfo = &hlist->nodeinfo;

    /* Make sure we've got the right type of node */
    if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
                sizeof NG_ETHER_NODE_TYPE - 1)) {
      log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``"
                 NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type);
      return ether_Abandon(dev, p);
    }

    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\n", nlink->ourhook,
                 nlink->peerhook);

      if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
          !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
        /*
         * Something is using the data coming out of this ``ether'' node.
         * If it's a PPPoE node, we use that node, otherwise we complain that
         * someone else is using the node.
         */
        if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE))
          /* Use this PPPoE node ! */
          snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id);
        else {
          log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n",
                     path, nlink->nodeinfo.type);
          return ether_Abandon(dev, p);
        }
        break;
      }
    }

    if (f == ninfo->hooks) {
      /*
       * Create a new ``PPPoE'' node connected to the ``ether'' node using
       * the ``orphan'' and ``ethernet'' hooks
       */
      snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
      snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
      snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
      snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id);

      log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n",
                 etherid, mkp.ourhook, mkp.peerhook);

      if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE,
                    NGM_MKPEER, &mkp, sizeof mkp) < 0) {
        log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n",
                   etherid, strerror(errno));
        return ether_Abandon(dev, p);
      }

      snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN);
    }

    snprintf(dev->hook, sizeof dev->hook, "%s%d",
             TUN_NAME, p->dl->bundle->unit);

    /*
     * Connect the PPPoE node to our socket node.
     * ngc.path has already been set up
     */
    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 PPPoE and socket netgraph "
                 "nodes: %s\n", strerror(errno));
      return ether_Abandon(dev, p);
    }

    /* Bring the Ethernet interface up */
    path[ifacelen] = '\0';	/* Remove the trailing ':' */
    if (!iface_SetFlags(path, IFF_UP))
      log_Printf(LogWARN, "%s: Failed to set the IFF_UP flag on %s\n",
                 p->link.name, path);

    snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);

    /* Configure node to 3Com mode if needed */
    if (p->cfg.pppoe_configured) {
      mode = p->cfg.nonstandard_pppoe ? NG_PPPOE_NONSTANDARD : NG_PPPOE_STANDARD;
      if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
		NGM_PPPOE_SETMODE, mode, strlen(mode) + 1) == -1) {
        log_Printf(LogWARN, "``%s'': Cannot configure netgraph node: %s\n",
                 connectpath, strerror(errno));
        return ether_Abandon(dev, p);
      }
    }

    /* And finally, request a connection to the given provider */

    data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen);
    snprintf(data->hook, sizeof data->hook, "%s", dev->hook);
    memcpy(data->data, provider, providerlen);
    data->data_len = providerlen;

    log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath);
    if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
                  NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) {
      log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n",
                 connectpath, strerror(errno));
      return ether_Abandon(dev, p);
    }

    /* Hook things up so that we monitor dev->cs */
    p->desc.UpdateSet = ether_UpdateSet;
    p->desc.IsSet = ether_IsSet;
    p->desc.Read = ether_DescriptorRead;

    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
    switch (p->cfg.cd.necessity) {
      case CD_VARIABLE:
        dev->dev.cd.delay = p->cfg.cd.delay;
        break;
      case CD_REQUIRED:
        dev->dev.cd = p->cfg.cd;
        break;
      case CD_NOTREQUIRED:
        log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n",
                   p->link.name, dev->dev.cd.delay);
      case CD_DEFAULT:
        break;
    }

    dev->timeout = dev->dev.cd.delay;
    dev->connected = CARRIER_PENDING;
    /* This will be overridden by our session id - if provided by netgraph */
    dev->slot = GetIfIndex(path);
  } else {
    /* See if we're a netgraph socket */
    struct stat st;

    if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
      struct sockaddr_storage ssock;
      struct sockaddr *sock = (struct sockaddr *)&ssock;
      int sz;

      sz = sizeof ssock;
      if (getsockname(p->fd, sock, &sz) == -1) {
        log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
        close(p->fd);
        p->fd = -1;
        return NULL;
      }

      if (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, &baseetherdevice, sizeof dev->dev);
        dev->cs = -1;
        dev->timeout = 0;
        dev->connected = CARRIER_OK;
        *dev->hook = '\0';

        /*
         * If we're being envoked from pppoed(8), we may have a SESSIONID
         * set in the environment.  If so, use it as the slot
         */
        if ((sessionid = getenv("SESSIONID")) != NULL) {
          char *end;
          u_long slot;

          slot = strtoul(sessionid, &end, 16);
          dev->slot = end != sessionid && *end == '\0' ? slot : 0;
        } else
          dev->slot = 0;
      }
    }
  }

  if (dev) {
    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
    return &dev->dev;
  }

  return NULL;
}