ether.c revision 74916
162053Smarkm/*-
2255362Smarkm * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
362053Smarkm * All rights reserved.
462053Smarkm *
562053Smarkm * Redistribution and use in source and binary forms, with or without
662053Smarkm * modification, are permitted provided that the following conditions
762053Smarkm * are met:
862053Smarkm * 1. Redistributions of source code must retain the above copyright
962053Smarkm *    notice, this list of conditions and the following disclaimer.
1062053Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1162053Smarkm *    notice, this list of conditions and the following disclaimer in the
1262053Smarkm *    documentation and/or other materials provided with the distribution.
1362053Smarkm *
1462053Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1562053Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1662053Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1762053Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1862053Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1962053Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2062053Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2162053Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2262053Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2362053Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2462053Smarkm * SUCH DAMAGE.
2562053Smarkm *
2662053Smarkm * $FreeBSD: head/usr.sbin/ppp/ether.c 74916 2001-03-28 09:45:27Z brian $
2762053Smarkm */
28119418Sobrien
29119418Sobrien#include <sys/param.h>
30119418Sobrien#include <sys/socket.h>
31256381Smarkm#include <sys/un.h>
32256381Smarkm#include <netinet/in.h>
3362053Smarkm#include <arpa/inet.h>
3465686Smarkm#include <netdb.h>
3574914Sjhb#include <netgraph.h>
36122871Smarkm#include <net/ethernet.h>
3767365Sjhb#include <net/if.h>
3862053Smarkm#include <net/route.h>
3974072Smarkm#include <netinet/in_systm.h>
40122871Smarkm#include <netinet/ip.h>
4169168Smarkm#include <netgraph/ng_ether.h>
42143418Sume#include <netgraph/ng_message.h>
43100082Smarkm#include <netgraph/ng_pppoe.h>
4469168Smarkm#include <netgraph/ng_socket.h>
4567112Smarkm
46254147Sobrien#include <errno.h>
47128059Smarkm#include <stdio.h>
4867112Smarkm#include <stdlib.h>
4962053Smarkm#include <string.h>
50255362Smarkm#include <sysexits.h>
51255362Smarkm#include <sys/fcntl.h>
52255362Smarkm#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
53255362Smarkm#include <sys/linker.h>
54255362Smarkm#include <sys/module.h>
55255362Smarkm#endif
56255362Smarkm#include <sys/stat.h>
57255362Smarkm#include <sys/uio.h>
58255362Smarkm#include <termios.h>
59255362Smarkm#include <sys/time.h>
60255362Smarkm#include <unistd.h>
61255362Smarkm
62255362Smarkm#include "layer.h"
63255362Smarkm#include "defs.h"
64255362Smarkm#include "mbuf.h"
65255362Smarkm#include "log.h"
66255362Smarkm#include "timer.h"
67255362Smarkm#include "lqr.h"
68255362Smarkm#include "hdlc.h"
69255362Smarkm#include "throughput.h"
70255362Smarkm#include "fsm.h"
71255362Smarkm#include "lcp.h"
72255362Smarkm#include "ccp.h"
73255362Smarkm#include "link.h"
74255362Smarkm#include "async.h"
75255362Smarkm#include "descriptor.h"
76255362Smarkm#include "physical.h"
77255362Smarkm#include "main.h"
78255362Smarkm#include "mp.h"
7974072Smarkm#include "chat.h"
8074072Smarkm#include "auth.h"
81255362Smarkm#include "chap.h"
82255362Smarkm#include "cbcp.h"
8374072Smarkm#include "datalink.h"
8474072Smarkm#include "slcompress.h"
8562765Smarkm#include "iplist.h"
8674072Smarkm#include "ipcp.h"
8762053Smarkm#include "filter.h"
8865686Smarkm#ifndef NORADIUS
89153575Sps#include "radius.h"
9062765Smarkm#endif
91255362Smarkm#include "bundle.h"
92255362Smarkm#include "id.h"
93255362Smarkm#include "iface.h"
94255362Smarkm#include "ether.h"
95255362Smarkm
96255362Smarkm
97255362Smarkm#define PPPOE_NODE_TYPE_LEN (sizeof NG_PPPOE_NODE_TYPE - 1) /* "PPPoE" */
98255362Smarkm
99255362Smarkmstruct etherdevice {
100255362Smarkm  struct device dev;			/* What struct physical knows about */
101255362Smarkm  int cs;				/* Control socket */
102256381Smarkm  int connected;			/* Are we connected yet ? */
103255362Smarkm  int timeout;				/* Seconds attempting to connect */
104255362Smarkm  char hook[sizeof TUN_NAME + 11];	/* Our socket node hook */
105255362Smarkm};
106255362Smarkm
107255362Smarkm#define device2ether(d) \
108255362Smarkm  ((d)->type == ETHER_DEVICE ? (struct etherdevice *)d : NULL)
109255362Smarkm
110255362Smarkmint
11174072Smarkmether_DeviceSize(void)
11274072Smarkm{
11374072Smarkm  return sizeof(struct etherdevice);
11462765Smarkm}
11591600Smarkm
11665686Smarkmstatic ssize_t
11791600Smarkmether_Write(struct physical *p, const void *v, size_t n)
11865686Smarkm{
119256381Smarkm  struct etherdevice *dev = device2ether(p->handler);
120256381Smarkm
121256381Smarkm  return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : n;
122256381Smarkm}
123256381Smarkm
124256381Smarkmstatic ssize_t
125256381Smarkmether_Read(struct physical *p, void *v, size_t n)
126256381Smarkm{
127256381Smarkm  char hook[sizeof TUN_NAME + 11];
128256381Smarkm
129256381Smarkm  return NgRecvData(p->fd, v, n, hook);
130256381Smarkm}
131256381Smarkm
132256381Smarkmstatic int
133256381Smarkmether_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
13474072Smarkm{
13574072Smarkm  struct etherdevice *dev = device2ether(p->handler);
136256381Smarkm  int result;
137256381Smarkm
138256381Smarkm  if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
13965686Smarkm    FD_CLR(dev->cs, r);
14074072Smarkm    log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
14174072Smarkm    result = 1;
14274072Smarkm  } else
14391600Smarkm    result = 0;
14474072Smarkm
14574072Smarkm  /* Careful... physical_RemoveFromSet() called us ! */
14674072Smarkm
14765686Smarkm  p->handler->removefromset = NULL;
14874072Smarkm  result += physical_RemoveFromSet(p, r, w, e);
14965686Smarkm  p->handler->removefromset = ether_RemoveFromSet;
15074072Smarkm
15174072Smarkm  return result;
15274072Smarkm}
15365686Smarkm
15474072Smarkmstatic void
15574072Smarkmether_Free(struct physical *p)
15674072Smarkm{
15765686Smarkm  struct etherdevice *dev = device2ether(p->handler);
15874072Smarkm
15974072Smarkm  physical_SetDescriptor(p);
16062765Smarkm  if (dev->cs != -1)
16162765Smarkm    close(dev->cs);
16274072Smarkm  free(dev);
163254147Sobrien}
16462053Smarkm
16574072Smarkmstatic const char *
166170025Srwatsonether_OpenInfo(struct physical *p)
16765686Smarkm{
16872364Smarkm  struct etherdevice *dev = device2ether(p->handler);
16972364Smarkm
17072364Smarkm  switch (dev->connected) {
171170025Srwatson    case CARRIER_PENDING:
172254147Sobrien      return "negotiating";
173128059Smarkm    case CARRIER_OK:
174128059Smarkm      return "established";
175128059Smarkm  }
176170025Srwatson
177128059Smarkm  return "disconnected";
178128059Smarkm}
179128059Smarkm
180128059Smarkmstatic void
181128059Smarkmether_device2iov(struct device *d, struct iovec *iov, int *niov,
182128059Smarkm                 int maxiov, int *auxfd, int *nauxfd)
183170025Srwatson{
184128059Smarkm  struct etherdevice *dev = device2ether(d);
185128059Smarkm  int sz = physical_MaxDeviceSize();
186128059Smarkm
187128059Smarkm  iov[*niov].iov_base = realloc(d, sz);
188128059Smarkm  if (iov[*niov].iov_base == NULL) {
189128059Smarkm    log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
190170025Srwatson    AbortProgram(EX_OSERR);
191128059Smarkm  }
192128059Smarkm  iov[*niov].iov_len = sz;
193255362Smarkm  (*niov)++;
194128059Smarkm
195128059Smarkm  if (dev->cs >= 0) {
196128059Smarkm    *auxfd = dev->cs;
197170025Srwatson    (*nauxfd)++;
198128059Smarkm  }
199128059Smarkm}
200255362Smarkm
201128059Smarkmstatic void
202128059Smarkmether_MessageIn(struct etherdevice *dev)
203128059Smarkm{
204170025Srwatson  char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
205128059Smarkm  struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
206128059Smarkm  struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
207128059Smarkm  char unknown[14];
208128059Smarkm  const char *msg;
209128059Smarkm  struct timeval t;
210128059Smarkm  fd_set *r;
21162765Smarkm  int ret;
21262765Smarkm
213255362Smarkm  if (dev->cs < 0)
214255362Smarkm    return;
21562765Smarkm
21662765Smarkm  if ((r = mkfdset()) == NULL) {
21765686Smarkm    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
21874072Smarkm    return;
21974072Smarkm  }
220255362Smarkm  zerofdset(r);
22165686Smarkm  FD_SET(dev->cs, r);
22274072Smarkm  t.tv_sec = t.tv_usec = 0;
223255362Smarkm  ret = select(dev->cs + 1, r, NULL, NULL, &t);
22469526Smarkm  free(r);
22574072Smarkm
226255362Smarkm  if (ret <= 0)
22762053Smarkm    return;
22862053Smarkm
22962053Smarkm  if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) < 0)
230128059Smarkm    return;
23162053Smarkm
23265686Smarkm  if (rep->header.version != NG_VERSION) {
23362765Smarkm    log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n",
23462765Smarkm               (long)rep->header.version, (long)NG_VERSION);
23562765Smarkm    return;
23674072Smarkm  }
23762765Smarkm
23865686Smarkm  if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
23965686Smarkm    log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n",
24063771Smarkm               (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
241255362Smarkm    return;
242255362Smarkm  }
243255362Smarkm
244255362Smarkm  switch (rep->header.cmd) {
24591600Smarkm    case NGM_PPPOE_SET_FLAG:	msg = "SET_FLAG";	break;
24691600Smarkm    case NGM_PPPOE_CONNECT:	msg = "CONNECT";	break;
24762053Smarkm    case NGM_PPPOE_LISTEN:	msg = "LISTEN";		break;
248256381Smarkm    case NGM_PPPOE_OFFER:	msg = "OFFER";		break;
249256381Smarkm    case NGM_PPPOE_SUCCESS:	msg = "SUCCESS";	break;
250256381Smarkm    case NGM_PPPOE_FAIL:	msg = "FAIL";		break;
251256381Smarkm    case NGM_PPPOE_CLOSE:	msg = "CLOSE";		break;
25265686Smarkm    case NGM_PPPOE_GET_STATUS:	msg = "GET_STATUS";	break;
25372200Sbmilekic    default:
25465686Smarkm      snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
25562053Smarkm      msg = unknown;
25662053Smarkm      break;
257255362Smarkm  }
25865686Smarkm
25965686Smarkm  log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%s\")\n", msg, sts->hook);
260255362Smarkm
26174072Smarkm  switch (rep->header.cmd) {
262255362Smarkm    case NGM_PPPOE_SUCCESS:
263255362Smarkm      dev->connected = CARRIER_OK;
264255362Smarkm      break;
265255362Smarkm    case NGM_PPPOE_FAIL:
26662765Smarkm    case NGM_PPPOE_CLOSE:
26763771Smarkm      dev->connected = CARRIER_LOST;
26863771Smarkm      break;
26963771Smarkm  }
27062053Smarkm}
27162765Smarkm
27262765Smarkmstatic int
27362765Smarkmether_AwaitCarrier(struct physical *p)
274255362Smarkm{
27574072Smarkm  struct etherdevice *dev = device2ether(p->handler);
276255362Smarkm
27762765Smarkm  if (dev->connected != CARRIER_OK && !dev->timeout--)
278255362Smarkm    dev->connected = CARRIER_LOST;
27962765Smarkm  else if (dev->connected == CARRIER_PENDING)
280255362Smarkm    ether_MessageIn(dev);
28165686Smarkm
282255362Smarkm  return dev->connected;
28362053Smarkm}
28462053Smarkm
28565686Smarkmstatic const struct device baseetherdevice = {
28665686Smarkm  ETHER_DEVICE,
28765686Smarkm  "ether",
28862053Smarkm  { CD_REQUIRED, DEF_ETHERCDDELAY },
289255362Smarkm  ether_AwaitCarrier,
290255362Smarkm  ether_RemoveFromSet,
29165686Smarkm  NULL,
292255362Smarkm  NULL,
293255362Smarkm  NULL,
294255362Smarkm  NULL,
29562053Smarkm  ether_Free,
29662053Smarkm  ether_Read,
29762053Smarkm  ether_Write,
298255362Smarkm  ether_device2iov,
299255362Smarkm  NULL,
300255362Smarkm  ether_OpenInfo
30162053Smarkm};
30262765Smarkm
30362053Smarkmstruct device *
304256381Smarkmether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
305256381Smarkm                 int maxiov, int *auxfd, int *nauxfd)
30674072Smarkm{
30762053Smarkm  if (type == ETHER_DEVICE) {
30862053Smarkm    struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base;
30962053Smarkm
31065686Smarkm    dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
31165686Smarkm    if (dev == NULL) {
31265686Smarkm      log_Printf(LogALERT, "Failed to allocate memory: %d\n",
31362053Smarkm                 (int)(sizeof *dev));
31465686Smarkm      AbortProgram(EX_OSERR);
31565686Smarkm    }
31662053Smarkm
317153575Sps    if (*nauxfd) {
318255362Smarkm      dev->cs = *auxfd;
319153575Sps      (*nauxfd)--;
32065686Smarkm    } else
32172200Sbmilekic      dev->cs = -1;
32262053Smarkm
32362053Smarkm    /* Refresh function pointers etc */
324100082Smarkm    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
32591600Smarkm
326128059Smarkm    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
32762053Smarkm    return &dev->dev;
32862053Smarkm  }
32962053Smarkm
330255362Smarkm  return NULL;
331107789Smarkm}
33291600Smarkm
33391600Smarkmstatic int
33462053Smarkmether_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
335256381Smarkm{
336256381Smarkm  struct physical *p = descriptor2physical(d);
337256381Smarkm  struct etherdevice *dev = device2ether(p->handler);
338256381Smarkm  int result;
33962875Smarkm
34072200Sbmilekic  if (r && dev->cs >= 0) {
34162875Smarkm    FD_SET(dev->cs, r);
34262053Smarkm    log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
34362053Smarkm    result = 1;
34462765Smarkm  } else
34562053Smarkm    result = 0;
34662053Smarkm
347255362Smarkm  result += physical_doUpdateSet(d, r, w, e, n, 0);
34862053Smarkm
349255362Smarkm  return result;
350255362Smarkm}
351255362Smarkm
352255362Smarkmstatic int
353107789Smarkmether_IsSet(struct fdescriptor *d, const fd_set *fdset)
354255362Smarkm{
35562053Smarkm  struct physical *p = descriptor2physical(d);
35662765Smarkm  struct etherdevice *dev = device2ether(p->handler);
35762053Smarkm  int result;
358107789Smarkm
359174073Ssimon  result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
36062053Smarkm  result += physical_IsSet(d, fdset);
36162053Smarkm
36262053Smarkm  return result;
36362053Smarkm}
364255362Smarkm
365255362Smarkmstatic void
36691600Smarkmether_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
367255362Smarkm                     const fd_set *fdset)
368255362Smarkm{
36962053Smarkm  struct physical *p = descriptor2physical(d);
37062765Smarkm  struct etherdevice *dev = device2ether(p->handler);
37162053Smarkm
37262053Smarkm  if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) {
37362053Smarkm    ether_MessageIn(dev);
37462053Smarkm    if (dev->connected == CARRIER_LOST) {
375128059Smarkm      log_Printf(LogPHASE, "%s: Device disconnected\n", p->link.name);
376255362Smarkm      datalink_Down(p->dl, CLOSE_NORMAL);
37762053Smarkm      return;
37862053Smarkm    }
37962053Smarkm  }
38072200Sbmilekic
381256381Smarkm  if (physical_IsSet(d, fdset))
38262053Smarkm    physical_DescriptorRead(d, bundle, fdset);
38362053Smarkm}
38462765Smarkm
38562053Smarkmstatic struct device *
38662053Smarkmether_Abandon(struct etherdevice *dev, struct physical *p)
38774072Smarkm{
388255362Smarkm  /* Abandon our node construction */
38962053Smarkm  close(dev->cs);
390255362Smarkm  close(p->fd);
391255362Smarkm  p->fd = -2;	/* Nobody else need try.. */
392255362Smarkm  free(dev);
39362053Smarkm
39462053Smarkm  return NULL;
395255362Smarkm}
39665686Smarkm
39762053Smarkmstruct device *
39862765Smarkmether_Create(struct physical *p)
39969172Smarkm{
40069172Smarkm  u_char rbuf[2048];
401128059Smarkm  struct etherdevice *dev;
40269172Smarkm  struct ng_mesg *resp;
403256381Smarkm  const struct hooklist *hlist;
404256381Smarkm  const struct nodeinfo *ninfo;
405256381Smarkm  char *path;
406256381Smarkm  int ifacelen, f;
407256381Smarkm
408256381Smarkm  dev = NULL;
409256381Smarkm  path = NULL;
410256381Smarkm  ifacelen = 0;
411256381Smarkm  if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE,
412256381Smarkm                                PPPOE_NODE_TYPE_LEN) &&
413256381Smarkm      p->name.full[PPPOE_NODE_TYPE_LEN] == ':') {
414256381Smarkm    const struct linkinfo *nlink;
415100082Smarkm    struct ngpppoe_init_data *data;
41669172Smarkm    struct ngm_mkpeer mkp;
417    struct ngm_connect ngc;
418    const char *iface, *provider;
419    char etherid[12];
420    int providerlen;
421    char connectpath[sizeof dev->hook + 2];	/* .:<hook> */
422
423    p->fd--;				/* We own the device - change fd */
424
425#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
426    if (modfind("netgraph") == -1) {
427      log_Printf(LogWARN, "Netgraph is not built into the kernel\n");
428      return NULL;
429    }
430
431    if (modfind("ng_ether") == -1 && ID0kldload("ng_ether") == -1)
432      /*
433       * Don't treat this as an error as older kernels have this stuff
434       * built in as part of the netgraph node itself.
435       */
436      log_Printf(LogWARN, "kldload: ng_ether: %s\n", strerror(errno));
437
438    if (modfind("ng_socket") == -1 && ID0kldload("ng_socket") == -1) {
439      log_Printf(LogWARN, "kldload: ng_socket: %s\n", strerror(errno));
440      return NULL;
441    }
442#endif
443
444    if ((dev = malloc(sizeof *dev)) == NULL)
445      return NULL;
446
447    iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1;
448
449    provider = strchr(iface, ':');
450    if (provider) {
451      ifacelen = provider - iface;
452      provider++;
453      providerlen = strlen(provider);
454    } else {
455      ifacelen = strlen(iface);
456      provider = "";
457      providerlen = 0;
458    }
459
460    /*
461     * We're going to do this (where tunN is our tunnel device):
462     *
463     * .---------.
464     * |  ether  |
465     * | <iface> |                         dev->cs
466     * `---------'                           |
467     *  (orphan)                     p->fd   |
468     *     |                           |     |
469     *     |                           |     |
470     * (ethernet)                      |     |
471     * .---------.                  .-----------.
472     * |  pppoe  |                  |  socket   |
473     * | <iface> |(tunN)<---->(tunN)| <unnamed> |
474     * `---------                   `-----------'
475     *   (tunX)
476     *     ^
477     *     |
478     *     `--->(tunX)
479     */
480
481    /* Create a socket node */
482    if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
483      log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
484                 strerror(errno));
485      free(dev);
486      return NULL;
487    }
488
489    /*
490     * Ask for a list of hooks attached to the "ether" node.  This node should
491     * magically exist as a way of hooking stuff onto an ethernet device
492     */
493    path = (char *)alloca(ifacelen + 2);
494    sprintf(path, "%.*s:", ifacelen, iface);
495    if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
496                  NULL, 0) < 0) {
497      log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n",
498                 path, strerror(errno));
499      return ether_Abandon(dev, p);
500    }
501
502    /* Get our list back */
503    resp = (struct ng_mesg *)rbuf;
504    if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) < 0) {
505      log_Printf(LogWARN, "Cannot get netgraph response: %s\n",
506                 strerror(errno));
507      return ether_Abandon(dev, p);
508    }
509
510    hlist = (const struct hooklist *)resp->data;
511    ninfo = &hlist->nodeinfo;
512
513    /* Make sure we've got the right type of node */
514    if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
515                sizeof NG_ETHER_NODE_TYPE - 1)) {
516      log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``"
517                 NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type);
518      return ether_Abandon(dev, p);
519    }
520
521    log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
522               path, ninfo->id);
523
524    /* look for a hook already attached.  */
525    for (f = 0; f < ninfo->hooks; f++) {
526      nlink = &hlist->link[f];
527
528      log_Printf(LogDEBUG, "  Found %s -> %s\n", nlink->ourhook,
529                 nlink->peerhook);
530
531      if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
532          !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
533        /*
534         * Something is using the data coming out of this ``ether'' node.
535         * If it's a PPPoE node, we use that node, otherwise we complain that
536         * someone else is using the node.
537         */
538        if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE))
539          /* Use this PPPoE node ! */
540          snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id);
541        else {
542          log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n",
543                     path, nlink->nodeinfo.type);
544          return ether_Abandon(dev, p);
545        }
546        break;
547      }
548    }
549
550    if (f == ninfo->hooks) {
551      /*
552       * Create a new ``PPPoE'' node connected to the ``ether'' node using
553       * the magic ``orphan'' and ``ethernet'' hooks
554       */
555      snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
556      snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
557      snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
558      snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id);
559
560      log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n",
561                 etherid, mkp.ourhook, mkp.peerhook);
562
563      if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE,
564                    NGM_MKPEER, &mkp, sizeof mkp) < 0) {
565        log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n",
566                   etherid, strerror(errno));
567        return ether_Abandon(dev, p);
568      }
569
570      snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN);
571    }
572
573    snprintf(dev->hook, sizeof dev->hook, "%s%d",
574             TUN_NAME, p->dl->bundle->unit);
575
576    /*
577     * Connect the PPPoE node to our socket node.
578     * ngc.path has already been set up
579     */
580    snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
581    memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
582
583    log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n",
584               ngc.ourhook, ngc.path, ngc.peerhook);
585    if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
586                  NGM_CONNECT, &ngc, sizeof ngc) < 0) {
587      log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph "
588                 "nodes: %s\n", strerror(errno));
589      return ether_Abandon(dev, p);
590    }
591
592    /* And finally, request a connection to the given provider */
593
594    data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen);
595    snprintf(data->hook, sizeof data->hook, "%s", dev->hook);
596    memcpy(data->data, provider, providerlen);
597    data->data_len = providerlen;
598
599    snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);
600    log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath);
601    if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
602                  NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) {
603      log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n",
604                 connectpath, strerror(errno));
605      return ether_Abandon(dev, p);
606    }
607
608    /* Hook things up so that we monitor dev->cs */
609    p->desc.UpdateSet = ether_UpdateSet;
610    p->desc.IsSet = ether_IsSet;
611    p->desc.Read = ether_DescriptorRead;
612
613    memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
614    switch (p->cfg.cd.necessity) {
615      case CD_VARIABLE:
616        dev->dev.cd.delay = p->cfg.cd.delay;
617        break;
618      case CD_REQUIRED:
619        dev->dev.cd = p->cfg.cd;
620        break;
621      case CD_NOTREQUIRED:
622        log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n",
623                   p->link.name, dev->dev.cd.delay);
624      case CD_DEFAULT:
625        break;
626    }
627
628    dev->timeout = dev->dev.cd.delay;
629    dev->connected = CARRIER_PENDING;
630
631  } else {
632    /* See if we're a netgraph socket */
633    struct stat st;
634
635    if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
636      struct sockaddr_storage ssock;
637      struct sockaddr *sock = (struct sockaddr *)&ssock;
638      int sz;
639
640      sz = sizeof ssock;
641      if (getsockname(p->fd, sock, &sz) == -1) {
642        log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
643        close(p->fd);
644        p->fd = -1;
645        return NULL;
646      }
647
648      if (sock->sa_family == AF_NETGRAPH) {
649        /*
650         * It's a netgraph node... We can't determine hook names etc, so we
651         * stay pretty impartial....
652         */
653        log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
654
655        if ((dev = malloc(sizeof *dev)) == NULL) {
656          log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
657                     p->link.name, strerror(errno));
658          return NULL;
659        }
660
661        memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
662        dev->cs = -1;
663        dev->timeout = 0;
664        dev->connected = CARRIER_OK;
665        *dev->hook = '\0';
666      }
667    }
668  }
669
670  if (dev) {
671    physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
672
673    /* Moan about (and fix) invalid LCP configurations */
674    if (p->link.lcp.cfg.mru > 1492) {
675      log_Printf(LogWARN, "%s: Reducing MRU to 1492\n", p->link.name);
676      p->link.lcp.cfg.mru = 1492;
677    }
678    if (p->dl->bundle->cfg.mtu > 1492) {
679      log_Printf(LogWARN, "%s: Reducing MTU to 1492\n", p->link.name);
680      p->dl->bundle->cfg.mtu = 1492;
681    }
682
683    if (path != NULL) {
684      /* Mark the interface as UP if it's not already */
685
686      path[ifacelen] = '\0';		/* Remove the trailing ':' */
687      if (!iface_SetFlags(path, IFF_UP))
688        log_Printf(LogWARN, "%s: Failed to set the IFF_UP flag on %s\n",
689                   p->link.name, path);
690    }
691
692    return &dev->dev;
693  }
694
695  return NULL;
696}
697