/*
* pppoe.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "pppoe.h"
#include "ngfunc.h"
#include "log.h"
#include "util.h"
#include <net/ethernet.h>
#include <netgraph/ng_message.h>
#ifdef __DragonFly__
#include <netgraph/pppoe/ng_pppoe.h>
#include <netgraph/ether/ng_ether.h>
#include <netgraph/tee/ng_tee.h>
#else
#include <netgraph/ng_pppoe.h>
#include <netgraph/ng_ether.h>
#include <netgraph/ng_tee.h>
#endif
#include <netgraph.h>
#include <sys/param.h>
#include <sys/linker.h>
/*
* DEFINITIONS
*/
#define PPPOE_MTU 1492 /* allow room for PPPoE overhead */
#define PPPOE_MRU 1492
#define PPPOE_REOPEN_PAUSE 5
#define PPPOE_CONNECT_TIMEOUT 9
#define ETHER_DEFAULT_HOOK NG_ETHER_HOOK_ORPHAN
#define PPPOE_MAXPARENTIFS 1024
#define MAX_PATH 64 /* XXX should be NG_PATHLEN */
#define MAX_SESSION 64 /* max length of PPPoE session name */
/* Per link private info */
struct pppoeinfo {
char path[MAX_PATH]; /* PPPoE node path */
char hook[NG_HOOKLEN + 1]; /* hook on that node */
char session[MAX_SESSION]; /* session name */
char acname[PPPOE_SERVICE_NAME_SIZE]; /* AC name */
u_char peeraddr[6]; /* Peer MAC address */
u_char incoming; /* incoming vs. outgoing */
u_char opened; /* PPPoE opened by phys */
struct optinfo options;
struct PppoeIf *PIf; /* pointer on parent ng_pppoe info */
struct pppTimer connectTimer; /* connection timeout timer */
};
typedef struct pppoeinfo *PppoeInfo;
static u_char gNgEtherLoaded = FALSE;
/* Set menu options */
enum {
SET_IFACE,
SET_SESSION,
SET_ACNAME,
SET_ENABLE,
SET_DISABLE,
};
enum {
PPPOE_CONF_ORIGINATE, /* allow originating connections to peer */
PPPOE_CONF_INCOMING, /* allow accepting connections from peer */
};
/*
Invariants:
----------
PPPOE_DOWN
- ng_pppoe(4) node does not exist
- pe->csock == -1
- Connect timeout timer is not running
PPPOE_CONNECTING
- ng_pppoe(4) node exists and is connected to ether and ppp nodes
- pe->csock != -1
- Listening for control messages rec'd on pe->csock
- Connect timeout timer is running
- NGM_PPPOE_CONNECT has been sent to the ng_pppoe(4) node, and
no response has been received yet
PPPOE_UP
- ng_pppoe(4) node exists and is connected to ether and ppp nodes
- pe->csock != -1
- Listening for control messages rec'd on pe->csock
- Connect timeout timer is not running
- NGM_PPPOE_CONNECT has been sent to the ng_pppoe(4) node, and
a NGM_PPPOE_SUCCESS has been received
*/
/*
* INTERNAL FUNCTIONS
*/
static int PppoeInit(PhysInfo p);
static void PppoeOpen(PhysInfo p);
static void PppoeClose(PhysInfo p);
static void PppoeShutdown(PhysInfo p);
static int PppoePeerAddr(PhysInfo p, void *buf, int buf_len);
static int PppoeCallingNum(PhysInfo p, void *buf, int buf_len);
static int PppoeCalledNum(PhysInfo p, void *buf, int buf_len);
static void PppoeCtrlReadEvent(int type, void *arg);
static void PppoeConnectTimeout(void *arg);
static void PppoeStat(Context ctx);
static int PppoeSetCommand(Context ctx, int ac, char *av[], void *arg);
static int PppoeOriginated(PhysInfo p);
static int PppoeIsSync(PhysInfo p);
static void PppoeNodeUpdate(PhysInfo p);
static void PppoeListenUpdate(void *arg);
/*
* GLOBAL VARIABLES
*/
const struct phystype gPppoePhysType = {
.name = "pppoe",
.minReopenDelay = PPPOE_REOPEN_PAUSE,
.mtu = PPPOE_MTU,
.mru = PPPOE_MRU,
.init = PppoeInit,
.open = PppoeOpen,
.close = PppoeClose,
.shutdown = PppoeShutdown,
.showstat = PppoeStat,
.originate = PppoeOriginated,
.issync = PppoeIsSync,
.peeraddr = PppoePeerAddr,
.callingnum = PppoeCallingNum,
.callednum = PppoeCalledNum,
};
const struct cmdtab PppoeSetCmds[] = {
{ "iface ifacename", "Set ethernet interface to use",
PppoeSetCommand, NULL, (void *)SET_IFACE },
{ "service string", "Set PPPoE session name",
PppoeSetCommand, NULL, (void *)SET_SESSION },
{ "acname string", "Set PPPoE access concentrator name",
PppoeSetCommand, NULL, (void *)SET_ACNAME },
{ "enable [opt ...]", "Enable option",
PppoeSetCommand, NULL, (void *)SET_ENABLE },
{ "disable [opt ...]", "Disable option",
PppoeSetCommand, NULL, (void *)SET_DISABLE },
{ NULL },
};
/*
* INTERNAL VARIABLES
*/
struct PppoeIf {
char ifnodepath[MAX_PATH];
char session[MAX_SESSION];
int listen;
int csock; /* netgraph Control socket */
int dsock; /* netgraph Data socket */
EventRef ctrlEvent; /* listen for ctrl messages */
EventRef dataEvent; /* listen for data messages */
};
int PppoeIfCount=0;
struct PppoeIf PppoeIfs[PPPOE_MAXPARENTIFS];
int PppoeListenUpdateSheduled=0;
struct pppTimer PppoeListenUpdateTimer;
static struct confinfo gConfList[] = {
{ 0, PPPOE_CONF_ORIGINATE, "originate" },
{ 0, PPPOE_CONF_INCOMING, "incoming" },
{ 0, 0, NULL },
};
/*
* PppoeInit()
*
* Initialize device-specific data in physical layer info
*/
static int
PppoeInit(PhysInfo p)
{
PppoeInfo pe;
/* Allocate private struct */
pe = (PppoeInfo)(p->info = Malloc(MB_PHYS, sizeof(*pe)));
pe->incoming = 0;
pe->opened = 0;
snprintf(pe->path, sizeof(pe->path), "undefined:");
snprintf(pe->hook, sizeof(pe->hook), "undefined");
snprintf(pe->session, sizeof(pe->session), "*");
memset(pe->peeraddr, 0x00, ETHER_ADDR_LEN);
pe->PIf = NULL;
/* Done */
return(0);
}
/*
* PppoeOpen()
*/
static void
PppoeOpen(PhysInfo p)
{
PppoeInfo pe = (PppoeInfo)p->info;
struct ngm_connect cn;
union {
u_char buf[sizeof(struct ngpppoe_init_data) + MAX_SESSION];
struct ngpppoe_init_data poeid;
} u;
struct ngpppoe_init_data *const idata = &u.poeid;
char path[NG_PATHLEN + 1];
char session_hook[NG_HOOKLEN + 1];
pe->opened=1;
if (p->link) {
Disable(&p->link->conf.options, LINK_CONF_ACFCOMP); /* RFC 2516 */
Deny(&p->link->conf.options, LINK_CONF_ACFCOMP); /* RFC 2516 */
}
snprintf(session_hook, sizeof(session_hook), "mpd%d-%s",
gPid, p->name);
if (pe->incoming == 1) {
Log(LG_PHYS2, ("[%s] PppoeOpen() on incoming call", p->name));
/* Path to the ng_tee node */
snprintf(path, sizeof(path), "%s%s.%s",
pe->path, pe->hook, session_hook);
/* Connect ng_tee(4) node to the ng_ppp(4) node. */
if (!PhysGetUpperHook(p, cn.path, cn.peerhook)) {
Log(LG_PHYS, ("[%s] PPPoE: can't get upper hook", p->name));
goto fail2;
}
snprintf(cn.ourhook, sizeof(cn.ourhook), "right");
if (NgSendMsg(pe->PIf->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT,
&cn, sizeof(cn)) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
p->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
goto fail2;
}
/* Shutdown ng_tee node */
if (NgFuncShutdownNode(pe->PIf->csock, p->name, path) < 0) {
Log(LG_ERR, ("[%s] PPPoE: Shutdown ng_tee node %s error: %s",
p->name, path, strerror(errno)));
}
if (p->state==PHYS_STATE_READY) {
TimerStop(&pe->connectTimer);
p->state = PHYS_STATE_UP;
PhysUp(p);
}
return;
}
/* Sanity check. */
if (p->state != PHYS_STATE_DOWN) {
Log(LG_PHYS, ("[%s] PPPoE allready active", p->name));
return;
};
if (!Enabled(&pe->options, PPPOE_CONF_ORIGINATE)) {
Log(LG_ERR, ("[%s] PPPoE originate option is not enabled",
p->name));
PhysDown(p, STR_DEV_NOT_READY, NULL);
return;
};
/* Create PPPoE node if necessary. */
PppoeNodeUpdate(p);
if (!pe->PIf) {
Log(LG_ERR, ("[%s] PPPoE node for link is not initialized",
p->name));
goto fail;
}
/* Connect our ng_ppp(4) node link hook to the ng_pppoe(4) node. */
snprintf(cn.ourhook, sizeof(cn.ourhook), "%s", session_hook);
snprintf(path, sizeof(path), "%s%s", pe->path, pe->hook);
if (!PhysGetUpperHook(p, cn.path, cn.peerhook)) {
Log(LG_PHYS, ("[%s] PPPoE: can't get upper hook", p->name));
goto fail2;
}
if (NgSendMsg(pe->PIf->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT,
&cn, sizeof(cn)) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
p->name, path, cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
goto fail2;
}
Log(LG_PHYS, ("[%s] PPPoE: Connecting to '%s'", p->name, pe->session));
/* Tell the PPPoE node to try to connect to a server. */
memset(idata, 0, sizeof(idata));
snprintf(idata->hook, sizeof(idata->hook), "%s", session_hook);
idata->data_len = strlen(pe->session);
strncpy(idata->data, pe->session, MAX_SESSION);
if (NgSendMsg(pe->PIf->csock, path, NGM_PPPOE_COOKIE, NGM_PPPOE_CONNECT,
idata, sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("[%s] PPPoE can't request connection to server: "
"%s", p->name, strerror(errno)));
goto fail2;
}
/* Set a timer to limit connection time. */
TimerInit(&pe->connectTimer, "PPPoE-connect",
PPPOE_CONNECT_TIMEOUT * SECONDS, PppoeConnectTimeout, p);
TimerStart(&pe->connectTimer);
/* OK */
p->state = PHYS_STATE_CONNECTING;
return;
fail2:
NgFuncDisconnect(pe->PIf->csock, p->name, path, session_hook);
fail:
PhysDown(p, STR_CON_FAILED0, NULL);
return;
}
/*
* PppoeConnectTimeout()
*/
static void
PppoeConnectTimeout(void *arg)
{
const PhysInfo p = (PhysInfo)arg;
/* Cancel connection. */
Log(LG_PHYS, ("[%s] PPPoE connection timeout after %d seconds",
p->name, PPPOE_CONNECT_TIMEOUT));
PhysDown(p, STR_CON_FAILED0, NULL);
PppoeShutdown(p);
}
/*
* PppoeClose()
*/
static void
PppoeClose(PhysInfo p)
{
const PppoeInfo pe = (PppoeInfo)p->info;
pe->opened = 0;
if (p->state == PHYS_STATE_DOWN)
return;
PhysDown(p, 0, NULL);
PppoeShutdown(p);
}
/*
* PppoeShutdown()
*
* Shut everything down and go to the PHYS_STATE_DOWN state.
*/
static void
PppoeShutdown(PhysInfo p)
{
const PppoeInfo pi = (PppoeInfo)p->info;
char path[NG_PATHLEN + 1];
char session_hook[NG_HOOKLEN + 1];
if (p->state == PHYS_STATE_DOWN)
return;
snprintf(path, sizeof(path), "%s%s", pi->path, pi->hook);
snprintf(session_hook, sizeof(session_hook), "mpd%d-%s",
gPid, p->name);
NgFuncDisconnect(pi->PIf->csock, p->name, path, session_hook);
TimerStop(&pi->connectTimer);
p->state = PHYS_STATE_DOWN;
pi->incoming = 0;
memset(pi->peeraddr, 0x00, ETHER_ADDR_LEN);
}
/*
* PppoeCtrlReadEvent()
*
* Receive an incoming control message from the PPPoE node
*/
static void
PppoeCtrlReadEvent(int type, void *arg)
{
union {
u_char buf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
struct ng_mesg resp;
} u;
char path[NG_PATHLEN + 1];
PhysInfo p = NULL;
PppoeInfo pi = NULL;
int k;
char ppphook[NG_HOOKLEN + 1];
struct PppoeIf *PIf=(struct PppoeIf*)arg;
/* Read control message. */
if (NgRecvMsg(PIf->csock, &u.resp, sizeof(u), path) < 0) {
Log(LG_ERR, ("PPPoE: error reading message from \"%s\": %s",
path, strerror(errno)));
return;
}
if (u.resp.header.typecookie != NGM_PPPOE_COOKIE) {
Log(LG_ERR, ("PPPoE: rec'd cookie %lu from \"%s\"",
(u_long)u.resp.header.typecookie, path));
return;
}
switch (u.resp.header.cmd) {
case NGM_PPPOE_SUCCESS:
case NGM_PPPOE_FAIL:
case NGM_PPPOE_CLOSE:
/* Restore context. */
for (k = 0; k < gNumPhyses; k++) {
if (gPhyses[k] && gPhyses[k]->type != &gPppoePhysType)
continue;
p = gPhyses[k];
pi = (PppoeInfo)p->info;
snprintf(ppphook, NG_HOOKLEN, "mpd%d-%s", gPid, p->name);
if ((PIf==pi->PIf) &&
(strcmp(ppphook, ((struct ngpppoe_sts *)u.resp.data)->hook) == 0))
break;
}
if (k == gNumPhyses) {
Log(LG_ERR, ("PPPoE: message from unknown hook \"%s\"",
((struct ngpppoe_sts *)u.resp.data)->hook));
return;
}
if (p->state == PHYS_STATE_DOWN) {
if (u.resp.header.cmd != NGM_PPPOE_CLOSE)
Log(LG_PHYS, ("[%s] PPPoE: message %d in DOWN state",
p->name, u.resp.header.cmd));
return;
}
}
/* Decode message. */
switch (u.resp.header.cmd) {
case NGM_PPPOE_SESSIONID: /* XXX: I do not know what to do with this? */
break;
case NGM_PPPOE_SUCCESS:
Log(LG_PHYS, ("[%s] PPPoE: connection successful", p->name));
if (pi->opened) {
TimerStop(&pi->connectTimer);
p->state = PHYS_STATE_UP;
PhysUp(p);
} else {
p->state = PHYS_STATE_READY;
}
break;
case NGM_PPPOE_FAIL:
Log(LG_PHYS, ("[%s] PPPoE: connection failed", p->name));
PhysDown(p, STR_CON_FAILED0, NULL);
PppoeShutdown(p);
break;
case NGM_PPPOE_CLOSE:
Log(LG_PHYS, ("[%s] PPPoE: connection closed", p->name));
PhysDown(p, STR_DROPPED, NULL);
PppoeShutdown(p);
break;
case NGM_PPPOE_ACNAME:
Log(LG_PHYS, ("PPPoE: rec'd ACNAME \"%s\"",
((struct ngpppoe_sts *)u.resp.data)->hook));
break;
default:
Log(LG_PHYS, ("PPPoE: rec'd command %lu from \"%s\"",
(u_long)u.resp.header.cmd, path));
break;
}
}
/*
* PppoeStat()
*/
void
PppoeStat(Context ctx)
{
const PppoeInfo pe = (PppoeInfo)ctx->phys->info;
char buf[64];
Printf("PPPoE configuration:\r\n");
Printf("\tIface Node : %s\r\n", pe->path);
Printf("\tIface Hook : %s\r\n", pe->hook);
Printf("\tSession : %s\r\n", pe->session);
Printf("PPPoE options:\r\n");
OptStat(ctx, &pe->options, gConfList);
Printf("PPPoE status:\r\n");
Printf("\tState : %s\r\n", gPhysStateNames[ctx->phys->state]);
if (ctx->phys->state != PHYS_STATE_DOWN) {
Printf("\tOpened : %s\r\n", (pe->opened?"YES":"NO"));
Printf("\tIncoming : %s\r\n", (pe->incoming?"YES":"NO"));
PppoePeerAddr(ctx->phys, buf, sizeof(buf));
Printf("\tCurrent peer : %s\r\n", buf);
}
}
/*
* PppoeOriginated()
*/
static int
PppoeOriginated(PhysInfo p)
{
PppoeInfo const pppoe = (PppoeInfo)p->info;
return (pppoe->incoming ? LINK_ORIGINATE_REMOTE : LINK_ORIGINATE_LOCAL);
}
/*
* PppoeIsSync()
*/
static int
PppoeIsSync(PhysInfo p)
{
return (1);
}
static int
PppoePeerAddr(PhysInfo p, void *buf, int buf_len)
{
PppoeInfo const pppoe = (PppoeInfo)p->info;
snprintf(buf, buf_len, "%02x%02x%02x%02x%02x%02x",
pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2],
pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]);
return (0);
}
static int
PppoeCallingNum(PhysInfo p, void *buf, int buf_len)
{
PppoeInfo const pppoe = (PppoeInfo)p->info;
if (pppoe->incoming) {
snprintf(buf, buf_len, "%02x%02x%02x%02x%02x%02x",
pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2],
pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]);
} else {
((char*)buf)[0] = 0;
}
return (0);
}
static int
PppoeCalledNum(PhysInfo p, void *buf, int buf_len)
{
PppoeInfo const pppoe = (PppoeInfo)p->info;
if (!pppoe->incoming) {
snprintf(buf, buf_len, "%02x%02x%02x%02x%02x%02x",
pppoe->peeraddr[0], pppoe->peeraddr[1], pppoe->peeraddr[2],
pppoe->peeraddr[3], pppoe->peeraddr[4], pppoe->peeraddr[5]);
} else {
strlcpy(buf, pppoe->session, buf_len);
}
return (0);
}
static int
CreatePppoeNode(PhysInfo p, const char *path, const char *hook, struct PppoeIf *PIf)
{
u_char rbuf[2048];
struct ng_mesg *resp;
const struct hooklist *hlist;
const struct nodeinfo *ninfo;
int f;
/* Make sure interface is up. */
char iface[IFNAMSIZ + 1];
snprintf(iface, sizeof(iface), "%s", path);
if (iface[strlen(iface) - 1] == ':')
iface[strlen(iface) - 1] = '\0';
if (ExecCmdNosh(LG_PHYS2, p->name, "%s %s up", PATH_IFCONFIG, iface) != 0) {
Log(LG_ERR, ("[%s] can't bring up interface %s",
p->name, iface));
return (0);
}
/* Create a new netgraph node */
if (NgMkSockNode(NULL, &PIf->csock, &PIf->dsock) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't create ctrl socket: %s",
p->name, strerror(errno)));
return(0);
}
(void)fcntl(PIf->csock, F_SETFD, 1);
(void)fcntl(PIf->dsock, F_SETFD, 1);
/* Check if NG_ETHER_NODE_TYPE is available. */
if (gNgEtherLoaded == FALSE) {
const struct typelist *tlist;
/* Ask for a list of available node types. */
if (NgSendMsg(PIf->csock, "", NGM_GENERIC_COOKIE, NGM_LISTTYPES,
NULL, 0) < 0) {
Log(LG_ERR, ("[%s] Cannot send a netgraph message: %s",
p->name, strerror(errno)));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
/* Get response. */
resp = (struct ng_mesg *)rbuf;
if (NgRecvMsg(PIf->csock, resp, sizeof(rbuf), NULL) <= 0) {
Log(LG_ERR, ("[%s] Cannot get netgraph response: %s",
p->name, strerror(errno)));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
/* Look for NG_ETHER_NODE_TYPE. */
tlist = (const struct typelist*) resp->data;
for (f = 0; f < tlist->numtypes; f++)
if (strncmp(tlist->typeinfo[f].type_name,
NG_ETHER_NODE_TYPE,
sizeof(NG_ETHER_NODE_TYPE) - 1) == 0)
gNgEtherLoaded = TRUE;
/* If not found try to load ng_ether and repeat the check. */
if (gNgEtherLoaded == FALSE && (kldload("ng_ether") < 0)) {
Log(LG_ERR, ("PPPoE: Cannot load ng_ether: %s",
strerror(errno)));
close(PIf->csock);
close(PIf->dsock);
assert (0);
}
gNgEtherLoaded = TRUE;
}
/*
* 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.
*/
if (NgSendMsg(PIf->csock, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
NULL, 0) < 0) {
Log(LG_ERR, ("[%s] Cannot send a netgraph message: %s:%s",
p->name, path, strerror(errno)));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
/* Get our list back. */
resp = (struct ng_mesg *)rbuf;
if (NgRecvMsg(PIf->csock, resp, sizeof(rbuf), NULL) <= 0) {
Log(LG_ERR, ("[%s] Cannot get netgraph response: %s",
p->name, strerror(errno)));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
hlist = (const struct hooklist *)resp->data;
ninfo = &hlist->nodeinfo;
/* Make sure we've got the right type of node. */
if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
sizeof(NG_ETHER_NODE_TYPE) - 1)) {
Log(LG_ERR, ("[%s] Unexpected node type ``%s'' (wanted ``"
NG_ETHER_NODE_TYPE "'') on %s",
p->name, ninfo->type, path));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
/* Look for a hook already attached. */
for (f = 0; f < ninfo->hooks; f++) {
const struct linkinfo *nlink = &hlist->link[f];
/* Search for "orphans" hook. */
if (strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) &&
strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT))
continue;
/*
* 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)) {
Log(LG_ERR, ("%s Node type ``%s'' is currently "
" using orphan hook\n",
path, nlink->nodeinfo.type));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
break;
}
if (f == ninfo->hooks) {
struct ngm_mkpeer mp;
/* Create new PPPoE node. */
snprintf(mp.type, sizeof(mp.type), "%s", NG_PPPOE_NODE_TYPE);
snprintf(mp.ourhook, sizeof(mp.ourhook), "%s", hook);
snprintf(mp.peerhook, sizeof(mp.peerhook), "%s",
NG_PPPOE_HOOK_ETHERNET);
if (NgSendMsg(PIf->csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mp,
sizeof(mp)) < 0) {
Log(LG_ERR, ("[%s] can't create %s peer to %s,%s: %s",
p->name, NG_PPPOE_NODE_TYPE,
path, hook, strerror(errno)));
close(PIf->csock);
close(PIf->dsock);
return (0);
}
};
/* Register an event listening to the control socket. */
EventRegister(&(PIf->ctrlEvent), EVENT_READ, PIf->csock,
EVENT_RECURRING, PppoeCtrlReadEvent, PIf);
return (1);
};
static void
PppoeListenEvent(int type, void *arg)
{
int i,k,sz;
struct PppoeIf *PIf=(struct PppoeIf *)(arg);
char rhook[NG_HOOKLEN + 1];
unsigned char response[1024];
char path[NG_PATHLEN + 1];
char path1[NG_PATHLEN + 1];
char session_hook[NG_HOOKLEN + 1];
struct ngm_connect cn;
struct ngm_mkpeer mp;
u_char *macaddr;
time_t const now = time(NULL);
int retry = 10;
union {
u_char buf[sizeof(struct ngpppoe_init_data) + MAX_SESSION];
struct ngpppoe_init_data poeid;
} u;
struct ngpppoe_init_data *const idata = &u.poeid;
switch (sz = NgRecvData(PIf->dsock, response, sizeof(response), rhook)) {
case -1:
Log(LG_ERR, ("NgRecvData: %d", sz));
return;
case 0:
Log(LG_ERR, ("NgRecvData: socket closed"));
return;
}
if (sz >= sizeof(struct ether_header)) {
macaddr = ((struct ether_header *)response)->ether_shost;
Log(LG_PHYS, ("Incoming PPPoE connection request via %s for "
"service \"%s\" from %s", PIf->ifnodepath, PIf->session,
ether_ntoa((const struct ether_addr *)macaddr)));
} else {
macaddr = NULL;
Log(LG_PHYS, ("Incoming PPPoE connection request via %s for "
"service \"%s\"", PIf->ifnodepath, PIf->session));
}
if (gShutdownInProgress) {
Log(LG_PHYS, ("Shutdown sequence in progress, ignoring request."));
return;
}
if (OVERLOAD()) {
Log(LG_PHYS, ("Daemon overloaded, ignoring request."));
return;
}
/* Examine all PPPoE links. */
for (k = 0; k < gNumPhyses; k++) {
PhysInfo p;
PppoeInfo pi;
if (gPhyses[k] && gPhyses[k]->type != &gPppoePhysType)
continue;
p = gPhyses[k];
pi = (PppoeInfo)p->info;
if ((PIf!=pi->PIf) ||
(p->state != PHYS_STATE_DOWN) ||
(now-p->lastClose < PPPOE_REOPEN_PAUSE) ||
!Enabled(&pi->options, PPPOE_CONF_INCOMING))
continue;
Log(LG_PHYS, ("[%s] Accepting PPPoE connection", p->name));
/* Path to the ng_pppoe */
snprintf(path, sizeof(path), "%s%s", pi->path, pi->hook);
/* Name of ng_pppoe session hook */
snprintf(session_hook, sizeof(session_hook), "mpd%d-%s",
gPid, p->name);
/* Create ng_tee(4) node and connect it to ng_pppoe(4). */
snprintf(mp.type, sizeof(mp.type), "%s", NG_TEE_NODE_TYPE);
snprintf(mp.ourhook, sizeof(mp.ourhook), session_hook);
snprintf(mp.peerhook, sizeof(mp.peerhook), "left");
if (NgSendMsg(pi->PIf->csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER,
&mp, sizeof(mp)) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't create %s peer to %s,%s: %s",
p->name, NG_TEE_NODE_TYPE,
path, "left", strerror(errno)));
goto close_socket;
}
/* Path to the ng_tee */
snprintf(path1, sizeof(path), "%s.%s", path, session_hook);
/* Connect our socket node link hook to the ng_tee(4) node. */
snprintf(cn.ourhook, sizeof(cn.ourhook), p->name);
snprintf(cn.path, sizeof(cn.path), "%s", path1);
snprintf(cn.peerhook, sizeof(cn.peerhook), "left2right");
if (NgSendMsg(pi->PIf->csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT,
&cn, sizeof(cn)) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
p->name, ".:", cn.ourhook, cn.path,
cn.peerhook, strerror(errno)));
goto shutdown_tee;
}
/* Put the PPPoE node into OFFER mode. */
memset(idata, 0, sizeof(idata));
snprintf(idata->hook, sizeof(idata->hook), "%s", session_hook);
if (pi->acname[0] != 0) {
strlcpy(idata->data, pi->acname, MAX_SESSION);
} else {
if (gethostname(idata->data, MAX_SESSION) == -1) {
Log(LG_ERR, ("[%s] PPPoE: gethostname() failed",
p->name));
idata->data[0] = 0;
}
if (idata->data[0] == 0)
strlcpy(idata->data, "NONAME", MAX_SESSION);
}
idata->data_len=strlen(idata->data);
if (NgSendMsg(pi->PIf->csock, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
idata, sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't send NGM_PPPOE_OFFER to %s,%s "
": %s",
p->name, path, idata->hook, strerror(errno)));
goto shutdown_tee;
}
memset(idata, 0, sizeof(idata));
snprintf(idata->hook, sizeof(idata->hook), "%s", session_hook);
idata->data_len = strlen(pi->session);
strncpy(idata->data, pi->session, MAX_SESSION);
if (NgSendMsg(pi->PIf->csock, path, NGM_PPPOE_COOKIE,
NGM_PPPOE_SERVICE, idata,
sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't send NGM_PPPOE_SERVICE to %s,"
"%s : %s",
p->name, path, idata->hook, strerror(errno)));
goto shutdown_tee;
}
/* And send our request data to the waiting node. */
if (NgSendData(pi->PIf->dsock, p->name, response, sz) == -1) {
Log(LG_ERR, ("[%s] PPPoE: Cannot send original request: %s",
p->name, strerror(errno)));
goto shutdown_tee;
}
if (NgFuncDisconnect(pi->PIf->csock, p->name, ".:", p->name) < 0) {
Log(LG_ERR, ("[%s] PPPoE: can't remove hook %s: %s",
p->name, p->name, strerror(errno)));
goto shutdown_tee;
}
p->state = PHYS_STATE_CONNECTING;
pi->incoming = 1;
/* Record the peer's MAC address */
if (macaddr)
for (i = 0; i < 6; i++)
pi->peeraddr[i] = macaddr[i];
Log(LG_PHYS2, ("[%s] PPPoE response sent", p->name));
/* Set a timer to limit connection time. */
TimerInit(&pi->connectTimer, "PPPoE-connect",
PPPOE_CONNECT_TIMEOUT * SECONDS, PppoeConnectTimeout, p);
TimerStart(&pi->connectTimer);
PhysIncoming(p);
/* Done. */
break;
shutdown_tee:
if (NgFuncShutdownNode(pi->PIf->csock, p->name, path1) < 0) {
Log(LG_ERR, ("[%s] Shutdown ng_tee node %s error: %s",
p->name, path1, strerror(errno)));
};
close_socket:
Log(LG_PHYS, ("[%s] PPPoE connection not accepted due to error",
p->name));
if ((retry--) <= 0) {
Log(LG_PHYS, ("[%s] Too many errors. Drop request.",
p->name));
break;
}
};
if (k == gNumPhyses)
Log(LG_PHYS, ("No free PPPoE link with requested parameters "
"was found"));
};
static int
ListenPppoeNode(const char *path, const char *hook, struct PppoeIf *PIf,
const char *session, int n)
{
union {
u_char buf[sizeof(struct ngpppoe_init_data) + MAX_SESSION];
struct ngpppoe_init_data poeid;
} u;
struct ngpppoe_init_data *const idata = &u.poeid;
char pat[NG_PATHLEN + 1];
struct ngm_connect cn;
if (n) {
/* Create a new netgraph node */
if (NgMkSockNode(NULL, &PIf->csock, &PIf->dsock) < 0) {
Log(LG_ERR, ("PPPoE: Can't create listening ctrl socket: %s",
strerror(errno)));
return(0);
}
(void)fcntl(PIf->csock, F_SETFD, 1);
(void)fcntl(PIf->dsock, F_SETFD, 1);
}
/* Connect our socket node link hook to the ng_pppoe(4) node. */
snprintf(cn.path, sizeof(cn.path), "%s%s", path, hook);
snprintf(cn.ourhook, sizeof(cn.ourhook), "listen-hook");
snprintf(cn.peerhook, sizeof(cn.peerhook), "listen-%s", session);
if (NgSendMsg(PIf->csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn,
sizeof(cn)) < 0) {
Log(LG_ERR, ("PPPoE: Can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\": %s",
".:", cn.ourhook, cn.path, cn.peerhook,
strerror(errno)));
return(0);
}
/* Tell the PPPoE node to be a server. */
snprintf(pat, sizeof(pat), "%s%s", path, hook);
memset(idata, 0, sizeof(idata));
snprintf(idata->hook, sizeof(idata->hook), "listen-%s", session);
idata->data_len = strlen(session);
strncpy(idata->data, session, MAX_SESSION);
if (NgSendMsg(PIf->csock, pat, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
idata, sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("PPPoE: Can't send NGM_PPPOE_LISTEN to %s hook "
"%s : %s", pat, idata->hook, strerror(errno)));
return (0);
}
Log(LG_PHYS, ("PPPoE: waiting for connection on %s, service \"%s\"",
path, idata->data));
/* Register an event listening to the data socket. */
EventRegister(&(PIf->dataEvent), EVENT_READ, PIf->dsock,
EVENT_RECURRING, PppoeListenEvent, PIf);
if (n) {
/* Register an event listening to the control socket. */
EventRegister(&(PIf->ctrlEvent), EVENT_READ, PIf->csock,
EVENT_RECURRING, PppoeCtrlReadEvent, PIf);
}
return (1);
};
/*
* PppoeNodeUpdate()
*/
static void
PppoeNodeUpdate(PhysInfo p)
{
int i, j = -1;
PppoeInfo pi = (PppoeInfo)p->info;
if (!pi->PIf) { // Do this only once for interface
if (!(strcmp(pi->path, "undefined:")
&&strcmp(pi->session, "undefined:"))) {
Log(LG_ERR, ("[%s] PPPoE: Skipping link %s with undefined "
"interface or session", p->name, p->name));
return;
}
for (i = 0; i < PppoeIfCount ; i++)
if (strcmp(PppoeIfs[i].ifnodepath, pi->path) == 0) {
j = i;
break;
}
if (j == -1) {
if (PppoeIfCount>=PPPOE_MAXPARENTIFS) {
Log(LG_ERR, ("[%s] PPPoE: Too many different parent interfaces! ",
p->name));
return;
}
if (CreatePppoeNode(p, pi->path, pi->hook, &PppoeIfs[PppoeIfCount])) {
strlcpy(PppoeIfs[PppoeIfCount].ifnodepath,
pi->path,
sizeof(PppoeIfs[PppoeIfCount].ifnodepath));
strlcpy(PppoeIfs[PppoeIfCount].session,
pi->session,
sizeof(PppoeIfs[PppoeIfCount].session));
PppoeIfs[PppoeIfCount].listen = 0;
pi->PIf=&PppoeIfs[PppoeIfCount];
PppoeIfCount++;
} else {
Log(LG_ERR, ("[%s] PPPoE: Error creating ng_pppoe "
"node on %s", p->name, pi->path));
return;
}
} else {
pi->PIf=&PppoeIfs[j];
}
}
if (Enabled(&pi->options, PPPOE_CONF_INCOMING) &&
(!PppoeListenUpdateSheduled)) {
/* Set a timer to run PppoeListenUpdate(). */
TimerInit(&PppoeListenUpdateTimer, "PppoeListenUpdate",
0, PppoeListenUpdate, NULL);
TimerStart(&PppoeListenUpdateTimer);
PppoeListenUpdateSheduled = 1;
}
}
/*
* PppoeListenUpdate()
*/
static void
PppoeListenUpdate(void *arg)
{
int k;
PppoeListenUpdateSheduled = 0;
/* Examine all PPPoE links. */
for (k = 0; k < gNumPhyses; k++) {
PppoeInfo pi;
PhysInfo p;
int i, j = -1;
if (gPhyses[k] == NULL ||
gPhyses[k]->type != &gPppoePhysType)
continue;
p = gPhyses[k];
pi = (PppoeInfo)p->info;
if (!(strcmp(pi->path, "undefined:")
&&strcmp(pi->session, "undefined:"))) {
Log(LG_ERR, ("PPPoE: Skipping link %s with undefined "
"interface or session", p->name));
continue;
}
if (!Enabled(&pi->options, PPPOE_CONF_INCOMING))
continue;
for (i = 0; i < PppoeIfCount; i++)
if ((strcmp(PppoeIfs[i].ifnodepath, pi->path) == 0) &&
(strcmp(PppoeIfs[i].session, pi->session) == 0)) {
j = i;
break;
}
if (j == -1) {
if (PppoeIfCount>=PPPOE_MAXPARENTIFS) {
Log(LG_ERR, ("[%s] PPPoE: Too many different parent interfaces! ",
p->name));
continue;
}
if (ListenPppoeNode(pi->path, pi->hook,
&(PppoeIfs[PppoeIfCount]), pi->session, 1)) {
strlcpy(PppoeIfs[PppoeIfCount].ifnodepath,
pi->path,
sizeof(PppoeIfs[PppoeIfCount].ifnodepath));
strlcpy(PppoeIfs[PppoeIfCount].session,
pi->session,
sizeof(PppoeIfs[PppoeIfCount].session));
PppoeIfs[PppoeIfCount].listen = 1;
pi->PIf=&PppoeIfs[PppoeIfCount];
PppoeIfCount++;
}
} else {
if ((PppoeIfs[j].listen == 0) &&
(ListenPppoeNode(pi->path, pi->hook, &(PppoeIfs[j]),
pi->session, 0))) {
PppoeIfs[j].listen=1;
}
pi->PIf=&PppoeIfs[j];
}
}
}
/*
* PppoeSetCommand()
*/
static int
PppoeSetCommand(Context ctx, int ac, char *av[], void *arg)
{
const PppoeInfo pi = (PppoeInfo) ctx->phys->info;
const char *hookname = ETHER_DEFAULT_HOOK;
const char *colon;
switch ((intptr_t)arg) {
case SET_IFACE:
switch (ac) {
case 2:
hookname = av[1];
/* fall through */
case 1:
colon = (av[0][strlen(av[0]) - 1] == ':') ? "" : ":";
snprintf(pi->path, sizeof(pi->path),
"%s%s", av[0], colon);
snprintf(pi->hook, sizeof(pi->hook),
"%s", hookname);
break;
default:
return(-1);
}
break;
case SET_SESSION:
if (ac != 1)
return(-1);
snprintf(pi->session, sizeof(pi->session), "%s", av[0]);
break;
case SET_ACNAME:
if (ac != 1)
return(-1);
snprintf(pi->acname, sizeof(pi->acname), "%s", av[0]);
break;
case SET_ENABLE:
EnableCommand(ac, av, &pi->options, gConfList);
PppoeNodeUpdate(ctx->phys);
break;
case SET_DISABLE:
DisableCommand(ac, av, &pi->options, gConfList);
break;
default:
assert(0);
}
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1