diff options
Diffstat (limited to 'libexec/pppoed')
-rw-r--r-- | libexec/pppoed/Makefile | 11 | ||||
-rw-r--r-- | libexec/pppoed/pppoed.8 | 219 | ||||
-rw-r--r-- | libexec/pppoed/pppoed.c | 692 |
3 files changed, 922 insertions, 0 deletions
diff --git a/libexec/pppoed/Makefile b/libexec/pppoed/Makefile new file mode 100644 index 0000000..1ffaffe --- /dev/null +++ b/libexec/pppoed/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= pppoed +DPADD= ${LIBNETGRAPH} +LDADD= -lnetgraph +MAN= pppoed.8 + +WARNS?= 1 +WFORMAT=0 + +.include <bsd.prog.mk> diff --git a/libexec/pppoed/pppoed.8 b/libexec/pppoed/pppoed.8 new file mode 100644 index 0000000..2f78353 --- /dev/null +++ b/libexec/pppoed/pppoed.8 @@ -0,0 +1,219 @@ +.\"- +.\" Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 8, 1999 +.Dt PPPOED 8 +.Os +.Sh NAME +.Nm pppoed +.Nd handle incoming PPP over Ethernet connections +.Sh SYNOPSIS +.Nm +.Op Fl Fd\& +.Op Fl P Ar pidfile +.Op Fl a Ar name +.Op Fl e Ar exec | Fl l Ar label +.Op Fl n Ar ngdebug +.Op Fl p Ar provider +.Ar interface +.Sh DESCRIPTION +The +.Nm +utility listens to the given +.Ar interface +for PPP over Ethernet (PPPoE) service request packets, and actions them +by negotiating a session then invoking a +.Xr ppp 8 +program. +The negotiation is implemented by the +.Dq pppoe +netgraph node. +See +.Xr ng_pppoe 4 +for details. +.Pp +The +.Nm +utility +will only offer services to clients requesting services from the given +.Ar provider , +which is taken as an empty name if not provided. +If a provider name of +.Dq * +is given, any PPPoE requests will be offered service. +.Pp +The supplied +.Ar name +will be given as the access concentrator name when establishing the connection. +If no +.Ar name +is given, the current base hostname is used. +.Pp +After receiving a request (PADI) from the PPPoE netgraph node, +.Nm +.Xr fork 2 Ns s +a child process and returns to service further requests. +The child process offers service +(using +.Ar name ) +and waits for a +.Dv SUCCESS +indication from the PPPoE node. +On receipt of the +.Dv SUCCESS +indication, +.Nm +will execute +.Pp +.D1 Ic exec Pa /usr/sbin/ppp Fl direct Ar label +.Pp +as a shell sub-process. +If +.Ar label +has not been specified, it defaults to +.Ar provider . +It is possible to specify another command using the +.Ar exec +argument. +This is mandatory if +.Ar provider +and +.Ar label +are not given. +The child process will have standard input and standard output +attached to the same +.Xr netgraph 4 +data socket +(see +.Xr ng_socket 4 ) +when started. +.Pp +The environment variables +.Ev HISMACADDR +and +.Ev ACNAME +are made available to the child process and are set to the MAC address of +the peer and the name of the AC respectively. +.Pp +Upon invocation, +.Nm +will attach a +.Dq pppoe +netgraph node to the relevant +.Dq ether +node using +.Dq Ar interface Ns \&: +as the node name, and then connect that +.Dq pppoe +node to a local +.Dq socket +node. +If the +.Fl F +option has not been given, +.Nm +will then go into the background and disassociate itself from the controlling +terminal. +When the +.Fl F +option is given, +.Nm +stays in the foreground. +.Pp +If the +.Fl d +option is given, additional diagnostics are provided (see the +.Sx DIAGNOSTICS +section below). +If the +.Fl n +option is given, +.Fn NgSetDebug +is called with an argument of +.Ar ngdebug . +.Pp +If +.Ar pidfile +is given, +.Nm +will write its process ID to this file on startup. +.Sh DIAGNOSTICS +After creating the necessary +.Xr netgraph 4 +nodes as described above, +.Nm +uses +.Xr syslogd 8 +to report all incoming connections. +If the +.Fl d +option is given, +.Nm +will report on the child processes creation of a new netgraph socket, its +service offer and the invocation of the +.Xr ppp 8 +program. +If the +.Fl n +option is given, netgraph diagnostic messages are also redirected to +.Xr syslogd 8 . +.Pp +It is sometimes useful to add the following to +.Pa /etc/syslog.conf : +.Bd -literal -offset indent +!pppoed +*.* /var/log/pppoed.log +.Ed +.Pp +and the following to +.Pa /etc/newsyslog.conf : +.Pp +.Dl "/var/log/pppoed.log 640 3 100 * Z" +.Sh SEE ALSO +.Xr NgSetDebug 3 , +.Xr netgraph 4 , +.Xr ng_ether 4 , +.Xr ng_pppoe 4 , +.Xr ng_socket 4 , +.Xr syslog.conf 5 , +.Xr ppp 8 , +.Xr syslogd 8 +.Sh HISTORY +The +.Nm +utility was written by +.An Brian Somers Aq brian@Awfulhak.org +and first appeared in +.Fx 3.4 . +.Sh BUGS +If another netgraph node is using the given interface, +.Nm +will fail to start. +This is because +.Xr netgraph 4 +does not currently allow node chaining. +This may change in the future. diff --git a/libexec/pppoed/pppoed.c b/libexec/pppoed/pppoed.c new file mode 100644 index 0000000..a5c0b7a --- /dev/null +++ b/libexec/pppoed/pppoed.c @@ -0,0 +1,692 @@ +/*- + * Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <netgraph.h> +#include <net/ethernet.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netgraph/ng_ether.h> +#include <netgraph/ng_message.h> +#include <netgraph/ng_pppoe.h> +#include <netgraph/ng_socket.h> + +#include <errno.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <sys/fcntl.h> +#ifndef NOKLDLOAD +#include <sys/linker.h> +#include <sys/module.h> +#endif +#include <sys/uio.h> +#include <sys/wait.h> +#include <syslog.h> +#include <termios.h> +#include <unistd.h> + + +#define DEFAULT_EXEC_PREFIX "exec /usr/sbin/ppp -direct " +#define HISMACADDR "HISMACADDR" +#define SESSION_ID "SESSION_ID" + +static void nglogx(const char *, ...) __printflike(1, 2); + +static int ReceivedSignal; + +static int +usage(const char *prog) +{ + fprintf(stderr, "usage: %s [-Fd] [-P pidfile] [-a name] [-e exec | -l label]" + " [-n ngdebug] [-p provider] interface\n", prog); + return EX_USAGE; +} + +static void +Farewell(int sig) +{ + ReceivedSignal = sig; +} + +static int +ConfigureNode(const char *prog, const char *iface, const char *provider, + int cs, int ds, int debug, struct ngm_connect *ngc) +{ + /* + * We're going to do this with the passed `ds' & `cs' descriptors: + * + * .---------. + * | ether | + * | <iface> | + * `---------' + * (orphan) ds cs + * | | | + * | | | + * (ethernet) | | + * .---------. .-----------. + * | pppoe | | socket | + * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> | + * `--------- `-----------' + * (exec-<pid>) + * ^ .-----------. .-------------. + * | | socket | | ppp -direct | + * `--->(exec-<pid>)| <unnamed> |--fd--| provider | + * `-----------' `-------------' + * + * where there are potentially many ppp processes running off of the + * same PPPoE node. + * The exec-<pid> hook isn't made 'till we Spawn(). + */ + + char *epath, *spath; + struct ngpppoe_init_data *data; + const struct hooklist *hlist; + const struct nodeinfo *ninfo; + const struct linkinfo *nlink; + struct ngm_mkpeer mkp; + struct ng_mesg *resp; + u_char rbuf[2048]; + int f, plen; + + /* + * Ask for a list of hooks attached to the "ether" node. This node should + * magically exist as a way of hooking stuff onto an ethernet device + */ + epath = (char *)alloca(strlen(iface) + 2); + sprintf(epath, "%s:", iface); + + if (debug) + fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath); + + if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { + if (errno == ENOENT) + fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n", + epath); + else + fprintf(stderr, "%s Cannot send a netgraph message: %s\n", + epath, strerror(errno)); + return EX_UNAVAILABLE; + } + + /* Get our list back */ + resp = (struct ng_mesg *)rbuf; + if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) { + perror("Cannot get netgraph response"); + return EX_UNAVAILABLE; + } + + hlist = (const struct hooklist *)resp->data; + ninfo = &hlist->nodeinfo; + + if (debug) + fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n", + ninfo->id, ninfo->type, ninfo->hooks); + + /* Make sure we've got the right type of node */ + if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) { + fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``" + NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type); + return EX_DATAERR; + } + + /* look for a hook already attached. */ + for (f = 0; f < ninfo->hooks; f++) { + nlink = &hlist->link[f]; + + if (debug) + fprintf(stderr, " Got [%x]:%s -> [%x]:%s\n", ninfo->id, + nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook); + + if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) || + !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) { + /* + * Something is using the data coming out of this `ether' node. + * If it's a PPPoE node, we use that node, otherwise we complain that + * someone else is using the node. + */ + if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) { + fprintf(stderr, "%s Node type %s is currently active\n", + epath, nlink->nodeinfo.type); + return EX_UNAVAILABLE; + } + break; + } + } + + if (f == ninfo->hooks) { + /* + * Create a new PPPoE node connected to the `ether' node using + * the magic `orphan' and `ethernet' hooks + */ + snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE); + snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN); + snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET); + + if (debug) + fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath, + mkp.ourhook, mkp.type, mkp.peerhook); + + if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, + NGM_MKPEER, &mkp, sizeof mkp) < 0) { + fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n", + epath, strerror(errno)); + return EX_OSERR; + } + } + + /* Connect the PPPoE node to our socket node. */ + snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN); + snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid()); + memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook); + + if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, + NGM_CONNECT, ngc, sizeof *ngc) < 0) { + perror("Cannot CONNECT PPPoE and socket nodes"); + return EX_OSERR; + } + + plen = strlen(provider); + + data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen); + snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook); + memcpy(data->data, provider, plen); + data->data_len = plen; + + spath = (char *)alloca(strlen(ngc->peerhook) + 3); + strcpy(spath, ".:"); + strcpy(spath + 2, ngc->ourhook); + + if (debug) { + if (provider) + fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n", + spath, provider); + else + fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath); + } + + if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN, + data, sizeof *data + plen) == -1) { + fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n", + spath, strerror(errno)); + return EX_OSERR; + } + + return 0; +} + +static void +Spawn(const char *prog, const char *acname, const char *provider, + const char *exec, struct ngm_connect ngc, int cs, int ds, void *request, + int sz, int debug) +{ + char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)]; + struct ng_mesg *rep = (struct ng_mesg *)msgbuf; + struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep); + struct ngpppoe_init_data *data; + char env[18], unknown[14], sessionid[5], *path; + unsigned char *macaddr; + const char *msg; + int ret, slen; + + switch ((ret = fork())) { + case -1: + syslog(LOG_ERR, "fork: %m"); + break; + + case 0: + switch (fork()) { + case 0: + break; + case -1: + _exit(errno); + default: + _exit(0); + } + close(cs); + close(ds); + + /* Create a new socket node */ + if (debug) + syslog(LOG_INFO, "Creating a new socket node"); + + if (NgMkSockNode(NULL, &cs, &ds) == -1) { + syslog(LOG_ERR, "Cannot create netgraph socket node: %m"); + _exit(EX_CANTCREAT); + } + + /* Connect the PPPoE node to our new socket node. */ + snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid()); + memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); + + if (debug) + syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s", + ngc.ourhook, ngc.path, ngc.peerhook); + if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, + NGM_CONNECT, &ngc, sizeof ngc) < 0) { + syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m"); + _exit(EX_OSERR); + } + + /* + * If we tell the socket node not to LINGER, it will go away when + * the last hook is removed. + */ + if (debug) + syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket"); + if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE, + NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) { + syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m"); + _exit(EX_OSERR); + } + + /* Put the PPPoE node into OFFER mode */ + slen = strlen(acname); + data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen); + snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook); + memcpy(data->data, acname, slen); + data->data_len = slen; + + path = (char *)alloca(strlen(ngc.ourhook) + 3); + strcpy(path, ".:"); + strcpy(path + 2, ngc.ourhook); + + syslog(LOG_INFO, "Offering to %s as access concentrator %s", + path, acname); + if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER, + data, sizeof *data + slen) == -1) { + syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path); + _exit(EX_OSERR); + } + /* If we have a provider code, set it */ + if (provider) { + slen = strlen(provider); + data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen); + snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook); + memcpy(data->data, provider, slen); + data->data_len = slen; + + syslog(LOG_INFO, "adding to %s as offered service %s", + path, acname); + if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE, + data, sizeof *data + slen) == -1) { + syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path); + _exit(EX_OSERR); + } + } + + /* Put the peer's MAC address in the environment */ + if (sz >= sizeof(struct ether_header)) { + macaddr = ((struct ether_header *)request)->ether_shost; + snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x", + macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], + macaddr[5]); + if (setenv(HISMACADDR, env, 1) != 0) + syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR); + } + + /* And send our request data to the waiting node */ + if (debug) + syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz); + if (NgSendData(ds, ngc.ourhook, request, sz) == -1) { + syslog(LOG_ERR, "Cannot send original request to %s: %m", path); + _exit(EX_OSERR); + } + + /* Then wait for a success indication */ + + if (debug) + syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path); + + do { + if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) { + syslog(LOG_ERR, "%s: Cannot receive a message: %m", path); + _exit(EX_OSERR); + } + + if (ret == 0) { + /* The socket has been closed */ + syslog(LOG_INFO, "%s: Client timed out", path); + _exit(EX_TEMPFAIL); + } + + if (rep->header.version != NG_VERSION) { + syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld", + (long)rep->header.version, (long)NG_VERSION); + _exit(EX_PROTOCOL); + } + + if (rep->header.typecookie != NGM_PPPOE_COOKIE) { + syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld", + (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE); + continue; + } + + switch (rep->header.cmd) { + case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break; + case NGM_PPPOE_CONNECT: msg = "CONNECT"; break; + case NGM_PPPOE_LISTEN: msg = "LISTEN"; break; + case NGM_PPPOE_OFFER: msg = "OFFER"; break; + case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break; + case NGM_PPPOE_FAIL: msg = "FAIL"; break; + case NGM_PPPOE_CLOSE: msg = "CLOSE"; break; + case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break; + case NGM_PPPOE_ACNAME: + msg = "ACNAME"; + if (setenv("ACNAME", sts->hook, 1) != 0) + syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m", + sts->hook); + break; + case NGM_PPPOE_SESSIONID: + msg = "SESSIONID"; + snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts); + if (setenv("SESSIONID", sessionid, 1) != 0) + syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m", + sessionid); + break; + default: + snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd); + msg = unknown; + break; + } + + switch (rep->header.cmd) { + case NGM_PPPOE_FAIL: + case NGM_PPPOE_CLOSE: + syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")", + msg, sts->hook); + _exit(0); + } + + syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook); + } while (rep->header.cmd != NGM_PPPOE_SUCCESS); + + dup2(ds, STDIN_FILENO); + dup2(ds, STDOUT_FILENO); + close(ds); + close(cs); + + setsid(); + syslog(LOG_INFO, "Executing: %s", exec); + execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL); + syslog(LOG_ERR, "execlp failed: %m"); + _exit(EX_OSFILE); + + default: + wait(&ret); + errno = ret; + if (errno) + syslog(LOG_ERR, "Second fork failed: %m"); + break; + } +} + +#ifndef NOKLDLOAD +static int +LoadModules(void) +{ + const char *module[] = { "netgraph", "ng_socket", "ng_ether", "ng_pppoe" }; + int f; + + for (f = 0; f < sizeof module / sizeof *module; f++) + if (modfind(module[f]) == -1 && kldload(module[f]) == -1) { + fprintf(stderr, "kldload: %s: %s\n", module[f], strerror(errno)); + return 0; + } + + return 1; +} +#endif + +static void +nglog(const char *fmt, ...) +{ + char nfmt[256]; + va_list ap; + + snprintf(nfmt, sizeof nfmt, "%s: %s", fmt, strerror(errno)); + va_start(ap, fmt); + vsyslog(LOG_INFO, nfmt, ap); + va_end(ap); +} + +static void +nglogx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_INFO, fmt, ap); + va_end(ap); +} + +int +main(int argc, char *argv[]) +{ + char hostname[MAXHOSTNAMELEN], *exec, rhook[NG_HOOKSIZ]; + unsigned char response[1024]; + const char *label, *prog, *provider, *acname; + struct ngm_connect ngc; + struct sigaction act; + int ch, cs, ds, ret, optF, optd, optn, sz, f; + const char *pidfile; + + prog = strrchr(argv[0], '/'); + prog = prog ? prog + 1 : argv[0]; + pidfile = NULL; + exec = NULL; + label = NULL; + acname = NULL; + provider = ""; + optF = optd = optn = 0; + + while ((ch = getopt(argc, argv, "FP:a:de:l:n:p:")) != -1) { + switch (ch) { + case 'F': + optF = 1; + break; + + case 'P': + pidfile = optarg; + break; + + case 'a': + acname = optarg; + break; + + case 'd': + optd = 1; + break; + + case 'e': + exec = optarg; + break; + + case 'l': + label = optarg; + break; + + case 'n': + optn = 1; + NgSetDebug(atoi(optarg)); + break; + + case 'p': + provider = optarg; + break; + + default: + return usage(prog); + } + } + + if (optind >= argc || optind + 2 < argc) + return usage(prog); + + if (exec != NULL && label != NULL) + return usage(prog); + + if (exec == NULL) { + if (label == NULL) + label = provider; + if (label == NULL) { + fprintf(stderr, "%s: Either a provider, a label or an exec command" + " must be given\n", prog); + return usage(prog); + } + exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(label)); + if (exec == NULL) { + fprintf(stderr, "%s: Cannot allocate %zu bytes\n", prog, + sizeof DEFAULT_EXEC_PREFIX + strlen(label)); + return EX_OSERR; + } + strcpy(exec, DEFAULT_EXEC_PREFIX); + strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, label); + } + + if (acname == NULL) { + char *dot; + + if (gethostname(hostname, sizeof hostname)) + strcpy(hostname, "localhost"); + else if ((dot = strchr(hostname, '.'))) + *dot = '\0'; + + acname = hostname; + } + +#ifndef NOKLDLOAD + if (!LoadModules()) + return EX_UNAVAILABLE; +#endif + + /* Create a socket node */ + if (NgMkSockNode(NULL, &cs, &ds) == -1) { + perror("Cannot create netgraph socket node"); + return EX_CANTCREAT; + } + + /* Connect it up (and fill in `ngc') */ + if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds, + optd, &ngc)) != 0) { + close(cs); + close(ds); + return ret; + } + + if (!optF && daemon(1, 0) == -1) { + perror("daemon()"); + close(cs); + close(ds); + return EX_OSERR; + } + + + if (pidfile != NULL) { + FILE *fp; + + if ((fp = fopen(pidfile, "w")) == NULL) { + perror(pidfile); + close(cs); + close(ds); + return EX_CANTCREAT; + } else { + fprintf(fp, "%d\n", (int)getpid()); + fclose(fp); + } + } + + openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON); + if (!optF && optn) + NgSetErrLog(nglog, nglogx); + + memset(&act, '\0', sizeof act); + act.sa_handler = Farewell; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + while (!ReceivedSignal) { + if (*provider) + syslog(LOG_INFO, "Listening as provider %s", provider); + else + syslog(LOG_INFO, "Listening"); + + switch (sz = NgRecvData(ds, response, sizeof response, rhook)) { + case -1: + syslog(LOG_INFO, "NgRecvData: %m"); + break; + case 0: + syslog(LOG_INFO, "NgRecvData: socket closed"); + break; + default: + if (optd) { + char *dbuf, *ptr; + + ptr = dbuf = alloca(sz * 2 + 1); + for (f = 0; f < sz; f++, ptr += 2) + sprintf(ptr, "%02x", (u_char)response[f]); + *ptr = '\0'; + syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf); + } + } + if (sz <= 0) { + ret = EX_UNAVAILABLE; + break; + } + Spawn(prog, acname, provider, exec, ngc, cs, ds, response, sz, optd); + } + + if (pidfile) + remove(pidfile); + + if (ReceivedSignal) { + syslog(LOG_INFO, "Received signal %d, exiting", ReceivedSignal); + + signal(ReceivedSignal, SIG_DFL); + raise(ReceivedSignal); + + /* NOTREACHED */ + + ret = -ReceivedSignal; + } + + return ret; +} |