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$
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"
6696580Sbrian#define	SESSION_ID		"SESSION_ID"
6753537Sbrian
6890160Skrisstatic void nglogx(const char *, ...) __printflike(1, 2);
6990160Skris
7069582Sbrianstatic int ReceivedSignal;
7169582Sbrian
7253537Sbrianstatic int
7353537Sbrianusage(const char *prog)
7453537Sbrian{
7595258Sdes  fprintf(stderr, "usage: %s [-Fd] [-P pidfile] [-a name] [-e exec | -l label]"
76141589Sru          " [-n ngdebug] [-p provider] interface\n", prog);
7753537Sbrian  return EX_USAGE;
7853537Sbrian}
7953537Sbrian
8053537Sbrianstatic void
8169582SbrianFarewell(int sig)
8253537Sbrian{
8369582Sbrian  ReceivedSignal = sig;
8453537Sbrian}
8553537Sbrian
8653537Sbrianstatic int
8753537SbrianConfigureNode(const char *prog, const char *iface, const char *provider,
8853537Sbrian              int cs, int ds, int debug, struct ngm_connect *ngc)
8953537Sbrian{
9053537Sbrian  /*
9153537Sbrian   * We're going to do this with the passed `ds' & `cs' descriptors:
9253537Sbrian   *
9353537Sbrian   * .---------.
9453537Sbrian   * |  ether  |
9553537Sbrian   * | <iface> |
9653537Sbrian   * `---------'
9753537Sbrian   *  (orphan)                                     ds    cs
9853537Sbrian   *     |                                         |     |
9953537Sbrian   *     |                                         |     |
10053537Sbrian   * (ethernet)                                    |     |
10153537Sbrian   * .---------.                                .-----------.
10253537Sbrian   * |  pppoe  |                                |  socket   |
10353537Sbrian   * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> |
10453537Sbrian   * `---------                                 `-----------'
10553537Sbrian   * (exec-<pid>)
10653537Sbrian   *     ^                .-----------.      .-------------.
10753537Sbrian   *     |                |   socket  |      | ppp -direct |
10853537Sbrian   *     `--->(exec-<pid>)| <unnamed> |--fd--|  provider   |
10953537Sbrian   *                      `-----------'      `-------------'
11053537Sbrian   *
11153537Sbrian   * where there are potentially many ppp processes running off of the
11253537Sbrian   * same PPPoE node.
11353537Sbrian   * The exec-<pid> hook isn't made 'till we Spawn().
11453537Sbrian   */
11553537Sbrian
11653537Sbrian  char *epath, *spath;
11753537Sbrian  struct ngpppoe_init_data *data;
11853537Sbrian  const struct hooklist *hlist;
11953537Sbrian  const struct nodeinfo *ninfo;
12053537Sbrian  const struct linkinfo *nlink;
12153537Sbrian  struct ngm_mkpeer mkp;
12253537Sbrian  struct ng_mesg *resp;
12353537Sbrian  u_char rbuf[2048];
12453537Sbrian  int f, plen;
12553537Sbrian
12653537Sbrian  /*
12753537Sbrian   * Ask for a list of hooks attached to the "ether" node.  This node should
12853537Sbrian   * magically exist as a way of hooking stuff onto an ethernet device
12953537Sbrian   */
13053537Sbrian  epath = (char *)alloca(strlen(iface) + 2);
13153537Sbrian  sprintf(epath, "%s:", iface);
13253537Sbrian
13353537Sbrian  if (debug)
13453537Sbrian    fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath);
13553537Sbrian
13653537Sbrian  if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) {
13753537Sbrian    if (errno == ENOENT)
13853537Sbrian      fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n",
13953537Sbrian              epath);
14053537Sbrian    else
14153537Sbrian      fprintf(stderr, "%s Cannot send a netgraph message: %s\n",
14253537Sbrian              epath, strerror(errno));
14353537Sbrian    return EX_UNAVAILABLE;
14453537Sbrian  }
14553537Sbrian
14653537Sbrian  /* Get our list back */
14753537Sbrian  resp = (struct ng_mesg *)rbuf;
14882276Sbrian  if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) {
14953537Sbrian    perror("Cannot get netgraph response");
15053537Sbrian    return EX_UNAVAILABLE;
15153537Sbrian  }
15253537Sbrian
15353537Sbrian  hlist = (const struct hooklist *)resp->data;
15453537Sbrian  ninfo = &hlist->nodeinfo;
15553537Sbrian
15653537Sbrian  if (debug)
15753537Sbrian    fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n",
15853537Sbrian            ninfo->id, ninfo->type, ninfo->hooks);
15953537Sbrian
16053537Sbrian  /* Make sure we've got the right type of node */
16153537Sbrian  if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) {
16253537Sbrian    fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``"
16353537Sbrian            NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type);
16453537Sbrian    return EX_DATAERR;
16553537Sbrian  }
16653537Sbrian
16753537Sbrian  /* look for a hook already attached.  */
16853537Sbrian  for (f = 0; f < ninfo->hooks; f++) {
16953537Sbrian    nlink = &hlist->link[f];
17053537Sbrian
17153537Sbrian    if (debug)
17253537Sbrian      fprintf(stderr, "  Got [%x]:%s -> [%x]:%s\n", ninfo->id,
17353537Sbrian              nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook);
17453537Sbrian
17553537Sbrian    if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
17653537Sbrian        !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
17753537Sbrian      /*
17853537Sbrian       * Something is using the data coming out of this `ether' node.
17953537Sbrian       * If it's a PPPoE node, we use that node, otherwise we complain that
18053537Sbrian       * someone else is using the node.
18153537Sbrian       */
18253537Sbrian      if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) {
18353537Sbrian        fprintf(stderr, "%s Node type %s is currently active\n",
18453537Sbrian                epath, nlink->nodeinfo.type);
18553537Sbrian        return EX_UNAVAILABLE;
18653537Sbrian      }
18753537Sbrian      break;
18853537Sbrian    }
18953537Sbrian  }
19053537Sbrian
19153537Sbrian  if (f == ninfo->hooks) {
19253537Sbrian    /*
19353537Sbrian     * Create a new PPPoE node connected to the `ether' node using
19453537Sbrian     * the magic `orphan' and `ethernet' hooks
19553537Sbrian     */
19653537Sbrian    snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
19753537Sbrian    snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
19853537Sbrian    snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
19953537Sbrian
20053537Sbrian    if (debug)
20153537Sbrian      fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath,
20253537Sbrian              mkp.ourhook, mkp.type, mkp.peerhook);
20353537Sbrian
20453537Sbrian    if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE,
20553537Sbrian                  NGM_MKPEER, &mkp, sizeof mkp) < 0) {
20653537Sbrian      fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n",
20753537Sbrian              epath, strerror(errno));
20853537Sbrian      return EX_OSERR;
20953537Sbrian    }
21053537Sbrian  }
21153537Sbrian
21253537Sbrian  /* Connect the PPPoE node to our socket node.  */
21353537Sbrian  snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN);
21453537Sbrian  snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid());
21553537Sbrian  memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook);
21653537Sbrian
21753537Sbrian  if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
21853537Sbrian                NGM_CONNECT, ngc, sizeof *ngc) < 0) {
21953537Sbrian    perror("Cannot CONNECT PPPoE and socket nodes");
22053537Sbrian    return EX_OSERR;
22153537Sbrian  }
22253537Sbrian
22353537Sbrian  plen = strlen(provider);
22453537Sbrian
22568032Sbrian  data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen);
22653537Sbrian  snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook);
22768846Sbrian  memcpy(data->data, provider, plen);
22868846Sbrian  data->data_len = plen;
22953537Sbrian
23053537Sbrian  spath = (char *)alloca(strlen(ngc->peerhook) + 3);
23153537Sbrian  strcpy(spath, ".:");
23253537Sbrian  strcpy(spath + 2, ngc->ourhook);
23353537Sbrian
23453537Sbrian  if (debug) {
23553537Sbrian    if (provider)
23653537Sbrian      fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n",
23753537Sbrian              spath, provider);
23853537Sbrian    else
23953537Sbrian      fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath);
24053537Sbrian  }
24153537Sbrian
24253537Sbrian  if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
24353537Sbrian                data, sizeof *data + plen) == -1) {
24453537Sbrian    fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n",
24553537Sbrian            spath, strerror(errno));
24653537Sbrian    return EX_OSERR;
24753537Sbrian  }
24853537Sbrian
24953537Sbrian  return 0;
25053537Sbrian}
25153537Sbrian
25253537Sbrianstatic void
25369948SjulianSpawn(const char *prog, const char *acname, const char *provider,
25486705Sbrian      const char *exec, struct ngm_connect ngc, int cs, int ds, void *request,
25586705Sbrian      int sz, int debug)
25653537Sbrian{
25753537Sbrian  char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
25853537Sbrian  struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
25953537Sbrian  struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
26053537Sbrian  struct ngpppoe_init_data *data;
261171195Sscf  char env[18], unknown[14], sessionid[5], *path;
26286705Sbrian  unsigned char *macaddr;
26353537Sbrian  const char *msg;
26453537Sbrian  int ret, slen;
26553537Sbrian
26653537Sbrian  switch ((ret = fork())) {
26753537Sbrian    case -1:
26853537Sbrian      syslog(LOG_ERR, "fork: %m");
26953537Sbrian      break;
27053537Sbrian
27153537Sbrian    case 0:
27253537Sbrian      switch (fork()) {
27353537Sbrian        case 0:
27453537Sbrian          break;
27553537Sbrian        case -1:
27653537Sbrian          _exit(errno);
27753537Sbrian        default:
27853537Sbrian          _exit(0);
27953537Sbrian      }
28053537Sbrian      close(cs);
28153537Sbrian      close(ds);
28253537Sbrian
28353537Sbrian      /* Create a new socket node */
28453537Sbrian      if (debug)
28553537Sbrian        syslog(LOG_INFO, "Creating a new socket node");
28653537Sbrian
28753537Sbrian      if (NgMkSockNode(NULL, &cs, &ds) == -1) {
28853537Sbrian        syslog(LOG_ERR, "Cannot create netgraph socket node: %m");
28953537Sbrian        _exit(EX_CANTCREAT);
29053537Sbrian      }
29153537Sbrian
29253537Sbrian      /* Connect the PPPoE node to our new socket node.  */
29353537Sbrian      snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid());
29453537Sbrian      memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
29553537Sbrian
29653537Sbrian      if (debug)
29753537Sbrian        syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s",
29853537Sbrian               ngc.ourhook, ngc.path, ngc.peerhook);
29953537Sbrian      if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
30053537Sbrian                    NGM_CONNECT, &ngc, sizeof ngc) < 0) {
30153537Sbrian        syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m");
30253537Sbrian        _exit(EX_OSERR);
30353537Sbrian      }
30453537Sbrian
30553537Sbrian      /*
30653537Sbrian       * If we tell the socket node not to LINGER, it will go away when
30753537Sbrian       * the last hook is removed.
30853537Sbrian       */
30953537Sbrian      if (debug)
31053537Sbrian        syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket");
31153537Sbrian      if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE,
31253537Sbrian                    NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) {
31353537Sbrian        syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m");
31453537Sbrian        _exit(EX_OSERR);
31553537Sbrian      }
31653537Sbrian
31753537Sbrian      /* Put the PPPoE node into OFFER mode */
31853537Sbrian      slen = strlen(acname);
31968032Sbrian      data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
32053537Sbrian      snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
32168846Sbrian      memcpy(data->data, acname, slen);
32268846Sbrian      data->data_len = slen;
32353537Sbrian
32453537Sbrian      path = (char *)alloca(strlen(ngc.ourhook) + 3);
32553537Sbrian      strcpy(path, ".:");
32653537Sbrian      strcpy(path + 2, ngc.ourhook);
32753537Sbrian
32853537Sbrian      syslog(LOG_INFO, "Offering to %s as access concentrator %s",
32953537Sbrian             path, acname);
33053537Sbrian      if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
33153537Sbrian                    data, sizeof *data + slen) == -1) {
33253537Sbrian        syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path);
33353537Sbrian        _exit(EX_OSERR);
33453537Sbrian      }
33569948Sjulian      /* If we have a provider code, set it */
33679597Sbrian      if (provider) {
33769948Sjulian        slen = strlen(provider);
33869948Sjulian        data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
33969948Sjulian        snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
34069948Sjulian        memcpy(data->data, provider, slen);
34169948Sjulian        data->data_len = slen;
34253537Sbrian
34369948Sjulian        syslog(LOG_INFO, "adding to %s as offered service %s",
34469948Sjulian             path, acname);
34569948Sjulian        if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE,
34669948Sjulian                    data, sizeof *data + slen) == -1) {
34769948Sjulian          syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path);
34869948Sjulian          _exit(EX_OSERR);
34969948Sjulian        }
35069948Sjulian      }
35169948Sjulian
35286705Sbrian      /* Put the peer's MAC address in the environment */
35386705Sbrian      if (sz >= sizeof(struct ether_header)) {
35486705Sbrian        macaddr = ((struct ether_header *)request)->ether_shost;
355171195Sscf        snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x",
35686762Sbrian                 macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4],
35786762Sbrian                 macaddr[5]);
358171195Sscf        if (setenv(HISMACADDR, env, 1) != 0)
359171195Sscf          syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR);
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;
41496580Sbrian          case NGM_PPPOE_SESSIONID:
41596580Sbrian            msg = "SESSIONID";
41696580Sbrian            snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts);
41796580Sbrian            if (setenv("SESSIONID", sessionid, 1) != 0)
41896580Sbrian              syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m",
41996580Sbrian                     sessionid);
42096580Sbrian            break;
42153537Sbrian          default:
42253537Sbrian            snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
42353537Sbrian            msg = unknown;
42453537Sbrian            break;
42553537Sbrian        }
42653537Sbrian
42753537Sbrian        switch (rep->header.cmd) {
42853537Sbrian          case NGM_PPPOE_FAIL:
42953537Sbrian          case NGM_PPPOE_CLOSE:
43053537Sbrian            syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")",
43153537Sbrian                   msg, sts->hook);
43253537Sbrian            _exit(0);
43353537Sbrian        }
43453537Sbrian
43553537Sbrian        syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook);
43653537Sbrian      } while (rep->header.cmd != NGM_PPPOE_SUCCESS);
43753537Sbrian
43853537Sbrian      dup2(ds, STDIN_FILENO);
43953537Sbrian      dup2(ds, STDOUT_FILENO);
44053537Sbrian      close(ds);
44153537Sbrian      close(cs);
44253537Sbrian
44353537Sbrian      setsid();
44453537Sbrian      syslog(LOG_INFO, "Executing: %s", exec);
44579452Sbrian      execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL);
44653537Sbrian      syslog(LOG_ERR, "execlp failed: %m");
44753537Sbrian      _exit(EX_OSFILE);
44853537Sbrian
44953537Sbrian    default:
45053537Sbrian      wait(&ret);
45153537Sbrian      errno = ret;
45253537Sbrian      if (errno)
45353537Sbrian        syslog(LOG_ERR, "Second fork failed: %m");
45453537Sbrian      break;
45553537Sbrian  }
45653537Sbrian}
45753537Sbrian
45866602Sbrian#ifndef NOKLDLOAD
45969582Sbrianstatic int
46066602SbrianLoadModules(void)
46166602Sbrian{
46266602Sbrian  const char *module[] = { "netgraph", "ng_socket", "ng_ether", "ng_pppoe" };
46366602Sbrian  int f;
46466602Sbrian
46566602Sbrian  for (f = 0; f < sizeof module / sizeof *module; f++)
46666602Sbrian    if (modfind(module[f]) == -1 && kldload(module[f]) == -1) {
46766602Sbrian      fprintf(stderr, "kldload: %s: %s\n", module[f], strerror(errno));
46866602Sbrian      return 0;
46966602Sbrian    }
47066602Sbrian
47166602Sbrian  return 1;
47266602Sbrian}
47366602Sbrian#endif
47466602Sbrian
47569582Sbrianstatic void
47666602Sbriannglog(const char *fmt, ...)
47766602Sbrian{
47866602Sbrian  char nfmt[256];
47966602Sbrian  va_list ap;
48066602Sbrian
48166602Sbrian  snprintf(nfmt, sizeof nfmt, "%s: %s", fmt, strerror(errno));
48266602Sbrian  va_start(ap, fmt);
48366602Sbrian  vsyslog(LOG_INFO, nfmt, ap);
48466602Sbrian  va_end(ap);
48566602Sbrian}
48666602Sbrian
48769582Sbrianstatic void
48866602Sbriannglogx(const char *fmt, ...)
48966602Sbrian{
49066602Sbrian  va_list ap;
49166602Sbrian
49266602Sbrian  va_start(ap, fmt);
49366602Sbrian  vsyslog(LOG_INFO, fmt, ap);
49466602Sbrian  va_end(ap);
49566602Sbrian}
49666602Sbrian
49766602Sbrianint
49890779Simpmain(int argc, char *argv[])
49953537Sbrian{
500122758Sharti  char hostname[MAXHOSTNAMELEN], *exec, rhook[NG_HOOKSIZ];
50166602Sbrian  unsigned char response[1024];
50280728Sbrian  const char *label, *prog, *provider, *acname;
50353537Sbrian  struct ngm_connect ngc;
50480724Sbrian  struct sigaction act;
50566602Sbrian  int ch, cs, ds, ret, optF, optd, optn, sz, f;
50669582Sbrian  const char *pidfile;
50753537Sbrian
50853537Sbrian  prog = strrchr(argv[0], '/');
50953537Sbrian  prog = prog ? prog + 1 : argv[0];
51053609Sbrian  pidfile = NULL;
51153537Sbrian  exec = NULL;
51280728Sbrian  label = NULL;
51353537Sbrian  acname = NULL;
51453537Sbrian  provider = "";
51566602Sbrian  optF = optd = optn = 0;
51653537Sbrian
51780728Sbrian  while ((ch = getopt(argc, argv, "FP:a:de:l:n:p:")) != -1) {
51853537Sbrian    switch (ch) {
51953537Sbrian      case 'F':
52053537Sbrian        optF = 1;
52153537Sbrian        break;
52253537Sbrian
52353609Sbrian      case 'P':
52453609Sbrian        pidfile = optarg;
52553609Sbrian        break;
52653609Sbrian
52753537Sbrian      case 'a':
52853537Sbrian        acname = optarg;
52953537Sbrian        break;
53053537Sbrian
53153537Sbrian      case 'd':
53253537Sbrian        optd = 1;
53353537Sbrian        break;
53453537Sbrian
53553537Sbrian      case 'e':
53653537Sbrian        exec = optarg;
53753537Sbrian        break;
53853537Sbrian
53980728Sbrian      case 'l':
54080728Sbrian        label = optarg;
54180728Sbrian        break;
54280728Sbrian
54366602Sbrian      case 'n':
54466602Sbrian        optn = 1;
54566602Sbrian        NgSetDebug(atoi(optarg));
54666602Sbrian        break;
54766602Sbrian
54853537Sbrian      case 'p':
54953537Sbrian        provider = optarg;
55053537Sbrian        break;
55153537Sbrian
55253537Sbrian      default:
55353537Sbrian        return usage(prog);
55453537Sbrian    }
55553537Sbrian  }
55653537Sbrian
55753537Sbrian  if (optind >= argc || optind + 2 < argc)
55853537Sbrian    return usage(prog);
55953537Sbrian
56080728Sbrian  if (exec != NULL && label != NULL)
56180728Sbrian    return usage(prog);
56280728Sbrian
56353537Sbrian  if (exec == NULL) {
56480728Sbrian    if (label == NULL)
56580728Sbrian      label = provider;
56680728Sbrian    if (label == NULL) {
56780728Sbrian      fprintf(stderr, "%s: Either a provider, a label or an exec command"
56853537Sbrian              " must be given\n", prog);
56953537Sbrian      return usage(prog);
57053537Sbrian    }
57180728Sbrian    exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(label));
57253537Sbrian    if (exec == NULL) {
573228586Sdim      fprintf(stderr, "%s: Cannot allocate %zu bytes\n", prog,
574228586Sdim              sizeof DEFAULT_EXEC_PREFIX + strlen(label));
57553537Sbrian      return EX_OSERR;
57653537Sbrian    }
57753537Sbrian    strcpy(exec, DEFAULT_EXEC_PREFIX);
57880728Sbrian    strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, label);
57953537Sbrian  }
58053537Sbrian
58153537Sbrian  if (acname == NULL) {
58253537Sbrian    char *dot;
58353537Sbrian
58453537Sbrian    if (gethostname(hostname, sizeof hostname))
58553537Sbrian      strcpy(hostname, "localhost");
58653537Sbrian    else if ((dot = strchr(hostname, '.')))
58753537Sbrian      *dot = '\0';
58853537Sbrian
58953537Sbrian    acname = hostname;
59053537Sbrian  }
59153537Sbrian
59253537Sbrian#ifndef NOKLDLOAD
59366602Sbrian  if (!LoadModules())
59453537Sbrian    return EX_UNAVAILABLE;
59553537Sbrian#endif
59653537Sbrian
59753537Sbrian  /* Create a socket node */
59853537Sbrian  if (NgMkSockNode(NULL, &cs, &ds) == -1) {
59953537Sbrian    perror("Cannot create netgraph socket node");
60053537Sbrian    return EX_CANTCREAT;
60153537Sbrian  }
60253537Sbrian
60353537Sbrian  /* Connect it up (and fill in `ngc') */
60453537Sbrian  if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds,
60553537Sbrian                           optd, &ngc)) != 0) {
60653537Sbrian    close(cs);
60753537Sbrian    close(ds);
60853537Sbrian    return ret;
60953537Sbrian  }
61053537Sbrian
61153537Sbrian  if (!optF && daemon(1, 0) == -1) {
61253537Sbrian    perror("daemon()");
61353609Sbrian    close(cs);
61453609Sbrian    close(ds);
61553537Sbrian    return EX_OSERR;
61653537Sbrian  }
61753537Sbrian
61853609Sbrian
61953609Sbrian  if (pidfile != NULL) {
62053609Sbrian    FILE *fp;
62153609Sbrian
62253609Sbrian    if ((fp = fopen(pidfile, "w")) == NULL) {
62353609Sbrian      perror(pidfile);
62453609Sbrian      close(cs);
62553609Sbrian      close(ds);
62653609Sbrian      return EX_CANTCREAT;
62753609Sbrian    } else {
62853609Sbrian      fprintf(fp, "%d\n", (int)getpid());
62953609Sbrian      fclose(fp);
63053609Sbrian    }
63153609Sbrian  }
63253609Sbrian
63353537Sbrian  openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON);
63466602Sbrian  if (!optF && optn)
63566602Sbrian    NgSetErrLog(nglog, nglogx);
63653537Sbrian
63780724Sbrian  memset(&act, '\0', sizeof act);
63880724Sbrian  act.sa_handler = Farewell;
63980733Sbrian  act.sa_flags = 0;
64080724Sbrian  sigemptyset(&act.sa_mask);
64180724Sbrian  sigaction(SIGHUP, &act, NULL);
64280724Sbrian  sigaction(SIGINT, &act, NULL);
64380724Sbrian  sigaction(SIGQUIT, &act, NULL);
64480724Sbrian  sigaction(SIGTERM, &act, NULL);
64553537Sbrian
64669582Sbrian  while (!ReceivedSignal) {
64753537Sbrian    if (*provider)
64853537Sbrian      syslog(LOG_INFO, "Listening as provider %s", provider);
64953537Sbrian    else
65053537Sbrian      syslog(LOG_INFO, "Listening");
65153537Sbrian
65253537Sbrian    switch (sz = NgRecvData(ds, response, sizeof response, rhook)) {
65353537Sbrian      case -1:
65453537Sbrian        syslog(LOG_INFO, "NgRecvData: %m");
65553537Sbrian        break;
65653537Sbrian      case 0:
65753537Sbrian        syslog(LOG_INFO, "NgRecvData: socket closed");
65853537Sbrian        break;
65953537Sbrian      default:
66053537Sbrian        if (optd) {
66153537Sbrian          char *dbuf, *ptr;
66253537Sbrian
66353537Sbrian          ptr = dbuf = alloca(sz * 2 + 1);
66453537Sbrian          for (f = 0; f < sz; f++, ptr += 2)
66553537Sbrian            sprintf(ptr, "%02x", (u_char)response[f]);
66653537Sbrian          *ptr = '\0';
66753537Sbrian          syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf);
66853537Sbrian        }
66953537Sbrian    }
67053537Sbrian    if (sz <= 0) {
67153537Sbrian      ret = EX_UNAVAILABLE;
67253537Sbrian      break;
67353537Sbrian    }
67469948Sjulian    Spawn(prog, acname, provider, exec, ngc, cs, ds, response, sz, optd);
67553537Sbrian  }
67653537Sbrian
67769582Sbrian  if (pidfile)
67869582Sbrian    remove(pidfile);
67969582Sbrian
68069582Sbrian  if (ReceivedSignal) {
68169582Sbrian    syslog(LOG_INFO, "Received signal %d, exiting", ReceivedSignal);
68269582Sbrian
68369582Sbrian    signal(ReceivedSignal, SIG_DFL);
68469582Sbrian    raise(ReceivedSignal);
68569582Sbrian
68669582Sbrian    /* NOTREACHED */
68769582Sbrian
68869582Sbrian    ret = -ReceivedSignal;
68969582Sbrian  }
69069582Sbrian
69153537Sbrian  return ret;
69253537Sbrian}
693