EXAMPLES
The following code uses libnetgraph to set up a ng_pppoe node and connect it to both a socket node and an Ethernet node. It can handle the case of when a ng_pppoe node is already attached to the Ethernet. It then starts a client session.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sysexits.h>
#include <errno.h>
#include <err.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <net/ethernet.h>
#include <netgraph.h>
#include <netgraph/ng_ether.h>
#include <netgraph/ng_pppoe.h>
#include <netgraph/ng_socket.h>
static int setup(char *ethername, char *service, char *sessname,
int *dfd, int *cfd);
int
main()
{
int fd1, fd2;
setup("xl0", NULL, "fred", &fd1, &fd2);
sleep (30);
}
static int
setup(char *ethername, char *service, char *sessname,
int *dfd, int *cfd)
{
struct ngm_connect ngc; /* connect */
struct ngm_mkpeer mkp; /* mkpeer */
/******** nodeinfo stuff **********/
u_charrbuf[2 * 1024];
struct ng_mesg *const resp = (struct ng_mesg *) rbuf;
struct hooklist *const hlist
= (struct hooklist *) resp->data;
struct nodeinfo *const ninfo = &hlist->nodeinfo;
int ch, no_hooks = 0;
struct linkinfo *link;
struct nodeinfo *peer;
/****message to connect PPPoE session*****/
struct {
struct ngpppoe_init_data idata;
char service[100];
}message;
/********tracking our little graph ********/
char path[100];
char source_ID[NG_NODESIZ];
char pppoe_node_name[100];
int k;
/*
* Create the data and control sockets
*/
if (NgMkSockNode(NULL, cfd, dfd) < 0) {
return (errno);
}
/*
* find the ether node of the name requested by asking it for
* its inquiry information.
*/
if (strlen(ethername) > 16)
return (EINVAL);
sprintf(path, "%s:", ethername);
if (NgSendMsg(*cfd, path, NGM_GENERIC_COOKIE,
NGM_LISTHOOKS, NULL, 0) < 0) {
return (errno);
}
/*
* the command was accepted so it exists. Await the reply (Its
* almost certainly already waiting).
*/
if (NgRecvMsg(*cfd, resp, sizeof(rbuf), NULL) < 0) {
return (errno);
}
/**
* The following is available about the node:
* ninfo->name(string)
* ninfo->type(string)
* ninfo->id (u_int32_t)
* ninfo->hooks (u_int32_t) (count of hooks)
* check it is the correct type. and get its ID for use
* with mkpeer later.
*/
if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
strlen(NG_ETHER_NODE_TYPE)) != 0) {
return (EPROTOTYPE);
}
sprintf(source_ID, "[%08x]:", ninfo->id);
/*
* look for a hook already attached.
*/
for (k = 0; k < ninfo->hooks; k++) {
/**
* The following are available about each hook.
* link->ourhook (string)
* link->peerhook (string)
* peer->name (string)
* peer->type (string)
* peer->id (u_int32_t)
* peer->hooks(u_int32_t)
*/
link = &hlist->link[k];
peer = &hlist->link[k].nodeinfo;
/* Ignore debug hooks */
if (strcmp("debug", link->ourhook) == 0)
continue;
/* If the orphans hook is attached, use that */
if (strcmp(NG_ETHER_HOOK_ORPHAN,
link->ourhook) == 0) {
break;
}
/* the other option is the divert hook */
if (strcmp("NG_ETHER_HOOK_DIVERT",
link->ourhook) == 0) {
break;
}
}
/*
* See if we found a hook there.
*/
if (k < ninfo->hooks) {
if (strcmp(peer->type, NG_PPPOE_NODE_TYPE) == 0) {
/*
* If its a type PPPoE, we skip making one
* ourself, but we continue, using
* the existing one.
*/
sprintf(pppoe_node_name, "[%08x]:", peer->id);
} else {
/*
* There is already someone hogging the data,
* return an error. Some day well try
* daisy-chaining..
*/
return (EBUSY);
}
} else {
/*
* Try make a node of type PPPoE against node "ID"
* On hook NG_ETHER_HOOK_ORPHAN.
*/
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);
/* Send message */
if (NgSendMsg(*cfd, source_ID, NGM_GENERIC_COOKIE,
NGM_MKPEER, &mkp, sizeof(mkp)) < 0) {
return (errno);
}
/*
* Work out a name for the new node.
*/
sprintf(pppoe_node_name, "%s:%s",
source_ID, NG_ETHER_HOOK_ORPHAN);
}
/*
* We now have a PPPoE node attached to the Ethernet
* card. The Ethernet is addressed as ethername: The PPPoE
* node is addressed as pppoe_node_name: attach to it.
* Connect socket node to specified node Use the same hook
* name on both ends of the link.
*/
snprintf(ngc.path, sizeof(ngc.path), "%s", pppoe_node_name);
snprintf(ngc.ourhook, sizeof(ngc.ourhook), "%s", sessname);
snprintf(ngc.peerhook, sizeof(ngc.peerhook), "%s", sessname);
if (NgSendMsg(*cfd, ".:", NGM_GENERIC_COOKIE,
NGM_CONNECT, &ngc, sizeof(ngc)) < 0) {
return (errno);
}
#ifdef NONSTANDARD
/*
* In some cases we are speaking to 3Com hardware, so
* configure node to non-standard mode.
*/
if (NgSendMsg(*cfd, ngc.path, NGM_PPPOE_COOKIE,
NGM_PPPOE_SETMODE, NG_PPPOE_NONSTANDARD,
strlen(NG_PPPOE_NONSTANDARD) + 1) == -1) {
return (errno);
}
#endif
/*
* Send it a message telling it to start up.
*/
bzero(&message, sizeof(message));
snprintf(message.idata.hook, sizeof(message.idata.hook),
"%s", sessname);
if (service == NULL) {
message.idata.data_len = 0;
} else {
snprintf(message.idata.data,
sizeof(message.idata.data), "%s", service);
message.idata.data_len = strlen(service);
}
/* Tell session/hook to start up as a client */
if (NgSendMsg(*cfd, ngc.path,
NGM_PPPOE_COOKIE, NGM_PPPOE_CONNECT, &message.idata,
sizeof(message.idata) + message.idata.data_len) < 0) {
return (errno);
}
return (0);
}
SEE ALSO
netgraph(3), netgraph(4), ng_ppp(4), ng_socket(4), ngctl(8), ppp(8)
.Rs "A Method for transmitting PPP over Ethernet (PPPoE)"
.Re
HISTORY
AUTHORS