ether.c revision 68032
16059Samurai/*-
26059Samurai * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
36059Samurai * All rights reserved.
46059Samurai *
56059Samurai * Redistribution and use in source and binary forms, with or without
66059Samurai * modification, are permitted provided that the following conditions
76059Samurai * are met:
86059Samurai * 1. Redistributions of source code must retain the above copyright
96059Samurai *    notice, this list of conditions and the following disclaimer.
106059Samurai * 2. Redistributions in binary form must reproduce the above copyright
116059Samurai *    notice, this list of conditions and the following disclaimer in the
126059Samurai *    documentation and/or other materials provided with the distribution.
136059Samurai *
146059Samurai * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
156059Samurai * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166059Samurai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
176059Samurai * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
186059Samurai * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
198857Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050479Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
218857Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
226059Samurai * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
236059Samurai * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443313Sbrian * SUCH DAMAGE.
2530715Sbrian *
2636285Sbrian * $FreeBSD: head/usr.sbin/ppp/ether.c 68032 2000-10-31 02:46:12Z brian $
2736285Sbrian */
2836285Sbrian
2930715Sbrian#include <sys/param.h>
3043888Sbrian#include <sys/socket.h>
3143888Sbrian#include <sys/un.h>
3237192Sbrian#include <netinet/in.h>
3336287Sbrian#include <arpa/inet.h>
3437192Sbrian#include <netdb.h>
3530715Sbrian#include <netgraph.h>
3643888Sbrian#include <net/ethernet.h>
3743888Sbrian#include <netinet/in_systm.h>
3849976Sbrian#include <netinet/ip.h>
3930715Sbrian#include <netgraph/ng_ether.h>
4044106Sbrian#include <netgraph/ng_message.h>
4143888Sbrian#include <netgraph/ng_pppoe.h>
4236285Sbrian#include <netgraph/ng_socket.h>
4343888Sbrian
4429840Sbrian#include <errno.h>
4546686Sbrian#include <stdio.h>
4630715Sbrian#include <stdlib.h>
4730715Sbrian#include <string.h>
4830715Sbrian#include <sysexits.h>
4930715Sbrian#include <sys/fcntl.h>
506059Samurai#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
5146686Sbrian#include <sys/linker.h>
526059Samurai#include <sys/module.h>
5336285Sbrian#endif
546059Samurai#include <sys/uio.h>
556735Samurai#include <termios.h>
5636285Sbrian#include <sys/time.h>
5736285Sbrian#include <unistd.h>
5836285Sbrian
5943888Sbrian#include "layer.h"
6036285Sbrian#include "defs.h"
6136285Sbrian#include "mbuf.h"
6236285Sbrian#include "log.h"
6336285Sbrian#include "timer.h"
6436285Sbrian#include "lqr.h"
6536285Sbrian#include "hdlc.h"
6636285Sbrian#include "throughput.h"
6736285Sbrian#include "fsm.h"
6843313Sbrian#include "lcp.h"
6943313Sbrian#include "ccp.h"
7043313Sbrian#include "link.h"
7136285Sbrian#include "async.h"
7236285Sbrian#include "descriptor.h"
7338174Sbrian#include "physical.h"
7443888Sbrian#include "main.h"
7536285Sbrian#include "mp.h"
7637192Sbrian#include "chat.h"
7736287Sbrian#include "auth.h"
7837192Sbrian#include "chap.h"
796059Samurai#include "cbcp.h"
8031343Sbrian#include "datalink.h"
8119866Sphk#include "slcompress.h"
826059Samurai#include "iplist.h"
8343693Sbrian#include "ipcp.h"
846059Samurai#include "filter.h"
8530715Sbrian#ifndef NORADIUS
8636285Sbrian#include "radius.h"
8743693Sbrian#endif
886059Samurai#include "bundle.h"
896059Samurai#include "id.h"
906059Samurai#include "ether.h"
916059Samurai
926059Samurai
9328679Sbrian#define PPPOE_NODE_TYPE_LEN (sizeof NG_PPPOE_NODE_TYPE - 1) /* "PPPoE" */
946059Samurai
956059Samuraistruct etherdevice {
966059Samurai  struct device dev;			/* What struct physical knows about */
9754912Sbrian  int cs;				/* Control socket */
9830715Sbrian  int connected;			/* Are we connected yet ? */
996059Samurai  int timeout;				/* Seconds attempting to connect */
10030715Sbrian  char hook[sizeof TUN_NAME + 11];	/* Our socket node hook */
10136285Sbrian};
10237926Sbrian
10337926Sbrian#define device2ether(d) \
10437926Sbrian  ((d)->type == ETHER_DEVICE ? (struct etherdevice *)d : NULL)
10537926Sbrian
10646686Sbrianint
10750867Sbrianether_DeviceSize(void)
1086059Samurai{
1096059Samurai  return sizeof(struct etherdevice);
11043693Sbrian}
11144123Sbrian
11244123Sbrianstatic ssize_t
11344123Sbrianether_Write(struct physical *p, const void *v, size_t n)
11444123Sbrian{
11544123Sbrian  struct etherdevice *dev = device2ether(p->handler);
1166059Samurai
11743693Sbrian  return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n;
11843693Sbrian}
11943693Sbrian
12043693Sbrianstatic ssize_t
12143693Sbrianether_Read(struct physical *p, void *v, size_t n)
12243693Sbrian{
12343693Sbrian  char hook[sizeof TUN_NAME + 11];
12444106Sbrian
12543693Sbrian  return NgRecvData(p->fd, v, n, hook);
12643693Sbrian}
12743693Sbrian
12843693Sbrianstatic int
12943693Sbrianether_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
13043693Sbrian{
13143693Sbrian  struct etherdevice *dev = device2ether(p->handler);
13244106Sbrian  int result;
13344106Sbrian
13444106Sbrian  if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
13544106Sbrian    FD_CLR(dev->cs, r);
13644106Sbrian    log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
13744106Sbrian    result = 1;
13844106Sbrian  } else
13944106Sbrian    result = 0;
14044106Sbrian
14143693Sbrian  /* Careful... physical_RemoveFromSet() called us ! */
14244106Sbrian
14344106Sbrian  p->handler->removefromset = NULL;
14444106Sbrian  result += physical_RemoveFromSet(p, r, w, e);
14544106Sbrian  p->handler->removefromset = ether_RemoveFromSet;
14644106Sbrian
14744106Sbrian  return result;
14844106Sbrian}
14944106Sbrian
15044106Sbrianstatic void
15144106Sbrianether_Free(struct physical *p)
15244106Sbrian{
15344106Sbrian  struct etherdevice *dev = device2ether(p->handler);
15444106Sbrian
15544106Sbrian  physical_SetDescriptor(p);
15644106Sbrian  if (dev->cs != -1)
15744106Sbrian    close(dev->cs);
15844106Sbrian  free(dev);
15944106Sbrian}
16044106Sbrian
16143693Sbrianstatic const char *
16243693Sbrianether_OpenInfo(struct physical *p)
16344106Sbrian{
16444106Sbrian  struct etherdevice *dev = device2ether(p->handler);
16544106Sbrian
16644106Sbrian  switch (dev->connected) {
16744106Sbrian    case CARRIER_PENDING:
16843693Sbrian      return "negotiating";
16943693Sbrian    case CARRIER_OK:
17043693Sbrian      return "established";
17143693Sbrian  }
17243693Sbrian
17343693Sbrian  return "disconnected";
17443693Sbrian}
17543693Sbrian
17643693Sbrianstatic void
17743693Sbrianether_device2iov(struct device *d, struct iovec *iov, int *niov,
17843693Sbrian                 int maxiov, int *auxfd, int *nauxfd)
17943693Sbrian{
18043693Sbrian  struct etherdevice *dev = device2ether(d);
18143693Sbrian  int sz = physical_MaxDeviceSize();
18243693Sbrian
18343693Sbrian  iov[*niov].iov_base = realloc(d, sz);
18443693Sbrian  if (iov[*niov].iov_base == NULL) {
18543693Sbrian    log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
18643693Sbrian    AbortProgram(EX_OSERR);
18743693Sbrian  }
18843693Sbrian  iov[*niov].iov_len = sz;
18943693Sbrian  (*niov)++;
19043693Sbrian
19143693Sbrian  if (dev->cs >= 0) {
19243693Sbrian    *auxfd = dev->cs;
19343693Sbrian    (*nauxfd)++;
19443693Sbrian  }
19543693Sbrian}
19643888Sbrian
19743888Sbrianstatic void
19843888Sbrianether_MessageIn(struct etherdevice *dev)
19943888Sbrian{
20043888Sbrian  char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
20147849Sbrian  struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
20243888Sbrian  struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
20343888Sbrian  char unknown[14];
20443888Sbrian  const char *msg;
20543888Sbrian  struct timeval t;
20643888Sbrian  fd_set *r;
20743888Sbrian  int ret;
20843888Sbrian
20943888Sbrian  if (dev->cs < 0)
21043888Sbrian    return;
21143888Sbrian
21243888Sbrian  if ((r = mkfdset()) == NULL) {
21343888Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
21443888Sbrian    return;
21543888Sbrian  }
21643888Sbrian  zerofdset(r);
21743888Sbrian  FD_SET(dev->cs, r);
21843888Sbrian  t.tv_sec = t.tv_usec = 0;
21943888Sbrian  ret = select(dev->cs + 1, r, NULL, NULL, &t);
22047849Sbrian  free(r);
22143888Sbrian
22243888Sbrian  if (ret <= 0)
22343888Sbrian    return;
22443888Sbrian
22543888Sbrian  if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) < 0)
22643888Sbrian    return;
22743888Sbrian
22843888Sbrian  if (rep->header.version != NG_VERSION) {
22943888Sbrian    log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n",
23043888Sbrian               (long)rep->header.version, (long)NG_VERSION);
23143888Sbrian    return;
23243888Sbrian  }
23343888Sbrian
23443888Sbrian  if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
23549976Sbrian    log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n",
23649976Sbrian               (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
23743888Sbrian    return;
23843888Sbrian  }
23949976Sbrian
24049976Sbrian  switch (rep->header.cmd) {
24143888Sbrian    case NGM_PPPOE_SET_FLAG:	msg = "SET_FLAG";	break;
24243888Sbrian    case NGM_PPPOE_CONNECT:	msg = "CONNECT";	break;
24343888Sbrian    case NGM_PPPOE_LISTEN:	msg = "LISTEN";		break;
24443888Sbrian    case NGM_PPPOE_OFFER:	msg = "OFFER";		break;
24549976Sbrian    case NGM_PPPOE_SUCCESS:	msg = "SUCCESS";	break;
24649976Sbrian    case NGM_PPPOE_FAIL:	msg = "FAIL";		break;
24743888Sbrian    case NGM_PPPOE_CLOSE:	msg = "CLOSE";		break;
24843888Sbrian    case NGM_PPPOE_GET_STATUS:	msg = "GET_STATUS";	break;
24943888Sbrian    default:
25047849Sbrian      snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
25143888Sbrian      msg = unknown;
25249976Sbrian      break;
25349976Sbrian  }
25443888Sbrian
25543888Sbrian  log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%s\")\n", msg, sts->hook);
25643888Sbrian
25743888Sbrian  switch (rep->header.cmd) {
25843888Sbrian    case NGM_PPPOE_SUCCESS:
25943888Sbrian      dev->connected = CARRIER_OK;
26043888Sbrian      break;
26143888Sbrian    case NGM_PPPOE_FAIL:
26245907Sbrian    case NGM_PPPOE_CLOSE:
26343888Sbrian      dev->connected = CARRIER_LOST;
26443888Sbrian      break;
26543888Sbrian  }
26643888Sbrian}
26743888Sbrian
26843888Sbrianstatic int
26943888Sbrianether_AwaitCarrier(struct physical *p)
27043888Sbrian{
27143888Sbrian  struct etherdevice *dev = device2ether(p->handler);
27243888Sbrian
27343888Sbrian  if (dev->connected != CARRIER_OK && !dev->timeout--)
27443888Sbrian    dev->connected = CARRIER_LOST;
27543888Sbrian  else if (dev->connected == CARRIER_PENDING)
27643888Sbrian    ether_MessageIn(dev);
27743888Sbrian
27843888Sbrian  return dev->connected;
27943888Sbrian}
28043888Sbrian
28143888Sbrianstatic const struct device baseetherdevice = {
28243888Sbrian  ETHER_DEVICE,
28343888Sbrian  "ether",
28443888Sbrian  { CD_REQUIRED, DEF_ETHERCDDELAY },
28543888Sbrian  ether_AwaitCarrier,
28643888Sbrian  ether_RemoveFromSet,
28743888Sbrian  NULL,
28843888Sbrian  NULL,
28943888Sbrian  NULL,
29043888Sbrian  NULL,
29145907Sbrian  ether_Free,
29244123Sbrian  ether_Read,
29344106Sbrian  ether_Write,
29444123Sbrian  ether_device2iov,
29543888Sbrian  NULL,
29643888Sbrian  ether_OpenInfo
29743888Sbrian};
29844123Sbrian
29944123Sbrianstruct device *
30044123Sbrianether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
30144123Sbrian                 int maxiov, int *auxfd, int *nauxfd)
30244123Sbrian{
30343888Sbrian  if (type == ETHER_DEVICE) {
30444106Sbrian    struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base;
30543888Sbrian
30645907Sbrian    dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
30744123Sbrian    if (dev == NULL) {
30844123Sbrian      log_Printf(LogALERT, "Failed to allocate memory: %d\n",
30944123Sbrian                 (int)(sizeof *dev));
31044123Sbrian      AbortProgram(EX_OSERR);
31143888Sbrian    }
31243888Sbrian
31343888Sbrian    if (*nauxfd) {
31443888Sbrian      dev->cs = *auxfd;
31544123Sbrian      (*nauxfd)--;
31644106Sbrian    } else
31744123Sbrian      dev->cs = -1;
31843888Sbrian
31943888Sbrian    /* Refresh function pointers etc */
32043888Sbrian    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
32143888Sbrian
32243888Sbrian    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
32343888Sbrian    return &dev->dev;
32443888Sbrian  }
32543888Sbrian
32643888Sbrian  return NULL;
32743888Sbrian}
32843888Sbrian
32943888Sbrianstatic int
33043888Sbrianether_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
33143888Sbrian{
33243888Sbrian  struct physical *p = descriptor2physical(d);
33343888Sbrian  struct etherdevice *dev = device2ether(p->handler);
33443888Sbrian  int result;
33543888Sbrian
33643888Sbrian  if (r && dev->cs >= 0) {
33743888Sbrian    FD_SET(dev->cs, r);
33843888Sbrian    log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
33943888Sbrian    result = 1;
34043888Sbrian  } else
34143888Sbrian    result = 0;
34243888Sbrian
34343888Sbrian  result += physical_doUpdateSet(d, r, w, e, n, 0);
34443888Sbrian
34543888Sbrian  return result;
34643888Sbrian}
34743888Sbrian
34843888Sbrianstatic int
34943888Sbrianether_IsSet(struct fdescriptor *d, const fd_set *fdset)
35043888Sbrian{
35143888Sbrian  struct physical *p = descriptor2physical(d);
35243888Sbrian  struct etherdevice *dev = device2ether(p->handler);
35343888Sbrian  int result;
35443888Sbrian
35543888Sbrian  result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
35643888Sbrian  result += physical_IsSet(d, fdset);
35743888Sbrian
35843888Sbrian  return result;
35943888Sbrian}
36043888Sbrian
36143888Sbrianstatic void
36243888Sbrianether_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
36343888Sbrian                     const fd_set *fdset)
36443888Sbrian{
36543888Sbrian  struct physical *p = descriptor2physical(d);
36643888Sbrian  struct etherdevice *dev = device2ether(p->handler);
36743888Sbrian
36843888Sbrian  if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) {
36943888Sbrian    ether_MessageIn(dev);
37043888Sbrian    if (dev->connected == CARRIER_LOST) {
37143888Sbrian      log_Printf(LogPHASE, "%s: Device disconnected\n", p->link.name);
37243888Sbrian      datalink_Down(p->dl, CLOSE_NORMAL);
37343888Sbrian      return;
37443888Sbrian    }
37543888Sbrian  }
37643888Sbrian
37743888Sbrian  if (physical_IsSet(d, fdset))
37843888Sbrian    physical_DescriptorRead(d, bundle, fdset);
37943888Sbrian}
38044123Sbrian
38144106Sbrianstatic struct device *
38244106Sbrianether_Abandon(struct etherdevice *dev, struct physical *p)
38344106Sbrian{
38444106Sbrian  /* Abandon our node construction */
38544123Sbrian  close(dev->cs);
38644106Sbrian  close(p->fd);
38743888Sbrian  p->fd = -2;	/* Nobody else need try.. */
38843888Sbrian  free(dev);
38943888Sbrian
39043888Sbrian  return NULL;
39143888Sbrian}
39243888Sbrian
39343888Sbrianstruct device *
39444123Sbrianether_Create(struct physical *p)
39544123Sbrian{
39644123Sbrian  u_char rbuf[2048];
39744123Sbrian  struct etherdevice *dev;
39844123Sbrian  struct ng_mesg *resp;
39943888Sbrian  const struct hooklist *hlist;
40043888Sbrian  const struct nodeinfo *ninfo;
40143888Sbrian  int f;
40243888Sbrian
40343888Sbrian  dev = NULL;
40443888Sbrian  if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE,
40543888Sbrian                                PPPOE_NODE_TYPE_LEN) &&
40643888Sbrian      p->name.full[PPPOE_NODE_TYPE_LEN] == ':') {
40743888Sbrian    const struct linkinfo *nlink;
40843888Sbrian    struct ngpppoe_init_data *data;
40943888Sbrian    struct ngm_mkpeer mkp;
41043888Sbrian    struct ngm_connect ngc;
41143888Sbrian    const char *iface, *provider;
41243888Sbrian    char *path, etherid[12];
41343693Sbrian    int ifacelen, providerlen;
41443693Sbrian    char connectpath[sizeof dev->hook + 2];	/* .:<hook> */
41543693Sbrian
41613379Sphk    p->fd--;				/* We own the device - change fd */
4176059Samurai
4186059Samurai#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
41943888Sbrian    if (modfind("netgraph") == -1) {
42043401Sbrian      log_Printf(LogWARN, "Netgraph is not built into the kernel\n");
42145907Sbrian      return NULL;
42243888Sbrian    }
42345907Sbrian
42443888Sbrian    if (modfind("ng_ether") == -1 && ID0kldload("ng_ether") == -1)
42543313Sbrian      /*
42643888Sbrian       * Don't treat this as an error as older kernels have this stuff
42743888Sbrian       * built in as part of the netgraph node itself.
42843888Sbrian       */
42943888Sbrian      log_Printf(LogWARN, "kldload: ng_ether: %s\n", strerror(errno));
43043888Sbrian
43143888Sbrian    if (modfind("ng_socket") == -1 && ID0kldload("ng_socket") == -1) {
43243313Sbrian      log_Printf(LogWARN, "kldload: ng_socket: %s\n", strerror(errno));
43343888Sbrian      return NULL;
43444106Sbrian    }
43544106Sbrian#endif
43644106Sbrian
43744106Sbrian    if ((dev = malloc(sizeof *dev)) == NULL)
43844106Sbrian      return NULL;
43944106Sbrian
44045907Sbrian    iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1;
44143888Sbrian
44243888Sbrian    provider = strchr(iface, ':');
44343888Sbrian    if (provider) {
44443313Sbrian      ifacelen = provider - iface;
44545907Sbrian      provider++;
44645907Sbrian      providerlen = strlen(provider);
4476059Samurai    } else {
4486059Samurai      ifacelen = strlen(iface);
44930715Sbrian      provider = "";
45043693Sbrian      providerlen = 0;
4516059Samurai    }
45243693Sbrian
45343693Sbrian    /*
45443693Sbrian     * We're going to do this (where tunN is our tunnel device):
45543693Sbrian     *
45643693Sbrian     * .---------.
4576059Samurai     * |  ether  |
45843693Sbrian     * | <iface> |                         dev->cs
45943693Sbrian     * `---------'                           |
46043693Sbrian     *  (orphan)                     p->fd   |
46143693Sbrian     *     |                           |     |
46243693Sbrian     *     |                           |     |
46343693Sbrian     * (ethernet)                      |     |
46443693Sbrian     * .---------.                  .-----------.
4656059Samurai     * |  pppoe  |                  |  socket   |
46643693Sbrian     * | <iface> |(tunN)<---->(tunN)| <unnamed> |
46743693Sbrian     * `---------                   `-----------'
46843693Sbrian     *   (tunX)
46943693Sbrian     *     ^
47043693Sbrian     *     |
47143693Sbrian     *     `--->(tunX)
47237926Sbrian     */
47344106Sbrian
47444123Sbrian    /* Create a socket node */
47544123Sbrian    if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
47644123Sbrian      log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
47744123Sbrian                 strerror(errno));
47844123Sbrian      free(dev);
47944106Sbrian      return NULL;
48044106Sbrian    }
48144106Sbrian
48244123Sbrian    /*
48344106Sbrian     * Ask for a list of hooks attached to the "ether" node.  This node should
48444106Sbrian     * magically exist as a way of hooking stuff onto an ethernet device
48544106Sbrian     */
48644106Sbrian    path = (char *)alloca(ifacelen + 2);
48744106Sbrian    sprintf(path, "%.*s:", ifacelen, iface);
48844123Sbrian    if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
48944123Sbrian                  NULL, 0) < 0) {
49044123Sbrian      log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n",
49144106Sbrian                 path, strerror(errno));
49244106Sbrian      return ether_Abandon(dev, p);
49344106Sbrian    }
49444106Sbrian
49544106Sbrian    /* Get our list back */
49644123Sbrian    resp = (struct ng_mesg *)rbuf;
49744106Sbrian    if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) < 0) {
49844106Sbrian      log_Printf(LogWARN, "Cannot get netgraph response: %s\n",
49944106Sbrian                 strerror(errno));
50044106Sbrian      return ether_Abandon(dev, p);
50144106Sbrian    }
50245907Sbrian
50344106Sbrian    hlist = (const struct hooklist *)resp->data;
50444106Sbrian    ninfo = &hlist->nodeinfo;
50544106Sbrian
50644106Sbrian    /* Make sure we've got the right type of node */
50744106Sbrian    if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
50844106Sbrian                sizeof NG_ETHER_NODE_TYPE - 1)) {
50944123Sbrian      log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``"
51044106Sbrian                 NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type);
51143693Sbrian      return ether_Abandon(dev, p);
51243693Sbrian    }
51343693Sbrian
51443888Sbrian    log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
51543888Sbrian               path, ninfo->id);
51643888Sbrian
51743888Sbrian    /* look for a hook already attached.  */
51843888Sbrian    for (f = 0; f < ninfo->hooks; f++) {
51943888Sbrian      nlink = &hlist->link[f];
52043888Sbrian
52143693Sbrian      log_Printf(LogDEBUG, "  Found %s -> %s\n", nlink->ourhook,
52245907Sbrian                 nlink->peerhook);
52344123Sbrian
52444106Sbrian      if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
52544106Sbrian          !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
52644123Sbrian        /*
52743693Sbrian         * Something is using the data coming out of this ``ether'' node.
52829840Sbrian         * If it's a PPPoE node, we use that node, otherwise we complain that
52943693Sbrian         * someone else is using the node.
53043888Sbrian         */
53143888Sbrian        if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE))
53243888Sbrian          /* Use this PPPoE node ! */
53343888Sbrian          snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id);
53443888Sbrian        else {
53546686Sbrian          log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n",
53646686Sbrian                     path, nlink->nodeinfo.type);
53743693Sbrian          return ether_Abandon(dev, p);
53846686Sbrian        }
53943693Sbrian        break;
54044106Sbrian      }
54144123Sbrian    }
54248817Sbrian
54344123Sbrian    if (f == ninfo->hooks) {
54444123Sbrian      /*
54544123Sbrian       * Create a new ``PPPoE'' node connected to the ``ether'' node using
54629840Sbrian       * the magic ``orphan'' and ``ethernet'' hooks
54746686Sbrian       */
54846686Sbrian      snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
54954912Sbrian      snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
55046686Sbrian      snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
55146686Sbrian      snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id);
55246686Sbrian
55346686Sbrian      log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n",
55446686Sbrian                 etherid, mkp.ourhook, mkp.peerhook);
55545220Sbrian
55654912Sbrian      if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE,
55746686Sbrian                    NGM_MKPEER, &mkp, sizeof mkp) < 0) {
55845220Sbrian        log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n",
55945220Sbrian                   etherid, strerror(errno));
56054912Sbrian        return ether_Abandon(dev, p);
56144159Sbrian      }
56244159Sbrian
56344159Sbrian      snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN);
56443693Sbrian    }
56543693Sbrian
56643693Sbrian    snprintf(dev->hook, sizeof dev->hook, "%s%d",
56743693Sbrian             TUN_NAME, p->dl->bundle->unit);
56854912Sbrian
56943693Sbrian    /*
57043693Sbrian     * Connect the PPPoE node to our socket node.
57143693Sbrian     * ngc.path has already been set up
57243693Sbrian     */
57346686Sbrian    snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
57443693Sbrian    memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
57543693Sbrian
57643693Sbrian    log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n",
57743693Sbrian               ngc.ourhook, ngc.path, ngc.peerhook);
57854912Sbrian    if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
57946686Sbrian                  NGM_CONNECT, &ngc, sizeof ngc) < 0) {
58025630Sbrian      log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph "
58143693Sbrian                 "nodes: %s\n", strerror(errno));
58229840Sbrian      return ether_Abandon(dev, p);
58344123Sbrian    }
58444106Sbrian
58544123Sbrian    /* And finally, request a connection to the given provider */
58643693Sbrian
58743693Sbrian    data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen);
58844106Sbrian
58944106Sbrian    snprintf(data->hook, sizeof data->hook, "%s", dev->hook);
59043693Sbrian    strcpy(data->data, provider);
59143693Sbrian
59254912Sbrian    snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);
59346686Sbrian    log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath);
59443693Sbrian    if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
59545907Sbrian                  NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) {
59645907Sbrian      log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n",
59743693Sbrian                 connectpath, strerror(errno));
59844123Sbrian      return ether_Abandon(dev, p);
59944106Sbrian    }
60044106Sbrian
60144106Sbrian    /* Hook things up so that we monitor dev->cs */
60244123Sbrian    p->desc.UpdateSet = ether_UpdateSet;
60343693Sbrian    p->desc.IsSet = ether_IsSet;
60443313Sbrian    p->desc.Read = ether_DescriptorRead;
60543693Sbrian
60643693Sbrian    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
60743693Sbrian    switch (p->cfg.cd.necessity) {
60843693Sbrian      case CD_VARIABLE:
60943693Sbrian        dev->dev.cd.delay = p->cfg.cd.delay;
61043693Sbrian        break;
61154912Sbrian      case CD_REQUIRED:
61246686Sbrian        dev->dev.cd = p->cfg.cd;
61343693Sbrian        break;
61443693Sbrian      case CD_NOTREQUIRED:
61543693Sbrian        log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n",
61654912Sbrian                   p->link.name, dev->dev.cd.delay);
61746686Sbrian      case CD_DEFAULT:
61843693Sbrian        break;
61943693Sbrian    }
62043693Sbrian
62143693Sbrian    dev->timeout = dev->dev.cd.delay;
62243693Sbrian    dev->connected = CARRIER_PENDING;
62344123Sbrian
62444106Sbrian  } else {
62544123Sbrian    /* See if we're a netgraph socket */
62643693Sbrian    struct sockaddr_ng ngsock;
62743313Sbrian    struct sockaddr *sock = (struct sockaddr *)&ngsock;
62843693Sbrian    int sz;
62943693Sbrian
63043693Sbrian    sz = sizeof ngsock;
63143693Sbrian    if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) {
63243693Sbrian      /*
63354912Sbrian       * It's a netgraph node... We can't determine hook names etc, so we
63446686Sbrian       * stay pretty impartial....
63543693Sbrian       */
63643693Sbrian      log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
63743693Sbrian
63843693Sbrian      if ((dev = malloc(sizeof *dev)) == NULL) {
63943401Sbrian        log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
64036285Sbrian                   p->link.name, strerror(errno));
64143693Sbrian        return NULL;
64243693Sbrian      }
64343693Sbrian
64443693Sbrian      memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
64544106Sbrian      dev->cs = -1;
64644106Sbrian      dev->timeout = 0;
64744106Sbrian      dev->connected = CARRIER_OK;
64844123Sbrian      *dev->hook = '\0';
64944106Sbrian    }
65044123Sbrian  }
65144123Sbrian
65244123Sbrian  if (dev) {
65343693Sbrian    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
65444106Sbrian
65544106Sbrian    /* Moan about (and fix) invalid LCP configurations */
65644123Sbrian    if (p->link.lcp.cfg.mru > 1492) {
65744106Sbrian      log_Printf(LogWARN, "%s: Reducing MRU to 1492\n", p->link.name);
65844123Sbrian      p->link.lcp.cfg.mru = 1492;
65944123Sbrian    }
66044123Sbrian    if (p->dl->bundle->cfg.mtu > 1492) {
66143693Sbrian      log_Printf(LogWARN, "%s: Reducing MTU to 1492\n", p->link.name);
66236285Sbrian      p->dl->bundle->cfg.mtu = 1492;
66343693Sbrian    }
66443693Sbrian
66543693Sbrian    return &dev->dev;
66643693Sbrian  }
66743693Sbrian
66843693Sbrian  return NULL;
66943693Sbrian}
67043693Sbrian