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) { 573229139Sdim fprintf(stderr, "%s: Cannot allocate %zu bytes\n", prog, 574229139Sdim 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