pppoed.c revision 90975
153537Sbrian/*-
280728Sbrian * Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org>
353537Sbrian * All rights reserved.
453537Sbrian *
553537Sbrian * Redistribution and use in source and binary forms, with or without
653537Sbrian * modification, are permitted provided that the following conditions
753537Sbrian * are met:
853537Sbrian * 1. Redistributions of source code must retain the above copyright
953537Sbrian *    notice, this list of conditions and the following disclaimer.
1053537Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1153537Sbrian *    notice, this list of conditions and the following disclaimer in the
1253537Sbrian *    documentation and/or other materials provided with the distribution.
1353537Sbrian *
1453537Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1553537Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1653537Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1753537Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1853537Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1953537Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2053537Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2153537Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2253537Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2353537Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2453537Sbrian * SUCH DAMAGE.
2553537Sbrian *
2653537Sbrian * $FreeBSD: head/libexec/pppoed/pppoed.c 90975 2002-02-20 15:52:20Z brian $
2753537Sbrian */
2853537Sbrian
2953537Sbrian#include <sys/param.h>
3053537Sbrian#include <sys/socket.h>
3153537Sbrian#include <sys/un.h>
3253537Sbrian#include <netinet/in.h>
3353537Sbrian#include <arpa/inet.h>
3453537Sbrian#include <netdb.h>
3553537Sbrian#include <netgraph.h>
3653537Sbrian#include <net/ethernet.h>
3753537Sbrian#include <netinet/in_systm.h>
3853537Sbrian#include <netinet/ip.h>
3953537Sbrian#include <netgraph/ng_ether.h>
4053537Sbrian#include <netgraph/ng_message.h>
4153537Sbrian#include <netgraph/ng_pppoe.h>
4253537Sbrian#include <netgraph/ng_socket.h>
4353537Sbrian
4453537Sbrian#include <errno.h>
4553537Sbrian#include <paths.h>
4653537Sbrian#include <signal.h>
4753537Sbrian#include <stdio.h>
4866602Sbrian#include <stdarg.h>
4953537Sbrian#include <stdlib.h>
5053537Sbrian#include <string.h>
5153537Sbrian#include <sysexits.h>
5253537Sbrian#include <sys/fcntl.h>
5353537Sbrian#ifndef NOKLDLOAD
5453537Sbrian#include <sys/linker.h>
5553537Sbrian#include <sys/module.h>
5653537Sbrian#endif
5753537Sbrian#include <sys/uio.h>
5853537Sbrian#include <sys/wait.h>
5953537Sbrian#include <syslog.h>
6053537Sbrian#include <termios.h>
6153537Sbrian#include <unistd.h>
6253537Sbrian
6353537Sbrian
6486705Sbrian#define	DEFAULT_EXEC_PREFIX	"exec /usr/sbin/ppp -direct "
6586705Sbrian#define	HISMACADDR		"HISMACADDR"
6653537Sbrian
6790160Skrisstatic void nglogx(const char *, ...) __printflike(1, 2);
6890160Skris
6969582Sbrianstatic int ReceivedSignal;
7069582Sbrian
7153537Sbrianstatic int
7253537Sbrianusage(const char *prog)
7353537Sbrian{
7480728Sbrian  fprintf(stderr, "Usage: %s [-Fd] [-P pidfile] [-a name] [-e exec | -l label]"
7553609Sbrian          " [-p provider] interface\n", prog);
7653537Sbrian  return EX_USAGE;
7753537Sbrian}
7853537Sbrian
7953537Sbrianstatic void
8069582SbrianFarewell(int sig)
8153537Sbrian{
8269582Sbrian  ReceivedSignal = sig;
8353537Sbrian}
8453537Sbrian
8553537Sbrianstatic int
8653537SbrianConfigureNode(const char *prog, const char *iface, const char *provider,
8753537Sbrian              int cs, int ds, int debug, struct ngm_connect *ngc)
8853537Sbrian{
8953537Sbrian  /*
9053537Sbrian   * We're going to do this with the passed `ds' & `cs' descriptors:
9153537Sbrian   *
9253537Sbrian   * .---------.
9353537Sbrian   * |  ether  |
9453537Sbrian   * | <iface> |
9553537Sbrian   * `---------'
9653537Sbrian   *  (orphan)                                     ds    cs
9753537Sbrian   *     |                                         |     |
9853537Sbrian   *     |                                         |     |
9953537Sbrian   * (ethernet)                                    |     |
10053537Sbrian   * .---------.                                .-----------.
10153537Sbrian   * |  pppoe  |                                |  socket   |
10253537Sbrian   * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> |
10353537Sbrian   * `---------                                 `-----------'
10453537Sbrian   * (exec-<pid>)
10553537Sbrian   *     ^                .-----------.      .-------------.
10653537Sbrian   *     |                |   socket  |      | ppp -direct |
10753537Sbrian   *     `--->(exec-<pid>)| <unnamed> |--fd--|  provider   |
10853537Sbrian   *                      `-----------'      `-------------'
10953537Sbrian   *
11053537Sbrian   * where there are potentially many ppp processes running off of the
11153537Sbrian   * same PPPoE node.
11253537Sbrian   * The exec-<pid> hook isn't made 'till we Spawn().
11353537Sbrian   */
11453537Sbrian
11553537Sbrian  char *epath, *spath;
11653537Sbrian  struct ngpppoe_init_data *data;
11753537Sbrian  const struct hooklist *hlist;
11853537Sbrian  const struct nodeinfo *ninfo;
11953537Sbrian  const struct linkinfo *nlink;
12053537Sbrian  struct ngm_mkpeer mkp;
12153537Sbrian  struct ng_mesg *resp;
12253537Sbrian  u_char rbuf[2048];
12353537Sbrian  int f, plen;
12453537Sbrian
12553537Sbrian  /*
12653537Sbrian   * Ask for a list of hooks attached to the "ether" node.  This node should
12753537Sbrian   * magically exist as a way of hooking stuff onto an ethernet device
12853537Sbrian   */
12953537Sbrian  epath = (char *)alloca(strlen(iface) + 2);
13053537Sbrian  sprintf(epath, "%s:", iface);
13153537Sbrian
13253537Sbrian  if (debug)
13353537Sbrian    fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath);
13453537Sbrian
13553537Sbrian  if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) {
13653537Sbrian    if (errno == ENOENT)
13753537Sbrian      fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n",
13853537Sbrian              epath);
13953537Sbrian    else
14053537Sbrian      fprintf(stderr, "%s Cannot send a netgraph message: %s\n",
14153537Sbrian              epath, strerror(errno));
14253537Sbrian    return EX_UNAVAILABLE;
14353537Sbrian  }
14453537Sbrian
14553537Sbrian  /* Get our list back */
14653537Sbrian  resp = (struct ng_mesg *)rbuf;
14782276Sbrian  if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) {
14853537Sbrian    perror("Cannot get netgraph response");
14953537Sbrian    return EX_UNAVAILABLE;
15053537Sbrian  }
15153537Sbrian
15253537Sbrian  hlist = (const struct hooklist *)resp->data;
15353537Sbrian  ninfo = &hlist->nodeinfo;
15453537Sbrian
15553537Sbrian  if (debug)
15653537Sbrian    fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n",
15753537Sbrian            ninfo->id, ninfo->type, ninfo->hooks);
15853537Sbrian
15953537Sbrian  /* Make sure we've got the right type of node */
16053537Sbrian  if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) {
16153537Sbrian    fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``"
16253537Sbrian            NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type);
16353537Sbrian    return EX_DATAERR;
16453537Sbrian  }
16553537Sbrian
16653537Sbrian  /* look for a hook already attached.  */
16753537Sbrian  for (f = 0; f < ninfo->hooks; f++) {
16853537Sbrian    nlink = &hlist->link[f];
16953537Sbrian
17053537Sbrian    if (debug)
17153537Sbrian      fprintf(stderr, "  Got [%x]:%s -> [%x]:%s\n", ninfo->id,
17253537Sbrian              nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook);
17353537Sbrian
17453537Sbrian    if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
17553537Sbrian        !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
17653537Sbrian      /*
17753537Sbrian       * Something is using the data coming out of this `ether' node.
17853537Sbrian       * If it's a PPPoE node, we use that node, otherwise we complain that
17953537Sbrian       * someone else is using the node.
18053537Sbrian       */
18153537Sbrian      if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) {
18253537Sbrian        fprintf(stderr, "%s Node type %s is currently active\n",
18353537Sbrian                epath, nlink->nodeinfo.type);
18453537Sbrian        return EX_UNAVAILABLE;
18553537Sbrian      }
18653537Sbrian      break;
18753537Sbrian    }
18853537Sbrian  }
18953537Sbrian
19053537Sbrian  if (f == ninfo->hooks) {
19153537Sbrian    /*
19253537Sbrian     * Create a new PPPoE node connected to the `ether' node using
19353537Sbrian     * the magic `orphan' and `ethernet' hooks
19453537Sbrian     */
19553537Sbrian    snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
19653537Sbrian    snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
19753537Sbrian    snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
19853537Sbrian
19953537Sbrian    if (debug)
20053537Sbrian      fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath,
20153537Sbrian              mkp.ourhook, mkp.type, mkp.peerhook);
20253537Sbrian
20353537Sbrian    if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE,
20453537Sbrian                  NGM_MKPEER, &mkp, sizeof mkp) < 0) {
20553537Sbrian      fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n",
20653537Sbrian              epath, strerror(errno));
20753537Sbrian      return EX_OSERR;
20853537Sbrian    }
20953537Sbrian  }
21053537Sbrian
21153537Sbrian  /* Connect the PPPoE node to our socket node.  */
21253537Sbrian  snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN);
21353537Sbrian  snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid());
21453537Sbrian  memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook);
21553537Sbrian
21653537Sbrian  if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
21753537Sbrian                NGM_CONNECT, ngc, sizeof *ngc) < 0) {
21853537Sbrian    perror("Cannot CONNECT PPPoE and socket nodes");
21953537Sbrian    return EX_OSERR;
22053537Sbrian  }
22153537Sbrian
22253537Sbrian  plen = strlen(provider);
22353537Sbrian
22468032Sbrian  data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen);
22553537Sbrian  snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook);
22668846Sbrian  memcpy(data->data, provider, plen);
22768846Sbrian  data->data_len = plen;
22853537Sbrian
22953537Sbrian  spath = (char *)alloca(strlen(ngc->peerhook) + 3);
23053537Sbrian  strcpy(spath, ".:");
23153537Sbrian  strcpy(spath + 2, ngc->ourhook);
23253537Sbrian
23353537Sbrian  if (debug) {
23453537Sbrian    if (provider)
23553537Sbrian      fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n",
23653537Sbrian              spath, provider);
23753537Sbrian    else
23853537Sbrian      fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath);
23953537Sbrian  }
24053537Sbrian
24153537Sbrian  if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
24253537Sbrian                data, sizeof *data + plen) == -1) {
24353537Sbrian    fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n",
24453537Sbrian            spath, strerror(errno));
24553537Sbrian    return EX_OSERR;
24653537Sbrian  }
24753537Sbrian
24853537Sbrian  return 0;
24953537Sbrian}
25053537Sbrian
25153537Sbrianstatic void
25269948SjulianSpawn(const char *prog, const char *acname, const char *provider,
25386705Sbrian      const char *exec, struct ngm_connect ngc, int cs, int ds, void *request,
25486705Sbrian      int sz, int debug)
25553537Sbrian{
25653537Sbrian  char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
25753537Sbrian  struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
25853537Sbrian  struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
25953537Sbrian  struct ngpppoe_init_data *data;
26086705Sbrian  unsigned char *macaddr;
26186705Sbrian  char env[sizeof(HISMACADDR)+18], unknown[14], *path;
26253537Sbrian  const char *msg;
26353537Sbrian  int ret, slen;
26453537Sbrian
26553537Sbrian  switch ((ret = fork())) {
26653537Sbrian    case -1:
26753537Sbrian      syslog(LOG_ERR, "fork: %m");
26853537Sbrian      break;
26953537Sbrian
27053537Sbrian    case 0:
27153537Sbrian      switch (fork()) {
27253537Sbrian        case 0:
27353537Sbrian          break;
27453537Sbrian        case -1:
27553537Sbrian          _exit(errno);
27653537Sbrian        default:
27753537Sbrian          _exit(0);
27853537Sbrian      }
27953537Sbrian      close(cs);
28053537Sbrian      close(ds);
28153537Sbrian
28253537Sbrian      /* Create a new socket node */
28353537Sbrian      if (debug)
28453537Sbrian        syslog(LOG_INFO, "Creating a new socket node");
28553537Sbrian
28653537Sbrian      if (NgMkSockNode(NULL, &cs, &ds) == -1) {
28753537Sbrian        syslog(LOG_ERR, "Cannot create netgraph socket node: %m");
28853537Sbrian        _exit(EX_CANTCREAT);
28953537Sbrian      }
29053537Sbrian
29153537Sbrian      /* Connect the PPPoE node to our new socket node.  */
29253537Sbrian      snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid());
29353537Sbrian      memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
29453537Sbrian
29553537Sbrian      if (debug)
29653537Sbrian        syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s",
29753537Sbrian               ngc.ourhook, ngc.path, ngc.peerhook);
29853537Sbrian      if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
29953537Sbrian                    NGM_CONNECT, &ngc, sizeof ngc) < 0) {
30053537Sbrian        syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m");
30153537Sbrian        _exit(EX_OSERR);
30253537Sbrian      }
30353537Sbrian
30453537Sbrian      /*
30553537Sbrian       * If we tell the socket node not to LINGER, it will go away when
30653537Sbrian       * the last hook is removed.
30753537Sbrian       */
30853537Sbrian      if (debug)
30953537Sbrian        syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket");
31053537Sbrian      if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE,
31153537Sbrian                    NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) {
31253537Sbrian        syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m");
31353537Sbrian        _exit(EX_OSERR);
31453537Sbrian      }
31553537Sbrian
31653537Sbrian      /* Put the PPPoE node into OFFER mode */
31753537Sbrian      slen = strlen(acname);
31868032Sbrian      data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
31953537Sbrian      snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
32068846Sbrian      memcpy(data->data, acname, slen);
32168846Sbrian      data->data_len = slen;
32253537Sbrian
32353537Sbrian      path = (char *)alloca(strlen(ngc.ourhook) + 3);
32453537Sbrian      strcpy(path, ".:");
32553537Sbrian      strcpy(path + 2, ngc.ourhook);
32653537Sbrian
32753537Sbrian      syslog(LOG_INFO, "Offering to %s as access concentrator %s",
32853537Sbrian             path, acname);
32953537Sbrian      if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
33053537Sbrian                    data, sizeof *data + slen) == -1) {
33153537Sbrian        syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path);
33253537Sbrian        _exit(EX_OSERR);
33353537Sbrian      }
33469948Sjulian      /* If we have a provider code, set it */
33579597Sbrian      if (provider) {
33669948Sjulian        slen = strlen(provider);
33769948Sjulian        data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
33869948Sjulian        snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
33969948Sjulian        memcpy(data->data, provider, slen);
34069948Sjulian        data->data_len = slen;
34153537Sbrian
34269948Sjulian        syslog(LOG_INFO, "adding to %s as offered service %s",
34369948Sjulian             path, acname);
34469948Sjulian        if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE,
34569948Sjulian                    data, sizeof *data + slen) == -1) {
34669948Sjulian          syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path);
34769948Sjulian          _exit(EX_OSERR);
34869948Sjulian        }
34969948Sjulian      }
35069948Sjulian
35186705Sbrian      /* Put the peer's MAC address in the environment */
35286705Sbrian      if (sz >= sizeof(struct ether_header)) {
35386705Sbrian
35486705Sbrian        macaddr = ((struct ether_header *)request)->ether_shost;
35586762Sbrian        snprintf(env, sizeof(env), "%s=%x:%x:%x:%x:%x:%x", HISMACADDR,
35686762Sbrian                 macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4],
35786762Sbrian                 macaddr[5]);
35886705Sbrian        if (putenv(env) != 0)
35986705Sbrian          syslog(LOG_INFO, "putenv: cannot set %s: %m", env);
36086705Sbrian      }
36186705Sbrian
36253537Sbrian      /* And send our request data to the waiting node */
36353537Sbrian      if (debug)
36453537Sbrian        syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz);
36553537Sbrian      if (NgSendData(ds, ngc.ourhook, request, sz) == -1) {
36653537Sbrian        syslog(LOG_ERR, "Cannot send original request to %s: %m", path);
36753537Sbrian        _exit(EX_OSERR);
36853537Sbrian      }
36953537Sbrian
37053537Sbrian      /* Then wait for a success indication */
37153537Sbrian
37253537Sbrian      if (debug)
37353537Sbrian        syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path);
37453537Sbrian
37553537Sbrian      do {
37682333Sbrian        if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) {
37753537Sbrian          syslog(LOG_ERR, "%s: Cannot receive a message: %m", path);
37853537Sbrian          _exit(EX_OSERR);
37953537Sbrian        }
38053537Sbrian
38182276Sbrian        if (ret == 0) {
38282276Sbrian          /* The socket has been closed */
38382276Sbrian          syslog(LOG_INFO, "%s: Client timed out", path);
38482276Sbrian          _exit(EX_TEMPFAIL);
38582276Sbrian        }
38682276Sbrian
38753537Sbrian        if (rep->header.version != NG_VERSION) {
38853537Sbrian          syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld",
38953537Sbrian                 (long)rep->header.version, (long)NG_VERSION);
39053537Sbrian          _exit(EX_PROTOCOL);
39153537Sbrian        }
39253537Sbrian
39353537Sbrian        if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
39453537Sbrian          syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld",
39553537Sbrian                 (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
39653537Sbrian          continue;
39753537Sbrian        }
39853537Sbrian
39953537Sbrian        switch (rep->header.cmd) {
40053537Sbrian          case NGM_PPPOE_SET_FLAG:	msg = "SET_FLAG";	break;
40153537Sbrian          case NGM_PPPOE_CONNECT:	msg = "CONNECT";	break;
40253537Sbrian          case NGM_PPPOE_LISTEN:	msg = "LISTEN";		break;
40353537Sbrian          case NGM_PPPOE_OFFER:		msg = "OFFER";		break;
40453537Sbrian          case NGM_PPPOE_SUCCESS:	msg = "SUCCESS";	break;
40553537Sbrian          case NGM_PPPOE_FAIL:		msg = "FAIL";		break;
40653537Sbrian          case NGM_PPPOE_CLOSE:		msg = "CLOSE";		break;
40753537Sbrian          case NGM_PPPOE_GET_STATUS:	msg = "GET_STATUS";	break;
40890975Sbrian          case NGM_PPPOE_ACNAME:
40990975Sbrian            msg = "ACNAME";
41090975Sbrian            if (setenv("ACNAME", sts->hook, 1) != 0)
41190975Sbrian              syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m",
41290975Sbrian                     sts->hook);
41390975Sbrian            break;
41453537Sbrian          default:
41553537Sbrian            snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
41653537Sbrian            msg = unknown;
41753537Sbrian            break;
41853537Sbrian        }
41953537Sbrian
42053537Sbrian        switch (rep->header.cmd) {
42153537Sbrian          case NGM_PPPOE_FAIL:
42253537Sbrian          case NGM_PPPOE_CLOSE:
42353537Sbrian            syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")",
42453537Sbrian                   msg, sts->hook);
42553537Sbrian            _exit(0);
42653537Sbrian        }
42753537Sbrian
42853537Sbrian        syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook);
42953537Sbrian      } while (rep->header.cmd != NGM_PPPOE_SUCCESS);
43053537Sbrian
43153537Sbrian      dup2(ds, STDIN_FILENO);
43253537Sbrian      dup2(ds, STDOUT_FILENO);
43353537Sbrian      close(ds);
43453537Sbrian      close(cs);
43553537Sbrian
43653537Sbrian      setsid();
43753537Sbrian      syslog(LOG_INFO, "Executing: %s", exec);
43879452Sbrian      execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL);
43953537Sbrian      syslog(LOG_ERR, "execlp failed: %m");
44053537Sbrian      _exit(EX_OSFILE);
44153537Sbrian
44253537Sbrian    default:
44353537Sbrian      wait(&ret);
44453537Sbrian      errno = ret;
44553537Sbrian      if (errno)
44653537Sbrian        syslog(LOG_ERR, "Second fork failed: %m");
44753537Sbrian      break;
44853537Sbrian  }
44953537Sbrian}
45053537Sbrian
45166602Sbrian#ifndef NOKLDLOAD
45269582Sbrianstatic int
45366602SbrianLoadModules(void)
45466602Sbrian{
45566602Sbrian  const char *module[] = { "netgraph", "ng_socket", "ng_ether", "ng_pppoe" };
45666602Sbrian  int f;
45766602Sbrian
45866602Sbrian  for (f = 0; f < sizeof module / sizeof *module; f++)
45966602Sbrian    if (modfind(module[f]) == -1 && kldload(module[f]) == -1) {
46066602Sbrian      fprintf(stderr, "kldload: %s: %s\n", module[f], strerror(errno));
46166602Sbrian      return 0;
46266602Sbrian    }
46366602Sbrian
46466602Sbrian  return 1;
46566602Sbrian}
46666602Sbrian#endif
46766602Sbrian
46869582Sbrianstatic void
46966602Sbriannglog(const char *fmt, ...)
47066602Sbrian{
47166602Sbrian  char nfmt[256];
47266602Sbrian  va_list ap;
47366602Sbrian
47466602Sbrian  snprintf(nfmt, sizeof nfmt, "%s: %s", fmt, strerror(errno));
47566602Sbrian  va_start(ap, fmt);
47666602Sbrian  vsyslog(LOG_INFO, nfmt, ap);
47766602Sbrian  va_end(ap);
47866602Sbrian}
47966602Sbrian
48069582Sbrianstatic void
48166602Sbriannglogx(const char *fmt, ...)
48266602Sbrian{
48366602Sbrian  va_list ap;
48466602Sbrian
48566602Sbrian  va_start(ap, fmt);
48666602Sbrian  vsyslog(LOG_INFO, fmt, ap);
48766602Sbrian  va_end(ap);
48866602Sbrian}
48966602Sbrian
49066602Sbrianint
49190779Simpmain(int argc, char *argv[])
49253537Sbrian{
49366602Sbrian  char hostname[MAXHOSTNAMELEN], *exec, rhook[NG_HOOKLEN + 1];
49466602Sbrian  unsigned char response[1024];
49580728Sbrian  const char *label, *prog, *provider, *acname;
49653537Sbrian  struct ngm_connect ngc;
49780724Sbrian  struct sigaction act;
49866602Sbrian  int ch, cs, ds, ret, optF, optd, optn, sz, f;
49969582Sbrian  const char *pidfile;
50053537Sbrian
50153537Sbrian  prog = strrchr(argv[0], '/');
50253537Sbrian  prog = prog ? prog + 1 : argv[0];
50353609Sbrian  pidfile = NULL;
50453537Sbrian  exec = NULL;
50580728Sbrian  label = NULL;
50653537Sbrian  acname = NULL;
50753537Sbrian  provider = "";
50866602Sbrian  optF = optd = optn = 0;
50953537Sbrian
51080728Sbrian  while ((ch = getopt(argc, argv, "FP:a:de:l:n:p:")) != -1) {
51153537Sbrian    switch (ch) {
51253537Sbrian      case 'F':
51353537Sbrian        optF = 1;
51453537Sbrian        break;
51553537Sbrian
51653609Sbrian      case 'P':
51753609Sbrian        pidfile = optarg;
51853609Sbrian        break;
51953609Sbrian
52053537Sbrian      case 'a':
52153537Sbrian        acname = optarg;
52253537Sbrian        break;
52353537Sbrian
52453537Sbrian      case 'd':
52553537Sbrian        optd = 1;
52653537Sbrian        break;
52753537Sbrian
52853537Sbrian      case 'e':
52953537Sbrian        exec = optarg;
53053537Sbrian        break;
53153537Sbrian
53280728Sbrian      case 'l':
53380728Sbrian        label = optarg;
53480728Sbrian        break;
53580728Sbrian
53666602Sbrian      case 'n':
53766602Sbrian        optn = 1;
53866602Sbrian        NgSetDebug(atoi(optarg));
53966602Sbrian        break;
54066602Sbrian
54153537Sbrian      case 'p':
54253537Sbrian        provider = optarg;
54353537Sbrian        break;
54453537Sbrian
54553537Sbrian      default:
54653537Sbrian        return usage(prog);
54753537Sbrian    }
54853537Sbrian  }
54953537Sbrian
55053537Sbrian  if (optind >= argc || optind + 2 < argc)
55153537Sbrian    return usage(prog);
55253537Sbrian
55380728Sbrian  if (exec != NULL && label != NULL)
55480728Sbrian    return usage(prog);
55580728Sbrian
55653537Sbrian  if (exec == NULL) {
55780728Sbrian    if (label == NULL)
55880728Sbrian      label = provider;
55980728Sbrian    if (label == NULL) {
56080728Sbrian      fprintf(stderr, "%s: Either a provider, a label or an exec command"
56153537Sbrian              " must be given\n", prog);
56253537Sbrian      return usage(prog);
56353537Sbrian    }
56480728Sbrian    exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(label));
56553537Sbrian    if (exec == NULL) {
56653537Sbrian      fprintf(stderr, "%s: Cannot allocate %d bytes\n", prog,
56780728Sbrian              (int)(sizeof DEFAULT_EXEC_PREFIX) + strlen(label));
56853537Sbrian      return EX_OSERR;
56953537Sbrian    }
57053537Sbrian    strcpy(exec, DEFAULT_EXEC_PREFIX);
57180728Sbrian    strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, label);
57253537Sbrian  }
57353537Sbrian
57453537Sbrian  if (acname == NULL) {
57553537Sbrian    char *dot;
57653537Sbrian
57753537Sbrian    if (gethostname(hostname, sizeof hostname))
57853537Sbrian      strcpy(hostname, "localhost");
57953537Sbrian    else if ((dot = strchr(hostname, '.')))
58053537Sbrian      *dot = '\0';
58153537Sbrian
58253537Sbrian    acname = hostname;
58353537Sbrian  }
58453537Sbrian
58553537Sbrian#ifndef NOKLDLOAD
58666602Sbrian  if (!LoadModules())
58753537Sbrian    return EX_UNAVAILABLE;
58853537Sbrian#endif
58953537Sbrian
59053537Sbrian  /* Create a socket node */
59153537Sbrian  if (NgMkSockNode(NULL, &cs, &ds) == -1) {
59253537Sbrian    perror("Cannot create netgraph socket node");
59353537Sbrian    return EX_CANTCREAT;
59453537Sbrian  }
59553537Sbrian
59653537Sbrian  /* Connect it up (and fill in `ngc') */
59753537Sbrian  if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds,
59853537Sbrian                           optd, &ngc)) != 0) {
59953537Sbrian    close(cs);
60053537Sbrian    close(ds);
60153537Sbrian    return ret;
60253537Sbrian  }
60353537Sbrian
60453537Sbrian  if (!optF && daemon(1, 0) == -1) {
60553537Sbrian    perror("daemon()");
60653609Sbrian    close(cs);
60753609Sbrian    close(ds);
60853537Sbrian    return EX_OSERR;
60953537Sbrian  }
61053537Sbrian
61153609Sbrian
61253609Sbrian  if (pidfile != NULL) {
61353609Sbrian    FILE *fp;
61453609Sbrian
61553609Sbrian    if ((fp = fopen(pidfile, "w")) == NULL) {
61653609Sbrian      perror(pidfile);
61753609Sbrian      close(cs);
61853609Sbrian      close(ds);
61953609Sbrian      return EX_CANTCREAT;
62053609Sbrian    } else {
62153609Sbrian      fprintf(fp, "%d\n", (int)getpid());
62253609Sbrian      fclose(fp);
62353609Sbrian    }
62453609Sbrian  }
62553609Sbrian
62653537Sbrian  openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON);
62766602Sbrian  if (!optF && optn)
62866602Sbrian    NgSetErrLog(nglog, nglogx);
62953537Sbrian
63080724Sbrian  memset(&act, '\0', sizeof act);
63180724Sbrian  act.sa_handler = Farewell;
63280733Sbrian  act.sa_flags = 0;
63380724Sbrian  sigemptyset(&act.sa_mask);
63480724Sbrian  sigaction(SIGHUP, &act, NULL);
63580724Sbrian  sigaction(SIGINT, &act, NULL);
63680724Sbrian  sigaction(SIGQUIT, &act, NULL);
63780724Sbrian  sigaction(SIGTERM, &act, NULL);
63853537Sbrian
63969582Sbrian  while (!ReceivedSignal) {
64053537Sbrian    if (*provider)
64153537Sbrian      syslog(LOG_INFO, "Listening as provider %s", provider);
64253537Sbrian    else
64353537Sbrian      syslog(LOG_INFO, "Listening");
64453537Sbrian
64553537Sbrian    switch (sz = NgRecvData(ds, response, sizeof response, rhook)) {
64653537Sbrian      case -1:
64753537Sbrian        syslog(LOG_INFO, "NgRecvData: %m");
64853537Sbrian        break;
64953537Sbrian      case 0:
65053537Sbrian        syslog(LOG_INFO, "NgRecvData: socket closed");
65153537Sbrian        break;
65253537Sbrian      default:
65353537Sbrian        if (optd) {
65453537Sbrian          char *dbuf, *ptr;
65553537Sbrian
65653537Sbrian          ptr = dbuf = alloca(sz * 2 + 1);
65753537Sbrian          for (f = 0; f < sz; f++, ptr += 2)
65853537Sbrian            sprintf(ptr, "%02x", (u_char)response[f]);
65953537Sbrian          *ptr = '\0';
66053537Sbrian          syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf);
66153537Sbrian        }
66253537Sbrian    }
66353537Sbrian    if (sz <= 0) {
66453537Sbrian      ret = EX_UNAVAILABLE;
66553537Sbrian      break;
66653537Sbrian    }
66769948Sjulian    Spawn(prog, acname, provider, exec, ngc, cs, ds, response, sz, optd);
66853537Sbrian  }
66953537Sbrian
67069582Sbrian  if (pidfile)
67169582Sbrian    remove(pidfile);
67269582Sbrian
67369582Sbrian  if (ReceivedSignal) {
67469582Sbrian    syslog(LOG_INFO, "Received signal %d, exiting", ReceivedSignal);
67569582Sbrian
67669582Sbrian    signal(ReceivedSignal, SIG_DFL);
67769582Sbrian    raise(ReceivedSignal);
67869582Sbrian
67969582Sbrian    /* NOTREACHED */
68069582Sbrian
68169582Sbrian    ret = -ReceivedSignal;
68269582Sbrian  }
68369582Sbrian
68453537Sbrian  return ret;
68553537Sbrian}
686