From c258ec9bc665fdbd479498a77aea5589b316e074 Mon Sep 17 00:00:00 2001
From: Kozlov Dmitry <dima@server>
Date: Fri, 8 Oct 2010 12:16:07 +0400
Subject: ctrl: implemented PPPoE server

---
 accel-pptpd/ctrl/CMakeLists.txt |   1 +
 accel-pptpd/ctrl/pppoe.c        | 884 +++++++++++++++++++++++++++++++++++++
 accel-pptpd/ctrl/pppoe.h        |  59 +++
 accel-pptpd/include/events.h    |   2 +
 accel-pptpd/include/if_pppox.h  |  13 +-
 accel-pptpd/ppp/ppp.c           |   2 +-
 accel-pptpd/ppp/ppp_ccp.c       |   2 +
 accel-pptpd/ppp/ppp_fsm.c       |   6 +-
 accel-pptpd/ppp/ppp_ipcp.c      |   2 +
 rfc/rfc2516.txt                 | 955 ++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1920 insertions(+), 6 deletions(-)
 create mode 100644 accel-pptpd/ctrl/pppoe.c
 create mode 100644 accel-pptpd/ctrl/pppoe.h
 create mode 100644 rfc/rfc2516.txt

diff --git a/accel-pptpd/ctrl/CMakeLists.txt b/accel-pptpd/ctrl/CMakeLists.txt
index 53b0dc1c..c26bdd57 100644
--- a/accel-pptpd/ctrl/CMakeLists.txt
+++ b/accel-pptpd/ctrl/CMakeLists.txt
@@ -1,6 +1,7 @@
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 
 ADD_LIBRARY(pptp SHARED pptp.c)
