/*
* 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 <net/ethernet.h>
#include <netgraph/ng_pppoe.h>
#include <netgraph/ng_ether.h>
#include <netgraph/ng_message.h>
#include <netgraph/ng_tee.h>
#include <netgraph.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 MAX_PATH 64 /* XXX should be NG_PATHLEN */
#define MAX_SESSION 64 /* max length of PPPoE session name */
/* Per link private info */
struct pppoeinfo {
int state; /* link layer state */
u_char incoming:1; /* Call is incoming vs. outgoing */
int csock; /* netgraph Control socket */
int dsock; /* netgraph Data socket */
char path[MAX_PATH + 1]; /* PPPoE node path */
char hook[NG_HOOKLEN + 1]; /* hook on that node */
char session[MAX_SESSION+1]; /* session name */
u_char peeraddr[6]; /* Peer MAC address for incoming connections */
struct optinfo options;
Link link; /* our link */
EventRef ctrlEvent; /* listen for ctrl messages */
struct pppTimer connectTimer; /* connection timeout timer */
};
typedef struct pppoeinfo *PppoeInfo;
/* Set menu options */
enum {
SET_IFACE,
SET_SESSION,
SET_ENABLE,
SET_DISABLE,
};
enum {
PPPOE_CONF_ORIGINATE, /* allow originating connections to peer */
PPPOE_CONF_INCOMING, /* allow accepting connections from peer */
};
/* Possible states */
#define PPPOE_DOWN 0
#define PPPOE_CONNECTING 1
#define PPPOE_UP 2
/*
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 void PppoeCtrlReadEvent(int type, void *arg);
static void PppoeConnectTimeout(void *arg);
static void PppoeStat(PhysInfo p);
static int PppoeSetCommand(int ac, char *av[], void *arg);
static int PppoeOriginated(PhysInfo p);
static void PppoeNodeUpdate(void);
static void PppoeListenUpdate(void *arg);
/*
* GLOBAL VARIABLES
*/
const struct phystype gPppoePhysType = {
"pppoe",
TRUE, PPPOE_REOPEN_PAUSE,
PPPOE_MTU, PPPOE_MRU,
PppoeInit,
PppoeOpen,
PppoeClose,
NULL,
PppoeShutdown,
PppoeStat,
PppoeOriginated,
};
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 },
{ "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+1];
char session[MAX_SESSION+1];
int listen;
int csock; /* netgraph Control socket */
int dsock; /* netgraph Data socket */
EventRef ctrlEvent; /* listen for ctrl messages */
};
int PppoeIfCount=0;
struct PppoeIf PppoeIfs[64];
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;
int i;
/* Allocate private struct */
pe = (PppoeInfo)(p->info = Malloc(MB_PHYS, sizeof(*pe)));
pe->state = PPPOE_DOWN;
pe->incoming = 0;
snprintf(pe->path, sizeof(pe->path), "undefined:");
snprintf(pe->hook, sizeof(pe->hook), "undefined");
snprintf(pe->session, sizeof(pe->session), "undefined");
for (i=0; i<6; i++)
pe->peeraddr[i]=0x00;
pe->link = lnk;
/* Done */
return(0);
}
/*
* PppoeOpen()
*/
static void
PppoeOpen(PhysInfo p)
{
const PppoeInfo pe = (PppoeInfo)p->info;
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];
char linkHook[NG_HOOKLEN + 1];
int i;
if (pe->incoming == 1) {
Log(LG_PHYS, ("[%s] PppoeOpen() on incoming call", lnk->name));
} else {
/* Sanity */
if (pe->state != PPPOE_DOWN) {
Log(LG_ERR, ("[%s] PPPoE allready active", lnk->name));
return;
};
if (!Enabled(&pe->options, PPPOE_CONF_ORIGINATE)) {
Log(LG_ERR, ("[%s] PPPoE originate option is not enabled", lnk->name));
PhysDown(STR_DEV_NOT_READY, NULL);
return;
};
/* Create PPPOE node if necessary */
PppoeNodeUpdate();
/* Create a new netgraph node */
if (NgMkSockNode(NULL, &pe->csock, &pe->dsock) < 0) {
Log(LG_ERR, ("[%s] PPPoE can't create ctrl socket: %s",
lnk->name, strerror(errno)));
goto fail;
}
(void)fcntl(pe->csock, F_SETFD, 1);
/* Connect our ng_ppp(4) node link hook to the ng_pppoe(4) node */
snprintf(session_hook, sizeof(session_hook), "mpd%d-%s", getpid(), lnk->name);
snprintf(path, sizeof(path), "%s%s", pe->path, pe->hook);
snprintf(linkHook, sizeof(linkHook),
"%s%d", NG_PPP_HOOK_LINK_PREFIX, lnk->bundleIndex);
if (NgFuncConnect(MPD_HOOK_PPP, linkHook, path, session_hook) < 0)
goto fail2;
/* 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->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",
lnk->name, strerror(errno)));
goto fail2;
}
for (i=0; i<6; i++)
pe->peeraddr[i]=0x00;
/* Set a timer to limit connection time */
TimerInit(&pe->connectTimer, "PPPoE-connect",
PPPOE_CONNECT_TIMEOUT * SECONDS, PppoeConnectTimeout, pe);
TimerStart(&pe->connectTimer);
};
/* Register an event listening to the control socket */
EventRegister(&pe->ctrlEvent, EVENT_READ, pe->csock,
DEV_PRIO, PppoeCtrlReadEvent, lnk);
/* OK */
pe->state = PPPOE_CONNECTING;
return;
fail2:
NgFuncDisconnect(path,session_hook);
fail:
PhysDown(STR_CON_FAILED0, NULL);
return;
}
/*
* PppoeConnectTimeout()
*/
static void
PppoeConnectTimeout(void *arg)
{
const PppoeInfo pe = arg;
/* Cancel connection */
assert(pe->state == PPPOE_CONNECTING);
Log(LG_ERR, ("[%s] PPPoE connection timeout after %d seconds",
lnk->name, PPPOE_CONNECT_TIMEOUT));
PhysDown(STR_CON_FAILED0, NULL);
PppoeShutdown(lnk->phys);
}
/*
* PppoeClose()
*/
static void
PppoeClose(PhysInfo p)
{
const PppoeInfo pe = (PppoeInfo)p->info;
if (pe->state == PPPOE_DOWN)
return;
PhysDown(0, NULL);
PppoeShutdown(p);
}
/*
* PppoeShutdown()
*
* Shut everything down and go to the PPPOE_DOWN state
*/
static void
PppoeShutdown(PhysInfo p)
{
const PppoeInfo pe = (PppoeInfo)p->info;
char path[NG_PATHLEN + 1];
char session_hook[NG_HOOKLEN + 1];
if (pe->state == PPPOE_DOWN)
return;
snprintf(path, sizeof(path), "%s%s", pe->path, pe->hook);
snprintf(session_hook, sizeof(session_hook), "mpd%d-%s", getpid(), lnk->name);
NgFuncDisconnect(path,session_hook);
EventUnRegister(&pe->ctrlEvent);
(void)close(pe->csock);
(void)close(pe->dsock);
pe->csock = -1;
pe->dsock = -1;
TimerStop(&pe->connectTimer);
pe->state = PPPOE_DOWN;
pe->incoming = 0;
}
/*
* 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];
PppoeInfo pe;
/* Restore context */
lnk = arg;
bund = lnk->bund;
pe = (PppoeInfo)lnk->phys->info;
assert(pe->state != PPPOE_DOWN);
/* Register new event */
EventRegister(&pe->ctrlEvent, EVENT_READ, pe->csock,
DEV_PRIO, PppoeCtrlReadEvent, lnk);
/* Read control message */
if (NgRecvMsg(pe->csock, &u.resp, sizeof(u), path) < 0) {
Log(LG_ERR, ("[%s] error reading message from \"%s\": %s",
lnk->name, path, strerror(errno)));
PhysDown(STR_ERROR, NULL);
PppoeShutdown(lnk->phys);
return;
}
if (u.resp.header.typecookie != NGM_PPPOE_COOKIE) {
Log(LG_ERR, ("[%s] rec'd cookie %lu from \"%s\"",
lnk->name, (u_long)u.resp.header.typecookie, path));
return;
}
/* Decode message */
switch (u.resp.header.cmd) {
case NGM_PPPOE_SESSIONID: // I do not know what to do with this?
break;
case NGM_PPPOE_SUCCESS:
Log(LG_PHYS, ("[%s] PPPoE connection successful", lnk->name));
Disable(&lnk->conf.options, LINK_CONF_ACFCOMP); /* RFC 2516 */
Deny(&lnk->conf.options, LINK_CONF_ACFCOMP); /* RFC 2516 */
TimerStop(&pe->connectTimer);
pe->state = PPPOE_UP;
PhysUp();
break;
case NGM_PPPOE_FAIL:
Log(LG_PHYS, ("[%s] PPPoE connection failed", lnk->name));
PhysDown(STR_CON_FAILED0, NULL);
PppoeShutdown(lnk->phys);
break;
case NGM_PPPOE_CLOSE:
Log(LG_PHYS, ("[%s] PPPoE connection closed", lnk->name));
PhysDown(STR_DROPPED, NULL);
PppoeShutdown(lnk->phys);
break;
case NGM_PPPOE_ACNAME:
Log(LG_PHYS, ("[%s] rec'd ACNAME \"%s\"", lnk->name,
((struct ngpppoe_sts *)u.resp.data)->hook));
break;
default:
Log(LG_ERR, ("[%s] rec'd command %lu from \"%s\"",
lnk->name, (u_long)u.resp.header.cmd, path));
break;
}
}
/*
* PppoeStat()
*/
void
PppoeStat(PhysInfo p)
{
const PppoeInfo pe = (PppoeInfo)p->info;
const char *ststr;
printf("PPPoE configuration:\n");
printf("\tNode : %s\n", pe->path);
printf("\tHook : %s\n", pe->hook);
printf("\tSession : %s\n", pe->session);
printf("PPPoE status:\n");
switch (pe->state) {
case PPPOE_DOWN:
ststr = "DOWN";
break;
case PPPOE_CONNECTING:
ststr = "CONNECTING";
break;
case PPPOE_UP:
ststr = "UP";
break;
default:
ststr = "???";
break;
}
printf("\tState : %s\n", ststr);
}
/*
* PppoeGetPeerAddr()
*/
u_char *
PppoeGetPeerAddr(void)
{
PppoeInfo const p = (PppoeInfo) lnk->phys->info;
if (lnk->phys->type == &gPppoePhysType) {
return(p->peeraddr);
} else {
return((u_char *)0);
};
}
/*
* PppoeOriginated()
*/
static int
PppoeOriginated(PhysInfo p)
{
PppoeInfo const pppoe = (PppoeInfo)p->info;
return(pppoe->incoming ? LINK_ORIGINATE_REMOTE : LINK_ORIGINATE_LOCAL);
}
static int
CreatePppoeNode(const char *path, const char *hook) {
u_char rbuf[2048];
struct ng_mesg *resp;
const struct hooklist *hlist;
const struct nodeinfo *ninfo;
const struct linkinfo *nlink;
int f;
int csock;
/* 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 (ExecCmd(LG_PHYS, "%s %s up", PATH_IFCONFIG, iface) != 0) {
Log(LG_ERR, ("[%s] can't bring up interface %s",
lnk->name, iface));
return(0);
}
/* Create a new netgraph node */
if (NgMkSockNode(NULL, &csock, NULL) < 0) {
Log(LG_ERR, ("[%s] can't create ctrl socket: %s",
lnk->name, strerror(errno)));
return(0);
}
(void)fcntl(csock, F_SETFD, 1);
/*
* 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(csock, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
NULL, 0) < 0) {
Log(LG_ERR, ("[%s] Cannot send a netgraph message: %s:%s",
lnk->name, path, strerror(errno)));
close(csock);
return(0);
}
/* Get our list back */
resp = (struct ng_mesg *)rbuf;
if (NgRecvMsg(csock, resp, sizeof rbuf, NULL) <= 0) {
Log(LG_ERR, ("[%s] Cannot get netgraph response: %s",
lnk->name, strerror(errno)));
close(csock);
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", lnk->name, ninfo->type, path));
close(csock);
return(0);
}
/* look for a hook already attached. */
for (f = 0; f < ninfo->hooks; f++) {
nlink = &hlist->link[f];
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)) {
Log(LG_ERR, ("%s Node type ``%s'' is currently using orphan hook\n",
path, nlink->nodeinfo.type));
close(csock);
return(0);
}
break;
}
}
if (f == ninfo->hooks) { /* Create new PPPoE node */
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(csock, path,
NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0 && errno != EEXIST)
{
Log(LG_ERR, ("[%s] can't create %s peer to %s,%s: %s",
lnk->name, NG_PPPOE_NODE_TYPE,
path, hook, strerror(errno)));
close(csock);
return(0);
}
};
close(csock);
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];
char linkHook[NG_HOOKLEN + 1];
struct ngm_connect cn;
struct ngm_mkpeer mp;
u_char *macaddr;
time_t const now = time(NULL);
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;
/* Register an event listening to the control socket */
EventRegister(&(PIf->ctrlEvent), EVENT_READ, PIf->dsock,
DEV_PRIO, PppoeListenEvent, arg);
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 {
Log(LG_PHYS, ("Incoming PPPoE connection request via %s for service \"%s\"", PIf->ifnodepath, PIf->session));
macaddr = (u_char *)NULL;
}
/* Examine all PPPoE links */
for (k = 0; k < gNumLinks; k++) {
if (gLinks[k] && gLinks[k]->phys->type == &gPppoePhysType) {
PhysInfo const ph = gLinks[k]->phys;
PppoeInfo const p = (PppoeInfo)ph->info;
if ((strcmp(PIf->ifnodepath,p->path) == 0)
&& (strcmp(PIf->session,p->session) == 0)
&& (ph->state == PHYS_DOWN)
&& (p->state == PPPOE_DOWN)
&& (now-ph->lastClose >= PPPOE_REOPEN_PAUSE)) {
/* Restore context */
lnk = gLinks[k];
bund = lnk->bund;
Log(LG_PHYS, ("[%s] Accepting PPPoE connection", lnk->name));
/* Create a new netgraph socket */
if (NgMkSockNode(NULL, &p->csock, &p->dsock) < 0) {
Log(LG_ERR, ("[%s] can't create ctrl socket: %s",
lnk->name, strerror(errno)));
} else {
/* Create ng_tee(4) node and connect it to ng_pppoe(4) */
snprintf(session_hook, sizeof(session_hook), "mpd%d-%s", getpid(), lnk->name);
snprintf(path, sizeof(path), "%s%s", p->path, p->hook);
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(p->csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mp, sizeof(mp)) < 0) {
Log(LG_ERR, ("[%s] can't create %s peer to %s,%s: %s",
lnk->name, NG_TEE_NODE_TYPE,
path, "left", strerror(errno)));
} else {
snprintf(path1, sizeof(path), "%s%s.%s", p->path, p->hook,session_hook);
snprintf(linkHook, sizeof(linkHook),
"%s%d", NG_PPP_HOOK_LINK_PREFIX, lnk->bundleIndex);
/* Connect our ng_ppp(4) node link hook to the ng_tee(4) node */
if (NgFuncConnect(MPD_HOOK_PPP, linkHook, path1, "right") < 0)
{
Log(LG_ERR, ("[%s] can't connect to ppp: %s",
lnk->name, strerror(errno)));
} else {
/* Connect our socket node "data" hook to the ng_tee(4) node */
snprintf(cn.path, sizeof(cn.path), "%s", path1);
snprintf(cn.ourhook, sizeof(cn.ourhook), "data");
snprintf(cn.peerhook, sizeof(cn.peerhook), "left2right");
if (NgSendMsg(p->csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) {
Log(LG_ERR, ("[%s] can't connect %s,%s and %s,%s: %s",
bund->name, ".:", cn.ourhook, cn.path, cn.peerhook, strerror(errno)));
} else {
/* Put the PPPoE node into OFFER mode */
memset(idata, 0, sizeof(idata));
snprintf(idata->hook, sizeof(idata->hook), "%s", session_hook);
if (gethostname(idata->data, MAX_SESSION) == -1) {
Log(LG_RADIUS, ("[%s] PPPOE: gethostname() failed", lnk->name));
idata->data[0]=0;
}
idata->data_len=strlen(idata->data);
if (NgSendMsg(p->csock, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
idata, sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("[%s] can't send NGM_PPPOE_OFFER to %s,%s : %s",
lnk->name, path, idata->hook, strerror(errno)));
} else {
memset(idata, 0, sizeof(idata));
snprintf(idata->hook, sizeof(idata->hook), "%s", session_hook);
idata->data_len = strlen(p->session);
strncpy(idata->data, p->session, MAX_SESSION);
if (NgSendMsg(p->csock, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE,
idata, sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("[%s] can't send NGM_PPPOE_SERVICE to %s,%s : %s",
lnk->name, path, idata->hook, strerror(errno)));
} else {
/* And send our request data to the waiting node */
if (NgSendData(p->dsock, "data", response, sz) == -1) {
Log(LG_ERR, ("[%s] Cannot send original request: %s", lnk->name, strerror(errno)));
} else {
if (NgFuncShutdownNode(bund, lnk->name, path1)<0) {
Log(LG_ERR, ("[%s] Shutdown ng_tee node %s error: %s", lnk->name, path1, strerror(errno)));
} else {
p->state=PPPOE_CONNECTING;
p->incoming = 1;
/* Record the peer's MAC address */
if (macaddr) {
for (i=0;i<6;i++)
p->peeraddr[i]=macaddr[i];
};
Log(LG_PHYS, ("[%s] PPPoE response sent", lnk->name));
/* Set a timer to limit connection time */
TimerInit(&p->connectTimer, "PPPoE-connect",
PPPOE_CONNECT_TIMEOUT * SECONDS, PppoeConnectTimeout, p);
TimerStart(&p->connectTimer);
RecordLinkUpDownReason(NULL, 1, STR_INCOMING_CALL, "", NULL);
IfaceOpenNcps();
break;
};
};
};
};
};
if (NgFuncDisconnect(MPD_HOOK_PPP,linkHook)<0) {
Log(LG_ERR, ("[%s] Disconnect ng_ppp node error: %s", lnk->name, strerror(errno)));
};
};
if (NgFuncShutdownNode(bund, lnk->name, path1)<0) {
Log(LG_ERR, ("[%s] Shutdown ng_tee node %s error: %s", lnk->name, path1, strerror(errno)));
};
};
close(p->csock);
close(p->dsock);
p->csock=-1;
p->dsock=-1;
Log(LG_PHYS, ("[%s] PPPoE connection not accepted due error", lnk->name));
};
};
};
};
if (k == gNumLinks) {
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) {
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;
/* Create a new netgraph node */
if (NgMkSockNode(NULL, &(PIf->csock), &(PIf->dsock)) < 0) {
Log(LG_ERR, ("[%s] can't create ctrl socket: %s",
lnk->name, strerror(errno)));
return(0);
}
(void)fcntl(PIf->csock, 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, ("[%s] can't connect %s,%s and %s,%s: %s",
bund->name, ".:", 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);
Log(LG_ERR, ("[%s] PPPoE server listening on %s for service \"%s\"",
lnk->name, path, idata->data));
if (NgSendMsg(PIf->csock, pat, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
idata, sizeof(*idata) + idata->data_len) < 0) {
Log(LG_ERR, ("[%s] can't send NGM_PPPOE_LISTEN to %s hook %s : %s",
lnk->name, pat, idata->hook, strerror(errno)));
return(0);
}
/* Register an event listening to the control socket */
EventRegister(&(PIf->ctrlEvent), EVENT_READ, PIf->dsock,
DEV_PRIO, PppoeListenEvent, PIf);
return(1);
};
/*
* PppoeNodeUpdate()
*/
static void
PppoeNodeUpdate(void)
{
int i,j,k;
/* Examine all PPPoE links */
for (k = 0; k < gNumLinks; k++) {
if (gLinks[k] && gLinks[k]->phys->type == &gPppoePhysType
&& strcmp(((PppoeInfo)(gLinks[k]->phys->info))->path,"undefined:")
&& strcmp(((PppoeInfo)(gLinks[k]->phys->info))->session,"undefined")) {
PppoeInfo const p = (PppoeInfo)gLinks[k]->phys->info;
j=-1;
for (i=0;i<PppoeIfCount;i++) {
if (strcmp(PppoeIfs[i].ifnodepath,p->path)==0)
j=i;
};
if (j==-1) {
if (CreatePppoeNode(p->path,p->hook)) {
snprintf(PppoeIfs[PppoeIfCount].ifnodepath,sizeof(PppoeIfs[PppoeIfCount].ifnodepath),"%s",p->path);
snprintf(PppoeIfs[PppoeIfCount].session,sizeof(PppoeIfs[PppoeIfCount].session),"%s",p->session);
PppoeIfs[PppoeIfCount++].listen=0;
} else {
Log(LG_ERR, ("[%s] Error in creation ng_pppoe node on %s", lnk->name, p->path));
return;
};
};
if (Enabled(&p->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 i,j,k;
/* Examine all PPPoE links */
for (k = 0; k < gNumLinks; k++) {
if (gLinks[k] && gLinks[k]->phys->type == &gPppoePhysType
&& strcmp(((PppoeInfo)(gLinks[k]->phys->info))->path,"undefined:")
&& strcmp(((PppoeInfo)(gLinks[k]->phys->info))->session,"undefined")) {
PppoeInfo const p = (PppoeInfo)gLinks[k]->phys->info;
if (Enabled(&p->options, PPPOE_CONF_INCOMING)) {
j=-1;
for (i=0;i<PppoeIfCount;i++) {
if ((strcmp(PppoeIfs[i].ifnodepath,p->path)==0)&&(strcmp(PppoeIfs[i].session,p->session)==0))
j=i;
};
if (j==-1) {
if (ListenPppoeNode(p->path,p->hook,&(PppoeIfs[PppoeIfCount]),p->session)) {
snprintf(PppoeIfs[PppoeIfCount].ifnodepath,sizeof(PppoeIfs[PppoeIfCount].ifnodepath),"%s",p->path);
snprintf(PppoeIfs[PppoeIfCount].session,sizeof(PppoeIfs[PppoeIfCount].session),"%s",p->session);
PppoeIfs[PppoeIfCount].listen=1;
PppoeIfCount++;
};
} else {
if ((PppoeIfs[j].listen==0)&&(ListenPppoeNode(p->path,p->hook,&(PppoeIfs[j]),p->session))) {
PppoeIfs[j].listen=1;
};
};
};
}
}
}
/*
* PppoeSetCommand()
*/
static int
PppoeSetCommand(int ac, char *av[], void *arg)
{
const PppoeInfo pe = (PppoeInfo) lnk->phys->info;
const char *hookname = ETHER_DEFAULT_HOOK;
const char *colon;
if (lnk->phys->type != &gPppoePhysType) {
Log(LG_ERR, ("[%s] link type is not pppoe", lnk->name));
return(0);
}
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(pe->path, sizeof(pe->path),
"%s%s", av[0], colon);
snprintf(pe->hook, sizeof(pe->hook),
"%s", hookname);
break;
default:
return(-1);
}
break;
case SET_SESSION:
if (ac != 1)
return(-1);
snprintf(pe->session, sizeof(pe->session), "%s", av[0]);
break;
case SET_ENABLE:
EnableCommand(ac, av, &pe->options, gConfList);
PppoeNodeUpdate();
break;
case SET_DISABLE:
DisableCommand(ac, av, &pe->options, gConfList);
PppoeNodeUpdate();
break;
default:
assert(0);
}
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1