ether.c revision 53062
137535Sdes/*-
2236103Sdes * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
337535Sdes * All rights reserved.
437535Sdes *
537535Sdes * Redistribution and use in source and binary forms, with or without
637535Sdes * modification, are permitted provided that the following conditions
737535Sdes * are met:
837535Sdes * 1. Redistributions of source code must retain the above copyright
937535Sdes *    notice, this list of conditions and the following disclaimer.
1037535Sdes * 2. Redistributions in binary form must reproduce the above copyright
1137535Sdes *    notice, this list of conditions and the following disclaimer in the
1237535Sdes *    documentation and/or other materials provided with the distribution.
1337535Sdes *
1437535Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1563012Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1637535Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1737535Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1837535Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1937535Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2037535Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2137535Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2237535Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2337535Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2437535Sdes * SUCH DAMAGE.
2537535Sdes *
2637535Sdes * $FreeBSD: head/usr.sbin/ppp/ether.c 53062 1999-11-09 19:05:48Z brian $
2737535Sdes */
2837535Sdes
2984203Sdillon#include <sys/param.h>
3084203Sdillon#include <sys/socket.h>
3184203Sdillon#include <sys/un.h>
3263236Sdes#include <netinet/in.h>
3363236Sdes#include <arpa/inet.h>
3463236Sdes#include <netdb.h>
3563236Sdes#include <netgraph.h>
3663236Sdes#include <net/ethernet.h>
3763236Sdes#include <netinet/in_systm.h>
3863236Sdes#include <netinet/ip.h>
3963236Sdes#include <netgraph/ng_ether.h>
4063236Sdes#include <netgraph/ng_message.h>
4163236Sdes#include <netgraph/ng_pppoe.h>
4263236Sdes#include <netgraph/ng_socket.h>
4363236Sdes
4463236Sdes#include <errno.h>
4563236Sdes#include <stdio.h>
4663236Sdes#include <stdlib.h>
4763236Sdes#include <string.h>
4863236Sdes#include <sysexits.h>
4990267Sdes#include <sys/fcntl.h>
5063236Sdes#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
5163236Sdes#include <sys/linker.h>
5263236Sdes#endif
5363236Sdes#include <sys/uio.h>
5463236Sdes#include <termios.h>
5563236Sdes#ifndef NONBLOCK_FIXED
5663236Sdes#include <sys/time.h>
5763236Sdes#endif
5863236Sdes#include <unistd.h>
5963236Sdes
6063236Sdes#include "layer.h"
6163236Sdes#include "defs.h"
6263236Sdes#include "mbuf.h"
6363236Sdes#include "log.h"
6437535Sdes#include "timer.h"
6560737Sume#include "lqr.h"
66186124Smurray#include "hdlc.h"
6737535Sdes#include "throughput.h"
6863012Sdes#include "fsm.h"
6937535Sdes#include "lcp.h"
7063012Sdes#include "ccp.h"
7160376Sdes#include "link.h"
7260189Sdes#include "async.h"
7337608Sdes#include "descriptor.h"
7437535Sdes#include "physical.h"
7537535Sdes#include "main.h"
7637535Sdes#include "mp.h"
7760376Sdes#include "chat.h"
7837535Sdes#include "auth.h"
79202613Sdes#include "chap.h"
8037535Sdes#include "cbcp.h"
81141958Skbyanc#include "datalink.h"
82141958Skbyanc#include "slcompress.h"
83141958Skbyanc#include "iplist.h"
8437535Sdes#include "ipcp.h"
8540939Sdes#include "filter.h"
8641862Sdes#ifndef NORADIUS
8737535Sdes#include "radius.h"
8863012Sdes#endif
89242032Seadler#include "bundle.h"
9037535Sdes#include "id.h"
9163012Sdes#include "ether.h"
9263012Sdes
9363012Sdes
9463012Sdes#define PPPOE_NODE_TYPE_LEN (sizeof NG_PPPOE_NODE_TYPE - 1) /* "PPPoE" */
9563012Sdes
9663012Sdesstruct etherdevice {
97186124Smurray  struct device dev;			/* What struct physical knows about */
98169386Sdes  int cs;				/* Control socket */
9963012Sdes  int connected;			/* Are we connected yet ? */
10087317Sdes  int timeout;				/* Seconds attempting to connect */
101125696Sdes  char hook[sizeof TUN_NAME + 11];	/* Our socket node hook */
10263012Sdes};
10360196Sdes
10463012Sdes#define device2ether(d) \
10590267Sdes  ((d)->type == ETHER_DEVICE ? (struct etherdevice *)d : NULL)
106169386Sdes
10790267Sdesint
10863012Sdesether_DeviceSize(void)
10988771Sdes{
11063012Sdes  return sizeof(struct etherdevice);
11190267Sdes}
11263012Sdes
11363012Sdesstatic ssize_t
11463012Sdesether_Write(struct physical *p, const void *v, size_t n)
11563012Sdes{
11697859Sdes  struct etherdevice *dev = device2ether(p->handler);
11737535Sdes
11897858Sdes  return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n;
11997866Sdes}
12097858Sdes
12197866Sdesstatic ssize_t
12297866Sdesether_Read(struct physical *p, void *v, size_t n)
12397866Sdes{
12497858Sdes  char hook[sizeof TUN_NAME + 11];
12597858Sdes
12697858Sdes  return NgRecvData(p->fd, v, n, hook);
12763281Sdes}
12890267Sdes
12963012Sdesstatic int
13037535Sdesether_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
13137535Sdes{
13237608Sdes  struct etherdevice *dev = device2ether(p->handler);
13363012Sdes  int result;
13437608Sdes
13537608Sdes  if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
136174588Sdes    FD_CLR(dev->cs, r);
13737608Sdes    log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
13890267Sdes    result = 1;
13990267Sdes  } else
140174588Sdes    result = 0;
14190267Sdes
14290267Sdes  /* Careful... physical_RemoveFromSet() called us ! */
143174761Sdes
14490267Sdes  p->handler->removefromset = NULL;
14590267Sdes  result += physical_RemoveFromSet(p, r, w, e);
146174761Sdes  p->handler->removefromset = ether_RemoveFromSet;
14790267Sdes
14890267Sdes  return result;
149174761Sdes}
15090267Sdes
151174761Sdesstatic void
15297859Sdesether_Free(struct physical *p)
15390267Sdes{
15490267Sdes  struct etherdevice *dev = device2ether(p->handler);
15597859Sdes
156176036Sdes  physical_SetDescriptor(p);
15790267Sdes  if (dev->cs != -1)
15890267Sdes    close(dev->cs);
15990267Sdes  free(dev);
16063281Sdes}
16190267Sdes
16297859Sdesstatic const char *
16397859Sdesether_OpenInfo(struct physical *p)
164106207Sdes{
16590267Sdes  struct etherdevice *dev = device2ether(p->handler);
166106207Sdes
167106207Sdes  switch (dev->connected) {
168106207Sdes    case CARRIER_PENDING:
16990267Sdes      return "negotiating";
17063012Sdes    case CARRIER_OK:
17190267Sdes      return "established";
17297859Sdes  }
17337608Sdes
17437608Sdes  return "disconnected";
17537608Sdes}
17697866Sdes
17797866Sdesstatic void
17897866Sdesether_device2iov(struct device *d, struct iovec *iov, int *niov,
179174588Sdes                 int maxiov, int *auxfd, int *nauxfd, pid_t newpid)
18097866Sdes{
18197866Sdes  struct etherdevice *dev = device2ether(d);
18297866Sdes  int sz = physical_MaxDeviceSize();
18397866Sdes
18497866Sdes  iov[*niov].iov_base = realloc(d, sz);
18597866Sdes  if (iov[*niov].iov_base == NULL) {
18697866Sdes    log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
18797866Sdes    AbortProgram(EX_OSERR);
18897866Sdes  }
18997866Sdes  iov[*niov].iov_len = sz;
190106044Sdes  (*niov)++;
19197866Sdes
19297866Sdes  if (dev->cs >= 0) {
19397866Sdes    *auxfd = dev->cs;
19437608Sdes    (*nauxfd)++;
19537608Sdes  }
19663012Sdes}
197174588Sdes
19837535Sdesstatic void
199231247Sbaptether_MessageIn(struct etherdevice *dev)
200231247Sbapt{
20197859Sdes  char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
20290267Sdes  struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
20397859Sdes  struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
20490267Sdes  char unknown[14];
20590267Sdes  const char *msg;
20697866Sdes#ifndef NONBLOCK_FIXED
207174588Sdes  struct timeval t;
20897866Sdes  fd_set r;
209231247Sbapt#endif
210231247Sbapt
21197866Sdes  if (dev->cs < 0)
212106185Sdes    return;
213231247Sbapt
21497866Sdes#ifndef NONBLOCK_FIXED
21597866Sdes  FD_ZERO(&r);
21697866Sdes  FD_SET(dev->cs, &r);
21797866Sdes  t.tv_sec = t.tv_usec = 0;
21897859Sdes  if (select(dev->cs + 1, &r, NULL, NULL, &t) <= 0)
219174588Sdes    return;
22090267Sdes#endif
22197859Sdes
22290267Sdes  if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) < 0)
22390267Sdes    return;
22497859Sdes
22590267Sdes  if (rep->header.version != NG_VERSION) {
22690267Sdes    log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n",
22737535Sdes               (long)rep->header.version, (long)NG_VERSION);
22863012Sdes    return;
22997866Sdes  }
23097866Sdes
231174588Sdes  if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
23290267Sdes    log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n",
233231247Sbapt               (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
234231247Sbapt    return;
23597866Sdes  }
236106185Sdes
237231247Sbapt  switch (rep->header.cmd) {
23897866Sdes    case NGM_PPPOE_SET_FLAG:	msg = "SET_FLAG";	break;
23990267Sdes    case NGM_PPPOE_CONNECT:	msg = "CONNECT";	break;
24097859Sdes    case NGM_PPPOE_LISTEN:	msg = "LISTEN";		break;
24197856Sdes    case NGM_PPPOE_OFFER:	msg = "OFFER";		break;
24297856Sdes    case NGM_PPPOE_SUCCESS:	msg = "SUCCESS";	break;
243174588Sdes    case NGM_PPPOE_FAIL:	msg = "FAIL";		break;
24497856Sdes    case NGM_PPPOE_CLOSE:	msg = "CLOSE";		break;
24590267Sdes    case NGM_PPPOE_GET_STATUS:	msg = "GET_STATUS";	break;
24690267Sdes    default:
24790267Sdes      snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
24897866Sdes      msg = unknown;
24990267Sdes      break;
25097866Sdes  }
25137535Sdes
25237535Sdes  log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%s\")\n", msg, sts->hook);
25337608Sdes
25437608Sdes  switch (rep->header.cmd) {
25537608Sdes    case NGM_PPPOE_SUCCESS:
25637535Sdes      dev->connected = CARRIER_OK;
257174588Sdes      break;
25837535Sdes    case NGM_PPPOE_FAIL:
25997859Sdes    case NGM_PPPOE_CLOSE:
26090267Sdes      dev->connected = CARRIER_LOST;
26163012Sdes      break;
26297859Sdes  }
26390267Sdes}
26497859Sdes
26590267Sdesstatic int
26663012Sdesether_AwaitCarrier(struct physical *p)
26790267Sdes{
26890267Sdes  struct etherdevice *dev = device2ether(p->handler);
26997866Sdes
270174588Sdes  if (!dev->timeout--)
27190267Sdes    dev->connected = CARRIER_LOST;
27297866Sdes  else if (dev->connected == CARRIER_PENDING)
27390267Sdes    ether_MessageIn(dev);
27490267Sdes
275176105Sdes  return dev->connected;
27697866Sdes}
27790267Sdes
27837535Sdesstatic const struct device baseetherdevice = {
279231247Sbapt  ETHER_DEVICE,
280231247Sbapt  "ether",
281231247Sbapt  ether_AwaitCarrier,
28290267Sdes  ether_RemoveFromSet,
283231247Sbapt  NULL,
28490267Sdes  NULL,
28537535Sdes  NULL,
28637535Sdes  NULL,
28737608Sdes  ether_Free,
28837608Sdes  ether_Read,
28937608Sdes  ether_Write,
29037535Sdes  ether_device2iov,
291174588Sdes  NULL,
29237535Sdes  ether_OpenInfo
29397859Sdes};
29490267Sdes
295174588Sdesstruct device *
29637535Sdesether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
29737535Sdes                 int maxiov, int *auxfd, int *nauxfd)
29837608Sdes{
29937608Sdes  if (type == ETHER_DEVICE) {
30037608Sdes    struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base;
30137535Sdes
302174588Sdes    dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
30337535Sdes    if (dev == NULL) {
30497859Sdes      log_Printf(LogALERT, "Failed to allocate memory: %d\n",
30590267Sdes                 (int)(sizeof *dev));
30663012Sdes      AbortProgram(EX_OSERR);
307174588Sdes    }
30897859Sdes
30997859Sdes    if (*nauxfd) {
31097859Sdes      dev->cs = *auxfd;
31190267Sdes      (*nauxfd)--;
31237535Sdes    } else
31337535Sdes      dev->cs = -1;
31437608Sdes
31563012Sdes    /* Refresh function pointers etc */
31637608Sdes    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
31763012Sdes
318174588Sdes    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
31937535Sdes    return &dev->dev;
32097859Sdes  }
32190267Sdes
32263012Sdes  return NULL;
323109967Sdes}
324174588Sdes
32590267Sdesstatic int
32690267Sdesether_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
32797859Sdes{
32897866Sdes  struct physical *p = descriptor2physical(d);
329174588Sdes  struct etherdevice *dev = device2ether(p->handler);
33090267Sdes  int result;
331174588Sdes
33297859Sdes  if (r && dev->cs >= 0) {
33390267Sdes    FD_SET(dev->cs, r);
33490267Sdes    log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
33590267Sdes    result = 1;
33663012Sdes  } else
33763012Sdes    result = 0;
33890267Sdes
33963012Sdes  result += physical_doUpdateSet(d, r, w, e, n, 0);
34063012Sdes
34163012Sdes  return result;
34263012Sdes}
34363012Sdes
34463012Sdesstatic int
34590267Sdesether_IsSet(struct descriptor *d, const fd_set *fdset)
34690267Sdes{
34790267Sdes  struct physical *p = descriptor2physical(d);
34890267Sdes  struct etherdevice *dev = device2ether(p->handler);
34990267Sdes  int result;
35090267Sdes
35190267Sdes  result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
35290267Sdes  result += physical_IsSet(d, fdset);
35390267Sdes
354202613Sdes  return result;
355202613Sdes}
35685093Sdes
35763012Sdesstatic void
35863012Sdesether_DescriptorRead(struct descriptor *d, struct bundle *bundle,
35963012Sdes                     const fd_set *fdset)
36090267Sdes{
36190267Sdes  struct physical *p = descriptor2physical(d);
36263012Sdes  struct etherdevice *dev = device2ether(p->handler);
36390267Sdes
36490267Sdes  if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) {
36590267Sdes    ether_MessageIn(dev);
36690267Sdes    if (dev->connected == CARRIER_LOST) {
36790267Sdes      log_Printf(LogPHASE, "%s: Device disconnected\n", p->link.name);
36890267Sdes      datalink_Down(p->dl, CLOSE_NORMAL);
369202613Sdes      return;
37090267Sdes    }
37163012Sdes  }
37263012Sdes
37363012Sdes  if (physical_IsSet(d, fdset))
37463012Sdes    physical_DescriptorRead(d, bundle, fdset);
37563012Sdes}
37663012Sdes
377174588Sdesstatic struct device *
37863012Sdesether_Abandon(struct etherdevice *dev, struct physical *p)
37990267Sdes{
38090267Sdes  /* Abandon our node construction */
38190267Sdes  close(dev->cs);
38290267Sdes  close(p->fd);
38363012Sdes  p->fd = -2;	/* Nobody else need try.. */
38490267Sdes  free(dev);
38590267Sdes
38690267Sdes  return NULL;
38790267Sdes}
38890267Sdes
38990267Sdesstruct device *
390174588Sdesether_Create(struct physical *p)
39190267Sdes{
39290267Sdes  u_char rbuf[2048];
39390267Sdes  struct etherdevice *dev;
394174588Sdes  struct ng_mesg *resp;
39590267Sdes  const struct hooklist *hlist;
39690267Sdes  const struct nodeinfo *ninfo;
39790267Sdes  int f;
398174588Sdes
39990267Sdes  dev = NULL;
40090267Sdes  if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE,
40190267Sdes                                PPPOE_NODE_TYPE_LEN) &&
40290267Sdes      p->name.full[PPPOE_NODE_TYPE_LEN] == ':') {
40363012Sdes    const struct linkinfo *nlink;
40463012Sdes    struct ngpppoe_init_data *data;
40563012Sdes    struct ngm_mkpeer mkp;
40663012Sdes    struct ngm_connect ngc;
40763012Sdes    const char *iface, *provider;
40863012Sdes    char *path, etherid[12];
409174588Sdes    int ifacelen, providerlen, oldflag;
41063012Sdes    char connectpath[sizeof dev->hook + 2];	/* .:<hook> */
41190267Sdes
41290267Sdes#ifdef KLDSYM_LOOKUP
413174588Sdes    /* First make sure we've got the right code loaded */
41490267Sdes    char basesym[] = "ng_make_node", socksym[] = "ngdomain";
41590267Sdes    struct kld_sym_lookup baselookup = { sizeof baselookup, basesym, 0, 0 };
41690267Sdes    struct kld_sym_lookup socklookup = { sizeof socklookup, socksym, 0, 0 };
41790267Sdes#endif
41890267Sdes
41990267Sdes    p->fd--;				/* We own the device - change fd */
42090267Sdes
42190267Sdes#ifdef KLDSYM_LOOKUP
42290267Sdes    if (kldsym(0, KLDSYM_LOOKUP, &baselookup) == -1) {
42390267Sdes      log_Printf(LogWARN, "Can't run without options NETGRAPH in the kernel\n");
42497856Sdes      return NULL;
42590267Sdes    }
42697856Sdes
42790267Sdes    if (kldsym(0, KLDSYM_LOOKUP, &socklookup) == -1 &&
42890267Sdes        ID0kldload("ng_socket") == -1) {
42990267Sdes      log_Printf(LogWARN, "kldload: ng_socket: %s\n", strerror(errno));
43090267Sdes      return NULL;
43190267Sdes    }
432174761Sdes#endif
433174761Sdes
434174761Sdes    if ((dev = malloc(sizeof *dev)) == NULL)
435174761Sdes      return NULL;
43690267Sdes
43790267Sdes    iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1;
43897856Sdes
43997856Sdes    provider = strchr(iface, ':');
44037535Sdes    if (provider) {
44137535Sdes      ifacelen = provider - iface;
44237608Sdes      provider++;
44390267Sdes      providerlen = strlen(provider);
44490267Sdes    } else {
44563012Sdes      ifacelen = strlen(iface);
44675891Sarchie      provider = "";
447174588Sdes      providerlen = 0;
44863012Sdes    }
449176036Sdes
450176036Sdes    /*
45190267Sdes     * We're going to do this (where tunN is our tunnel device):
45290267Sdes     *
45390267Sdes     * .---------.
454174761Sdes     * |  ether  |
45590267Sdes     * | <iface> |                         dev->cs
45690267Sdes     * `---------'                           |
45763012Sdes     *  (orphan)                     p->fd   |
45863012Sdes     *     |                           |     |
459202613Sdes     *     |                           |     |
46063012Sdes     * (ethernet)                      |     |
461202613Sdes     * .---------.                  .-----------.
462202613Sdes     * |  pppoe  |                  |  socket   |
463202613Sdes     * | <iface> |(tunN)<---->(tunN)| <unnamed> |
464221821Sdes     * `---------                   `-----------'
465202613Sdes     *   (tunX)
466202613Sdes     *     ^
467202613Sdes     *     |
468202613Sdes     *     `--->(tunX)
469202613Sdes     */
470202613Sdes
471202613Sdes    /* Create a socket node */
472221821Sdes    if (NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
473202613Sdes      log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
474202613Sdes                 strerror(errno));
475202613Sdes      free(dev);
476202613Sdes      return NULL;
47763012Sdes    }
478202613Sdes
479202613Sdes    /*
480202613Sdes     * Ask for a list of hooks attached to the "ether" node.  This node should
481202613Sdes     * magically exist as a way of hooking stuff onto an ethernet device
482202613Sdes     */
483202613Sdes    path = (char *)alloca(ifacelen + 2);
484202613Sdes    sprintf(path, "%.*s:", ifacelen, iface);
485202613Sdes    if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
486202613Sdes                  NULL, 0) < 0) {
487202613Sdes      log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n",
488202613Sdes                 path, strerror(errno));
489202613Sdes      return ether_Abandon(dev, p);
490202613Sdes    }
49163012Sdes
492202613Sdes    /* Get our list back */
493202613Sdes    resp = (struct ng_mesg *)rbuf;
494202613Sdes    if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) < 0) {
495202613Sdes      log_Printf(LogWARN, "Cannot get netgraph response: %s\n",
49690267Sdes                 strerror(errno));
497221821Sdes      return ether_Abandon(dev, p);
498202613Sdes    }
499202613Sdes
500202613Sdes    hlist = (const struct hooklist *)resp->data;
501202613Sdes    ninfo = &hlist->nodeinfo;
502202613Sdes
503202613Sdes    /* Make sure we've got the right type of node */
504202613Sdes    if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
505202613Sdes                sizeof NG_ETHER_NODE_TYPE - 1)) {
506221821Sdes      log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``"
507202613Sdes                 NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type);
508202613Sdes      return ether_Abandon(dev, p);
509221821Sdes    }
510202613Sdes
51197856Sdes    log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
51297856Sdes               path, ninfo->id);
513202613Sdes
514202613Sdes    /* look for a hook already attached.  */
515202613Sdes    for (f = 0; f < ninfo->hooks; f++) {
516202613Sdes      nlink = &hlist->link[f];
517202613Sdes
518221820Sdes      log_Printf(LogDEBUG, "  Found %s -> %s\n", nlink->ourhook,
519202613Sdes                 nlink->peerhook);
520221821Sdes
521202613Sdes      if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
522221821Sdes          !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
523202613Sdes        /*
524202613Sdes         * Something is using the data coming out of this ``ether'' node.
52597856Sdes         * If it's a PPPoE node, we use that node, otherwise we complain that
52697856Sdes         * someone else is using the node.
527202613Sdes         */
528202613Sdes        if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE))
529202613Sdes          /* Use this PPPoE node ! */
530202613Sdes          snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id);
531202613Sdes        else {
532202613Sdes          log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n",
533202613Sdes                     path, nlink->nodeinfo.type);
534202613Sdes          return ether_Abandon(dev, p);
535202613Sdes        }
536202613Sdes        break;
537221821Sdes      }
538202613Sdes    }
539221821Sdes
540221821Sdes    if (f == ninfo->hooks) {
541202613Sdes      /*
542202613Sdes       * Create a new ``PPPoE'' node connected to the ``ether'' node using
543202613Sdes       * the magic ``orphan'' and ``ethernet'' hooks
544202613Sdes       */
545221821Sdes      snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
546202613Sdes      snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
547202613Sdes      snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
548202613Sdes      snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id);
549221821Sdes
550202613Sdes      log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n",
551202613Sdes                 etherid, mkp.ourhook, mkp.peerhook);
552202613Sdes
553202613Sdes      if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE,
554202613Sdes                    NGM_MKPEER, &mkp, sizeof mkp) < 0) {
555202613Sdes        log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n",
556202613Sdes                   etherid, strerror(errno));
557202613Sdes        return ether_Abandon(dev, p);
558202613Sdes      }
559202613Sdes
560202613Sdes      snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN);
561202613Sdes    }
562202613Sdes
563202613Sdes    snprintf(dev->hook, sizeof dev->hook, "%s%d",
564202613Sdes             TUN_NAME, p->dl->bundle->unit);
565221821Sdes
566202613Sdes    /*
56790267Sdes     * Connect the PPPoE node to our socket node.
56890267Sdes     * ngc.path has already been set up
56990267Sdes     */
57090267Sdes    snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
57190267Sdes    memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
57290267Sdes
57390267Sdes    log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n",
574202613Sdes               ngc.ourhook, ngc.path, ngc.peerhook);
57590267Sdes    if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
576202613Sdes                  NGM_CONNECT, &ngc, sizeof ngc) < 0) {
57790267Sdes      log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph "
57863012Sdes                 "nodes: %s\n", strerror(errno));
57963012Sdes      return ether_Abandon(dev, p);
580202613Sdes    }
581202613Sdes
582202613Sdes    /* And finally, request a connection to the given provider */
583202613Sdes
584221821Sdes    data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen + 1);
585221821Sdes
586202613Sdes    snprintf(data->hook, sizeof data->hook, "%s", dev->hook);
587202613Sdes    strcpy(data->data, provider);
588221821Sdes    data->data_len = providerlen;
589221821Sdes
590202613Sdes    snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);
591202613Sdes    log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath);
592202613Sdes    if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
593202613Sdes                  NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) {
594202613Sdes      log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n",
595202613Sdes                 connectpath, strerror(errno));
596202613Sdes      return ether_Abandon(dev, p);
597202613Sdes    }
598202613Sdes
599202613Sdes    /*
600202613Sdes     * Now make our control socket non-blocking so that we can read()
601202613Sdes     * without having to select()
602202613Sdes     *
603202613Sdes     * XXX: Does this work (#define NONBLOCK_FIXED) ?
604202613Sdes     */
605202613Sdes    oldflag = fcntl(dev->cs, F_GETFL, 0);
606202613Sdes    if (oldflag < 0) {
607202613Sdes      log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
608202613Sdes                 p->link.name, strerror(errno));
609202613Sdes      return ether_Abandon(dev, p);
610202613Sdes    } else
611202613Sdes      fcntl(dev->cs, F_SETFL, oldflag & ~O_NONBLOCK);
612202613Sdes
613202613Sdes    dev->timeout = p->cfg.cd.delay;
614202613Sdes    dev->connected = CARRIER_PENDING;
615202613Sdes
616202613Sdes    /* Hook things up so that we monitor dev->cs */
617202613Sdes    p->desc.UpdateSet = ether_UpdateSet;
618202613Sdes    p->desc.IsSet = ether_IsSet;
619202613Sdes    p->desc.Read = ether_DescriptorRead;
620202613Sdes  } else {
621202613Sdes    /* See if we're a netgraph socket */
622202613Sdes    struct sockaddr_ng ngsock;
623202613Sdes    struct sockaddr *sock = (struct sockaddr *)&ngsock;
624202613Sdes    int sz;
625202613Sdes
626202613Sdes    sz = sizeof ngsock;
627202613Sdes    if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) {
628202613Sdes      /*
629202613Sdes       * It's a netgraph node... We can't determine hook names etc, so we
630221821Sdes       * stay pretty impartial....
631202613Sdes       */
632202613Sdes      log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
633202613Sdes
634202613Sdes      if ((dev = malloc(sizeof *dev)) == NULL) {
635202613Sdes        log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
636202613Sdes                   p->link.name, strerror(errno));
637202613Sdes        return NULL;
638221821Sdes      }
639202613Sdes
640202613Sdes      dev->cs = -1;
641221821Sdes      dev->timeout = 0;
642202613Sdes      dev->connected = CARRIER_OK;
643221821Sdes      *dev->hook = '\0';
644202613Sdes    }
645221821Sdes  }
646202613Sdes
647221821Sdes  if (dev) {
648202613Sdes    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
649221821Sdes
650202613Sdes    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
651202613Sdes
652202613Sdes    /* Moan about (and fix) invalid LCP configurations */
653202613Sdes    if (p->link.lcp.cfg.mru > 1492) {
654202613Sdes      log_Printf(LogWARN, "%s: Reducing MRU to 1492\n", p->link.name);
655202613Sdes      p->link.lcp.cfg.mru = 1492;
656202613Sdes    }
657202613Sdes    if (p->dl->bundle->cfg.mtu > 1492) {
658202613Sdes      log_Printf(LogWARN, "%s: Reducing MTU to 1492\n", p->link.name);
659202613Sdes      p->dl->bundle->cfg.mtu = 1492;
660202613Sdes    }
661202613Sdes
662221821Sdes    return &dev->dev;
663202613Sdes  }
664202613Sdes
665202613Sdes  return NULL;
666202613Sdes}
667202613Sdes