+ADD_LIBRARY(pppoe SHARED pppoe.c)
 
 INSTALL(TARGETS pptp
 	LIBRARY DESTINATION usr/lib/accel-pptp
diff --git a/accel-pptpd/ctrl/pppoe.c b/accel-pptpd/ctrl/pppoe.c
new file mode 100644
index 00000000..7a9616a2
--- /dev/null
+++ b/accel-pptpd/ctrl/pppoe.c
@@ -0,0 +1,884 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/ethernet.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+
+#include <openssl/md5.h>
+
+#include "events.h"
+#include "triton.h"
+#include "log.h"
+#include "ppp.h"
+#include "mempool.h"
+
+#include "pppoe.h"
+
+#include "memdebug.h"
+
+
+struct pppoe_serv_t
+{
+	struct triton_context_t ctx;
+	struct triton_md_handler_t hnd;
+	uint8_t hwaddr[ETH_ALEN];
+	const char *ifname;
+
+	pthread_mutex_t lock;
+	struct pppoe_conn_t *conn[MAX_SID];
+	uint16_t sid;
+};
+
+struct pppoe_conn_t
+{
+	struct triton_context_t ctx;
+	struct pppoe_serv_t *serv;
+	int disc_sock;
+	uint16_t sid;
+	uint8_t addr[ETH_ALEN];
+	int ppp_started:1;
+
+	struct pppoe_tag *relay_sid;
+	struct pppoe_tag *host_uniq;
+	
+	struct ppp_ctrl_t ctrl;
+	struct ppp_t ppp;
+};
+
+static int conf_verbose = 0;
+static const char *conf_service_name = "";
+static const char *conf_ac_name = "accel-pptp";
+
+static mempool_t conn_pool;
+
+#define SECRET_SIZE 16
+static uint8_t *secret;
+
+static uint8_t bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static void pppoe_send_PADT(struct pppoe_conn_t *conn);
+
+static void disconnect(struct pppoe_conn_t *conn)
+{
+	if (conn->ppp_started) {
+		conn->ppp_started = 0;
+		ppp_terminate(&conn->ppp, 1);
+	}
+
+	pppoe_send_PADT(conn);
+
+	close(conn->disc_sock);
+
+	triton_event_fire(EV_CTRL_FINISHED, &conn->ppp);
+
+	if (conf_verbose)
+		log_ppp_info("disconnected\n");
+
+	pthread_mutex_lock(&conn->serv->lock);
+	conn->serv->conn[conn->sid] = NULL;
+	pthread_mutex_unlock(&conn->serv->lock);
+
+	_free(conn->ctrl.calling_station_id);
+	_free(conn->ctrl.called_station_id);
+	if (conn->host_uniq)
+		_free(conn->host_uniq);
+	if (conn->relay_sid)
+		_free(conn->relay_sid);
+	
+	triton_context_unregister(&conn->ctx);
+
+	mempool_free(conn);
+}
+
+static void ppp_started(struct ppp_t *ppp)
+{
+	log_ppp_debug("pppoe: ppp started\n");
+}
+
+static void ppp_finished(struct ppp_t *ppp)
+{
+	struct pppoe_conn_t *conn = container_of(ppp, typeof(*conn), ppp);
+
+	log_ppp_debug("pppoe: ppp finished\n");
+
+	if (conn->ppp_started) {
+		conn->ppp_started = 0;
+		disconnect(conn);
+	}
+}
+
+static void pppoe_conn_close(struct triton_context_t *ctx)
+{
+	struct pppoe_conn_t *conn = container_of(ctx, typeof(*conn), ctx);
+
+	if (conn->ppp_started)
+		ppp_terminate(&conn->ppp, 0);
+	else
+		disconnect(conn);
+}
+
+static struct pppoe_conn_t *allocate_channel(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid)
+{
+	struct pppoe_conn_t *conn;
+	int sid;
+
+	conn = mempool_alloc(conn_pool);
+	if (!conn) {
+		log_emerg("pppoe: out of memory\n");
+		return NULL;
+	}
+
+	memset(conn, 0, sizeof(*conn));
+
+	pthread_mutex_lock(&serv->lock);
+	for (sid = serv->sid + 1; sid != serv->sid; sid++) {
+		if (sid == MAX_SID)
+			sid = 1;
+		if (!serv->conn[sid]) {
+			conn->sid = sid;
+			serv->sid = sid;
+			serv->conn[sid] = conn;
+			break;
+		}
+	}
+	pthread_mutex_unlock(&serv->lock);
+
+	if (!conn->sid) {
+		log_warn("pppoe: no free sid available\n");
+		mempool_free(conn);
+		return NULL;
+	}
+	
+	conn->serv = serv;
+	memcpy(conn->addr, addr, ETH_ALEN);
+
+	if (host_uniq) {
+		conn->host_uniq = _malloc(sizeof(*host_uniq) + ntohs(host_uniq->tag_len));
+		memcpy(conn->host_uniq, host_uniq, sizeof(*host_uniq) + ntohs(host_uniq->tag_len));
+	}
+
+	if (relay_sid) {
+		conn->relay_sid = _malloc(sizeof(*relay_sid) + ntohs(relay_sid->tag_len));
+		memcpy(conn->relay_sid, relay_sid, sizeof(*relay_sid) + ntohs(relay_sid->tag_len));
+	}
+
+	conn->ctx.close = pppoe_conn_close;
+	conn->ctrl.ctx = &conn->ctx;
+	conn->ctrl.started = ppp_started;
+	conn->ctrl.finished = ppp_finished;
+	conn->ctrl.max_mtu = MAX_PPPOE_MTU;
+	conn->ctrl.name = "pppoe";
+
+	conn->ctrl.calling_station_id = _malloc(19);
+	conn->ctrl.called_station_id = _malloc(19);
+	sprintf(conn->ctrl.calling_station_id, "%02x:%02x:%02x:%02x:%02x:%02x",
+		addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+	sprintf(conn->ctrl.called_station_id, "%02x:%02x:%02x:%02x:%02x:%02x",
+		serv->hwaddr[0], serv->hwaddr[1], serv->hwaddr[2], serv->hwaddr[3], serv->hwaddr[4], serv->hwaddr[5]);
+	
+	ppp_init(&conn->ppp);
+
+	conn->ppp.ctrl = &conn->ctrl;
+	conn->ppp.chan_name = conn->ctrl.calling_station_id;
+	
+	triton_context_register(&conn->ctx, &conn->ppp);
+	triton_context_wakeup(&conn->ctx);
+	
+	triton_event_fire(EV_CTRL_STARTING, &conn->ppp);
+	triton_event_fire(EV_CTRL_STARTED, &conn->ppp);
+
+	conn->disc_sock = dup(serv->hnd.fd);
+
+	return conn;
+}
+
+static int connect_channel(struct pppoe_conn_t *conn)
+{
+	int sock;
+	struct sockaddr_pppox sp;
+
+	sock = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
+	if (!sock) {
+		log_error("pppoe: socket(PPPOX): %s\n", strerror(errno));
+		return -1;
+	}
+
+	memset(&sp, 0, sizeof(sp));
+
+	sp.sa_family = AF_PPPOX;
+	sp.sa_protocol = PX_PROTO_OE;
+	sp.sa_addr.pppoe.sid = htons(conn->sid);
+	strcpy(sp.sa_addr.pppoe.dev, conn->serv->ifname);
+	memcpy(sp.sa_addr.pppoe.remote, conn->addr, ETH_ALEN);
+
+	if (connect(sock, (struct sockaddr *)&sp, sizeof(sp))) {
+		log_error("pppoe: connect: %s\n", strerror(errno));
+		close(sock);
+		return -1;
+	}
+
+	conn->ppp.fd = sock;
+
+	if (establish_ppp(&conn->ppp)) {
+		close(sock);
+		return -1;
+	}
+	
+	return 0;
+}
+
+static void print_tag_string(struct pppoe_tag *tag)
+{
+	int i;
+
+	for (i = 0; i < ntohs(tag->tag_len); i++)
+		log_info("%c", tag->tag_data[i]);
+}
+
+static void print_tag_octets(struct pppoe_tag *tag)
+{
+	int i;
+
+	for (i = 0; i < ntohs(tag->tag_len); i++)
+		log_info("%02x", (uint8_t)tag->tag_data[i]);
+}
+
+static void print_packet(uint8_t *pack)
+{
+	struct ethhdr *ethhdr = (struct ethhdr *)pack;
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	struct pppoe_tag *tag;
+	int n;
+
+	log_info("[PPPoE ");
+
+	switch (hdr->code) {
+		case CODE_PADI:
+			log_info("PADI");
+			break;
+		case CODE_PADO:
+			log_info("PADO");
+			break;
+		case CODE_PADR:
+			log_info("PADR");
+			break;
+		case CODE_PADS:
+			log_info("PADS");
+			break;
+		case CODE_PADT:
+			log_info("PADT");
+			break;
+	}
+	
+	log_info(" %02x:%02x:%02x:%02x:%02x:%02x => %02x:%02x:%02x:%02x:%02x:%02x", 
+		ethhdr->h_source[0], ethhdr->h_source[1], ethhdr->h_source[2], ethhdr->h_source[3], ethhdr->h_source[4], ethhdr->h_source[5],
+		ethhdr->h_dest[0], ethhdr->h_dest[1], ethhdr->h_dest[2], ethhdr->h_dest[3], ethhdr->h_dest[4], ethhdr->h_dest[5]);
+
+	log_info(" sid=%04x", ntohs(hdr->sid));
+
+	for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) {
+		tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n);
+		switch (ntohs(tag->tag_type)) {
+			case TAG_END_OF_LIST:
+				log_info(" <End-Of-List>");
+				break;
+			case TAG_SERVICE_NAME:
+				log_info(" <Service-Name ");
+				print_tag_string(tag);
+				log_info(">");
+				break;
+			case TAG_AC_NAME:
+				log_info(" <AC-Name ");
+				print_tag_string(tag);
+				log_info(">");
+				break;
+			case TAG_HOST_UNIQ:
+				log_info(" <Host-Uniq ");
+				print_tag_octets(tag);
+				log_info(">");
+				break;
+			case TAG_AC_COOKIE:
+				log_info(" <AC-Cookie ");
+				print_tag_octets(tag);
+				log_info(">");
+				break;
+			case TAG_VENDOR_SPECIFIC:
+				log_info(" <Vendor-Specific>");
+				break;
+			case TAG_RELAY_SESSION_ID:
+				log_info(" <Relay-Session-Id");
+				print_tag_octets(tag);
+				log_info(">");
+				break;
+			case TAG_SERVICE_NAME_ERROR:
+				log_info(" <Service-Name-Error>");
+				break;
+			case TAG_AC_SYSTEM_ERROR:
+				log_info(" <AC-System-Error>");
+				break;
+			case TAG_GENERIC_ERROR:
+				log_info(" <Generic-Error>");
+				break;
+			default:
+				log_info(" <Unknown (%x)>", ntohs(tag->tag_type));
+				break;
+		}
+	}
+
+	log_info("]\n");
+}
+
+static void generate_cookie(const uint8_t *src, const uint8_t *dst, const struct pppoe_tag *host_uniq, uint8_t *cookie)
+{
+	MD5_CTX ctx;
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, secret, SECRET_SIZE);
+	MD5_Update(&ctx, src, ETH_ALEN);
+	MD5_Update(&ctx, dst, ETH_ALEN);
+	if (host_uniq)
+		MD5_Update(&ctx, host_uniq->tag_data, ntohs(host_uniq->tag_len));
+	MD5_Update(&ctx, conf_ac_name, strlen(conf_ac_name));
+	MD5_Update(&ctx, secret, SECRET_SIZE);
+	MD5_Final(cookie, &ctx);
+}
+
+static void setup_header(uint8_t *pack, const uint8_t *src, const uint8_t *dst, int code, uint16_t sid)
+{
+	struct ethhdr *ethhdr = (struct ethhdr *)pack;
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+
+	memcpy(ethhdr->h_source, src, ETH_ALEN);
+	memcpy(ethhdr->h_dest, dst, ETH_ALEN);
+	ethhdr->h_proto = htons(ETH_P_PPP_DISC);
+
+	hdr->ver = 1;
+	hdr->type = 1;
+	hdr->code = code;
+	hdr->sid = htons(sid);
+	hdr->length = 0;
+}
+
+static void add_tag(uint8_t *pack, int type, const uint8_t *data, int len)
+{
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length));
+
+	tag->tag_type = htons(type);
+	tag->tag_len = htons(len);
+	memcpy(tag->tag_data, data, len);
+
+	hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + len);
+}
+
+static void add_tag2(uint8_t *pack, const struct pppoe_tag *t)
+{
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length));
+
+	memcpy(tag, t, sizeof(*t) + ntohs(t->tag_len));
+	
+	hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + ntohs(t->tag_len));
+}
+
+static void pppoe_send(int fd, const uint8_t *pack)
+{
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	int n, s;
+
+	s = ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length);
+	n = write(fd, pack, s);
+	if (n < 0 )
+		log_error("pppoe: write: %s\n", strerror(errno));
+	else if (n != s) {
+		log_warn("pppoe: short write %i/%i\n", n,s);
+	}
+}
+
+static void pppoe_send_PADO(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid)
+{
+	uint8_t pack[ETHER_MAX_LEN];
+	uint8_t cookie[MD5_DIGEST_LENGTH];
+
+	setup_header(pack, serv->hwaddr, addr, CODE_PADO, 0);
+
+	add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name));
+	add_tag(pack, TAG_SERVICE_NAME, (uint8_t *)conf_service_name, strlen(conf_service_name));
+
+	generate_cookie(serv->hwaddr, addr, host_uniq, cookie);
+	add_tag(pack, TAG_AC_COOKIE, cookie, MD5_DIGEST_LENGTH);
+
+	if (host_uniq)
+		add_tag2(pack, host_uniq);
+	
+	if (relay_sid)
+		add_tag2(pack, relay_sid);
+
+	if (conf_verbose) {
+		log_info("send ");
+		print_packet(pack);
+	}
+
+	pppoe_send(serv->hnd.fd, pack);
+}
+
+static void pppoe_send_err(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, int code, int tag_type)
+{
+	uint8_t pack[ETHER_MAX_LEN];
+
+	setup_header(pack, serv->hwaddr, addr, code, 0);
+
+	add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name));
+	add_tag(pack, tag_type, NULL, 0);
+
+	if (host_uniq)
+		add_tag2(pack, host_uniq);
+	
+	if (relay_sid)
+		add_tag2(pack, relay_sid);
+
+	if (conf_verbose) {
+		log_info("send ");
+		print_packet(pack);
+	}
+
+	pppoe_send(serv->hnd.fd, pack);
+}
+
+static void pppoe_send_PADS(struct pppoe_conn_t *conn)
+{
+	uint8_t pack[ETHER_MAX_LEN];
+
+	setup_header(pack, conn->serv->hwaddr, conn->addr, CODE_PADS, conn->sid);
+
+	add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name));
+	add_tag(pack, TAG_SERVICE_NAME, (uint8_t *)conf_service_name, strlen(conf_service_name));
+
+	if (conn->host_uniq)
+		add_tag2(pack, conn->host_uniq);
+	
+	if (conn->relay_sid)
+		add_tag2(pack, conn->relay_sid);
+
+	if (conf_verbose) {
+		log_info("send ");
+		print_packet(pack);
+	}
+
+	pppoe_send(conn->disc_sock, pack);
+}
+
+static void pppoe_send_PADT(struct pppoe_conn_t *conn)
+{
+	uint8_t pack[ETHER_MAX_LEN];
+
+	setup_header(pack, conn->serv->hwaddr, conn->addr, CODE_PADT, conn->sid);
+
+	add_tag(pack, TAG_AC_NAME, (uint8_t *)conf_ac_name, strlen(conf_ac_name));
+	add_tag(pack, TAG_SERVICE_NAME, (uint8_t *)conf_service_name, strlen(conf_service_name));
+
+	if (conn->host_uniq)
+		add_tag2(pack, conn->host_uniq);
+	
+	if (conn->relay_sid)
+		add_tag2(pack, conn->relay_sid);
+
+	if (conf_verbose) {
+		log_info("send ");
+		print_packet(pack);
+	}
+
+	pppoe_send(conn->disc_sock, pack);
+}
+
+static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size)
+{
+	struct ethhdr *ethhdr = (struct ethhdr *)pack;
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	struct pppoe_tag *tag;
+	struct pppoe_tag *host_uniq_tag = NULL;
+	struct pppoe_tag *relay_sid_tag = NULL;
+	int n, service_match = 0;
+
+	if (hdr->sid) {
+		log_warn("pppoe: discaring PADI packet (sid is not zero)\n");
+		return;
+	}
+
+	if (conf_verbose) {
+		log_info("recv ");
+		print_packet(pack);
+	}
+	
+	for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) {
+		tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n);
+		switch (ntohs(tag->tag_type)) {
+			case TAG_END_OF_LIST:
+				break;
+			case TAG_SERVICE_NAME:
+				if (tag->tag_len == 0)
+					service_match = 1;
+				else if (conf_service_name) {
+					if (ntohs(tag->tag_len) != strlen(conf_service_name))
+						break;
+					if (memcmp(tag->tag_data, conf_service_name, ntohs(tag->tag_len)))
+						break;
+					service_match = 1;
+				}
+				break;
+			case TAG_HOST_UNIQ:
+				host_uniq_tag = tag;
+				break;
+			case TAG_RELAY_SESSION_ID:
+				relay_sid_tag = tag;
+				break;
+		}
+	}
+
+	if (!service_match) {
+		if (conf_verbose)
+			log_warn("pppoe: discarding PADI packet (Service-Name mismatch)\n");
+		return;
+	}
+	
+	pppoe_send_PADO(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag);
+}
+
+static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size)
+{
+	struct ethhdr *ethhdr = (struct ethhdr *)pack;
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	struct pppoe_tag *tag;
+	struct pppoe_tag *host_uniq_tag = NULL;
+	struct pppoe_tag *relay_sid_tag = NULL;
+	struct pppoe_tag *ac_cookie_tag = NULL;
+	uint8_t cookie[MD5_DIGEST_LENGTH];
+	int n, service_match = 0;
+	struct pppoe_conn_t *conn;
+
+	if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) {
+		if (conf_verbose)
+			log_warn("pppoe: discard PADR (destination address is broadcast)\n");
+		return;
+	}
+	
+	if (hdr->sid) {
+		if (conf_verbose)
+			log_warn("pppoe: discaring PADR packet (sid is not zero)\n");
+		return;
+	}
+
+	if (conf_verbose) {
+		log_info("recv ");
+		print_packet(pack);
+	}
+	
+	for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) {
+		tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n);
+		switch (ntohs(tag->tag_type)) {
+			case TAG_END_OF_LIST:
+				break;
+			case TAG_SERVICE_NAME:
+				if (tag->tag_len == 0)
+					service_match = 1;
+				else if (conf_service_name) {
+					if (ntohs(tag->tag_len) != strlen(conf_service_name))
+						break;
+					if (memcmp(tag->tag_data, conf_service_name, ntohs(tag->tag_len)))
+						break;
+					service_match = 1;
+				}
+				break;
+			case TAG_HOST_UNIQ:
+				host_uniq_tag = tag;
+				break;
+			case TAG_AC_COOKIE:
+				ac_cookie_tag = tag;
+				break;
+			case TAG_RELAY_SESSION_ID:
+				relay_sid_tag = tag;
+				break;
+		}
+	}
+
+	if (!ac_cookie_tag) {
+		if (conf_verbose)
+			log_warn("pppoe: discard PADR packet (no AC-Cookie tag present)\n");
+		return;
+	}
+
+	if (ntohs(ac_cookie_tag->tag_len) != MD5_DIGEST_LENGTH) {
+		if (conf_verbose)
+			log_warn("pppoe: discard PADR packet (incorrect AC-Cookie tag length)\n");
+		return;
+	}
+
+	generate_cookie(serv->hwaddr, ethhdr->h_source, host_uniq_tag, cookie);
+
+	if (memcmp(cookie, ac_cookie_tag->tag_data, MD5_DIGEST_LENGTH)) {
+		if (conf_verbose)
+			log_warn("pppoe: discard PADR packet (incorrect AC-Cookie)\n");
+		return;
+	}
+
+	if (!service_match) {
+		if (conf_verbose)
+			log_warn("pppoe: Service-Name mismatch\n");
+		pppoe_send_err(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag, CODE_PADS, TAG_SERVICE_NAME_ERROR);
+		return;
+	}
+
+	conn = allocate_channel(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag);
+	if (!conn)
+		pppoe_send_err(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag, CODE_PADS, TAG_AC_SYSTEM_ERROR);
+	else {
+		pppoe_send_PADS(conn);
+		if (connect_channel(conn))
+			disconnect(conn);
+		else
+			conn->ppp_started = 1;
+	}
+}
+
+static void pppoe_recv_PADT(struct pppoe_serv_t *serv, uint8_t *pack)
+{
+	struct ethhdr *ethhdr = (struct ethhdr *)pack;
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	struct pppoe_conn_t *conn;
+	
+	if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) {
+		if (conf_verbose)
+			log_warn("pppoe: discard PADT (destination address is broadcast)\n");
+		return;
+	}
+	
+	if (conf_verbose) {
+		log_info("recv ");
+		print_packet(pack);
+	}
+
+	pthread_mutex_lock(&serv->lock);
+	conn = serv->conn[ntohs(hdr->sid)];
+	if (conn && !memcmp(conn->addr, ethhdr->h_source, ETH_ALEN))
+		triton_context_call(&conn->ctx, (void (*)(void *))disconnect, conn);
+	pthread_mutex_unlock(&serv->lock);
+}
+
+static int pppoe_serv_read(struct triton_md_handler_t *h)
+{
+	struct pppoe_serv_t *serv = container_of(h, typeof(*serv), hnd);
+	uint8_t pack[ETHER_MAX_LEN];
+	struct ethhdr *ethhdr = (struct ethhdr *)pack;
+	struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN);
+	int n;
+
+	n = read(h->fd, pack, sizeof(pack));
+	if (n < 0) {
+		log_error("pppoe: read: %s\n", strerror(errno));
+		return 0;
+	}
+
+	if (n < ETH_HLEN + sizeof(*hdr)) {
+		if (conf_verbose)
+			log_warn("pppoe: short packet received (%i)\n", n);
+		return 0;
+	}
+
+	if (memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN) && memcmp(ethhdr->h_dest, serv->hwaddr, ETH_ALEN))
+		return 0;
+
+	if (!memcmp(ethhdr->h_source, bc_addr, ETH_ALEN)) {
+		if (conf_verbose)
+			log_warn("pppoe: discarding packet (host address is broadcast)\n");
+		return 0;
+	}
+
+	if ((ethhdr->h_source[0] & 1) != 0) {
+		if (conf_verbose)
+			log_warn("pppoe: discarding packet (host address is not unicast)\n");
+		return 0;
+	}
+
+	if (n < ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)) {
+		if (conf_verbose)
+			log_warn("pppoe: short packet received\n");
+		return 0;
+	}
+
+	if (hdr->ver != 1) {
+		if (conf_verbose)
+			log_warn("pppoe: discarding packet (unsupported version %i)\n", hdr->ver);
+		return 0;
+	}
+	
+	if (hdr->type != 1) {
+		if (conf_verbose)
+			log_warn("pppoe: discarding packet (unsupported type %i)\n", hdr->type);
+	}
+
+	switch (hdr->code) {
+		case CODE_PADI:
+			pppoe_recv_PADI(serv, pack, n);
+			break;
+		case CODE_PADR:
+			pppoe_recv_PADR(serv, pack, n);
+			break;
+		case CODE_PADT:
+			pppoe_recv_PADT(serv, pack);
+			break;
+	}
+
+	return 0;
+}
+
+static void pppoe_serv_close(struct triton_context_t *ctx)
+{
+	struct pppoe_serv_t *serv = container_of(ctx, typeof(*serv), ctx);
+	
+	triton_md_unregister_handler(&serv->hnd);
+	close(serv->hnd.fd);
+	triton_context_unregister(&serv->ctx);
+}
+
+static void pppoe_start_server(const char *ifname)
+{
+	struct pppoe_serv_t *serv = _malloc(sizeof(*serv));
+	int sock;
+	int opt = 1;
+	struct ifreq ifr;
+	struct sockaddr_ll sa;
+
+	memset(serv, 0, sizeof(*serv));
+
+	sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_DISC));
+	if (sock < 0) {
+		log_emerg("pppoe: socket: %s\n", strerror(errno));
+		_free(serv);
+		return;
+	}
+
+	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt))) {
+		log_emerg("pppoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno));
+		goto out_err;
+	}
+
+	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
+		log_emerg("pppoe: ioctl(SIOCGIFHWADDR): %s\n", strerror(errno));
+		goto out_err;
+	}
+
+#ifdef ARPHDR_ETHER
+	if (ifr.ifr_hwaddr.sa_family != ARPHDR_ETHER) {
+		log_emerg("pppoe: interface %s is not ethernet\n", ifname);
+		goto out_err;
+	}
+#endif
+
+	if ((ifr.ifr_hwaddr.sa_data[0] & 1) != 0) {
+		log_emerg("pppoe: interface %s has not unicast address\n", ifname);
+		goto out_err;
+	}
+
+	memcpy(serv->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	if (ioctl(sock, SIOCGIFMTU, &ifr)) {
+		log_emerg("pppoe: ioctl(SIOCGIFMTU): %s\n", strerror(errno));
+		goto out_err;
+	}
+
+	if (ifr.ifr_mtu < ETH_DATA_LEN)
+		log_emerg("pppoe: interface %s has MTU of %i, should be %i\n", ifname, ifr.ifr_mtu, ETH_DATA_LEN);
+	
+	if (ioctl(sock, SIOCGIFINDEX, &ifr)) {
+		log_emerg("pppoe: ioctl(SIOCGIFINDEX): %s\n", strerror(errno));
+		goto out_err;
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sll_family = AF_PACKET;
+	sa.sll_protocol = htons(ETH_P_PPP_DISC);
+	sa.sll_ifindex = ifr.ifr_ifindex;
+
+	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) {
+		log_emerg("pppoe: bind: %s\n", strerror(errno));
+		goto out_err;
+	}
+
+	serv->ctx.close = pppoe_serv_close;
+	serv->hnd.fd = sock;
+	serv->hnd.read = pppoe_serv_read;
+	serv->ifname = ifname;
+	pthread_mutex_init(&serv->lock, NULL);
+
+	triton_context_register(&serv->ctx, NULL);
+	triton_md_register_handler(&serv->ctx, &serv->hnd);
+	triton_md_enable_handler(&serv->hnd, MD_MODE_READ);
+	triton_context_wakeup(&serv->ctx);
+
+	return;
+
+out_err:
+	close(sock);
+	_free(serv);
+}
+
+static int init_secret(void)
+{
+	int fd;
+
+	secret = malloc(SECRET_SIZE);
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd < 0) {
+		log_emerg("pppoe: cann't open /dev/urandom: %s\n", strerror(errno));
+		return -1;
+	}
+
+	if (read(fd, secret, SECRET_SIZE) < 0) {
+		log_emerg("pppoe: faild to read /dev/urandom\n", strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
+static void __init pppoe_init(void)
+{
+	struct conf_sect_t *s = conf_get_section("pppoe");
+	struct conf_option_t *opt;
+
+	conn_pool = mempool_create(sizeof(struct pppoe_conn_t));
+
+	if (init_secret())
+		_exit(EXIT_FAILURE);
+
+	if (!s) {
+		log_emerg("pppoe: no configuration, disabled...\n");
+		return;
+	}
+	
+	list_for_each_entry(opt, &s->items, entry) {
+		if (!strcmp(opt->name, "interface"))
+			pppoe_start_server(opt->val);
+		else if (!strcmp(opt->name, "verbose")) {
+			if (atoi(opt->val) > 0)
+				conf_verbose = 1;
+		}	else if (!strcmp(opt->name, "ac-name") || !strcmp(opt->name, "AC-Name"))
+			conf_ac_name = opt->val;
+		else if (!strcmp(opt->name, "service-name") || !strcmp(opt->name, "Service-Name"))
+			conf_service_name = opt->val;
+	}
+}
+
diff --git a/accel-pptpd/ctrl/pppoe.h b/accel-pptpd/ctrl/pppoe.h
new file mode 100644
index 00000000..05061083
--- /dev/null
+++ b/accel-pptpd/ctrl/pppoe.h
@@ -0,0 +1,59 @@
+#ifndef __PPPOE_H
+#define __PPPOE_H
+
+#include <linux/if.h>
+#include <linux/if_pppox.h>
+
+/* PPPoE codes */
+#define CODE_PADI           0x09
+#define CODE_PADO           0x07
+#define CODE_PADR           0x19
+#define CODE_PADS           0x65
+#define CODE_PADT           0xA7
+#define CODE_SESS           0x00
+
+/* PPPoE Tags */
+#define TAG_END_OF_LIST        0x0000
+#define TAG_SERVICE_NAME       0x0101
+#define TAG_AC_NAME            0x0102
+#define TAG_HOST_UNIQ          0x0103
+#define TAG_AC_COOKIE          0x0104
+#define TAG_VENDOR_SPECIFIC    0x0105
+#define TAG_RELAY_SESSION_ID   0x0110
+#define TAG_SERVICE_NAME_ERROR 0x0201
+#define TAG_AC_SYSTEM_ERROR    0x0202
+#define TAG_GENERIC_ERROR      0x0203
+
+/* Discovery phase states */
+#define STATE_SENT_PADI     0
+#define STATE_RECEIVED_PADO 1
+#define STATE_SENT_PADR     2
+#define STATE_SESSION       3
+#define STATE_TERMINATED    4
+
+/* Header size of a PPPoE packet */
+#define PPPOE_OVERHEAD 6  /* type, code, session, length */
+#define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD)
+#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD)
+#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - 2)
+
+#define MAX_SID 65534
+
+struct pppoe_tag_t
+{
+	struct list_head entry;
+	int type;
+	int len;
+};
+
+struct pppoe_packet_t
+{
+	uint8_t src[ETH_ALEN];
+	uint8_t dst[ETH_ALEN];
+	int code;
+	uint16_t sid;
+	struct list_head tags;
+};
+
+#endif
+
diff --git a/accel-pptpd/include/events.h b/accel-pptpd/include/events.h
index 2f4c4e5c..f0785be8 100644
--- a/accel-pptpd/include/events.h
+++ b/accel-pptpd/include/events.h
@@ -1,6 +1,8 @@
 #ifndef __EVENTS_H
 #define __EVENTS_H
 
+#include <stdint.h>
+
 #define EV_PPP_STARTING     1
 #define EV_PPP_STARTED      2
 #define EV_PPP_FINISHING    3
diff --git a/accel-pptpd/include/if_pppox.h b/accel-pptpd/include/if_pppox.h
index da327a1c..0532d18f 100644
--- a/accel-pptpd/include/if_pppox.h
+++ b/accel-pptpd/include/if_pppox.h
@@ -20,11 +20,11 @@
 #include <asm/types.h>
 #include <asm/byteorder.h>
 #include <linux/version.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
 
 #ifdef  __KERNEL__
 #include <linux/in.h>
-#include <linux/if_ether.h>
-#include <linux/if.h>
 #include <linux/netdevice.h>
 #include <linux/ppp_channel.h>
 #endif /* __KERNEL__ */
@@ -37,7 +37,13 @@
 #define PF_PPPOX	AF_PPPOX
 #endif /* !(AF_PPPOX) */
 
-struct pptp_addr{
+struct pppoe_addr {
+	__be16 sid;
+	unsigned char remote[ETH_ALEN];
+	char dev[IFNAMSIZ];
+};
+
+struct pptp_addr {
        __u16           call_id;
        struct in_addr  sin_addr;
 };
@@ -57,6 +63,7 @@ struct sockaddr_pppox {
        sa_family_t     sa_family;            /* address family, AF_PPPOX */
        unsigned int    sa_protocol;          /* protocol identifier */
        union{
+	       			 struct pppoe_addr       pppoe;
 	       			 struct pptp_addr        pptp;
        }sa_addr;
 }__attribute__ ((packed));
diff --git a/accel-pptpd/ppp/ppp.c b/accel-pptpd/ppp/ppp.c
index 3e51ecbb..24795b12 100644
--- a/accel-pptpd/ppp/ppp.c
+++ b/accel-pptpd/ppp/ppp.c
@@ -80,7 +80,7 @@ int __export establish_ppp(struct ppp_t *ppp)
 {
 	/* Open an instance of /dev/ppp and connect the channel to it */
 	if (ioctl(ppp->fd, PPPIOCGCHAN, &ppp->chan_idx) == -1) {
-		log_ppp_error("Couldn't get channel number\n");
+		log_ppp_error("ioctl(PPPIOCGCHAN): %s\n", strerror(errno));
 		return -1;
 	}
 
diff --git a/accel-pptpd/ppp/ppp_ccp.c b/accel-pptpd/ppp/ppp_ccp.c
index ae9e6375..163881b3 100644
--- a/accel-pptpd/ppp/ppp_ccp.c
+++ b/accel-pptpd/ppp/ppp_ccp.c
@@ -153,6 +153,8 @@ void ccp_layer_finish(struct ppp_layer_data_t *ld)
 	ccp_set_flags(ccp->ppp->unit_fd, 0, 0);
 
 	ccp->fsm.fsm_state = FSM_Closed;
+	
+	log_ppp_debug("ccp_layer_finished\n");
 	ppp_layer_finished(ccp->ppp, &ccp->ld);
 }
 
diff --git a/accel-pptpd/ppp/ppp_fsm.c b/accel-pptpd/ppp/ppp_fsm.c
index 4769dc89..3478d252 100644
--- a/accel-pptpd/ppp/ppp_fsm.c
+++ b/accel-pptpd/ppp/ppp_fsm.c
@@ -399,7 +399,7 @@ void ppp_fsm_recv_term_req(struct ppp_fsm_t *layer)
 			layer->fsm_state=FSM_Req_Sent;
 			break;
 		default:
-			layer->send_term_req(layer);
+			layer->send_term_ack(layer);
 			break;
 	}
 }
@@ -487,8 +487,10 @@ static void init_req_counter(struct ppp_fsm_t *layer,int timeout)
 }
 static void zero_req_counter(struct ppp_fsm_t *layer)
 {
-	layer->restart_timer.expire_tv.tv_sec=0;
 	layer->restart_counter=0;
+	
+	if (!layer->restart_timer.tpd)
+		triton_timer_add(layer->ppp->ctrl->ctx, &layer->restart_timer, 0);
 }
 
 static void restart_timer_func(struct triton_timer_t *t)
diff --git a/accel-pptpd/ppp/ppp_ipcp.c b/accel-pptpd/ppp/ppp_ipcp.c
index c46b1bdb..90e2b9c4 100644
--- a/accel-pptpd/ppp/ppp_ipcp.c
+++ b/accel-pptpd/ppp/ppp_ipcp.c
@@ -116,6 +116,8 @@ void ipcp_layer_finish(struct ppp_layer_data_t *ld)
 	log_ppp_debug("ipcp_layer_finish\n");
 
 	ipcp->fsm.fsm_state = FSM_Closed;
+	
+	log_ppp_debug("ipcp_layer_finished\n");
 	ppp_layer_finished(ipcp->ppp, &ipcp->ld);
 }
 
diff --git a/rfc/rfc2516.txt b/rfc/rfc2516.txt
new file mode 100644
index 00000000..5397c863
--- /dev/null
+++ b/rfc/rfc2516.txt
@@ -0,0 +1,955 @@
+
+
+
+
+
+
+Network Working Group                                        L. Mamakos
+Request for Comments: 2516                                      K. Lidl
+Category: Informational                                       J. Evarts
+                                               UUNET Technologies, Inc.
+                                                              D. Carrel
+                                                              D. Simone
+                                                 RedBack Networks, Inc.
+                                                             R. Wheeler
+                                                       RouterWare, Inc.
+                                                          February 1999
+
+
+          A Method for Transmitting PPP Over Ethernet (PPPoE)
+
+Status of this Memo
+
+   This memo provides information for the Internet community.  It does
+   not specify an Internet standard of any kind.  Distribution of this
+   memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (1999).  All Rights Reserved.
+
+Abstract
+
+   The Point-to-Point Protocol (PPP) [1] provides a standard method for
+   transporting multi-protocol datagrams over point-to-point links.
+
+   This document describes how to build PPP sessions and encapsulate PPP
+   packets over Ethernet.
+
+Applicability
+
+   This specification is intended to provide the facilities which are
+   defined for PPP, such as the Link Control Protocol, Network-layer
+   Control Protocols, authentication, and more.  These capabilities
+   require a point-to-point relationship between the peers, and are not
+   designed for the multi-point relationships which are available in
+   Ethernet and other multi-access environments.
+
+   This specification can be used by multiple hosts on a shared,
+   Ethernet to open PPP sessions to multiple destinations via one or
+   more bridging modems.  It is intended to be used with broadband
+   remote access technologies that provide a bridged Ethernet topology,
+   when access providers wish to maintain the session abstraction
+   associated with PPP.
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 1]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   This document describes the PPP Over Ethernet encapsulation that is
+   being deployed by RedBack Networks, RouterWare, UUNET and others.
+
+1. Introduction
+
+   Modern access technologies are faced with several conflicting goals.
+   It is desirable to connect multiple hosts at a remote site through
+   the same customer premise access device.  It is also a goal to
+   provide access control and billing functionality in a manner similar
+   to dial-up services using PPP.  In many access technologies, the most
+   cost effective method to attach multiple hosts to the customer
+   premise access device, is via Ethernet.  In addition, it is desirable
+   to keep the cost of this device as low as possible while requiring
+   little or no configuration.
+
+   PPP over Ethernet (PPPoE) provides the ability to connect a network
+   of hosts over a simple bridging access device to a remote Access
+   Concentrator.  With this model, each host utilizes it's own PPP stack
+   and the user is presented with a familiar user interface.  Access
+   control, billing and type of service can be done on a per-user,
+   rather than a per-site, basis.
+
+   To provide a point-to-point connection over Ethernet, each PPP
+   session must learn the Ethernet address of the remote peer, as well
+   as establish a unique session identifier.  PPPoE includes a discovery
+   protocol that provides this.
+
+2. Conventions
+
+   The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD,
+   SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when they appear in this
+   document, are to be interpreted as described in [2].
+
+3. Protocol Overview
+
+   PPPoE has two distinct stages.  There is a Discovery stage and a PPP
+   Session stage.  When a Host wishes to initiate a PPPoE session, it
+   must first perform Discovery to identify the Ethernet MAC address of
+   the peer and establish a PPPoE SESSION_ID.  While PPP defines a
+   peer-to-peer relationship, Discovery is inherently a client-server
+   relationship.  In the Discovery process, a Host (the client)
+   discovers an Access Concentrator (the server).  Based on the network
+   topology, there may be more than one Access Concentrator that the
+   Host can communicate with.  The Discovery stage allows the Host to
+   discover all Access Concentrators and then select one.  When
+   Discovery completes successfully, both the Host and the selected
+   Access Concentrator have the information they will use to build their
+   point-to-point connection over Ethernet.
+
+
+
+Mamakos, et. al.             Informational                      [Page 2]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   The Discovery stage remains stateless until a PPP session is
+   established.  Once a PPP session is established, both the Host and
+   the Access Concentrator MUST allocate the resources for a PPP virtual
+   interface.
+
+4. Payloads
+
+   The following packet formats are defined here.  The payload contents
+   will be defined in the Discovery and PPP sections.
+
+   An Ethernet frame is as follows:
+
+                                       1
+                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                  |       DESTINATION_ADDR        |
+                  |          (6 octets)           |
+                  |                               |
+                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                  |         SOURCE_ADDR           |
+                  |          (6 octets)           |
+                  |                               |
+                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                  |    ETHER_TYPE  (2 octets)     |
+                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                  ~                               ~
+                  ~           payload             ~
+                  ~                               ~
+                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                  |           CHECKSUM            |
+                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The DESTINATION_ADDR field contains either a unicast Ethernet
+   destination address, or the Ethernet broadcast address (0xffffffff).
+   For Discovery packets, the value is either a unicast or broadcast
+   address as defined in the Discovery section.  For PPP session
+   traffic, this field MUST contain the peer's unicast address as
+   determined from the Discovery stage.
+
+   The SOURCE_ADDR field MUST contains the Ethernet MAC address of the
+   source device.
+
+   The ETHER_TYPE is set to either 0x8863 (Discovery Stage) or 0x8864
+   (PPP Session Stage).
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 3]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   The Ethernet payload for PPPoE is as follows:
+
+                        1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |  VER  | TYPE  |      CODE     |          SESSION_ID           |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |            LENGTH             |           payload             ~
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   The VER field is four bits and MUST be set to 0x1 for this version of
+   the PPPoE specification.
+
+   The TYPE field is four bits and MUST be set to 0x1 for this version
+   of the PPPoE specification.
+
+   The CODE field is eight bits and is defined below for the Discovery
+   and PPP Session stages.
+
+   The SESSION_ID field is sixteen bits.  It is an unsigned value in
+   network byte order.  It's value is defined below for Discovery
+   packets.  The value is fixed for a given PPP session and, in fact,
+   defines a PPP session along with the Ethernet SOURCE_ADDR and
+   DESTINATION_ADDR.  A value of 0xffff is reserved for future use and
+   MUST NOT be used
+
+   The LENGTH field is sixteen bits.  The value, in network byte order,
+   indicates the length of the PPPoE payload.  It does not include the
+   length of the Ethernet or PPPoE headers.
+
+5. Discovery Stage
+
+   There are four steps to the Discovery stage.  When it completes, both
+   peers know the PPPoE SESSION_ID and the peer's Ethernet address,
+   which together define the PPPoE session uniquely.  The steps consist
+   of the Host broadcasting an Initiation packet, one or more Access
+   Concentrators sending Offer packets, the Host sending a unicast
+   Session Request packet and the selected Access Concentrator sending a
+   Confirmation packet.  When the Host receives the Confirmation packet,
+   it may proceed to the PPP Session Stage.  When the Access
+   Concentrator sends the Confirmation packet, it may proceed to the PPP
+   Session Stage.
+
+   All Discovery Ethernet frames have the ETHER_TYPE field set to the
+   value 0x8863.
+
+
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 4]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   The PPPoE payload contains zero or more TAGs.  A TAG is a TLV (type-
+   length-value) construct and is defined as follows:
+
+                        1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |          TAG_TYPE             |        TAG_LENGTH             |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |          TAG_VALUE ...                                        ~
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   TAG_TYPE is a sixteen bit field in network byte order.  Appendix A
+   contains a list of all TAG_TYPEs and their TAG_VALUEs.
+
+   TAG_LENGTH is a sixteen bit field.  It is an unsigned number in
+   network byte order, indicating the length in octets of the TAG_VALUE.
+
+   If a discovery packet is received with a TAG of unknown TAG_TYPE, the
+   TAG MUST be ignored unless otherwise specified in this document.
+   This provides for backwards compatibility if/when new TAGs are added.
+   If new mandatory TAGs are added, the version number will be
+   incremented.
+
+   Some example Discovery packets are shown in Appendix B.
+
+5.1 The PPPoE Active Discovery Initiation (PADI) packet
+
+   The Host sends the PADI packet with the DESTINATION_ADDR set to the
+   broadcast address.  The CODE field is set to 0x09 and the SESSION_ID
+   MUST be set to 0x0000.
+
+   The PADI packet MUST contain exactly one TAG of TAG_TYPE Service-
+   Name, indicating the service the Host is requesting, and any number
+   of other TAG types.  An entire PADI packet (including the PPPoE
+   header) MUST NOT exceed 1484 octets so as to leave sufficient room
+   for a relay agent to add a Relay-Session-Id TAG.
+
+5.2 The PPPoE Active Discovery Offer (PADO) packet
+
+   When the Access Concentrator receives a PADI that it can serve, it
+   replies by sending a PADO packet.  The DESTINATION_ADDR is the
+   unicast address of the Host that sent the PADI.  The CODE field is
+   set to 0x07 and the SESSION_ID MUST be set to 0x0000.
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 5]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   The PADO packet MUST contain one AC-Name TAG containing the Access
+   Concentrator's name, a Service-Name TAG identical to the one in the
+   PADI, and any number of other Service-Name TAGs indicating other
+   services that the Access Concentrator offers.  If the Access
+   Concentrator can not serve the PADI it MUST NOT respond with a PADO.
+
+5.3 The PPPoE Active Discovery Request (PADR) packet
+
+   Since the PADI was broadcast, the Host may receive more than one
+   PADO.  The Host looks through the PADO packets it receives and
+   chooses one.  The choice can be based on the AC-Name or the Services
+   offered.  The Host then sends one PADR packet to the Access
+   Concentrator that it has chosen.  The DESTINATION_ADDR field is set
+   to the unicast Ethernet address of the Access Concentrator that sent
+   the PADO.  The CODE field is set to 0x19 and the SESSION_ID MUST be
+   set to 0x0000.
+
+   The PADR packet MUST contain exactly one TAG of TAG_TYPE Service-
+   Name, indicating the service the Host is requesting, and any number
+   of other TAG types.
+
+5.4 The PPPoE Active Discovery Session-confirmation (PADS) packet
+
+   When the Access Concentrator receives a PADR packet, it prepares to
+   begin a PPP session.  It generates a unique SESSION_ID for the PPPoE
+   session and replies to the Host with a PADS packet.  The
+   DESTINATION_ADDR field is the unicast Ethernet address of the Host
+   that sent the PADR.  The CODE field is set to 0x65 and the SESSION_ID
+   MUST be set to the unique value generated for this PPPoE session.
+
+   The PADS packet contains exactly one TAG of TAG_TYPE Service-Name,
+   indicating the service under which Access Concentrator has accepted
+   the PPPoE session, and any number of other TAG types.
+
+   If the Access Concentrator does not like the Service-Name in the
+   PADR, then it MUST reply with a PADS containing a TAG of TAG_TYPE
+   Service-Name-Error (and any number of other TAG types).  In this case
+   the SESSION_ID MUST be set to 0x0000.
+
+5.5 The PPPoE Active Discovery Terminate (PADT) packet
+
+   This packet may be sent anytime after a session is established to
+   indicate that a PPPoE session has been terminated.  It may be sent by
+   either the Host or the Access Concentrator.  The DESTINATION_ADDR
+   field is a unicast Ethernet address, the CODE field is set to 0xa7
+   and the SESSION_ID MUST be set to indicate which session is to be
+   terminated.  No TAGs are required.
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 6]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   When a PADT is received, no further PPP traffic is allowed to be sent
+   using that session.  Even normal PPP termination packets MUST NOT be
+   sent after sending or receiving a PADT.  A PPP peer SHOULD use the
+   PPP protocol itself to bring down a PPPoE session, but the PADT MAY
+   be used when PPP can not be used.
+
+6. PPP Session Stage
+
+   Once the PPPoE session begins, PPP data is sent as in any other PPP
+   encapsulation.  All Ethernet packets are unicast.  The ETHER_TYPE
+   field is set to 0x8864.  The PPPoE CODE MUST be set to 0x00.  The
+   SESSION_ID MUST NOT change for that PPPoE session and MUST be the
+   value assigned in the Discovery stage.  The PPPoE payload contains a
+   PPP frame.  The frame begins with the PPP Protocol-ID.
+
+   An example packet is shown in Appendix B.
+
+7. LCP Considerations
+
+   The Magic Number LCP configuration option is RECOMMENDED, and the
+   Protocol Field Compression (PFC) option is NOT RECOMMENDED.  An
+   implementation MUST NOT request any of the following options, and
+   MUST reject a request for such an option:
+
+      Field Check Sequence (FCS) Alternatives,
+
+      Address-and-Control-Field-Compression (ACFC),
+
+      Asynchronous-Control-Character-Map (ACCM)
+
+   The Maximum-Receive-Unit (MRU) option MUST NOT be negotiated to a
+   larger size than 1492.  Since Ethernet has a maximum payload size of
+   1500 octets, the PPPoE header is 6 octets and the PPP Protocol ID is
+   2 octets, the PPP MTU MUST NOT be greater than 1492.
+
+   It is RECOMMENDED that the Access Concentrator ocassionally send
+   Echo-Request packets to the Host to determine the state of the
+   session.  Otherwise, if the Host terminates a session without sending
+   a Terminate-Request packet, the Access Concentrator will not be able
+   to determine that the session has gone away.
+
+   When LCP terminates, the Host and Access concentrator MUST stop using
+   that PPPoE session.  If the Host wishes to start another PPP session,
+   it MUST return to the PPPoE Discovery stage.
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 7]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+8. Other Considerations
+
+   When a host does not receive a PADO packet within a specified amount
+   of time, it SHOULD resend it's PADI packet and double the waiting
+   period. This is repeated as many times as desired.  If the Host is
+   waiting to receive a PADS packet, a similar timeout mechanism SHOULD
+   be used, with the Host re-sending the PADR.  After a specified number
+   of retries, the Host SHOULD then resend a PADI packet.
+
+   The ETHER_TYPEs used in this document (0x8863 and 0x8864) have been
+   assigned by the IEEE for use by PPP Over Ethernet (PPPoE).  Use of
+   these values and the PPPoE VER (version) field uniquely identify this
+   protocol.
+
+   UTF-8 [5] is used throughout this document instead of ASCII.  UTF-8
+   supports the entire ASCII character set while allowing for
+   international character sets as well.  See [5] for more details.
+
+9. Security Considerations
+
+   To help protect against Denial of Service (DOS) attacks, the Access
+   Concentrator can employ the AC-Cookie TAG.  The Access Concentrator
+   SHOULD be able to uniquely regenerate the TAG_VALUE based on the PADR
+   SOURCE_ADDR.  Using this, the Access Concentrator can ensure that the
+   PADI SOURCE_ADDR is indeed reachable and can then limit concurrent
+   sessions for that address.  What algorithm to use is not defined and
+   left as an implementation detail.  An example is HMAC [3] over the
+   Host MAC address using a key known only to the Access > Concentrator.
+   While the AC-Cookie is useful against some DOS attacks, it can not
+   protect against all DOS attacks and an Access Concentrator MAY employ
+   other means to protect resources.
+
+   While the AC-Cookie is useful against some DOS attacks, it can not
+   protect against all DOS attacks and an Access Concentrator MAY employ
+   other means to protect resources.
+
+   Many Access Concentrators will not wish to offer information
+   regarding what services they offer to an unauthenticated entity.  In
+   that case the Access Concentrator should employ one of two policies.
+   It SHOULD never refuse a request based on the Service-Name TAG, and
+   always return the TAG_VALUE that was sent to it.  Or it SHOULD only
+   accept requests with a Service-Name TAG with a zero TAG_LENGTH
+   (indicating any service).  The former solution is RECOMMENDED.
+
+10. Acknowledgments
+
+   This document is based on concepts discussed in several forums,
+   including the ADSL forum.
+
+
+
+Mamakos, et. al.             Informational                      [Page 8]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   Copious amounts of text have been stolen from RFC 1661, RFC 1662 and
+   RFC 2364.
+
+11. References
+
+   [1] Simpson, W., Editor, "The Point-to-Point Protocol (PPP)", STD 51,
+       RFC 1661, July 1994
+
+   [2] Bradner, S., "Key words for use in RFCs to Indicate Requirement
+       Levels", BCP 14, RFC 2119, March 1997.
+
+   [3] Krawczyk, H., Bellare, M. and R. Canetti, "HMAC: Keyed-Hashing
+       for Message Authentication", RFC 2104, February 1998.
+
+   [4] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+       October 1994.  See also: http://www.iana.org/numbers.html
+
+   [5] Yergeau, F., "UTF-8, a transformation format of ISO 10646", RFC
+       2279, January 1998.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                      [Page 9]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+Appendix A
+
+   TAG_TYPES and TAG_VALUES
+
+   0x0000 End-Of-List
+
+      This TAG indicates that there are no further TAGs in the list. The
+      TAG_LENGTH of this TAG MUST always be zero.  Use of this TAG is
+      not required, but remains for backwards compatibility.
+
+   0x0101 Service-Name
+
+      This TAG indicates that a service name follows.  The TAG_VALUE is
+      an UTF-8 string that is NOT NULL terminated. When the TAG_LENGTH
+      is zero this TAG is used to indicate that any service is
+      acceptable.  Examples of the use of the Service-Name TAG are to
+      indicate an ISP name or a class or quality of service.
+
+   0x0102 AC-Name
+
+      This TAG indicates that a string follows which uniquely identifies
+      this particular Access Concentrator unit from all others. It may
+      be a combination of trademark, model, and serial id information,
+      or simply an UTF-8 rendition of the MAC address of the box.  It is
+      not NULL terminated.
+
+   0x0103 Host-Uniq
+
+      This TAG is used by a Host to uniquely associate an Access
+      Concentrator response (PADO or PADS) to a particular Host request
+      (PADI or PADR).  The TAG_VALUE is binary data of any value and
+      length that the Host chooses.  It is not interpreted by the Access
+      Concentrator.  The Host MAY include a Host-Uniq TAG in a PADI or
+      PADR.  If the Access Concentrator receives this TAG, it MUST
+      include the TAG unmodified in the associated PADO or PADS
+      response.
+
+   0x0104 AC-Cookie
+
+      This TAG is used by the Access Concentrator to aid in protecting
+      against denial of service attacks (see the Security Considerations
+      section for an explanation of how this works).  The Access
+      Concentrator MAY include this TAG in a PADO packet.  If a Host
+      receives this TAG, it MUST return the TAG unmodified in the
+      following PADR.  The TAG_VALUE is binary data of any value and
+      length and is not interpreted by the Host.
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 10]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   0x0105 Vendor-Specific
+
+      This TAG is used to pass vendor proprietary information.  The
+      first four octets of the TAG_VALUE contain the vendor id and the
+      remainder is unspecified.  The high-order octet of the vendor id
+      is 0 and the low-order 3 octets are the SMI Network Management
+      Private Enterprise Code of the Vendor in network byte order, as
+      defined in the Assigned Numbers RFC [4].
+
+      Use of this TAG is NOT RECOMMENDED.  To ensure inter-operability,
+      an implementation MAY silently ignore a Vendor-Specific TAG.
+
+   0x0110 Relay-Session-Id
+
+      This TAG MAY be added to any discovery packet by an intermediate
+      agent that is relaying traffic.  The TAG_VALUE is opaque to both
+      the Host and the Access Concentrator.  If either the Host or
+      Access Concentrator receives this TAG they MUST include it
+      unmodified in any discovery packet they send as a response.  All
+      PADI packets MUST guarantee sufficient room for the addition of a
+      Relay-Session-Id TAG with a TAG_VALUE length of 12 octets.
+
+      A Relay-Session-Id TAG MUST NOT be added if the discovery packet
+      already contains one.  In that case the intermediate agent SHOULD
+      use the existing Relay-Session-Id TAG.  If it can not use the
+      existing TAG or there is insufficient room to add a Relay-
+      Session-Id TAG, then it SHOULD return a Generic-Error TAG to the
+      sender.
+
+   0x0201 Service-Name-Error
+
+      This TAG (typically with a zero-length data section) indicates
+      that for one reason or another, the requested Service-Name request
+      could not be honored.
+
+      If there is data, and the first octet of the data is nonzero, then
+      it MUST be a printable UTF-8 string which explains why the request
+      was denied.  This string MAY NOT be NULL terminated.
+
+   0x0202 AC-System-Error
+
+      This TAG indicates that the Access Concentrator experienced some
+      error in performing the Host request.  (For example insufficient
+      resources to create a virtual circuit.)  It MAY be included in
+      PADS packets.
+
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 11]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+      If there is data, and the first octet of the data is nonzero, then
+      it MUST be a printable UTF-8 string which explains the nature of
+      the error.  This string MAY NOT be NULL terminated.
+
+   0x0203 Generic-Error
+
+      This TAG indicates an error.  It can be added to PADO, PADR or
+      PADS packets when an unrecoverable error occurs and no other error
+      TAG is appropriate.  If there is data then it MUST be an UTF-8
+      string which explains the nature of the error.  This string MUST
+      NOT be NULL terminated.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 12]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+Appendix B
+
+   The following are some example packets:
+
+   A PADI packet:
+
+                           1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                         0xffffffff                            |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |           0xffff              |        Host_mac_addr          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                    Host_mac_addr (cont)                       |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    ETHER_TYPE = 0x8863        | v = 1 | t = 1 |  CODE = 0x09  |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     SESSION_ID = 0x0000       |      LENGTH = 0x0004          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |      TAG_TYPE = 0x0101        |    TAG_LENGTH = 0x0000        |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 13]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   A PADO packet:
+
+                           1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                         Host_mac_addr                         |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |      Host_mac_addr (cont)     | Access_Concentrator_mac_addr  |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |             Access_Concentrator_mac_addr (cont)               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    ETHER_TYPE = 0x8863        | v = 1 | t = 1 |  CODE = 0x07  |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     SESSION_ID = 0x0000       |      LENGTH = 0x0020          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |      TAG_TYPE = 0x0101        |    TAG_LENGTH = 0x0000        |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |      TAG_TYPE = 0x0102        |    TAG_LENGTH = 0x0018        |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     0x47      |     0x6f      |     0x20      |     0x52      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     0x65      |     0x64      |     0x42      |     0x61      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     0x63      |     0x6b      |     0x20      |     0x2d      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     0x20      |     0x65      |     0x73      |     0x68      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     0x73      |     0x68      |     0x65      |     0x73      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     0x68      |     0x6f      |     0x6f      |     0x74      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 14]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   A PPP LCP packet:  The PPP protocol value is shown (0xc021) but the
+   PPP payload is left to the reader.  This is a packet from the Host to
+   the Access Concentrator.
+
+                           1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                  Access_Concentrator_mac_addr                 |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |Access_Concentrator_mac_addr(c)|        Host_mac_addr          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                     Host_mac_addr (cont)                      |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    ETHER_TYPE = 0x8864        | v = 1 | t = 1 |  CODE = 0x00  |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     SESSION_ID = 0x1234       |      LENGTH = 0x????          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    PPP PROTOCOL = 0xc021      |        PPP payload            ~
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Authors'  Addresses
+
+   Louis Mamakos
+   UUNET Technologies, Inc.
+   3060 Williams Drive
+   Fairfax, VA  22031-4648
+   United States of America
+
+   EMail: louie@uu.net
+
+
+   Kurt Lidl
+   UUNET Technologies, Inc.
+   3060 Williams Drive
+   Fairfax, VA  22031-4648
+   United States of America
+
+   EMail: lidl@uu.net
+
+
+   Jeff Evarts
+   UUNET Technologies, Inc.
+   3060 Williams Drive
+   Fairfax, VA  22031-4648
+   United States of America
+
+   EMail: jde@uu.net
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 15]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+   David Carrel
+   RedBack Networks, Inc.
+   1389 Moffett Park Drive
+   Sunnyvale, CA  94089-1134
+   United States of America
+
+   EMail: carrel@RedBack.net
+
+
+   Dan Simone
+   RedBack Networks, Inc.
+   1389 Moffett Park Drive
+   Sunnyvale, CA  94089-1134
+   United States of America
+
+   EMail:dan@RedBack.net
+
+
+   Ross Wheeler
+   RouterWare, Inc.
+   3961 MacArthur Blvd., Suite 212
+   Newport Beach, CA  92660
+   United States of America
+
+   EMail: ross@routerware.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 16]
+
+RFC 2516             Transmitting PPP Over Ethernet        February 1999
+
+
+Full Copyright Statement
+
+   Copyright (C) The Internet Society (1999).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mamakos, et. al.             Informational                     [Page 17]
+
-- 
cgit v1.2.3