Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
Index: Makefile
===================================================================
--- Makefile	(nonexistent)
+++ Makefile	(revision 5)
@@ -0,0 +1,63 @@
+
+COMPONENT_TARGETS = $(HARDWARE_NOARCH)
+
+
+include ../../../../build-system/constants.mk
+
+
+url         = $(DOWNLOAD_SERVER)/sources/packages/n/rp-pppoe
+
+versions    = 3.14
+pkgname     = rp-pppoe
+suffix      = tar.gz
+
+tarballs    = $(addsuffix .$(suffix), $(addprefix $(pkgname)-, $(versions)))
+sha1s       = $(addsuffix .sha1sum, $(tarballs))
+
+patches     = $(CURDIR)/patches/rp-pppoe-3.14-plugin.patch
+patches    += $(CURDIR)/patches/rp-pppoe-3.14-plugin-path.patch
+patches    += $(CURDIR)/patches/rp-pppoe-3.14-ip-allocation.patch
+patches    += $(CURDIR)/patches/rp-pppoe-3.14-man-pages.patch
+
+
+.NOTPARALLEL: $(patches)
+
+
+BUILD_TARGETS = $(tarballs) $(sha1s) $(patches)
+
+
+include ../../../../build-system/core.mk
+
+
+.PHONY: download_clean
+
+
+$(tarballs):
+	@echo -e "\n======= Downloading source tarballs =======" ; \
+	 for tarball in $(tarballs) ; do \
+	   echo "$(url)/$$tarball" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & \
+	 done ; wait
+
+$(sha1s): $(tarballs)
+	@for sha in $@ ; do \
+	   echo -e "\n======= Downloading '$$sha' signature =======\n" ; \
+	   echo "$(url)/$$sha" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & wait %1 ; \
+	   touch $$sha ; \
+	   echo -e "\n======= Check the '$$sha' sha1sum =======\n" ; \
+	   sha1sum --check $$sha ; ret="$$?" ; \
+	   if [ "$$ret" == "1" ]; then \
+	     echo -e "\n======= ERROR: Bad '$$sha' sha1sum =======\n" ; \
+	     exit 1 ; \
+	   fi ; \
+	 done
+
+$(patches): $(sha1s)
+	@echo -e "\n======= Create Patches =======\n" ; \
+	 ( cd create-3.14-plugin-patch        ; ./create.patch.sh ) ; \
+	 ( cd create-3.14-plugin-path-patch   ; ./create.patch.sh ) ; \
+	 ( cd create-3.14-ip-allocation-patch ; ./create.patch.sh ) ; \
+	 ( cd create-3.14-man-pages-patch     ; ./create.patch.sh ) ; \
+	 echo -e "\n"
+
+download_clean:
+	@rm -f $(tarballs) $(sha1s) $(patches)
Index: create-3.14-ip-allocation-patch/create.patch.sh
===================================================================
--- create-3.14-ip-allocation-patch/create.patch.sh	(nonexistent)
+++ create-3.14-ip-allocation-patch/create.patch.sh	(revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=3.14
+
+tar --files-from=file.list -xzvf ../rp-pppoe-$VERSION.tar.gz
+mv rp-pppoe-$VERSION rp-pppoe-$VERSION-orig
+
+cp -rf ./rp-pppoe-$VERSION-new ./rp-pppoe-$VERSION
+
+diff --unified -Nr  rp-pppoe-$VERSION-orig  rp-pppoe-$VERSION > rp-pppoe-$VERSION-ip-allocation.patch
+
+mv rp-pppoe-$VERSION-ip-allocation.patch ../patches
+
+rm -rf ./rp-pppoe-$VERSION
+rm -rf ./rp-pppoe-$VERSION-orig

Property changes on: create-3.14-ip-allocation-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-3.14-ip-allocation-patch/file.list
===================================================================
--- create-3.14-ip-allocation-patch/file.list	(nonexistent)
+++ create-3.14-ip-allocation-patch/file.list	(revision 5)
@@ -0,0 +1 @@
+rp-pppoe-3.14/src/pppoe-server.c
Index: create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src/pppoe-server.c
===================================================================
--- create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src/pppoe-server.c	(nonexistent)
+++ create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src/pppoe-server.c	(revision 5)
@@ -0,0 +1,2426 @@
+/***********************************************************************
+*
+* pppoe-server.c
+*
+* Implementation of a user-space PPPoE server
+*
+* Copyright (C) 2000-2012 Roaring Penguin Software Inc.
+* Copyright (C) 2018-2020 Dianne Skoll
+*
+* This program may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+* $Id$
+*
+* LIC: GPL
+*
+***********************************************************************/
+
+#include "config.h"
+
+#include <sys/socket.h>
+#if defined(HAVE_LINUX_IF_H)
+#include <linux/if.h>
+#elif defined(HAVE_NET_IF_H)
+#include <net/if.h>
+#endif
+
+#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H)
+#define _POSIX_SOURCE 1 /* For sigaction defines */
+#endif
+
+#include "pppoe-server.h"
+#include "md5.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <time.h>
+
+#include <signal.h>
+
+#ifdef HAVE_LICENSE
+#include "license.h"
+#include "licensed-only/servfuncs.h"
+static struct License const *ServerLicense;
+static struct License const *ClusterLicense;
+#else
+#define control_session_started(x) (void) 0
+#define control_session_terminated(x) (void) 0
+#define control_exit() (void) 0
+#define realpeerip peerip
+#endif
+
+#ifdef HAVE_L2TP
+extern PppoeSessionFunctionTable L2TPSessionFunctionTable;
+extern void pppoe_to_l2tp_add_interface(EventSelector *es,
+					Interface *interface);
+#endif
+
+static void InterfaceHandler(EventSelector *es,
+			int fd, unsigned int flags, void *data);
+static void startPPPD(ClientSession *sess);
+static void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest,
+			  int errorTag, char *errorMsg);
+
+#define CHECK_ROOM(cursor, start, len) \
+do {\
+    if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \
+	syslog(LOG_ERR, "Would create too-long packet"); \
+	return; \
+    } \
+} while(0)
+
+static void PppoeStopSession(ClientSession *ses, char const *reason);
+static int PppoeSessionIsActive(ClientSession *ses);
+
+/* Service-Names we advertise */
+#define MAX_SERVICE_NAMES 64
+static int NumServiceNames = 0;
+static char const *ServiceNames[MAX_SERVICE_NAMES];
+
+PppoeSessionFunctionTable DefaultSessionFunctionTable = {
+    PppoeStopSession,
+    PppoeSessionIsActive,
+    NULL
+};
+
+/* An array of client sessions */
+ClientSession *Sessions = NULL;
+ClientSession *FreeSessions = NULL;
+ClientSession *LastFreeSession = NULL;
+ClientSession *BusySessions = NULL;
+
+/* Interfaces we're listening on */
+Interface interfaces[MAX_INTERFACES];
+int NumInterfaces = 0;
+
+/* The number of session slots */
+size_t NumSessionSlots;
+
+/* Maximum number of sessions per MAC address */
+int MaxSessionsPerMac;
+
+/* Number of active sessions */
+size_t NumActiveSessions = 0;
+
+/* Offset of first session */
+size_t SessOffset = 0;
+
+/* Event Selector */
+EventSelector *event_selector;
+
+/* Use Linux kernel-mode PPPoE? */
+static int UseLinuxKernelModePPPoE = 0;
+
+/* Requested max_ppp_payload */
+static UINT16_t max_ppp_payload = 0;
+
+/* File with PPPD options */
+static char *pppoptfile = NULL;
+
+static char *pppd_path = PPPD_PATH;
+static char *pppoe_path = PPPOE_PATH;
+
+static char *motd_string = NULL;
+static char *hurl_string = NULL;
+
+static int Debug = 0;
+static int CheckPoolSyntax = 0;
+
+/* Synchronous mode */
+static int Synchronous = 0;
+
+/* Ignore PADI if no free sessions */
+static int IgnorePADIIfNoFreeSessions = 0;
+
+static int KidPipe[2] = {-1, -1};
+static int LockFD = -1;
+
+/* Random seed for cookie generation */
+#define SEED_LEN 16
+#define MD5_LEN 16
+#define COOKIE_LEN (MD5_LEN + sizeof(pid_t)) /* Cookie is 16-byte MD5 + PID of server */
+
+static unsigned char CookieSeed[SEED_LEN];
+
+#define MAXLINE 512
+
+/* Default interface if no -I option given */
+#define DEFAULT_IF "eth0"
+
+/* Access concentrator name */
+char *ACName = NULL;
+
+/* Options to pass to pppoe process */
+char PppoeOptions[SMALLBUF] = "";
+
+/* Our local IP address */
+unsigned char LocalIP[IPV4ALEN] = {10, 0, 0, 1}; /* Counter optionally STARTS here */
+unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */
+
+/* Delegates the allocation of IP addresses to pppd (as the pptpd doing) */
+int DelegateIPAllocation = 0;
+
+/* Do we increment local IP for each connection? */
+int IncrLocalIP = 0;
+
+/* Do we randomize session numbers? */
+int RandomizeSessionNumbers = 0;
+
+/* Do we pass the "unit" option to pppd?  (2.4 or greater) */
+int PassUnitOptionToPPPD = 0;
+
+static PPPoETag hostUniq;
+static PPPoETag relayId;
+static PPPoETag receivedCookie;
+static PPPoETag requestedService;
+
+#define HOSTNAMELEN 256
+
+static int
+count_sessions_from_mac(unsigned char *eth)
+{
+    int n=0;
+    ClientSession *s = BusySessions;
+    while(s) {
+	if (!memcmp(eth, s->eth, ETH_ALEN)) n++;
+	s = s->next;
+    }
+    return n;
+}
+
+/**********************************************************************
+*%FUNCTION: childHandler
+*%ARGUMENTS:
+* pid -- pid of child
+* status -- exit status
+* ses -- which session terminated
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Called synchronously when a child dies.  Remove from busy list.
+***********************************************************************/
+static void
+childHandler(pid_t pid, int status, void *s)
+{
+    ClientSession *session = s;
+
+    /* Temporary structure for sending PADT's. */
+    PPPoEConnection conn;
+
+#ifdef HAVE_L2TP
+    /* We're acting as LAC, so when child exits, become a PPPoE <-> L2TP
+       relay */
+    if (session->flags & FLAG_ACT_AS_LAC) {
+	syslog(LOG_INFO, "Session %u for client "
+	       "%02x:%02x:%02x:%02x:%02x:%02x handed off to LNS %s",
+	       (unsigned int) ntohs(session->sess),
+	       session->eth[0], session->eth[1], session->eth[2],
+	       session->eth[3], session->eth[4], session->eth[5],
+	       inet_ntoa(session->tunnel_endpoint.sin_addr));
+	session->pid = 0;
+	session->funcs = &L2TPSessionFunctionTable;
+	return;
+    }
+#endif
+
+    memset(&conn, 0, sizeof(conn));
+    conn.hostUniq = NULL;
+
+    if (!DelegateIPAllocation) {
+     syslog(LOG_INFO,
+	   "Session %u closed for client "
+	   "%02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s",
+	   (unsigned int) ntohs(session->sess),
+	   session->eth[0], session->eth[1], session->eth[2],
+	   session->eth[3], session->eth[4], session->eth[5],
+	   (int) session->realpeerip[0], (int) session->realpeerip[1],
+	   (int) session->realpeerip[2], (int) session->realpeerip[3],
+	   session->ethif->name);
+    } else {
+      syslog(LOG_INFO,
+      "Session %u closed for client "
+      "%02x:%02x:%02x:%02x:%02x:%02x on %s",
+      (unsigned int) ntohs(session->sess),
+      session->eth[0], session->eth[1], session->eth[2],
+      session->eth[3], session->eth[4], session->eth[5],
+      session->ethif->name);
+    }
+    memcpy(conn.myEth, session->ethif->mac, ETH_ALEN);
+    conn.discoverySocket = session->ethif->sock;
+    conn.session = session->sess;
+    memcpy(conn.peerEth, session->eth, ETH_ALEN);
+    if (!(session->flags & FLAG_SENT_PADT)) {
+	if (session->flags & FLAG_RECVD_PADT) {
+	    sendPADT(&conn, "RP-PPPoE: Received PADT from peer");
+	} else {
+	    sendPADT(&conn, "RP-PPPoE: Child pppd process terminated");
+	}
+	session->flags |= FLAG_SENT_PADT;
+    }
+
+    session->serviceName = "";
+    control_session_terminated(session);
+    if (pppoe_free_session(session) < 0) {
+	return;
+    }
+
+}
+
+/**********************************************************************
+*%FUNCTION: incrementIPAddress (static)
+*%ARGUMENTS:
+* addr -- a 4-byte array representing IP address
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Increments addr in-place
+***********************************************************************/
+static void
+incrementIPAddress(unsigned char ip[IPV4ALEN])
+{
+    ip[3]++;
+    if (!ip[3]) {
+	ip[2]++;
+	if (!ip[2]) {
+	    ip[1]++;
+	    if (!ip[1]) {
+		ip[0]++;
+	    }
+	}
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: killAllSessions
+*%ARGUMENTS:
+* None
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Kills all pppd processes (and hence all PPPoE sessions)
+***********************************************************************/
+void
+killAllSessions(void)
+{
+    ClientSession *sess = BusySessions;
+    while(sess) {
+	sess->funcs->stop(sess, "Shutting Down");
+	sess = sess->next;
+    }
+#ifdef HAVE_L2TP
+    pppoe_close_l2tp_tunnels();
+#endif
+}
+
+/**********************************************************************
+*%FUNCTION: parseAddressPool
+*%ARGUMENTS:
+* fname -- name of file containing IP address pool.
+* install -- if true, install IP addresses in sessions.
+*%RETURNS:
+* Number of valid IP addresses found.
+*%DESCRIPTION:
+* Reads a list of IP addresses from a file.
+***********************************************************************/
+static int
+parseAddressPool(char const *fname, int install)
+{
+    FILE *fp = fopen(fname, "r");
+    int numAddrs = 0;
+    unsigned int a, b, c, d;
+    unsigned int e, f, g, h;
+    char line[MAXLINE];
+
+    if (!fp) {
+	sysErr("Cannot open address pool file");
+	exit(1);
+    }
+
+    while (!feof(fp)) {
+	if (!fgets(line, MAXLINE, fp)) {
+	    break;
+	}
+	if ((sscanf(line, "%u.%u.%u.%u:%u.%u.%u.%u",
+		    &a, &b, &c, &d, &e, &f, &g, &h) == 8) &&
+	    a < 256 && b < 256 && c < 256 && d < 256 &&
+	    e < 256 && f < 256 && g < 256 && h < 256) {
+
+	    /* Both specified (local:remote) */
+	    if (install) {
+		Sessions[numAddrs].myip[0] = (unsigned char) a;
+		Sessions[numAddrs].myip[1] = (unsigned char) b;
+		Sessions[numAddrs].myip[2] = (unsigned char) c;
+		Sessions[numAddrs].myip[3] = (unsigned char) d;
+		Sessions[numAddrs].peerip[0] = (unsigned char) e;
+		Sessions[numAddrs].peerip[1] = (unsigned char) f;
+		Sessions[numAddrs].peerip[2] = (unsigned char) g;
+		Sessions[numAddrs].peerip[3] = (unsigned char) h;
+#ifdef HAVE_LICENSE
+		memcpy(Sessions[numAddrs].realpeerip,
+		       Sessions[numAddrs].peerip, IPV4ALEN);
+#endif
+	    }
+	    numAddrs++;
+	} else if ((sscanf(line, "%u.%u.%u.%u-%u", &a, &b, &c, &d, &e) == 5) &&
+		   a < 256 && b < 256 && c < 256 && d < 256 && e < 256) {
+	    /* Remote specied as a.b.c.d-e.  Example: 1.2.3.4-8 yields:
+	       1.2.3.4, 1.2.3.5, 1.2.3.6, 1.2.3.7, 1.2.3.8 */
+	    /* Swap d and e so that e >= d */
+	    if (e < d) {
+		f = d;
+		d = e;
+		e = f;
+	    }
+	    if (install) {
+		while (d <= e) {
+		    Sessions[numAddrs].peerip[0] = (unsigned char) a;
+		    Sessions[numAddrs].peerip[1] = (unsigned char) b;
+		    Sessions[numAddrs].peerip[2] = (unsigned char) c;
+		    Sessions[numAddrs].peerip[3] = (unsigned char) d;
+#ifdef HAVE_LICENSE
+		    memcpy(Sessions[numAddrs].realpeerip,
+			   Sessions[numAddrs].peerip, IPV4ALEN);
+#endif
+		d++;
+		numAddrs++;
+		}
+	    } else {
+		numAddrs += (e-d) + 1;
+	    }
+	} else if ((sscanf(line, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) &&
+		   a < 256 && b < 256 && c < 256 && d < 256) {
+	    /* Only remote specified */
+	    if (install) {
+		Sessions[numAddrs].peerip[0] = (unsigned char) a;
+		Sessions[numAddrs].peerip[1] = (unsigned char) b;
+		Sessions[numAddrs].peerip[2] = (unsigned char) c;
+		Sessions[numAddrs].peerip[3] = (unsigned char) d;
+#ifdef HAVE_LICENSE
+		memcpy(Sessions[numAddrs].realpeerip,
+		       Sessions[numAddrs].peerip, IPV4ALEN);
+#endif
+	    }
+	    numAddrs++;
+	}
+    }
+    fclose(fp);
+    if (!numAddrs) {
+	rp_fatal("No valid ip addresses found in pool file");
+    }
+    return numAddrs;
+}
+
+/**********************************************************************
+*%FUNCTION: parsePADITags
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data
+* extra -- extra user data.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Picks interesting tags out of a PADI packet
+***********************************************************************/
+void
+parsePADITags(UINT16_t type, UINT16_t len, unsigned char *data,
+	      void *extra)
+{
+    switch(type) {
+    case TAG_PPP_MAX_PAYLOAD:
+	if (len == sizeof(max_ppp_payload)) {
+	    memcpy(&max_ppp_payload, data, sizeof(max_ppp_payload));
+	    max_ppp_payload = ntohs(max_ppp_payload);
+	    if (max_ppp_payload <= ETH_PPPOE_MTU) {
+		max_ppp_payload = 0;
+	    }
+	}
+	break;
+    case TAG_SERVICE_NAME:
+	/* Copy requested service name */
+	requestedService.type = htons(type);
+	requestedService.length = htons(len);
+	memcpy(requestedService.payload, data, len);
+	break;
+    case TAG_RELAY_SESSION_ID:
+	relayId.type = htons(type);
+	relayId.length = htons(len);
+	memcpy(relayId.payload, data, len);
+	break;
+    case TAG_HOST_UNIQ:
+	hostUniq.type = htons(type);
+	hostUniq.length = htons(len);
+	memcpy(hostUniq.payload, data, len);
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: parsePADRTags
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data
+* extra -- extra user data.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Picks interesting tags out of a PADR packet
+***********************************************************************/
+void
+parsePADRTags(UINT16_t type, UINT16_t len, unsigned char *data,
+	      void *extra)
+{
+    switch(type) {
+    case TAG_PPP_MAX_PAYLOAD:
+	if (len == sizeof(max_ppp_payload)) {
+	    memcpy(&max_ppp_payload, data, sizeof(max_ppp_payload));
+	    max_ppp_payload = ntohs(max_ppp_payload);
+	    if (max_ppp_payload <= ETH_PPPOE_MTU) {
+		max_ppp_payload = 0;
+	    }
+	}
+	break;
+    case TAG_RELAY_SESSION_ID:
+	relayId.type = htons(type);
+	relayId.length = htons(len);
+	memcpy(relayId.payload, data, len);
+	break;
+    case TAG_HOST_UNIQ:
+	hostUniq.type = htons(type);
+	hostUniq.length = htons(len);
+	memcpy(hostUniq.payload, data, len);
+	break;
+    case TAG_AC_COOKIE:
+	receivedCookie.type = htons(type);
+	receivedCookie.length = htons(len);
+	memcpy(receivedCookie.payload, data, len);
+	break;
+    case TAG_SERVICE_NAME:
+	requestedService.type = htons(type);
+	requestedService.length = htons(len);
+	memcpy(requestedService.payload, data, len);
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: fatalSys
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message plus the errno value to stderr and syslog and exits.
+***********************************************************************/
+void
+fatalSys(char const *str)
+{
+    char buf[SMALLBUF];
+    snprintf(buf, SMALLBUF, "%s: %s", str, strerror(errno));
+    printErr(buf);
+    control_exit();
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: sysErr
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message plus the errno value to syslog.
+***********************************************************************/
+void
+sysErr(char const *str)
+{
+    char buf[1024];
+    sprintf(buf, "%.256s: %.256s", str, strerror(errno));
+    printErr(buf);
+}
+
+/**********************************************************************
+*%FUNCTION: rp_fatal
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message to stderr and syslog and exits.
+***********************************************************************/
+void
+rp_fatal(char const *str)
+{
+    printErr(str);
+    control_exit();
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: genCookie
+*%ARGUMENTS:
+* peerEthAddr -- peer Ethernet address (6 bytes)
+* myEthAddr -- my Ethernet address (6 bytes)
+* seed -- random cookie seed to make things tasty (16 bytes)
+* cookie -- buffer which is filled with server PID and
+*           md5 sum of previous items
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Forms the md5 sum of peer MAC address, our MAC address and seed, useful
+* in a PPPoE Cookie tag.
+***********************************************************************/
+void
+genCookie(unsigned char const *peerEthAddr,
+	  unsigned char const *myEthAddr,
+	  unsigned char const *seed,
+	  unsigned char *cookie)
+{
+    struct MD5Context ctx;
+    pid_t pid = getpid();
+
+    MD5Init(&ctx);
+    MD5Update(&ctx, peerEthAddr, ETH_ALEN);
+    MD5Update(&ctx, myEthAddr, ETH_ALEN);
+    MD5Update(&ctx, seed, SEED_LEN);
+    MD5Final(cookie, &ctx);
+    memcpy(cookie+MD5_LEN, &pid, sizeof(pid));
+}
+
+/**********************************************************************
+*%FUNCTION: processPADI
+*%ARGUMENTS:
+* ethif -- Interface
+* packet -- PPPoE PADI packet
+* len -- length of received packet
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADO packet back to client
+***********************************************************************/
+void
+processPADI(Interface *ethif, PPPoEPacket *packet, int len)
+{
+    PPPoEPacket pado;
+    PPPoETag acname;
+    PPPoETag servname;
+    PPPoETag cookie;
+    size_t acname_len;
+    unsigned char *cursor = pado.payload;
+    UINT16_t plen;
+
+    int sock = ethif->sock;
+    int i;
+    int ok = 0;
+    unsigned char *myAddr = ethif->mac;
+
+    /* Ignore PADI's which don't come from a unicast address */
+    if (NOT_UNICAST(packet->ethHdr.h_source)) {
+	syslog(LOG_ERR, "PADI packet from non-unicast source address");
+	return;
+    }
+
+    /* If no free sessions and "-i" flag given, ignore */
+    if (IgnorePADIIfNoFreeSessions && !FreeSessions) {
+	syslog(LOG_INFO, "PADI ignored - No free session slots available");
+	return;
+    }
+
+    /* If number of sessions per MAC is limited, check here and don't
+       send PADO if already max number of sessions. */
+    if (MaxSessionsPerMac) {
+	if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) {
+	    syslog(LOG_INFO, "PADI: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)",
+		   packet->ethHdr.h_source[0],
+		   packet->ethHdr.h_source[1],
+		   packet->ethHdr.h_source[2],
+		   packet->ethHdr.h_source[3],
+		   packet->ethHdr.h_source[4],
+		   packet->ethHdr.h_source[5],
+		   MaxSessionsPerMac);
+	    return;
+	}
+    }
+
+    acname.type = htons(TAG_AC_NAME);
+    acname_len = strlen(ACName);
+    acname.length = htons(acname_len);
+    memcpy(acname.payload, ACName, acname_len);
+
+    relayId.type = 0;
+    hostUniq.type = 0;
+    requestedService.type = 0;
+    max_ppp_payload = 0;
+
+    parsePacket(packet, parsePADITags, NULL);
+
+    /* If PADI specified non-default service name, and we do not offer
+       that service, DO NOT send PADO */
+    if (requestedService.type) {
+	int slen = ntohs(requestedService.length);
+	if (slen) {
+	    for (i=0; i<NumServiceNames; i++) {
+		if (slen == strlen(ServiceNames[i]) &&
+		    !memcmp(ServiceNames[i], &requestedService.payload, slen)) {
+		    ok = 1;
+		    break;
+		}
+	    }
+	} else {
+	    ok = 1;		/* Default service requested */
+	}
+    } else {
+	ok = 1;			/* No Service-Name tag in PADI */
+    }
+
+    if (!ok) {
+	/* PADI asked for unsupported service */
+	return;
+    }
+
+    /* Generate a cookie */
+    cookie.type = htons(TAG_AC_COOKIE);
+    cookie.length = htons(COOKIE_LEN);
+    genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookie.payload);
+
+    /* Construct a PADO packet */
+    memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN);
+    memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN);
+    pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    pado.vertype = PPPOE_VER_TYPE(1, 1);
+    pado.code = CODE_PADO;
+    pado.session = 0;
+    plen = TAG_HDR_SIZE + acname_len;
+
+    CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE);
+    memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE);
+    cursor += acname_len + TAG_HDR_SIZE;
+
+    /* If we asked for an MTU, handle it */
+    if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) {
+	/* Shrink payload to fit */
+	if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_PPPOE_MTU) {
+	    PPPoETag maxPayload;
+	    UINT16_t mru = htons(max_ppp_payload);
+	    maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+	    maxPayload.length = htons(sizeof(mru));
+	    memcpy(maxPayload.payload, &mru, sizeof(mru));
+	    CHECK_ROOM(cursor, pado.payload, sizeof(mru) + TAG_HDR_SIZE);
+	    memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+	    cursor += sizeof(mru) + TAG_HDR_SIZE;
+	    plen += sizeof(mru) + TAG_HDR_SIZE;
+	}
+    }
+    /* If no service-names specified on command-line, just send default
+       zero-length name.  Otherwise, add all service-name tags */
+    servname.type = htons(TAG_SERVICE_NAME);
+    if (!NumServiceNames) {
+	servname.length = 0;
+	CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE);
+	memcpy(cursor, &servname, TAG_HDR_SIZE);
+	cursor += TAG_HDR_SIZE;
+	plen += TAG_HDR_SIZE;
+    } else {
+	for (i=0; i<NumServiceNames; i++) {
+	    int slen = strlen(ServiceNames[i]);
+	    servname.length = htons(slen);
+	    CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE+slen);
+	    memcpy(cursor, &servname, TAG_HDR_SIZE);
+	    memcpy(cursor+TAG_HDR_SIZE, ServiceNames[i], slen);
+	    cursor += TAG_HDR_SIZE+slen;
+	    plen += TAG_HDR_SIZE+slen;
+	}
+    }
+
+    CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + COOKIE_LEN);
+    memcpy(cursor, &cookie, TAG_HDR_SIZE + COOKIE_LEN);
+    cursor += TAG_HDR_SIZE + COOKIE_LEN;
+    plen += TAG_HDR_SIZE + COOKIE_LEN;
+
+    if (relayId.type) {
+	CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE);
+	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
+    }
+    if (hostUniq.type) {
+	CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE);
+	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
+	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+    }
+    pado.length = htons(plen);
+    sendPacket(NULL, sock, &pado, (int) (plen + HDR_SIZE));
+}
+
+/**********************************************************************
+*%FUNCTION: processPADT
+*%ARGUMENTS:
+* ethif -- interface
+* packet -- PPPoE PADT packet
+* len -- length of received packet
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Kills session whose session-ID is in PADT packet.
+***********************************************************************/
+void
+processPADT(Interface *ethif, PPPoEPacket *packet, int len)
+{
+    size_t i;
+
+    unsigned char *myAddr = ethif->mac;
+
+    /* Ignore PADT's not directed at us */
+    if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return;
+
+    /* Get session's index */
+    i = ntohs(packet->session) - 1 - SessOffset;
+    if (i >= NumSessionSlots) return;
+    if (Sessions[i].sess != packet->session) {
+	syslog(LOG_ERR, "Session index %u doesn't match session number %u",
+	       (unsigned int) i, (unsigned int) ntohs(packet->session));
+	return;
+    }
+
+
+    /* If source MAC does not match, do not kill session */
+    if (memcmp(packet->ethHdr.h_source, Sessions[i].eth, ETH_ALEN)) {
+	syslog(LOG_WARNING, "PADT for session %u received from "
+	       "%02X:%02X:%02X:%02X:%02X:%02X; should be from "
+	       "%02X:%02X:%02X:%02X:%02X:%02X",
+	       (unsigned int) ntohs(packet->session),
+	       packet->ethHdr.h_source[0],
+	       packet->ethHdr.h_source[1],
+	       packet->ethHdr.h_source[2],
+	       packet->ethHdr.h_source[3],
+	       packet->ethHdr.h_source[4],
+	       packet->ethHdr.h_source[5],
+	       Sessions[i].eth[0],
+	       Sessions[i].eth[1],
+	       Sessions[i].eth[2],
+	       Sessions[i].eth[3],
+	       Sessions[i].eth[4],
+	       Sessions[i].eth[5]);
+	return;
+    }
+    Sessions[i].flags |= FLAG_RECVD_PADT;
+    parsePacket(packet, parseLogErrs, NULL);
+    Sessions[i].funcs->stop(&Sessions[i], "Received PADT");
+}
+
+/**********************************************************************
+*%FUNCTION: processPADR
+*%ARGUMENTS:
+* ethif -- Ethernet interface
+* packet -- PPPoE PADR packet
+* len -- length of received packet
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADS packet back to client and starts a PPP session if PADR
+* packet is OK.
+***********************************************************************/
+void
+processPADR(Interface *ethif, PPPoEPacket *packet, int len)
+{
+    unsigned char cookieBuffer[COOKIE_LEN];
+    ClientSession *cliSession;
+    pid_t child;
+    PPPoEPacket pads;
+    unsigned char *cursor = pads.payload;
+    UINT16_t plen;
+    int i;
+    int sock = ethif->sock;
+    unsigned char *myAddr = ethif->mac;
+    int slen = 0;
+    char const *serviceName = NULL;
+
+    /* Temporary structure for sending PADM's. */
+    PPPoEConnection conn;
+
+
+#ifdef HAVE_LICENSE
+    int freemem;
+#endif
+
+    /* Initialize some globals */
+    relayId.type = 0;
+    hostUniq.type = 0;
+    receivedCookie.type = 0;
+    requestedService.type = 0;
+
+    /* Ignore PADR's not directed at us */
+    if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return;
+
+    /* Ignore PADR's from non-unicast addresses */
+    if (NOT_UNICAST(packet->ethHdr.h_source)) {
+	syslog(LOG_ERR, "PADR packet from non-unicast source address");
+	return;
+    }
+
+    /* If number of sessions per MAC is limited, check here and don't
+       send PADS if already max number of sessions. */
+    if (MaxSessionsPerMac) {
+	if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) {
+	    syslog(LOG_INFO, "PADR: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)",
+		   packet->ethHdr.h_source[0],
+		   packet->ethHdr.h_source[1],
+		   packet->ethHdr.h_source[2],
+		   packet->ethHdr.h_source[3],
+		   packet->ethHdr.h_source[4],
+		   packet->ethHdr.h_source[5],
+		   MaxSessionsPerMac);
+	    return;
+	}
+    }
+
+    max_ppp_payload = 0;
+    parsePacket(packet, parsePADRTags, NULL);
+
+    /* Check that everything's cool */
+    if (!receivedCookie.type) {
+	/* Drop it -- do not send error PADS */
+	return;
+    }
+
+    /* Is cookie kosher? */
+    if (receivedCookie.length != htons(COOKIE_LEN)) {
+	/* Drop it -- do not send error PADS */
+	return;
+    }
+
+    genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookieBuffer);
+    if (memcmp(receivedCookie.payload, cookieBuffer, COOKIE_LEN)) {
+	/* Drop it -- do not send error PADS */
+	return;
+    }
+
+    /* Check service name */
+    if (!requestedService.type) {
+	syslog(LOG_ERR, "Received PADR packet with no SERVICE_NAME tag");
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: No service name tag");
+	return;
+    }
+
+    slen = ntohs(requestedService.length);
+    if (slen) {
+	/* Check supported services */
+	for(i=0; i<NumServiceNames; i++) {
+	    if (slen == strlen(ServiceNames[i]) &&
+		!memcmp(ServiceNames[i], &requestedService.payload, slen)) {
+		serviceName = ServiceNames[i];
+		break;
+	    }
+	}
+
+	if (!serviceName) {
+	    syslog(LOG_ERR, "Received PADR packet asking for unsupported service %.*s", (int) ntohs(requestedService.length), requestedService.payload);
+	    sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+			  TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: Invalid service name tag");
+	    return;
+	}
+    } else {
+	serviceName = "";
+    }
+
+
+#ifdef HAVE_LICENSE
+    /* Are we licensed for this many sessions? */
+    if (License_NumLicenses("PPPOE-SESSIONS") <= NumActiveSessions) {
+	syslog(LOG_ERR, "Insufficient session licenses (%02x:%02x:%02x:%02x:%02x:%02x)",
+	       (unsigned int) packet->ethHdr.h_source[0],
+	       (unsigned int) packet->ethHdr.h_source[1],
+	       (unsigned int) packet->ethHdr.h_source[2],
+	       (unsigned int) packet->ethHdr.h_source[3],
+	       (unsigned int) packet->ethHdr.h_source[4],
+	       (unsigned int) packet->ethHdr.h_source[5]);
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No session licenses available");
+	return;
+    }
+#endif
+    /* Enough free memory? */
+#ifdef HAVE_LICENSE
+    freemem = getFreeMem();
+    if (freemem < MIN_FREE_MEMORY) {
+	syslog(LOG_WARNING,
+	       "Insufficient free memory to create session: Want %d, have %d",
+	       MIN_FREE_MEMORY, freemem);
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Insufficient free RAM");
+	return;
+    }
+#endif
+    /* Looks cool... find a slot for the session */
+    cliSession = pppoe_alloc_session();
+    if (!cliSession) {
+	syslog(LOG_ERR, "No client slots available (%02x:%02x:%02x:%02x:%02x:%02x)",
+	       (unsigned int) packet->ethHdr.h_source[0],
+	       (unsigned int) packet->ethHdr.h_source[1],
+	       (unsigned int) packet->ethHdr.h_source[2],
+	       (unsigned int) packet->ethHdr.h_source[3],
+	       (unsigned int) packet->ethHdr.h_source[4],
+	       (unsigned int) packet->ethHdr.h_source[5]);
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No client slots available");
+	return;
+    }
+
+    /* Set up client session peer Ethernet address */
+    memcpy(cliSession->eth, packet->ethHdr.h_source, ETH_ALEN);
+    cliSession->ethif = ethif;
+    cliSession->flags = 0;
+    cliSession->funcs = &DefaultSessionFunctionTable;
+    cliSession->startTime = time(NULL);
+    cliSession->serviceName = serviceName;
+
+    /* Create child process, send PADS packet back */
+    child = fork();
+    if (child < 0) {
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: Unable to start session process");
+	pppoe_free_session(cliSession);
+	return;
+    }
+    if (child != 0) {
+	/* In the parent process.  Mark pid in session slot */
+	cliSession->pid = child;
+	Event_HandleChildExit(event_selector, child,
+			      childHandler, cliSession);
+	control_session_started(cliSession);
+	return;
+    }
+
+    /* In the child process */
+
+    /* Reset signal handlers to default */
+    signal(SIGTERM, SIG_DFL);
+    signal(SIGINT, SIG_DFL);
+
+    /* Close all file descriptors except for socket */
+    closelog();
+    if (LockFD >= 0) close(LockFD);
+    for (i=0; i<CLOSEFD; i++) {
+	if (i != sock) {
+	    close(i);
+	}
+    }
+
+    openlog("pppoe-server", LOG_PID, LOG_DAEMON);
+    /* pppd has a nasty habit of killing all processes in its process group.
+       Start a new session to stop pppd from killing us! */
+    setsid();
+
+    /* Send PADS and Start pppd */
+    memcpy(pads.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN);
+    memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN);
+    pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    pads.vertype = PPPOE_VER_TYPE(1, 1);
+    pads.code = CODE_PADS;
+
+    pads.session = cliSession->sess;
+    plen = 0;
+
+    /* Copy requested service name tag back in.  If requested-service name
+       length is zero, and we have non-zero services, use first service-name
+       as default */
+    if (!slen && NumServiceNames) {
+	slen = strlen(ServiceNames[0]);
+	memcpy(&requestedService.payload, ServiceNames[0], slen);
+	requestedService.length = htons(slen);
+    }
+    memcpy(cursor, &requestedService, TAG_HDR_SIZE+slen);
+    cursor += TAG_HDR_SIZE+slen;
+    plen += TAG_HDR_SIZE+slen;
+
+    /* If we asked for an MTU, handle it */
+    if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) {
+	/* Shrink payload to fit */
+	if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_PPPOE_MTU) {
+	    PPPoETag maxPayload;
+	    UINT16_t mru = htons(max_ppp_payload);
+	    maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+	    maxPayload.length = htons(sizeof(mru));
+	    memcpy(maxPayload.payload, &mru, sizeof(mru));
+	    CHECK_ROOM(cursor, pads.payload, sizeof(mru) + TAG_HDR_SIZE);
+	    memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+	    cursor += sizeof(mru) + TAG_HDR_SIZE;
+	    plen += sizeof(mru) + TAG_HDR_SIZE;
+	    cliSession->requested_mtu = max_ppp_payload;
+	}
+    }
+
+    if (relayId.type) {
+	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
+    }
+    if (hostUniq.type) {
+	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
+	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+    }
+    pads.length = htons(plen);
+    sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE));
+
+    if (hurl_string || motd_string) {
+	memset(&conn, 0, sizeof(conn));
+	conn.hostUniq = NULL;
+
+	memcpy(conn.myEth, cliSession->ethif->mac, ETH_ALEN);
+	conn.discoverySocket = sock;
+	conn.session = cliSession->sess;
+	memcpy(conn.peerEth, cliSession->eth, ETH_ALEN);
+	if (hurl_string != NULL)
+	    sendHURLorMOTM(&conn, hurl_string, TAG_HURL);
+	if (motd_string != NULL)
+	    sendHURLorMOTM(&conn, motd_string, TAG_MOTM);
+    }
+    /* Close sock; don't need it any more */
+    close(sock);
+
+    startPPPD(cliSession);
+}
+
+/**********************************************************************
+*%FUNCTION: termHandler
+*%ARGUMENTS:
+* sig -- signal number
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Called by SIGTERM or SIGINT.  Causes all sessions to be killed!
+***********************************************************************/
+static void
+termHandler(int sig)
+{
+    syslog(LOG_INFO,
+	   "Terminating on signal %d -- killing all PPPoE sessions",
+	   sig);
+    killAllSessions();
+    control_exit();
+    exit(0);
+}
+
+/**********************************************************************
+*%FUNCTION: usage
+*%ARGUMENTS:
+* argv0 -- argv[0] from main
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints usage instructions
+***********************************************************************/
+void
+usage(char const *argv0)
+{
+    fprintf(stderr, "Usage: %s [options]\n", argv0);
+    fprintf(stderr, "Options:\n");
+#ifdef USE_BPF
+    fprintf(stderr, "   -I if_name     -- Specify interface (REQUIRED)\n");
+#else
+    fprintf(stderr, "   -I if_name     -- Specify interface (default %s.)\n",
+	    DEFAULT_IF);
+#endif
+    fprintf(stderr, "   -T timeout     -- Specify inactivity timeout in seconds.\n");
+    fprintf(stderr, "   -C name        -- Set access concentrator name.\n");
+    fprintf(stderr, "   -m MSS         -- Clamp incoming and outgoing MSS options.\n");
+    fprintf(stderr, "   -L ip          -- Set local IP address.\n");
+    fprintf(stderr, "   -l             -- Increment local IP address for each session.\n");
+    fprintf(stderr, "   -R ip          -- Set start address of remote IP pool.\n");
+    fprintf(stderr, "   -D             -- Delegates the allocation of IP addresses to pppd.\n");
+    fprintf(stderr, "   -S name        -- Advertise specified service-name.\n");
+    fprintf(stderr, "   -O fname       -- Use PPPD options from specified file\n");
+    fprintf(stderr, "                     (default %s).\n", PPPOE_SERVER_OPTIONS);
+    fprintf(stderr, "   -p fname       -- Optain IP address pool from specified file.\n");
+    fprintf(stderr, "   -N num         -- Allow 'num' concurrent sessions.\n");
+    fprintf(stderr, "   -o offset      -- Assign session numbers starting at offset+1.\n");
+    fprintf(stderr, "   -f disc:sess   -- Set Ethernet frame types (hex).\n");
+    fprintf(stderr, "   -s             -- Use synchronous PPP mode.\n");
+    fprintf(stderr, "   -X pidfile     -- Write PID and lock pidfile.\n");
+    fprintf(stderr, "   -q /path/pppd  -- Specify full path to pppd.\n");
+    fprintf(stderr, "   -Q /path/pppoe -- Specify full path to pppoe.\n");
+#ifdef HAVE_LINUX_KERNEL_PPPOE
+    fprintf(stderr, "   -k             -- Use kernel-mode PPPoE.\n");
+#endif
+    fprintf(stderr, "   -u             -- Pass 'unit' option to pppd.\n");
+    fprintf(stderr, "   -r             -- Randomize session numbers.\n");
+    fprintf(stderr, "   -d             -- Debug session creation.\n");
+    fprintf(stderr, "   -x n           -- Limit to 'n' sessions/MAC address.\n");
+    fprintf(stderr, "   -P             -- Check pool file for correctness and exit.\n");
+#ifdef HAVE_LICENSE
+    fprintf(stderr, "   -c secret:if:port -- Enable clustering on interface 'if'.\n");
+    fprintf(stderr, "   -1             -- Allow only one session per user.\n");
+#endif
+
+    fprintf(stderr, "   -i             -- Ignore PADI if no free sessions.\n");
+    fprintf(stderr, "   -M msg         -- Send MSG in a MOTM tag in PADM packet after PADS.\n");
+    fprintf(stderr, "   -H url         -- Send URL in a HURL tag in PADM packet after PADS.\n");
+    fprintf(stderr, "   -h             -- Print usage information.\n\n");
+    fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001-2009 Roaring Penguin Software Inc.\n", RP_VERSION);
+    fprintf(stderr, "                     %*s  Copyright (C) 2018-2020 Dianne Skoll\n", (int) strlen(RP_VERSION), "");
+
+#ifndef HAVE_LICENSE
+    fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n");
+    fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
+    fprintf(stderr, "under the terms of the GNU General Public License, version 2\n");
+    fprintf(stderr, "or (at your option) any later version.\n");
+#endif
+    fprintf(stderr, "https://dianne.skoll.ca/projects/rp-pppoe/\n");
+}
+
+/**********************************************************************
+*%FUNCTION: main
+*%ARGUMENTS:
+* argc, argv -- usual suspects
+*%RETURNS:
+* Exit status
+*%DESCRIPTION:
+* Main program of PPPoE server
+***********************************************************************/
+int
+main(int argc, char **argv)
+{
+
+    FILE *fp;
+    int i, j;
+    int opt;
+    int d[IPV4ALEN];
+    int beDaemon = 1;
+    int found;
+    unsigned int discoveryType, sessionType;
+    char *addressPoolFname = NULL;
+    char *pidfile = NULL;
+    char c;
+
+#ifdef HAVE_LICENSE
+    int use_clustering = 0;
+#endif
+
+#ifndef HAVE_LINUX_KERNEL_PPPOE
+    char *options = "X:ix:hI:C:L:R:DT:m:FN:f:O:o:sp:lrudPc:S:1q:Q:H:M:";
+#else
+    char *options = "X:ix:hI:C:L:R:DT:m:FN:f:O:o:skp:lrudPc:S:1q:Q:H:M:";
+#endif
+
+    if (getuid() != geteuid() ||
+	getgid() != getegid()) {
+	fprintf(stderr, "SECURITY WARNING: pppoe-server will NOT run suid or sgid.  Fix your installation.\n");
+	exit(1);
+    }
+
+    memset(interfaces, 0, sizeof(interfaces));
+
+    /* Initialize syslog */
+    openlog("pppoe-server", LOG_PID, LOG_DAEMON);
+
+    /* Default number of session slots */
+    NumSessionSlots = DEFAULT_MAX_SESSIONS;
+    MaxSessionsPerMac = 0; /* No limit */
+    NumActiveSessions = 0;
+
+    /* Parse command-line options */
+    while((opt = getopt(argc, argv, options)) != -1) {
+	switch(opt) {
+	case 'i':
+	    IgnorePADIIfNoFreeSessions = 1;
+	    break;
+	case 'x':
+	    if (sscanf(optarg, "%d", &MaxSessionsPerMac) != 1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (MaxSessionsPerMac < 0) {
+		MaxSessionsPerMac = 0;
+	    }
+	    break;
+
+#ifdef HAVE_LINUX_KERNEL_PPPOE
+	case 'k':
+	    UseLinuxKernelModePPPoE = 1;
+	    break;
+#endif
+	case 'S':
+	    if (NumServiceNames == MAX_SERVICE_NAMES) {
+		fprintf(stderr, "Too many '-S' options (%d max)",
+			MAX_SERVICE_NAMES);
+		exit(1);
+	    }
+	    ServiceNames[NumServiceNames] = strdup(optarg);
+	    if (!ServiceNames[NumServiceNames]) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    NumServiceNames++;
+	    break;
+	case 'q':
+	    pppd_path = strdup(optarg);
+	    if (!pppd_path) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+	case 'Q':
+	    pppoe_path = strdup(optarg);
+	    if (!pppoe_path) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+
+	case 'M':
+	    if (motd_string) {
+		free(motd_string);
+		motd_string = NULL;
+	    }
+	    motd_string = strdup(optarg);
+	    if (!motd_string) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+
+	case 'H':
+	    if (hurl_string) {
+		free(hurl_string);
+		hurl_string = NULL;
+	    }
+	    hurl_string = strdup(optarg);
+	    if (!hurl_string) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+	case 'c':
+#ifndef HAVE_LICENSE
+	    fprintf(stderr, "Clustering capability not available.\n");
+	    exit(1);
+#else
+	    cluster_handle_option(optarg);
+	    use_clustering = 1;
+	    break;
+#endif
+
+	case 'd':
+	    Debug = 1;
+	    break;
+	case 'P':
+	    CheckPoolSyntax = 1;
+	    break;
+	case 'u':
+	    PassUnitOptionToPPPD = 1;
+	    break;
+
+	case 'r':
+	    RandomizeSessionNumbers = 1;
+	    break;
+
+	case 'l':
+	    IncrLocalIP = 1;
+	    break;
+
+	case 'p':
+	    SET_STRING(addressPoolFname, optarg);
+	    break;
+
+	case 'X':
+	    SET_STRING(pidfile, optarg);
+	    break;
+	case 's':
+	    Synchronous = 1;
+	    /* Pass the Synchronous option on to pppoe */
+	    snprintf(PppoeOptions + strlen(PppoeOptions),
+		     SMALLBUF-strlen(PppoeOptions),
+		     " -s");
+	    break;
+
+	case 'f':
+	    if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) {
+		fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n");
+		exit(EXIT_FAILURE);
+	    }
+	    Eth_PPPOE_Discovery = (UINT16_t) discoveryType;
+	    Eth_PPPOE_Session   = (UINT16_t) sessionType;
+	    /* This option gets passed to pppoe */
+	    snprintf(PppoeOptions + strlen(PppoeOptions),
+		     SMALLBUF-strlen(PppoeOptions),
+		     " -%c %s", opt, optarg);
+	    break;
+
+	case 'F':
+	    beDaemon = 0;
+	    break;
+
+	case 'N':
+	    if (sscanf(optarg, "%d", &opt) != 1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (opt <= 0) {
+		fprintf(stderr, "-N: Value must be positive\n");
+		exit(EXIT_FAILURE);
+	    }
+	    NumSessionSlots = opt;
+	    break;
+
+	case 'O':
+	    SET_STRING(pppoptfile, optarg);
+	    break;
+
+	case 'o':
+	    if (sscanf(optarg, "%d", &opt) != 1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (opt < 0) {
+		fprintf(stderr, "-o: Value must be non-negative\n");
+		exit(EXIT_FAILURE);
+	    }
+	    SessOffset = (size_t) opt;
+	    break;
+
+	case 'I':
+	    if (NumInterfaces >= MAX_INTERFACES) {
+		fprintf(stderr, "Too many -I options (max %d)\n",
+			MAX_INTERFACES);
+		exit(EXIT_FAILURE);
+	    }
+	    found = 0;
+	    for (i=0; i<NumInterfaces; i++) {
+		if (!strncmp(interfaces[i].name, optarg, IFNAMSIZ)) {
+		    found = 1;
+		    break;
+		}
+	    }
+	    if (!found) {
+		strncpy(interfaces[NumInterfaces].name, optarg, IFNAMSIZ);
+		NumInterfaces++;
+	    }
+	    break;
+
+	case 'C':
+	    SET_STRING(ACName, optarg);
+	    break;
+
+	case 'L':
+	case 'R':
+	    /* Get local/remote IP address */
+	    if (sscanf(optarg, "%d.%d.%d.%d", &d[0], &d[1], &d[2], &d[3]) != 4) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    for (i=0; i<IPV4ALEN; i++) {
+		if (d[i] < 0 || d[i] > 255) {
+		    usage(argv[0]);
+		    exit(EXIT_FAILURE);
+		}
+		if (opt == 'L') {
+		    LocalIP[i] = (unsigned char) d[i];
+		} else {
+		    RemoteIP[i] = (unsigned char) d[i];
+		}
+	    }
+	    break;
+
+	case 'D':
+	    DelegateIPAllocation = 1;
+	    break;
+
+	case 'T':
+	case 'm':
+	    /* These just get passed to pppoe */
+	    snprintf(PppoeOptions + strlen(PppoeOptions),
+		     SMALLBUF-strlen(PppoeOptions),
+		     " -%c %s", opt, optarg);
+	    break;
+
+	case 'h':
+	    usage(argv[0]);
+	    exit(EXIT_SUCCESS);
+	case '1':
+#ifdef HAVE_LICENSE
+	    MaxSessionsPerUser = 1;
+#else
+	    fprintf(stderr, "-1 option not valid.\n");
+	    exit(1);
+#endif
+	    break;
+	}
+    }
+
+    if (!pppoptfile) {
+	pppoptfile = PPPOE_SERVER_OPTIONS;
+    }
+
+#ifdef HAVE_LICENSE
+    License_SetVersion(SERVPOET_VERSION);
+    License_ReadBundleFile("/etc/rp/bundle.txt");
+    License_ReadFile("/etc/rp/license.txt");
+    ServerLicense = License_GetFeature("PPPOE-SERVER");
+    if (!ServerLicense) {
+	fprintf(stderr, "License: GetFeature failed: %s\n",
+		License_ErrorMessage());
+	exit(1);
+    }
+#endif
+
+#ifdef USE_LINUX_PACKET
+#ifndef HAVE_STRUCT_SOCKADDR_LL
+    fprintf(stderr, "The PPPoE server does not work on Linux 2.0 kernels.\n");
+    exit(EXIT_FAILURE);
+#endif
+#endif
+
+    if (!NumInterfaces) {
+	strcpy(interfaces[0].name, DEFAULT_IF);
+	NumInterfaces = 1;
+    }
+
+    if (!ACName) {
+	ACName = malloc(HOSTNAMELEN);
+	if (gethostname(ACName, HOSTNAMELEN) < 0) {
+	    fatalSys("gethostname");
+	}
+    }
+
+    /* If address pool filename given, count number of addresses */
+    if (addressPoolFname) {
+	NumSessionSlots = parseAddressPool(addressPoolFname, 0);
+	if (CheckPoolSyntax) {
+	    printf("%lu\n", (unsigned long) NumSessionSlots);
+	    exit(0);
+	}
+    }
+
+    /* Max 65534 - SessOffset sessions */
+    if (NumSessionSlots + SessOffset > 65534) {
+	fprintf(stderr, "-N and -o options must add up to at most 65534\n");
+	exit(EXIT_FAILURE);
+    }
+
+    /* Allocate memory for sessions */
+    Sessions = calloc(NumSessionSlots, sizeof(ClientSession));
+    if (!Sessions) {
+	rp_fatal("Cannot allocate memory for session slots");
+    }
+
+    /* Fill in local addresses first (let pool file override later */
+    for (i=0; i<NumSessionSlots; i++) {
+	memcpy(Sessions[i].myip, LocalIP, sizeof(LocalIP));
+	if (IncrLocalIP) {
+	    incrementIPAddress(LocalIP);
+	}
+    }
+
+    /* Fill in remote IP addresses from pool (may also overwrite local ips) */
+    if (addressPoolFname) {
+	(void) parseAddressPool(addressPoolFname, 1);
+    }
+
+    /* For testing -- generate sequential remote IP addresses */
+    for (i=0; i<NumSessionSlots; i++) {
+	Sessions[i].pid = 0;
+	Sessions[i].funcs = &DefaultSessionFunctionTable;
+	Sessions[i].sess = htons(i+1+SessOffset);
+
+	if (!addressPoolFname) {
+	    memcpy(Sessions[i].peerip, RemoteIP, sizeof(RemoteIP));
+#ifdef HAVE_LICENSE
+	    memcpy(Sessions[i].realpeerip, RemoteIP, sizeof(RemoteIP));
+#endif
+	    incrementIPAddress(RemoteIP);
+	}
+    }
+
+    /* Initialize our random cookie.  Try /dev/urandom; if that fails,
+       use PID and rand() */
+    fp = fopen("/dev/urandom", "r");
+    if (fp) {
+	unsigned int x;
+	fread(&x, 1, sizeof(x), fp);
+	srand(x);
+	fread(&CookieSeed, 1, SEED_LEN, fp);
+	fclose(fp);
+    } else {
+	srand((unsigned int) getpid() * (unsigned int) time(NULL));
+	CookieSeed[0] = getpid() & 0xFF;
+	CookieSeed[1] = (getpid() >> 8) & 0xFF;
+	for (i=2; i<SEED_LEN; i++) {
+	    CookieSeed[i] = (rand() >> (i % 9)) & 0xFF;
+	}
+    }
+
+    if (RandomizeSessionNumbers) {
+	int *permutation;
+	int tmp;
+	permutation = malloc(sizeof(int) * NumSessionSlots);
+	if (!permutation) {
+	    fprintf(stderr, "Could not allocate memory to randomize session numbers\n");
+	    exit(EXIT_FAILURE);
+	}
+	for (i=0; i<NumSessionSlots; i++) {
+	    permutation[i] = i;
+	}
+	for (i=0; i<NumSessionSlots-1; i++) {
+	    j = i + rand() % (NumSessionSlots - i);
+	    if (j != i) {
+		tmp = permutation[j];
+		permutation[j] = permutation[i];
+		permutation[i] = tmp;
+	    }
+	}
+	/* Link sessions together */
+	FreeSessions = &Sessions[permutation[0]];
+	LastFreeSession = &Sessions[permutation[NumSessionSlots-1]];
+	for (i=0; i<NumSessionSlots-1; i++) {
+	    Sessions[permutation[i]].next = &Sessions[permutation[i+1]];
+	}
+	Sessions[permutation[NumSessionSlots-1]].next = NULL;
+	free(permutation);
+    } else {
+	/* Link sessions together */
+	FreeSessions = &Sessions[0];
+	LastFreeSession = &Sessions[NumSessionSlots - 1];
+	for (i=0; i<NumSessionSlots-1; i++) {
+	    Sessions[i].next = &Sessions[i+1];
+	}
+	Sessions[NumSessionSlots-1].next = NULL;
+    }
+
+    if (Debug) {
+	/* Dump session array and exit */
+	ClientSession *ses = FreeSessions;
+	while(ses) {
+	    printf("Session %u local %d.%d.%d.%d remote %d.%d.%d.%d\n",
+		   (unsigned int) (ntohs(ses->sess)),
+		   ses->myip[0], ses->myip[1],
+		   ses->myip[2], ses->myip[3],
+		   ses->peerip[0], ses->peerip[1],
+		   ses->peerip[2], ses->peerip[3]);
+	    ses = ses->next;
+	}
+	exit(0);
+    }
+
+    /* Open all the interfaces */
+    for (i=0; i<NumInterfaces; i++) {
+	interfaces[i].mtu = 0;
+	interfaces[i].sock = openInterface(interfaces[i].name, Eth_PPPOE_Discovery, interfaces[i].mac, &interfaces[i].mtu);
+    }
+
+    /* Ignore SIGPIPE */
+    signal(SIGPIPE, SIG_IGN);
+
+    /* Create event selector */
+    event_selector = Event_CreateSelector();
+    if (!event_selector) {
+	rp_fatal("Could not create EventSelector -- probably out of memory");
+    }
+
+    /* Control channel */
+#ifdef HAVE_LICENSE
+    if (control_init(argc, argv, event_selector)) {
+	rp_fatal("control_init failed");
+    }
+#endif
+
+    /* Create event handler for each interface */
+    for (i = 0; i<NumInterfaces; i++) {
+	interfaces[i].eh = Event_AddHandler(event_selector,
+					    interfaces[i].sock,
+					    EVENT_FLAG_READABLE,
+					    InterfaceHandler,
+					    &interfaces[i]);
+#ifdef HAVE_L2TP
+	interfaces[i].session_sock = -1;
+#endif
+	if (!interfaces[i].eh) {
+	    rp_fatal("Event_AddHandler failed");
+	}
+    }
+
+#ifdef HAVE_LICENSE
+    if (use_clustering) {
+	ClusterLicense = License_GetFeature("PPPOE-CLUSTER");
+	if (!ClusterLicense) {
+	    fprintf(stderr, "License: GetFeature failed: %s\n",
+		    License_ErrorMessage());
+	    exit(1);
+	}
+	if (!License_Expired(ClusterLicense)) {
+	    if (cluster_init(event_selector) < 0) {
+		rp_fatal("cluster_init failed");
+	    }
+	}
+    }
+#endif
+
+#ifdef HAVE_L2TP
+    for (i=0; i<NumInterfaces; i++) {
+	pppoe_to_l2tp_add_interface(event_selector,
+				    &interfaces[i]);
+    }
+#endif
+
+    /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */
+    if (beDaemon) {
+	if (pipe(KidPipe) < 0) {
+	    fatalSys("pipe");
+	}
+	i = fork();
+	if (i < 0) {
+	    fatalSys("fork");
+	} else if (i != 0) {
+	    /* parent */
+	    close(KidPipe[1]);
+	    KidPipe[1] = -1;
+	    /* Wait for child to give the go-ahead */
+	    while(1) {
+		int r = read(KidPipe[0], &c, 1);
+		if (r == 0) {
+		    fprintf(stderr, "EOF from child - something went wrong; please check logs.\n");
+		    exit(EXIT_FAILURE);
+		}
+		if (r < 0) {
+		    if (errno == EINTR) continue;
+		    fatalSys("read");
+		}
+		break;
+	    }
+
+	    if (c == 'X') {
+		exit(EXIT_SUCCESS);
+	    }
+
+	    /* Read error message from child */
+	    while (1) {
+		int r = read(KidPipe[0], &c, 1);
+		if (r == 0) exit(EXIT_FAILURE);
+		if (r < 0) {
+		    if (errno == EINTR) continue;
+		    fatalSys("read");
+		}
+		fprintf(stderr, "%c", c);
+	    }
+	    exit(EXIT_FAILURE);
+	}
+	setsid();
+	signal(SIGHUP, SIG_IGN);
+	i = fork();
+	if (i < 0) {
+	    fatalSys("fork");
+	} else if (i != 0) {
+	    exit(EXIT_SUCCESS);
+	}
+
+	chdir("/");
+
+	if (KidPipe[0] >= 0) {
+	    close(KidPipe[0]);
+	    KidPipe[0] = -1;
+	}
+
+	/* Point stdin/stdout/stderr to /dev/null */
+	for (i=0; i<3; i++) {
+	    close(i);
+	}
+	i = open("/dev/null", O_RDWR);
+	if (i >= 0) {
+	    dup2(i, 0);
+	    dup2(i, 1);
+	    dup2(i, 2);
+	    if (i > 2) close(i);
+	}
+    }
+
+    if (pidfile) {
+	FILE *foo = NULL;
+	if (KidPipe[1] >= 0) foo = fdopen(KidPipe[1], "w");
+	struct flock fl;
+	char buf[64];
+	fl.l_type = F_WRLCK;
+	fl.l_whence = SEEK_SET;
+	fl.l_start = 0;
+	fl.l_len = 0;
+	LockFD = open(pidfile, O_RDWR|O_CREAT, 0666);
+	if (LockFD < 0) {
+	    syslog(LOG_INFO, "Could not open PID file %s: %s", pidfile, strerror(errno));
+	    if (foo) fprintf(foo, "ECould not open PID file %s: %s\n", pidfile, strerror(errno));
+	    exit(1);
+	}
+	if (fcntl(LockFD, F_SETLK, &fl) < 0) {
+	    syslog(LOG_INFO, "Could not lock PID file %s: Is another process running?", pidfile);
+	    if (foo) fprintf(foo, "ECould not lock PID file %s: Is another process running?\n", pidfile);
+	    exit(1);
+	}
+	ftruncate(LockFD, 0);
+	snprintf(buf, sizeof(buf), "%lu\n", (unsigned long) getpid());
+	write(LockFD, buf, strlen(buf));
+	/* Do not close fd... use it to retain lock */
+    }
+
+    /* Set signal handlers for SIGTERM and SIGINT */
+    if (Event_HandleSignal(event_selector, SIGTERM, termHandler) < 0 ||
+	Event_HandleSignal(event_selector, SIGINT, termHandler) < 0) {
+	fatalSys("Event_HandleSignal");
+    }
+
+    /* Tell parent all is cool */
+    if (KidPipe[1] >= 0) {
+	write(KidPipe[1], "X", 1);
+	close(KidPipe[1]);
+	KidPipe[1] = -1;
+    }
+
+    for(;;) {
+	i = Event_HandleEvent(event_selector);
+	if (i < 0) {
+	    fatalSys("Event_HandleEvent");
+	}
+
+#ifdef HAVE_LICENSE
+	if (License_Expired(ServerLicense)) {
+	    syslog(LOG_INFO, "Server license has expired -- killing all PPPoE sessions");
+	    killAllSessions();
+	    control_exit();
+	    exit(0);
+	}
+#endif
+    }
+    return 0;
+}
+
+void
+serverProcessPacket(Interface *i)
+{
+    int len;
+    PPPoEPacket packet;
+    int sock = i->sock;
+
+    if (receivePacket(sock, &packet, &len) < 0) {
+	return;
+    }
+
+    if (len < HDR_SIZE) {
+	/* Impossible - ignore */
+	return;
+    }
+
+    /* Sanity check on packet */
+    if (PPPOE_VER(packet.vertype) != 1 || PPPOE_TYPE(packet.vertype) != 1) {
+	/* Syslog an error */
+	return;
+    }
+
+    /* Check length */
+    if (ntohs(packet.length) + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	       (unsigned int) ntohs(packet.length));
+	return;
+    }
+
+    switch(packet.code) {
+    case CODE_PADI:
+	processPADI(i, &packet, len);
+	break;
+    case CODE_PADR:
+	processPADR(i, &packet, len);
+	break;
+    case CODE_PADT:
+	/* Kill the child */
+	processPADT(i, &packet, len);
+	break;
+    case CODE_SESS:
+	/* Ignore SESS -- children will handle them */
+	break;
+    case CODE_PADO:
+    case CODE_PADS:
+	/* Ignore PADO and PADS totally */
+	break;
+    default:
+	/* Syslog an error */
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: sendErrorPADS
+*%ARGUMENTS:
+* sock -- socket to write to
+* source -- source Ethernet address
+* dest -- destination Ethernet address
+* errorTag -- error tag
+* errorMsg -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADS packet with an error message
+***********************************************************************/
+void
+sendErrorPADS(int sock,
+	      unsigned char *source,
+	      unsigned char *dest,
+	      int errorTag,
+	      char *errorMsg)
+{
+    PPPoEPacket pads;
+    unsigned char *cursor = pads.payload;
+    UINT16_t plen;
+    PPPoETag err;
+    int elen = strlen(errorMsg);
+
+    memcpy(pads.ethHdr.h_dest, dest, ETH_ALEN);
+    memcpy(pads.ethHdr.h_source, source, ETH_ALEN);
+    pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    pads.vertype = PPPOE_VER_TYPE(1, 1);
+    pads.code = CODE_PADS;
+
+    pads.session = htons(0);
+    plen = 0;
+
+    err.type = htons(errorTag);
+    err.length = htons(elen);
+
+    memcpy(err.payload, errorMsg, elen);
+    memcpy(cursor, &err, TAG_HDR_SIZE+elen);
+    cursor += TAG_HDR_SIZE + elen;
+    plen += TAG_HDR_SIZE + elen;
+
+    if (relayId.type) {
+	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
+    }
+    if (hostUniq.type) {
+	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
+	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+    }
+    pads.length = htons(plen);
+    sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE));
+}
+
+
+/**********************************************************************
+*%FUNCTION: startPPPDUserMode
+*%ARGUMENTS:
+* session -- client session record
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Starts PPPD for user-mode PPPoE
+***********************************************************************/
+void
+startPPPDUserMode(ClientSession *session)
+{
+    /* Leave some room */
+    char *argv[64];
+
+    char buffer[2 * SMALLBUF];
+
+    int c = 0;
+
+    argv[c++] = "pppd";
+    argv[c++] = "pty";
+
+    /* Let's hope service-name does not have ' in it... */
+    snprintf(buffer, sizeof(buffer), "%s -n -I %s -e %u:%02x:%02x:%02x:%02x:%02x:%02x%s -S '%s'",
+	     pppoe_path, session->ethif->name,
+	     (unsigned int) ntohs(session->sess),
+	     session->eth[0], session->eth[1], session->eth[2],
+	     session->eth[3], session->eth[4], session->eth[5],
+	     PppoeOptions, session->serviceName);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+
+    argv[c++] = "file";
+    argv[c++] = pppoptfile;
+
+    snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d:%d.%d.%d.%d",
+	    (int) session->myip[0], (int) session->myip[1],
+	    (int) session->myip[2], (int) session->myip[3],
+	    (int) session->peerip[0], (int) session->peerip[1],
+	    (int) session->peerip[2], (int) session->peerip[3]);
+    syslog(LOG_INFO,
+	   "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'",
+	   (unsigned int) ntohs(session->sess),
+	   session->eth[0], session->eth[1], session->eth[2],
+	   session->eth[3], session->eth[4], session->eth[5],
+	   (int) session->peerip[0], (int) session->peerip[1],
+	   (int) session->peerip[2], (int) session->peerip[3],
+	   session->ethif->name,
+	   session->serviceName);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+    argv[c++] = "nodetach";
+    argv[c++] = "noaccomp";
+    argv[c++] = "nopcomp";
+    argv[c++] = "default-asyncmap";
+    if (Synchronous) {
+	argv[c++] = "sync";
+    }
+    if (PassUnitOptionToPPPD) {
+	argv[c++] = "unit";
+	sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1));
+	argv[c++] = buffer;
+    }
+    if (session->requested_mtu > 1492) {
+	sprintf(buffer, "%u", (unsigned int) session->requested_mtu);
+	argv[c++] = "mru";
+	argv[c++] = buffer;
+	argv[c++] = "mtu";
+	argv[c++] = buffer;
+    } else {
+	argv[c++] = "mru";
+	argv[c++] = "1492";
+	argv[c++] = "mtu";
+	argv[c++] = "1492";
+    }
+
+    argv[c++] = NULL;
+
+    execv(pppd_path, argv);
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: startPPPDLinuxKernelMode
+*%ARGUMENTS:
+* session -- client session record
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Starts PPPD for kernel-mode PPPoE on Linux
+***********************************************************************/
+void
+startPPPDLinuxKernelMode(ClientSession *session)
+{
+    /* Leave some room */
+    char *argv[32];
+
+    int c = 0;
+
+    char buffer[SMALLBUF];
+
+    argv[c++] = "pppd";
+    argv[c++] = "plugin";
+    argv[c++] = PLUGIN_PATH;
+
+    /* Add "nic-" to interface name */
+    snprintf(buffer, SMALLBUF, "nic-%s", session->ethif->name);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	exit(EXIT_FAILURE);
+    }
+
+    snprintf(buffer, SMALLBUF, "%u:%02x:%02x:%02x:%02x:%02x:%02x",
+	     (unsigned int) ntohs(session->sess),
+	     session->eth[0], session->eth[1], session->eth[2],
+	     session->eth[3], session->eth[4], session->eth[5]);
+    argv[c++] = "rp_pppoe_sess";
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+    argv[c++] = "rp_pppoe_service";
+    argv[c++] = (char *) session->serviceName;
+    argv[c++] = "file";
+    argv[c++] = pppoptfile;
+
+    if (!DelegateIPAllocation) {
+      snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d",
+	    (int) session->myip[0], (int) session->myip[1],
+	    (int) session->myip[2], (int) session->myip[3],
+	    (int) session->peerip[0], (int) session->peerip[1],
+	    (int) session->peerip[2], (int) session->peerip[3]);
+      syslog(LOG_INFO,
+	   "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'",
+	   (unsigned int) ntohs(session->sess),
+	   session->eth[0], session->eth[1], session->eth[2],
+	   session->eth[3], session->eth[4], session->eth[5],
+	   (int) session->peerip[0], (int) session->peerip[1],
+	   (int) session->peerip[2], (int) session->peerip[3],
+	   session->ethif->name,
+	   session->serviceName);
+      argv[c++] = strdup(buffer);
+    } else {
+      syslog(LOG_INFO,
+	    "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x on %s using Service-Name '%s'",
+	    (unsigned int) ntohs(session->sess),
+	    session->eth[0], session->eth[1], session->eth[2],
+	    session->eth[3], session->eth[4], session->eth[5],
+	    session->ethif->name,
+	    session->serviceName);
+    }
+
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+    argv[c++] = "nodetach";
+    argv[c++] = "noaccomp";
+    argv[c++] = "nopcomp";
+    argv[c++] = "default-asyncmap";
+    if (PassUnitOptionToPPPD) {
+	argv[c++] = "unit";
+	sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset));
+	argv[c++] = buffer;
+    }
+    if (session->requested_mtu > 1492) {
+	sprintf(buffer, "%u", (unsigned int) session->requested_mtu);
+	argv[c++] = "mru";
+	argv[c++] = buffer;
+	argv[c++] = "mtu";
+	argv[c++] = buffer;
+    } else {
+	argv[c++] = "mru";
+	argv[c++] = "1492";
+	argv[c++] = "mtu";
+	argv[c++] = "1492";
+    }
+    argv[c++] = NULL;
+    execv(pppd_path, argv);
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: startPPPD
+*%ARGUMENTS:
+* session -- client session record
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Starts PPPD
+***********************************************************************/
+void
+startPPPD(ClientSession *session)
+{
+    if (UseLinuxKernelModePPPoE) startPPPDLinuxKernelMode(session);
+    else startPPPDUserMode(session);
+}
+
+/**********************************************************************
+* %FUNCTION: InterfaceHandler
+* %ARGUMENTS:
+*  es -- event selector (ignored)
+*  fd -- file descriptor which is readable
+*  flags -- ignored
+*  data -- Pointer to the Interface structure
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Handles a packet ready at an interface
+***********************************************************************/
+void
+InterfaceHandler(EventSelector *es,
+		 int fd,
+		 unsigned int flags,
+		 void *data)
+{
+    serverProcessPacket((Interface *) data);
+}
+
+/**********************************************************************
+* %FUNCTION: PppoeStopSession
+* %ARGUMENTS:
+*  ses -- the session
+*  reason -- reason session is being stopped.
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Kills pppd.
+***********************************************************************/
+static void
+PppoeStopSession(ClientSession *ses,
+		 char const *reason)
+{
+    /* Temporary structure for sending PADT's. */
+    PPPoEConnection conn;
+
+    memset(&conn, 0, sizeof(conn));
+    conn.hostUniq = NULL;
+
+    memcpy(conn.myEth, ses->ethif->mac, ETH_ALEN);
+    conn.discoverySocket = ses->ethif->sock;
+    conn.session = ses->sess;
+    memcpy(conn.peerEth, ses->eth, ETH_ALEN);
+    sendPADT(&conn, reason);
+    ses->flags |= FLAG_SENT_PADT;
+
+    if (ses->pid) {
+	kill(ses->pid, SIGTERM);
+    }
+    ses->funcs = &DefaultSessionFunctionTable;
+}
+
+/**********************************************************************
+* %FUNCTION: PppoeSessionIsActive
+* %ARGUMENTS:
+*  ses -- the session
+* %RETURNS:
+*  True if session is active, false if not.
+***********************************************************************/
+static int
+PppoeSessionIsActive(ClientSession *ses)
+{
+    return (ses->pid != 0);
+}
+
+#ifdef HAVE_LICENSE
+/**********************************************************************
+* %FUNCTION: getFreeMem
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  The amount of free RAM in kilobytes, or -1 if it could not be
+*  determined
+* %DESCRIPTION:
+*  Reads Linux-specific /proc/meminfo file and extracts free RAM
+***********************************************************************/
+int
+getFreeMem(void)
+{
+    char buf[512];
+    int memfree=0, buffers=0, cached=0;
+    FILE *fp = fopen("/proc/meminfo", "r");
+    if (!fp) return -1;
+
+    while (fgets(buf, sizeof(buf), fp)) {
+	if (!strncmp(buf, "MemFree:", 8)) {
+	    if (sscanf(buf, "MemFree: %d", &memfree) != 1) {
+		fclose(fp);
+		return -1;
+	    }
+	} else if (!strncmp(buf, "Buffers:", 8)) {
+	    if (sscanf(buf, "Buffers: %d", &buffers) != 1) {
+		fclose(fp);
+		return -1;
+	    }
+	} else if (!strncmp(buf, "Cached:", 7)) {
+	    if (sscanf(buf, "Cached: %d", &cached) != 1) {
+		fclose(fp);
+		return -1;
+	    }
+	}
+    }
+    fclose(fp);
+    /* return memfree + buffers + cached; */
+    return memfree;
+}
+#endif
+
+/**********************************************************************
+* %FUNCTION: pppoe_alloc_session
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  NULL if no session is available, otherwise a ClientSession structure.
+* %DESCRIPTION:
+*  Allocates a ClientSession structure and removes from free list, puts
+*  on busy list
+***********************************************************************/
+ClientSession *
+pppoe_alloc_session(void)
+{
+    ClientSession *ses = FreeSessions;
+    if (!ses) return NULL;
+
+    /* Remove from free sessions list */
+    if (ses == LastFreeSession) {
+	LastFreeSession = NULL;
+    }
+    FreeSessions = ses->next;
+
+    /* Put on busy sessions list */
+    ses->next = BusySessions;
+    BusySessions = ses;
+
+    /* Initialize fields to sane values */
+    ses->funcs = &DefaultSessionFunctionTable;
+    ses->pid = 0;
+    ses->ethif = NULL;
+    memset(ses->eth, 0, ETH_ALEN);
+    ses->flags = 0;
+    ses->startTime = time(NULL);
+    ses->serviceName = "";
+    ses->requested_mtu = 0;
+#ifdef HAVE_LICENSE
+    memset(ses->user, 0, MAX_USERNAME_LEN+1);
+    memset(ses->realm, 0, MAX_USERNAME_LEN+1);
+    memset(ses->realpeerip, 0, IPV4ALEN);
+#endif
+#ifdef HAVE_L2TP
+    ses->l2tp_ses = NULL;
+#endif
+    NumActiveSessions++;
+    return ses;
+}
+
+/**********************************************************************
+* %FUNCTION: pppoe_free_session
+* %ARGUMENTS:
+*  ses -- session to free
+* %RETURNS:
+*  0 if OK, -1 if error
+* %DESCRIPTION:
+*  Places a ClientSession on the free list.
+***********************************************************************/
+int
+pppoe_free_session(ClientSession *ses)
+{
+    ClientSession *cur, *prev;
+
+    cur = BusySessions;
+    prev = NULL;
+    while (cur) {
+	if (ses == cur) break;
+	prev = cur;
+	cur = cur->next;
+    }
+
+    if (!cur) {
+	syslog(LOG_ERR, "pppoe_free_session: Could not find session %p on busy list", (void *) ses);
+	return -1;
+    }
+
+    /* Remove from busy sessions list */
+    if (prev) {
+	prev->next = ses->next;
+    } else {
+	BusySessions = ses->next;
+    }
+
+    /* Add to end of free sessions */
+    ses->next = NULL;
+    if (LastFreeSession) {
+	LastFreeSession->next = ses;
+	LastFreeSession = ses;
+    } else {
+	FreeSessions = ses;
+	LastFreeSession = ses;
+    }
+
+    /* Initialize fields to sane values */
+    ses->funcs = &DefaultSessionFunctionTable;
+    ses->pid = 0;
+    memset(ses->eth, 0, ETH_ALEN);
+    ses->flags = 0;
+#ifdef HAVE_L2TP
+    ses->l2tp_ses = NULL;
+#endif
+    NumActiveSessions--;
+    return 0;
+}
+
+/**********************************************************************
+* %FUNCTION: sendHURLorMOTM
+* %ARGUMENTS:
+*  conn -- PPPoE connection
+*  url -- a URL, which *MUST* begin with "http://" or it won't be sent, or
+*         a message.
+*  tag -- one of TAG_HURL or TAG_MOTM
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Sends a PADM packet contaning a HURL or MOTM tag to the victim...er, peer.
+***********************************************************************/
+void
+sendHURLorMOTM(PPPoEConnection *conn, char const *url, UINT16_t tag)
+{
+    PPPoEPacket packet;
+    PPPoETag hurl;
+    size_t elen;
+    unsigned char *cursor = packet.payload;
+    UINT16_t plen = 0;
+
+    if (!conn->session) return;
+    if (conn->discoverySocket < 0) return;
+
+    if (tag == TAG_HURL) {
+	if (strncmp(url, "http://", 7) && strncmp(url, "https://", 8)) {
+	    syslog(LOG_WARNING, "sendHURL(%s): URL must begin with http:// or https://", url);
+	    return;
+	}
+    } else {
+	tag = TAG_MOTM;
+    }
+
+    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
+    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
+
+    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
+    packet.code = CODE_PADM;
+    packet.session = conn->session;
+
+    elen = strlen(url);
+    if (elen > 256) {
+	syslog(LOG_WARNING, "MOTM or HURL too long: %d", (int) elen);
+	return;
+    }
+
+    hurl.type = htons(tag);
+    hurl.length = htons(elen);
+    strcpy((char *) hurl.payload, url);
+    memcpy(cursor, &hurl, elen + TAG_HDR_SIZE);
+    cursor += elen + TAG_HDR_SIZE;
+    plen += elen + TAG_HDR_SIZE;
+
+    packet.length = htons(plen);
+
+    sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
+#ifdef DEBUGGING_ENABLED
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, &packet, "SENT");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+#endif
+}
Index: create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src
===================================================================
--- create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src	(nonexistent)
+++ create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src	(revision 5)

Property changes on: create-3.14-ip-allocation-patch/rp-pppoe-3.14-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-ip-allocation-patch/rp-pppoe-3.14-new
===================================================================
--- create-3.14-ip-allocation-patch/rp-pppoe-3.14-new	(nonexistent)
+++ create-3.14-ip-allocation-patch/rp-pppoe-3.14-new	(revision 5)

Property changes on: create-3.14-ip-allocation-patch/rp-pppoe-3.14-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-ip-allocation-patch
===================================================================
--- create-3.14-ip-allocation-patch	(nonexistent)
+++ create-3.14-ip-allocation-patch	(revision 5)

Property changes on: create-3.14-ip-allocation-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-man-pages-patch/create.patch.sh
===================================================================
--- create-3.14-man-pages-patch/create.patch.sh	(nonexistent)
+++ create-3.14-man-pages-patch/create.patch.sh	(revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=3.14
+
+tar --files-from=file.list -xzvf ../rp-pppoe-$VERSION.tar.gz
+mv rp-pppoe-$VERSION rp-pppoe-$VERSION-orig
+
+cp -rf ./rp-pppoe-$VERSION-new ./rp-pppoe-$VERSION
+
+diff --unified -Nr  rp-pppoe-$VERSION-orig  rp-pppoe-$VERSION > rp-pppoe-$VERSION-man-pages.patch
+
+mv rp-pppoe-$VERSION-man-pages.patch ../patches
+
+rm -rf ./rp-pppoe-$VERSION
+rm -rf ./rp-pppoe-$VERSION-orig

Property changes on: create-3.14-man-pages-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-3.14-man-pages-patch/file.list
===================================================================
--- create-3.14-man-pages-patch/file.list	(nonexistent)
+++ create-3.14-man-pages-patch/file.list	(revision 5)
@@ -0,0 +1,3 @@
+rp-pppoe-3.14/man/pppoe-server.8
+rp-pppoe-3.14/man/pppoe.8
+rp-pppoe-3.14/src/pppoe.c
Index: create-3.14-man-pages-patch/rp-pppoe-3.14-new/man/pppoe-server.8
===================================================================
--- create-3.14-man-pages-patch/rp-pppoe-3.14-new/man/pppoe-server.8	(nonexistent)
+++ create-3.14-man-pages-patch/rp-pppoe-3.14-new/man/pppoe-server.8	(revision 5)
@@ -0,0 +1,236 @@
+.\" LIC: GPL
+.TH PPPOE-SERVER 8 "21 June 2008"
+.\""
+.UC 4
+.SH NAME
+pppoe-server \- user-space PPPoE server
+.SH SYNOPSIS
+.B pppoe-server \fR[\fIoptions\fR]
+
+.SH DESCRIPTION
+\fBpppoe-server\fR is a user-space server for PPPoE (Point-to-Point Protocol
+over Ethernet) for Linux and other UNIX systems.  \fBpppoe-server\fR works in
+concert with the \fBpppoe\fR client to respond to PPPoE discovery packets
+and set up PPPoE sessions.
+
+.SH OPTIONS
+.TP
+.B \-F
+The \fB\-F\fR option causes \fBpppoe-server\fR not to fork and become a
+daemon.  The default is to fork and become a daemon.
+
+.TP
+.B \-I \fIinterface\fR
+The \fB\-I\fR option specifies the Ethernet interface to use.  Under
+Linux, it is typically \fIeth0\fR or \fIeth1\fR.  The interface should
+be "up" before you start \fBpppoe-server\fR, but need not have an IP
+address.  You can supply multiple \fB\-I\fR options if you want the
+server to respond on more than one interface.
+
+.TP
+.B \-X \fIpidfile\fR
+This option causes \fBpppoe-server\fR to write its process ID to
+\fIpidfile\fR.  Additionally, it keeps the file locked so that only
+a single process may be started for a given \fIpidfile\fR.
+
+.TP
+.B \-q \fI/path/to/pppd\fR
+Specifies the full path to the \fBpppd\fR program.  The default is determined
+at compile time.  One use of this option is to supply a wrapper program that
+modifies the arguments passed to pppd.  This lets you do things not directly
+supported by the server (for example, specify IPv6 addresses.)
+
+.TP
+.B \-Q \fI/path/to/pppoe\fR
+Specifies the full path to the \fBpppoe\fR program.  The default is determined
+at compile time.  This option is only relevant if you are \fInot\fR
+using kernel-mode PPPoE.
+
+.TP
+.B \-T \fItimeout\fR
+This option is passed directly to \fBpppoe\fR; see \fBpppoe\fR(8) for
+details.  If you are using kernel-mode PPPoE, this option has \fIno effect\fR.
+
+.TP
+.B \-C \fIac_name\fR
+Specifies which name to report as the access concentrator name.  If not
+supplied, the host name is used.
+
+.TP
+.B \-S \fIname\fR
+Offer a service named \fIname\fR.  Multiple \fB\-S\fR options may
+be specified; each one causes the named service to be advertised
+in a Service-Name tag in the PADO frame.  The first \fB\-S\fR option
+specifies the default service, and is used if the PPPoE client
+requests a Service-Name of length zero.
+
+.TP
+.B \-m \fIMSS\fR
+This option is passed directly to \fBpppoe\fR; see \fBpppoe\fR(8) for
+details.  If you are using kernel-mode PPPoE, this option has \fIno effect\fR.
+
+.TP
+.B \-x \fIn\fR
+Limit the number of sessions per peer MAC address to \fIn\fR.  If a given
+MAC address attempts to create more than \fIn\fR sessions, then its
+PADI and PADR packets are ignored.  If you set \fIn\fR to 0 (the default),
+then no limit is imposed on the number of sessions per peer MAC address.
+
+.TP
+.B \-P
+Check pool file for correctness and exit.
+
+.TP
+.B \-s
+This option is passed directly to \fBpppoe\fR; see \fBpppoe\fR(8) for
+details.  In addition, it causes \fBpppd\fR to be invoked with the
+\fIsync\fR option.
+
+.TP
+.B \-l
+Increment local IP address for each session.
+
+.TP
+.B \-L \fIip\fR
+Sets the local IP address.  This is passed to spawned \fBpppd\fR processes.
+If not specified, the default is 10.0.0.1.
+
+.TP
+.B \-R \fIip\fR
+Sets the starting remote IP address.  As sessions are established,
+IP addresses are assigned starting from \fIip\fR.   \fBpppoe-server\fR
+automatically keeps track of the pool of addresses and passes a
+valid remote IP address to \fBpppd\fR.  If not specified, a starting address
+of 10.67.15.1 is used.
+
+.TP
+.B \-D
+Delegate the allocation of IP addresses to \fBpppd\fR.  If specified, no
+local and remote addresses passed to pppd.
+
+.TP
+.B \-N \fInum\fR
+Allows at most \fInum\fR concurrent PPPoE sessions.  If not specified,
+the default is 64.
+
+.TP
+.B \-M \fIstring\fR
+Sends \fIstring\fR in a MOTM tag in a PADM packet right after sending
+the PADS to a client.
+
+.TP
+.B \-H \fIurl\fR
+Sends \fIurl\fR in a HURL tag in a PADM packet right after sending the
+PADS to a client.  Note that \fIurl\fR must start with either
+\fBhttp://\fR or \fBhttps://\fR.
+
+.TP
+.B \-O \fIfname\fR
+This option causes \fBpppoe-server\fR to tell \fBpppd\fR to use the option
+file \fIfname\fR instead of the default \fI/etc/ppp/pppoe-server-options\fR.
+
+.TP
+.B \-p \fIfname\fR
+Reads the specified file \fIfname\fR which is a text file consisting of
+one IP address per line.  These IP addresses will be assigned to clients.
+The number of sessions allowed will equal the number of addresses found
+in the file.  The \fB\-p\fR option overrides both \fB\-R\fR and \fB\-N\fR.
+
+In addition to containing IP addresses, the pool file can contain lines
+of the form:
+
+.nf
+	a.b.c.d-e
+.fi
+
+which includes all IP addresses from a.b.c.d to a.b.c.e.  For example,
+the line:
+
+.nf
+	1.2.3.4-7
+.fi
+
+is equivalent to:
+
+.nf
+	1.2.3.4
+	1.2.3.5
+	1.2.3.6
+	1.2.3.7
+.fi
+
+.TP
+.B \-r
+Tells the PPPoE server to randomly permute session numbers.  Instead of
+handing out sessions in order, the session numbers are assigned in an
+unpredictable order.
+
+.TP
+.B \-d
+Debug session creation.
+
+.TP
+.B \-u
+Tells the server to invoke \fBpppd\fR with the \fIunit\fR option.  Note
+that this option only works for \fBpppd\fR version 2.4.0 or newer.
+
+.TP
+.B \-o \fIoffset\fR
+Instead of numbering PPPoE sessions starting at 1, they will be numbered
+starting at \fIoffset\fR+1.  This allows you to run multiple servers on
+a given machine; just make sure that their session numbers do not
+overlap.
+
+.TP
+.B \-f disc:sess
+The \fB\-f\fR option sets the Ethernet frame types for PPPoE discovery
+and session frames.  The types are specified as hexadecimal numbers
+separated by a colon.  Standard PPPoE uses frame types 8863:8864.
+\fIYou should not use this option\fR unless you are absolutely sure
+the peer you are dealing with uses non-standard frame types.
+
+.TP
+.B \-k
+The \fB\-k\fR option tells the server to use kernel-mode PPPoE on Linux.
+This option is available only on Linux kernels 2.4.0 and later, and
+only if the server was built with kernel-mode support.
+
+.TP
+.B \-i
+The \fB\-i\fR option tells the server to completely ignore PADI frames
+if there are no free session slots.
+
+.TP
+.B \-h
+The \fB\-h\fR option prints a brief usage message and exits.
+
+.SH OPERATION
+
+\fBpppoe-server\fR listens for incoming PPPoE discovery packets.  When
+a session is established, it spawns a \fBpppd\fR process.  The following
+options are passed to \fBpppd\fR:
+
+.nf
+nodetach noaccomp nobsdcom nodeflate nopcomp novj novjccomp
+default-asyncmap
+.fi
+
+In addition, the local and remote IP address are set based on the
+\fB\-L\fR and \fB\-R\fR options.  The \fBpty\fR option is supplied along
+with a \fBpppoe\fR command to initiate the PPPoE session.  Finally,
+additional \fBpppd\fR options can be placed in the file
+\fB/etc/ppp/pppoe-server-options\fR (which must exist, even if it is just
+empty!)
+
+Note that \fBpppoe-server\fR is meant mainly for testing PPPoE clients.
+It is \fInot\fR a high-performance server meant for production use.
+
+.SH AUTHORS
+\fBpppoe-server\fR was written by Dianne Skoll <dianne@skoll.ca>.
+
+The \fBpppoe\fR home page is \fIhttps://dianne.skoll.ca/projects/rp-pppoe/\fR.
+
+.SH SEE ALSO
+pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5),
+pppoe(8), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-relay(8)
+
Index: create-3.14-man-pages-patch/rp-pppoe-3.14-new/man/pppoe.8
===================================================================
--- create-3.14-man-pages-patch/rp-pppoe-3.14-new/man/pppoe.8	(nonexistent)
+++ create-3.14-man-pages-patch/rp-pppoe-3.14-new/man/pppoe.8	(revision 5)
@@ -0,0 +1,251 @@
+.\" LIC: GPL
+.TH PPPOE 8 "5 October 2015"
+.UC 4
+.SH NAME
+pppoe \- user-space PPPoE client.
+.SH SYNOPSIS
+.B pppd pty 'pppoe \fR[\fIpppoe_options\fR]\fB' \fR[\fIpppd_options\fR]
+.P
+.B pppoe -A \fR[\fIpppoe_options\fR]
+.SH DESCRIPTION
+\fBpppoe\fR is a user-space client for PPPoE (Point-to-Point Protocol
+over Ethernet) for Linux and other UNIX systems.  \fBpppoe\fR works in
+concert with the \fBpppd\fR PPP daemon to provide a PPP connection
+over Ethernet, as is used by many DSL service providers.
+
+.SH OPTIONS
+.TP
+.B \-I \fIinterface\fR
+The \fB\-I\fR option specifies the Ethernet interface to use.  Under Linux,
+it is typically \fIeth0\fR or \fIeth1\fR.  The interface should be "up"
+before you start \fBpppoe\fR, but should \fInot\fR be configured to have
+an IP address.
+
+.TP
+.B \-T \fItimeout\fR
+The \fB\-T\fR option causes \fBpppoe\fR to exit if no session traffic
+is detected for \fItimeout\fR seconds.  I recommend that you use this
+option as an extra safety measure, but if you do, you should make sure
+that PPP generates enough traffic so the timeout will normally not be
+triggered.  The best way to do this is to use the
+\fIlcp-echo-interval\fR option to \fBpppd\fR.  You should set the
+PPPoE timeout to be about four times the LCP echo interval.
+
+.TP
+.B \-t \fItimeout\fR
+The \fB\-t\fR option sets the initial timeout for discovery packets in seconds.
+
+.TP
+.B \-D \fIfile_name\fR
+The \fB\-D\fR option causes every packet to be dumped to the specified
+\fIfile_name\fR.  This is intended for debugging only; it produces huge
+amounts of output and greatly reduces performance.
+
+.TP
+.B \-V
+The \fB\-V\fR option causes \fBpppoe\fR to print its version number and
+exit.
+
+.TP
+.B \-A
+The \fB\-A\fR option causes \fBpppoe\fR to send a PADI packet and then print
+the names of access concentrators in each PADO packet it receives.  Do not
+use this option in conjunction with \fBpppd\fR; the \fB\-A\fR option is
+meant to be used interactively to give interesting information about the
+access concentrator.
+
+.TP
+.B \-S \fIservice_name\fR
+Specifies the desired service name.  \fBpppoe\fR will only initiate sessions
+with access concentrators which can provide the specified service.  In
+most cases, you should \fInot\fR specify this option.  Use it only if you
+know that there are multiple access concentrators or know that you need a
+specific service name.
+
+.TP
+.B \-C \fIac_name\fR
+Specifies the desired access concentrator name.  \fBpppoe\fR will only
+initiate sessions with the specified access concentrator.  In
+most cases, you should \fInot\fR specify this option.  Use it only if you
+know that there are multiple access concentrators.  If both the
+\fB\-S\fR and \fB\-C\fR options are specified, they must \fIboth\fR match
+for \fBpppoe\fR to initiate a session.
+
+.TP
+.B \-U
+Causes \fBpppoe\fR to use the Host-Uniq tag in its discovery packets.  This
+lets you run multiple \fBpppoe\fR daemons without having their discovery
+packets interfere with one another.  You must supply this option to
+\fIall\fR \fBpppoe\fR daemons if you intend to run multiple daemons
+simultaneously.  The specific Host-Uniq value used is the hexadecimal
+representation of the \fBpppoe\fR process's PID.
+
+.TP
+.B \-W value
+Causes \fBpppoe\fR to use the Host-Uniq tag in its discovery packets,
+and furthermore to set the value of Host-Uniq to \fIvalue\fR.  Use with
+caution.  Note that \fB\-W\fR and \fB\-U\fR are mutually-incompatible.
+
+.TP
+.B \-s
+Causes \fBpppoe\fR to use \fIsynchronous\fR PPP encapsulation.  If you
+use this option, then you \fImust\fR use the \fBsync\fR option with
+\fBpppd\fR.  You are encouraged to use this option if it works, because
+it greatly reduces the CPU overhead of \fBpppoe\fR.  However, it
+MAY be unreliable on slow machines -- there is a race condition between
+pppd writing data and pppoe reading it.  For this reason, the default
+setting is asynchronous.  If you encounter bugs or crashes with Synchronous
+PPP, turn it off -- don't e-mail me for support!
+
+.TP
+.B \-m \fIMSS\fR
+Causes \fBpppoe\fR to \fIclamp\fR the TCP maximum segment size at the specified
+value.  Because of PPPoE overhead, the maximum segment size for PPPoE is
+smaller than for normal Ethernet encapsulation.  This could cause problems
+for machines on a LAN behind a gateway using PPPoE.  If you have a LAN
+behind a gateway, and the gateway connects to the Internet using PPPoE,
+you are strongly recommended to use a \fB\-m 1412\fR option.  This avoids
+having to set the MTU on all the hosts on the LAN.
+
+.TP
+.B \-p \fIfile\fR
+Causes \fBpppoe\fR to write its process-ID to the specified file.  This
+can be used to locate and kill \fBpppoe\fR processes.
+
+.TP
+.B \-e \fIsess:mac\fR
+Causes \fBpppoe\fR to skip the discovery phase and move directly to the
+session phase.  The session is given by \fIsess\fR and the MAC address of
+the peer by \fImac\fR.  This mode is \fInot\fR meant for normal use; it
+is designed only for \fBpppoe-server\fR(8).
+
+.TP
+.B \-n
+Causes \fBpppoe\fR not to open a discovery socket.  This mode is
+\fInot\fR meant for normal use; it is designed only for
+\fBpppoe-server\fR(8).
+
+.TP
+.B \-k
+Causes \fBpppoe\fR to terminate an existing session by sending a PADT frame,
+and then exit.  You must use the \fB\-e\fR option in conjunction with this
+option to specify the session to kill.  This may be useful for killing
+sessions when a buggy peer does not realize the session has ended.
+
+.TP
+.B \-d
+Causes \fBpppoe\fR to perform discovery and then exit, after printing
+session information to standard output.  The session information is printed
+in exactly the format expected by the \fB\-e\fR option.  This option lets
+you initiate a PPPoE discovery, perform some other work, and then start
+the actual PPP session.  \fIBe careful\fR; if you use this option in a loop,
+you can create many sessions, which may annoy your peer.
+
+.TP
+.B \-f disc:sess
+The \fB\-f\fR option sets the Ethernet frame types for PPPoE discovery
+and session frames.  The types are specified as hexadecimal numbers
+separated by a colon.  Standard PPPoE uses frame types 8863:8864.
+\fIYou should not use this option\fR unless you are absolutely sure
+the peer you are dealing with uses non-standard frame types.  If your
+ISP uses non-standard frame types, complain!
+
+.TP
+.B \-F numfloods
+The \fB\-F\fR option sets the discovery flood, only used for stress-testing.
+
+.TP
+.B \-h
+The \fB\-h\fR option causes \fBpppoe\fR to print usage information and
+exit.
+
+.SH PPPOE BACKGROUND
+
+PPPoE (Point-to-Point Protocol over Ethernet) is described in RFC 2516
+and is a protocol which allows the session abstraction to be maintained
+over bridged Ethernet networks.
+
+PPPoE works by encapsulating PPP frames in Ethernet frames.  The protocol
+has two distinct stages:  The \fIdiscovery\fR and the \fIsession\fR stage.
+
+In the discovery stage, the host broadcasts a special PADI (PPPoE
+Active Discovery Initiation) frame to discover any \fIaccess
+concentrators\fR.  The access concentrators (typically, only one
+access concentrator) reply with PADO (PPPoE Active Discovery Offer)
+packets, announcing their presence and the services they offer.  The
+host picks one of the access concentrators and transmits a PADR (PPPoE
+Active Discovery Request) packet, asking for a session.  The access
+concentrator replies with a PADS (PPPoE Active Discovery
+Session-Confirmation) packet.  The protocol then moves to the session stage.
+
+In the session stage, the host and access concentrator exchange PPP frames
+embedded in Ethernet frames.  The normal Ethernet MTU is 1500 bytes, but
+the PPPoE overhead plus two bytes of overhead for the encapsulated PPP
+frame mean that the MTU of the PPP interface is at most 1492 bytes.
+This causes \fIall kinds of problems\fR if you are using a Linux machine
+as a firewall and interfaces behind the firewall have an MTU greater than
+1492.  In fact, to be safe, I recommend setting the MTU of machines
+behind the firewall to 1412, to allow for worst-case TCP and IP options
+in their respective headers.
+
+Normally, PPP uses the Link Control Protocol (LCP) to shut down a PPP
+link.  However, the PPPoE specification allows the link to be shut down
+with a special PADT (PPPoE Active Discovery Terminate) packet.  This client
+recognizes this packet and will correctly terminate if a terminate request
+is received for the PPP session.
+
+.SH DESIGN GOALS
+
+My design goals for this PPPoE client were as follows, in descending order
+of importance:
+
+.TP
+.B o
+It must work.
+
+.TP
+.B o
+It must be a user-space program and not a kernel patch.
+
+.TP
+.B o
+The code must be easy to read and maintain.
+
+.TP
+.B o
+It must be fully compliant with RFC 2516, the proposed PPPoE standard.
+
+.TP
+.B o
+It must never hang up forever -- if the connection is broken, it must
+detect this and exit, allowing a wrapper script to restart the connection.
+
+.TP
+.B o
+It must be fairly efficient.
+
+.P
+I believe I have achieved all of these goals, but (of course) am open
+to suggestions, patches and ideas.  See my home page,
+https://dianne.skoll.ca/projects/rp-pppoe/, for contact information.
+
+.SH NOTES
+
+For best results, you must give \fBpppd\fR an mtu option of
+1492.  I have observed problems with excessively-large frames
+unless I set this option.  Also, if \fBpppoe\fR is running on a firewall
+machine, all machines behind the firewall should have MTU's of 1412.
+
+If you have problems, check your system logs.  \fBpppoe\fR logs interesting
+things to syslog.  You may have to turn on logging of \fIdebug\fR-level
+messages for complete diagnosis.
+
+.SH AUTHORS
+\fBpppoe\fR was written by Dianne Skoll <dianne@skoll.ca>,
+with much inspiration from an earlier version by Luke Stras.
+
+The \fBpppoe\fR home page is \fIhttps://dianne.skoll.ca/projects/rp-pppoe/\fR.
+
+.SH SEE ALSO
+pppoe-start(8), pppoe-stop(8), pppoe-connect(8), pppd(8), pppoe.conf(5), pppoe-setup(8), pppoe-status(8), pppoe-sniff(8), pppoe-server(8), pppoe-relay(8)
+
Index: create-3.14-man-pages-patch/rp-pppoe-3.14-new/man
===================================================================
--- create-3.14-man-pages-patch/rp-pppoe-3.14-new/man	(nonexistent)
+++ create-3.14-man-pages-patch/rp-pppoe-3.14-new/man	(revision 5)

Property changes on: create-3.14-man-pages-patch/rp-pppoe-3.14-new/man
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-man-pages-patch/rp-pppoe-3.14-new/src/pppoe.c
===================================================================
--- create-3.14-man-pages-patch/rp-pppoe-3.14-new/src/pppoe.c	(nonexistent)
+++ create-3.14-man-pages-patch/rp-pppoe-3.14-new/src/pppoe.c	(revision 5)
@@ -0,0 +1,978 @@
+/***********************************************************************
+*
+* pppoe.c
+*
+* Implementation of user-space PPPoE redirector for Linux.
+*
+* Copyright (C) 2000-2015 by Roaring Penguin Software Inc.
+* Copyright (C) 2018-2020 Dianne Skoll
+*
+* This program may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+* LIC: GPL
+*
+***********************************************************************/
+
+#define _GNU_SOURCE 1
+#include "pppoe.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef USE_LINUX_PACKET
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_N_HDLC
+#ifndef N_HDLC
+#include <pty.h>
+#endif
+#endif
+
+/* Default interface if no -I option given */
+#define DEFAULT_IF "eth0"
+
+/* Global variables -- options */
+int optInactivityTimeout = 0;	/* Inactivity timeout */
+int optClampMSS          = 0;	/* Clamp MSS to this value */
+int optSkipSession       = 0;	/* Perform discovery, print session info
+				   and exit */
+int optFloodDiscovery    = 0;   /* Flood server with discovery requests.
+				   USED FOR STRESS-TESTING ONLY.  DO NOT
+				   USE THE -F OPTION AGAINST A REAL ISP */
+
+PPPoEConnection *Connection = NULL; /* Must be global -- used
+				       in signal handler */
+
+/***********************************************************************
+*%FUNCTION: sendSessionPacket
+*%ARGUMENTS:
+* conn -- PPPoE connection
+* packet -- the packet to send
+* len -- length of data to send
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Transmits a session packet to the peer.
+***********************************************************************/
+void
+sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len)
+{
+    packet->length = htons(len);
+    if (optClampMSS) {
+	clampMSS(packet, "outgoing", optClampMSS);
+    }
+    if (sendPacket(conn, conn->sessionSocket, packet, len + HDR_SIZE) < 0) {
+	if (errno == ENOBUFS) {
+	    /* No buffer space is a transient error */
+	    return;
+	}
+	exit(EXIT_FAILURE);
+    }
+#ifdef DEBUGGING_ENABLED
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, packet, "SENT");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+#endif
+
+}
+
+#ifdef USE_BPF
+/**********************************************************************
+*%FUNCTION: sessionDiscoveryPacket
+*%ARGUMENTS:
+* packet -- the discovery packet that was received
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* We got a discovery packet during the session stage.  This most likely
+* means a PADT.
+*
+* The BSD version uses a single socket for both discovery and session
+* packets.  When a packet comes in over the wire once we are in
+* session mode, either syncReadFromEth() or asyncReadFromEth() will
+* have already read the packet and determined it to be a discovery
+* packet before passing it here.
+***********************************************************************/
+static void
+sessionDiscoveryPacket(PPPoEPacket *packet)
+{
+    /* Sanity check */
+    if (packet->code != CODE_PADT) {
+	return;
+    }
+
+    /* It's a PADT, all right.  Is it for us? */
+    if (packet->session != Connection->session) {
+	/* Nope, ignore it */
+	return;
+    }
+    if (memcmp(packet->ethHdr.h_dest, Connection->myEth, ETH_ALEN)) {
+	return;
+    }
+
+    if (memcmp(packet->ethHdr.h_source, Connection->peerEth, ETH_ALEN)) {
+	return;
+    }
+
+    syslog(LOG_INFO,
+	   "Session %d terminated -- received PADT from peer",
+	   (int) ntohs(packet->session));
+    parsePacket(packet, parseLogErrs, NULL);
+    sendPADT(Connection, "Received PADT from peer");
+    exit(EXIT_SUCCESS);
+}
+#else
+/**********************************************************************
+*%FUNCTION: sessionDiscoveryPacket
+*%ARGUMENTS:
+* conn -- PPPoE connection
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* We got a discovery packet during the session stage.  This most likely
+* means a PADT.
+***********************************************************************/
+static void
+sessionDiscoveryPacket(PPPoEConnection *conn)
+{
+    PPPoEPacket packet;
+    int len;
+
+    if (receivePacket(conn->discoverySocket, &packet, &len) < 0) {
+	return;
+    }
+
+    /* Check length */
+    if (ntohs(packet.length) + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	       (unsigned int) ntohs(packet.length));
+	return;
+    }
+
+    /* Is it for our session? */
+    if (packet.session != conn->session) {
+	/* Nope, ignore it */
+	return;
+    }
+
+    /* Is it for our Ethernet interface? */
+    if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
+	/* Nope, ignore it */
+	return;
+    }
+
+    /* Is it from our peer's Ethernet interface? */
+    if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
+	/* Nope, ignore it */
+	return;
+    }
+
+    if (packet.code != CODE_PADT) {
+	/* Not PADT; ignore it */
+	return;
+    }
+
+#ifdef DEBUGGING_ENABLED
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, &packet, "RCVD");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+#endif
+    syslog(LOG_INFO,
+	   "Session %d terminated -- received PADT from peer",
+	   (int) ntohs(packet.session));
+    parsePacket(&packet, parseLogErrs, NULL);
+    sendPADT(conn, "Received PADT from peer");
+    exit(EXIT_SUCCESS);
+}
+#endif /* USE_BPF */
+
+/**********************************************************************
+*%FUNCTION: session
+*%ARGUMENTS:
+* conn -- PPPoE connection info
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Handles the "session" phase of PPPoE
+***********************************************************************/
+void
+session(PPPoEConnection *conn)
+{
+    fd_set readable;
+    PPPoEPacket packet;
+    struct timeval tv;
+    struct timeval *tvp = NULL;
+    int maxFD = 0;
+    int r;
+
+    /* Drop privileges */
+    dropPrivs();
+
+    /* Prepare for select() */
+    if (conn->sessionSocket > maxFD)   maxFD = conn->sessionSocket;
+    if (conn->discoverySocket > maxFD) maxFD = conn->discoverySocket;
+    maxFD++;
+
+    /* Fill in the constant fields of the packet to save time */
+    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
+    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
+    packet.ethHdr.h_proto = htons(Eth_PPPOE_Session);
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
+    packet.code = CODE_SESS;
+    packet.session = conn->session;
+
+    initPPP();
+
+#ifdef USE_BPF
+    /* check for buffered session data */
+    while (BPF_BUFFER_HAS_DATA) {
+	if (conn->synchronous) {
+	    syncReadFromEth(conn, conn->sessionSocket, optClampMSS);
+	} else {
+	    asyncReadFromEth(conn, conn->sessionSocket, optClampMSS);
+	}
+    }
+#endif
+
+    for (;;) {
+	if (optInactivityTimeout > 0) {
+	    tv.tv_sec = optInactivityTimeout;
+	    tv.tv_usec = 0;
+	    tvp = &tv;
+	}
+	FD_ZERO(&readable);
+	FD_SET(0, &readable);     /* ppp packets come from stdin */
+	if (conn->discoverySocket >= 0) {
+	    FD_SET(conn->discoverySocket, &readable);
+	}
+	FD_SET(conn->sessionSocket, &readable);
+	while(1) {
+	    r = select(maxFD, &readable, NULL, NULL, tvp);
+	    if (r >= 0 || errno != EINTR) break;
+	}
+	if (r < 0) {
+	    fatalSys("select (session)");
+	}
+	if (r == 0) { /* Inactivity timeout */
+	    syslog(LOG_ERR, "Inactivity timeout... something wicked happened on session %d",
+		   (int) ntohs(conn->session));
+	    sendPADT(conn, "RP-PPPoE: Inactivity timeout");
+	    exit(EXIT_FAILURE);
+	}
+
+	/* Handle ready sockets */
+	if (FD_ISSET(0, &readable)) {
+	    if (conn->synchronous) {
+		syncReadFromPPP(conn, &packet);
+	    } else {
+		asyncReadFromPPP(conn, &packet);
+	    }
+	}
+
+	if (FD_ISSET(conn->sessionSocket, &readable)) {
+	    do {
+		if (conn->synchronous) {
+		    syncReadFromEth(conn, conn->sessionSocket, optClampMSS);
+		} else {
+		    asyncReadFromEth(conn, conn->sessionSocket, optClampMSS);
+		}
+	    } while (BPF_BUFFER_HAS_DATA);
+	}
+
+#ifndef USE_BPF
+	/* BSD uses a single socket, see *syncReadFromEth() */
+	/* for calls to sessionDiscoveryPacket() */
+	if (conn->discoverySocket >= 0) {
+	    if (FD_ISSET(conn->discoverySocket, &readable)) {
+		sessionDiscoveryPacket(conn);
+	    }
+	}
+#endif
+
+    }
+}
+
+
+/***********************************************************************
+*%FUNCTION: sigPADT
+*%ARGUMENTS:
+* src -- signal received
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* If an established session exists send PADT to terminate from session
+*  from our end
+***********************************************************************/
+static void
+sigPADT(int src)
+{
+  syslog(LOG_DEBUG,"Received signal %d on session %d.",
+	 (int)src, (int) ntohs(Connection->session));
+  sendPADTf(Connection, "RP-PPPoE: Received signal %d", src);
+  exit(EXIT_SUCCESS);
+}
+
+/**********************************************************************
+*%FUNCTION: usage
+*%ARGUMENTS:
+* argv0 -- program name
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints usage information and exits.
+***********************************************************************/
+void
+usage(char const *argv0)
+{
+    fprintf(stderr, "Usage: %s [options]\n", argv0);
+    fprintf(stderr, "Options:\n");
+#ifdef USE_BPF
+    fprintf(stderr, "   -I if_name     -- Specify interface (REQUIRED)\n");
+#else
+    fprintf(stderr, "   -I if_name     -- Specify interface (default %s.)\n",
+	    DEFAULT_IF);
+#endif
+#ifdef DEBUGGING_ENABLED
+    fprintf(stderr, "   -D filename    -- Log debugging information in filename.\n");
+#endif
+    fprintf(stderr,
+	    "   -T timeout     -- Specify inactivity timeout in seconds.\n"
+	    "   -t timeout     -- Initial timeout for discovery packets in seconds\n"
+	    "   -V             -- Print version and exit.\n"
+	    "   -A             -- Print access concentrator names and exit.\n"
+	    "   -S name        -- Set desired service name.\n"
+	    "   -C name        -- Set desired access concentrator name.\n"
+	    "   -U             -- Use Host-Unique to allow multiple PPPoE sessions.\n"
+	    "   -W value       -- Use Host-Unique set to 'value' specifically.\n"
+	    "   -s             -- Use synchronous PPP encapsulation.\n"
+	    "   -m MSS         -- Clamp incoming and outgoing MSS options.\n"
+	    "   -p pidfile     -- Write process-ID to pidfile.\n"
+	    "   -e sess:mac    -- Skip discovery phase; use existing session.\n"
+	    "   -n             -- Do not open discovery socket.\n"
+	    "   -k             -- Kill a session with PADT (requires -e)\n"
+	    "   -d             -- Perform discovery, print session info and exit.\n"
+	    "   -f disc:sess   -- Set Ethernet frame types (hex).\n"
+	    "   -F numfloods   -- Set the discovery flood, only used for stress-testing.\n"
+	    "   -h             -- Print usage information.\n\n"
+	    "RP-PPPoE Version %s, Copyright (C) 2001-2018 Roaring Penguin Software Inc.\n"
+	    "                 %*s  Copyright (C) 2018-2020 Dianne Skoll\n"
+	    "RP-PPPoE comes with ABSOLUTELY NO WARRANTY.\n"
+	    "This is free software, and you are welcome to redistribute it under the terms\n"
+	    "of the GNU General Public License, version 2 or any later version.\n"
+	    "https://dianne.skoll.ca/projects/rp-pppoe/\n", RP_VERSION, (int) strlen(RP_VERSION), "");
+    exit(EXIT_SUCCESS);
+}
+
+/**********************************************************************
+*%FUNCTION: main
+*%ARGUMENTS:
+* argc, argv -- count and values of command-line arguments
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Main program
+***********************************************************************/
+int
+main(int argc, char *argv[])
+{
+    int opt;
+    int n;
+    unsigned int m[6];		/* MAC address in -e option */
+    unsigned int s;		/* Temporary to hold session */
+    FILE *pidfile;
+    unsigned int discoveryType, sessionType;
+    char const *options;
+
+    PPPoEConnection conn;
+
+#ifdef HAVE_N_HDLC
+    int disc = N_HDLC;
+    long flags;
+#endif
+
+    if (getuid() != geteuid() ||
+	getgid() != getegid()) {
+	IsSetID = 1;
+    }
+
+    /* Initialize connection info */
+    memset(&conn, 0, sizeof(conn));
+    conn.discoverySocket = -1;
+    conn.sessionSocket = -1;
+    conn.discoveryTimeout = PADI_TIMEOUT;
+
+    /* For signal handler */
+    Connection = &conn;
+
+    /* Initialize syslog */
+    openlog("pppoe", LOG_PID, LOG_DAEMON);
+
+#ifdef DEBUGGING_ENABLED
+    options = "I:VAT:D:hS:C:UW:sm:np:e:kdf:F:t:";
+#else
+    options = "I:VAT:hS:C:UW:sm:np:e:kdf:F:t:";
+#endif
+    while((opt = getopt(argc, argv, options)) != -1) {
+	switch(opt) {
+	case 't':
+	    if (sscanf(optarg, "%d", &conn.discoveryTimeout) != 1) {
+		fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n");
+		exit(EXIT_FAILURE);
+	    }
+	    if (conn.discoveryTimeout < 1) {
+		conn.discoveryTimeout = 1;
+	    }
+	    break;
+	case 'F':
+	    if (sscanf(optarg, "%d", &optFloodDiscovery) != 1) {
+		fprintf(stderr, "Illegal argument to -F: Should be -F numFloods\n");
+		exit(EXIT_FAILURE);
+	    }
+	    if (optFloodDiscovery < 1) optFloodDiscovery = 1;
+	    fprintf(stderr,
+		    "WARNING: DISCOVERY FLOOD IS MEANT FOR STRESS-TESTING\n"
+		    "A PPPOE SERVER WHICH YOU OWN.  DO NOT USE IT AGAINST\n"
+		    "A REAL ISP.  YOU HAVE 5 SECONDS TO ABORT.\n");
+	    sleep(5);
+	    break;
+	case 'f':
+	    if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) {
+		fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n");
+		exit(EXIT_FAILURE);
+	    }
+	    Eth_PPPOE_Discovery = (UINT16_t) discoveryType;
+	    Eth_PPPOE_Session   = (UINT16_t) sessionType;
+	    break;
+	case 'd':
+	    optSkipSession = 1;
+	    break;
+
+	case 'k':
+	    conn.killSession = 1;
+	    break;
+
+	case 'n':
+	    /* Do not even open a discovery socket -- used when invoked
+	       by pppoe-server */
+	    conn.noDiscoverySocket = 1;
+	    break;
+
+	case 'e':
+	    /* Existing session: "sess:xx:yy:zz:aa:bb:cc" where "sess" is
+	       session-ID, and xx:yy:zz:aa:bb:cc is MAC-address of peer */
+	    n = sscanf(optarg, "%u:%2x:%2x:%2x:%2x:%2x:%2x",
+		       &s, &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]);
+	    if (n != 7) {
+		fprintf(stderr, "Illegal argument to -e: Should be sess:xx:yy:zz:aa:bb:cc\n");
+		exit(EXIT_FAILURE);
+	    }
+
+	    /* Copy MAC address of peer */
+	    for (n=0; n<6; n++) {
+		conn.peerEth[n] = (unsigned char) m[n];
+	    }
+
+	    /* Convert session */
+	    conn.session = htons(s);
+
+	    /* Skip discovery phase! */
+	    conn.skipDiscovery = 1;
+	    break;
+
+	case 'p':
+	    switchToRealID();
+	    pidfile = fopen(optarg, "w");
+	    if (pidfile) {
+		fprintf(pidfile, "%lu\n", (unsigned long) getpid());
+		fclose(pidfile);
+	    }
+	    switchToEffectiveID();
+	    break;
+	case 'S':
+	    SET_STRING(conn.serviceName, optarg);
+	    break;
+	case 'C':
+	    SET_STRING(conn.acName, optarg);
+	    break;
+	case 's':
+	    conn.synchronous = 1;
+	    break;
+	case 'U':
+	    if (conn.hostUniq) {
+		fprintf(stderr, "-U and -W are mutually-exclusive and may only be used once.\n");
+		exit(EXIT_FAILURE);
+	    }
+	    /* Allows for a 64-bit PID */
+	    conn.hostUniq = malloc(17);
+	    if (!conn.hostUniq) {
+		fprintf(stderr, "Out of memory.\n");
+		exit(EXIT_FAILURE);
+	    }
+	    sprintf(conn.hostUniq, "%lx", (unsigned long) getpid());
+	    break;
+	case 'W':
+	    if (conn.hostUniq) {
+		fprintf(stderr, "-U and -W are mutually-exclusive and may only be used once.\n");
+		exit(EXIT_FAILURE);
+	    }
+	    SET_STRING(conn.hostUniq, optarg);
+	    break;
+#ifdef DEBUGGING_ENABLED
+	case 'D':
+	    switchToRealID();
+	    conn.debugFile = fopen(optarg, "w");
+	    switchToEffectiveID();
+	    if (!conn.debugFile) {
+		fprintf(stderr, "Could not open %s: %s\n",
+			optarg, strerror(errno));
+		exit(EXIT_FAILURE);
+	    }
+	    fprintf(conn.debugFile, "rp-pppoe-%s\n", RP_VERSION);
+	    fflush(conn.debugFile);
+	    break;
+#endif
+	case 'T':
+	    optInactivityTimeout = (int) strtol(optarg, NULL, 10);
+	    if (optInactivityTimeout < 0) {
+		optInactivityTimeout = 0;
+	    }
+	    break;
+	case 'm':
+	    optClampMSS = (int) strtol(optarg, NULL, 10);
+	    if (optClampMSS < 536) {
+		fprintf(stderr, "-m: %d is too low (min 536)\n", optClampMSS);
+		exit(EXIT_FAILURE);
+	    }
+	    if (optClampMSS > 1452) {
+		fprintf(stderr, "-m: %d is too high (max 1452)\n", optClampMSS);
+		exit(EXIT_FAILURE);
+	    }
+	    break;
+	case 'I':
+	    SET_STRING(conn.ifName, optarg);
+	    break;
+	case 'V':
+	    printf("RP-PPPoE Version %s\n", RP_VERSION);
+	    exit(EXIT_SUCCESS);
+	case 'A':
+	    conn.printACNames = 1;
+	    break;
+	case 'h':
+	    usage(argv[0]);
+	    break;
+	default:
+	    usage(argv[0]);
+	}
+    }
+
+    /* Pick a default interface name */
+    if (!conn.ifName) {
+#ifdef USE_BPF
+	fprintf(stderr, "No interface specified (-I option)\n");
+	exit(EXIT_FAILURE);
+#else
+	SET_STRING(conn.ifName, DEFAULT_IF);
+#endif
+    }
+
+    if (!conn.printACNames) {
+
+#ifdef HAVE_N_HDLC
+	if (conn.synchronous) {
+	    if (ioctl(0, TIOCSETD, &disc) < 0) {
+		printErr("Unable to set line discipline to N_HDLC.  Make sure your kernel supports the N_HDLC line discipline, or do not use the SYNCHRONOUS option.  Quitting.");
+		exit(EXIT_FAILURE);
+	    } else {
+		syslog(LOG_INFO,
+		       "Changed pty line discipline to N_HDLC for synchronous mode");
+	    }
+	    /* There is a bug in Linux's select which returns a descriptor
+	     * as readable if N_HDLC line discipline is on, even if
+	     * it isn't really readable.  This return happens only when
+	     * select() times out.  To avoid blocking forever in read(),
+	     * make descriptor 0 non-blocking */
+	    flags = fcntl(0, F_GETFL);
+	    if (flags < 0) fatalSys("fcntl(F_GETFL)");
+	    if (fcntl(0, F_SETFL, (long) flags | O_NONBLOCK) < 0) {
+		fatalSys("fcntl(F_SETFL)");
+	    }
+	}
+#endif
+
+    }
+
+    if (optFloodDiscovery) {
+	for (n=0; n < optFloodDiscovery; n++) {
+	    if (conn.printACNames) {
+		fprintf(stderr, "Sending discovery flood %d\n", n+1);
+	    }
+            conn.discoverySocket =
+	        openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL);
+	    discovery(&conn);
+	    conn.discoveryState = STATE_SENT_PADI;
+	    close(conn.discoverySocket);
+	}
+	exit(EXIT_SUCCESS);
+    }
+
+    /* Open session socket before discovery phase, to avoid losing session */
+    /* packets sent by peer just after PADS packet (noted on some Cisco    */
+    /* server equipment).                                                  */
+    /* Opening this socket just before waitForPADS in the discovery()      */
+    /* function would be more appropriate, but it would mess-up the code   */
+    if (!optSkipSession)
+        conn.sessionSocket = openInterface(conn.ifName, Eth_PPPOE_Session, conn.myEth, NULL);
+
+    /* Skip discovery and don't open discovery socket? */
+    if (conn.skipDiscovery && conn.noDiscoverySocket) {
+	conn.discoveryState = STATE_SESSION;
+    } else {
+        conn.discoverySocket =
+	    openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL);
+        discovery(&conn);
+    }
+    if (optSkipSession) {
+	printf("%u:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       ntohs(conn.session),
+	       conn.peerEth[0],
+	       conn.peerEth[1],
+	       conn.peerEth[2],
+	       conn.peerEth[3],
+	       conn.peerEth[4],
+	       conn.peerEth[5]);
+	exit(EXIT_SUCCESS);
+    }
+
+    /* Set signal handlers: send PADT on HUP; ignore TERM and INT */
+    signal(SIGTERM, SIG_IGN);
+    signal(SIGINT, SIG_IGN);
+    signal(SIGHUP, sigPADT);
+    session(&conn);
+    return 0;
+}
+
+/**********************************************************************
+*%FUNCTION: fatalSys
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message plus the errno value to stderr and syslog and exits.
+***********************************************************************/
+void
+fatalSys(char const *str)
+{
+    char buf[1024];
+    sprintf(buf, "%.256s: Session %d: %.256s",
+	    str, (int) ntohs(Connection->session), strerror(errno));
+    printErr(buf);
+    sendPADTf(Connection, "RP-PPPoE: System call error: %s",
+	      strerror(errno));
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: sysErr
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message plus the errno value to syslog.
+***********************************************************************/
+void
+sysErr(char const *str)
+{
+    char buf[1024];
+    sprintf(buf, "%.256s: %.256s", str, strerror(errno));
+    printErr(buf);
+}
+
+/**********************************************************************
+*%FUNCTION: rp_fatal
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message to stderr and syslog and exits.
+***********************************************************************/
+void
+rp_fatal(char const *str)
+{
+    printErr(str);
+    sendPADTf(Connection, "RP-PPPoE: Session %d: %.256s",
+	      (int) ntohs(Connection->session), str);
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: asyncReadFromEth
+*%ARGUMENTS:
+* conn -- PPPoE connection info
+* sock -- Ethernet socket
+* clampMss -- if non-zero, do MSS-clamping
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Reads a packet from the Ethernet interface and sends it to async PPP
+* device.
+***********************************************************************/
+void
+asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss)
+{
+    PPPoEPacket packet;
+    int len;
+    int plen;
+    int i;
+    unsigned char pppBuf[4096];
+    unsigned char *ptr = pppBuf;
+    unsigned char c;
+    UINT16_t fcs;
+    unsigned char header[2] = {FRAME_ADDR, FRAME_CTRL};
+    unsigned char tail[2];
+#ifdef USE_BPF
+    int type;
+#endif
+
+    if (receivePacket(sock, &packet, &len) < 0) {
+	return;
+    }
+
+    /* Check length */
+    if (ntohs(packet.length) + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	       (unsigned int) ntohs(packet.length));
+	return;
+    }
+#ifdef DEBUGGING_ENABLED
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, &packet, "RCVD");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+#endif
+
+#ifdef USE_BPF
+    /* Make sure this is a session packet before processing further */
+    type = etherType(&packet);
+    if (type == Eth_PPPOE_Discovery) {
+	sessionDiscoveryPacket(&packet);
+    } else if (type != Eth_PPPOE_Session) {
+	return;
+    }
+#endif
+
+    /* Sanity check */
+    if (packet.code != CODE_SESS) {
+	syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code);
+	return;
+    }
+    if (PPPOE_VER(packet.vertype) != 1) {
+	syslog(LOG_ERR, "Unexpected packet version %d", PPPOE_VER(packet.vertype));
+	return;
+    }
+    if (PPPOE_TYPE(packet.vertype) != 1) {
+	syslog(LOG_ERR, "Unexpected packet type %d", PPPOE_TYPE(packet.vertype));
+	return;
+    }
+    if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
+	return;
+    }
+    if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
+	/* Not for us -- must be another session.  This is not an error,
+	   so don't log anything.  */
+	return;
+    }
+
+    if (packet.session != conn->session) {
+	/* Not for us -- must be another session.  This is not an error,
+	   so don't log anything.  */
+	return;
+    }
+    plen = ntohs(packet.length);
+    if (plen + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus length field in session packet %d (%d)",
+	       (int) plen, (int) len);
+	return;
+    }
+
+    /* Clamp MSS */
+    if (clampMss) {
+	clampMSS(&packet, "incoming", clampMss);
+    }
+
+    /* Compute FCS */
+    fcs = pppFCS16(PPPINITFCS16, header, 2);
+    fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff;
+    tail[0] = fcs & 0x00ff;
+    tail[1] = (fcs >> 8) & 0x00ff;
+
+    /* Build a buffer to send to PPP */
+    *ptr++ = FRAME_FLAG;
+    *ptr++ = FRAME_ADDR;
+    *ptr++ = FRAME_ESC;
+    *ptr++ = FRAME_CTRL ^ FRAME_ENC;
+
+    for (i=0; i<plen; i++) {
+	c = packet.payload[i];
+	if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) {
+	    *ptr++ = FRAME_ESC;
+	    *ptr++ = c ^ FRAME_ENC;
+	} else {
+	    *ptr++ = c;
+	}
+    }
+    for (i=0; i<2; i++) {
+	c = tail[i];
+	if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) {
+	    *ptr++ = FRAME_ESC;
+	    *ptr++ = c ^ FRAME_ENC;
+	} else {
+	    *ptr++ = c;
+	}
+    }
+    *ptr++ = FRAME_FLAG;
+
+    /* Ship it out */
+    if (write(1, pppBuf, (ptr-pppBuf)) < 0) {
+	fatalSys("asyncReadFromEth: write");
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: syncReadFromEth
+*%ARGUMENTS:
+* conn -- PPPoE connection info
+* sock -- Ethernet socket
+* clampMss -- if true, clamp MSS.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Reads a packet from the Ethernet interface and sends it to sync PPP
+* device.
+***********************************************************************/
+void
+syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss)
+{
+    PPPoEPacket packet;
+    int len;
+    int plen;
+    struct iovec vec[2];
+    unsigned char dummy[2];
+#ifdef USE_BPF
+    int type;
+#endif
+
+    if (receivePacket(sock, &packet, &len) < 0) {
+	return;
+    }
+
+    /* Check length */
+    if (ntohs(packet.length) + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	       (unsigned int) ntohs(packet.length));
+	return;
+    }
+#ifdef DEBUGGING_ENABLED
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, &packet, "RCVD");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+#endif
+
+#ifdef USE_BPF
+    /* Make sure this is a session packet before processing further */
+    type = etherType(&packet);
+    if (type == Eth_PPPOE_Discovery) {
+	sessionDiscoveryPacket(&packet);
+    } else if (type != Eth_PPPOE_Session) {
+	return;
+    }
+#endif
+
+    /* Sanity check */
+    if (packet.code != CODE_SESS) {
+	syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code);
+	return;
+    }
+    if (PPPOE_VER(packet.vertype) != 1) {
+	syslog(LOG_ERR, "Unexpected packet version %d", PPPOE_VER(packet.vertype));
+	return;
+    }
+    if (PPPOE_TYPE(packet.vertype) != 1) {
+	syslog(LOG_ERR, "Unexpected packet type %d", PPPOE_TYPE(packet.vertype));
+	return;
+    }
+    if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) {
+	/* Not for us -- must be another session.  This is not an error,
+	   so don't log anything.  */
+	return;
+    }
+    if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) {
+	/* Not for us -- must be another session.  This is not an error,
+	   so don't log anything.  */
+	return;
+    }
+    if (packet.session != conn->session) {
+	/* Not for us -- must be another session.  This is not an error,
+	   so don't log anything.  */
+	return;
+    }
+    plen = ntohs(packet.length);
+    if (plen + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus length field in session packet %d (%d)",
+	       (int) plen, (int) len);
+	return;
+    }
+
+    /* Clamp MSS */
+    if (clampMss) {
+	clampMSS(&packet, "incoming", clampMss);
+    }
+
+    /* Ship it out */
+    vec[0].iov_base = (void *) dummy;
+    dummy[0] = FRAME_ADDR;
+    dummy[1] = FRAME_CTRL;
+    vec[0].iov_len = 2;
+    vec[1].iov_base = (void *) packet.payload;
+    vec[1].iov_len = plen;
+
+    if (writev(1, vec, 2) < 0) {
+	fatalSys("syncReadFromEth: write");
+    }
+}
Index: create-3.14-man-pages-patch/rp-pppoe-3.14-new/src
===================================================================
--- create-3.14-man-pages-patch/rp-pppoe-3.14-new/src	(nonexistent)
+++ create-3.14-man-pages-patch/rp-pppoe-3.14-new/src	(revision 5)

Property changes on: create-3.14-man-pages-patch/rp-pppoe-3.14-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-man-pages-patch/rp-pppoe-3.14-new
===================================================================
--- create-3.14-man-pages-patch/rp-pppoe-3.14-new	(nonexistent)
+++ create-3.14-man-pages-patch/rp-pppoe-3.14-new	(revision 5)

Property changes on: create-3.14-man-pages-patch/rp-pppoe-3.14-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-man-pages-patch
===================================================================
--- create-3.14-man-pages-patch	(nonexistent)
+++ create-3.14-man-pages-patch	(revision 5)

Property changes on: create-3.14-man-pages-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-plugin-patch/create.patch.sh
===================================================================
--- create-3.14-plugin-patch/create.patch.sh	(nonexistent)
+++ create-3.14-plugin-patch/create.patch.sh	(revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=3.14
+
+tar --files-from=file.list -xzvf ../rp-pppoe-$VERSION.tar.gz
+mv rp-pppoe-$VERSION rp-pppoe-$VERSION-orig
+
+cp -rf ./rp-pppoe-$VERSION-new ./rp-pppoe-$VERSION
+
+diff --unified -Nr  rp-pppoe-$VERSION-orig  rp-pppoe-$VERSION > rp-pppoe-$VERSION-plugin.patch
+
+mv rp-pppoe-$VERSION-plugin.patch ../patches
+
+rm -rf ./rp-pppoe-$VERSION
+rm -rf ./rp-pppoe-$VERSION-orig

Property changes on: create-3.14-plugin-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-3.14-plugin-patch/file.list
===================================================================
--- create-3.14-plugin-patch/file.list	(nonexistent)
+++ create-3.14-plugin-patch/file.list	(revision 5)
@@ -0,0 +1 @@
+rp-pppoe-3.14/src/configure.in
Index: create-3.14-plugin-patch/rp-pppoe-3.14-new/src/configure.in
===================================================================
--- create-3.14-plugin-patch/rp-pppoe-3.14-new/src/configure.in	(nonexistent)
+++ create-3.14-plugin-patch/rp-pppoe-3.14-new/src/configure.in	(revision 5)
@@ -0,0 +1,299 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl LIC: GPL
+AC_INIT(pppoe.c)
+
+dnl pppd directory for kernel-mode PPPoE
+PPPD_DIR=ppp-2.4.1.pppoe2
+
+AC_CONFIG_HEADER(config.h)
+
+AC_PREFIX_DEFAULT(/usr)
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_RANLIB
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(fcntl.h sys/dlpi.h sys/ioctl.h sys/time.h syslog.h unistd.h net/if_arp.h netinet/if_ether.h getopt.h sys/uio.h sys/param.h fcntl.h net/bpf.h netpacket/packet.h net/ethernet.h asm/types.h linux/if_packet.h linux/if_ether.h sys/socket.h net/if.h net/if_dl.h net/if_ether.h net/if_types.h netinet/if_ether.h net/if_types.h net/if_dl.h)
+AC_CHECK_HEADERS(linux/if.h, [], [], [#include<sys/socket.h>])
+AC_CHECK_HEADERS(linux/if_pppox.h, [], [],
+[
+#include<sys/socket.h>
+#include<net/ethernet.h>
+#include<linux/if.h>
+#include<linux/in.h>
+#include<linux/in6.h>
+])
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_PID_T
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+dnl Check for sockaddr_ll
+AC_MSG_CHECKING(for struct sockaddr_ll)
+AC_TRY_COMPILE([#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+], [struct sockaddr_ll sa;],
+ac_cv_struct_sockaddr_ll=yes,
+ac_cv_struct_sockaddr_ll=no)
+AC_MSG_RESULT($ac_cv_struct_sockaddr_ll)
+
+if test "$ac_cv_struct_sockaddr_ll" = yes ; then
+AC_DEFINE(HAVE_STRUCT_SOCKADDR_LL)
+fi
+
+dnl Check for N_HDLC line discipline
+AC_MSG_CHECKING(for N_HDLC line discipline)
+AC_TRY_COMPILE([
+#include <pty.h>
+],
+	[int x = N_HDLC;],
+	ac_cv_n_hdlc=yes,
+	ac_cv_n_hdlc=no)
+AC_MSG_RESULT($ac_cv_n_hdlc)
+if test "$ac_cv_n_hdlc" = yes ; then
+AC_DEFINE(HAVE_N_HDLC)
+fi
+
+AC_ARG_ENABLE(plugin, [  --enable-plugin=pppd_src_path   build pppd plugin], ac_cv_pluginpath=$enableval, ac_cv_pluginpath=no)
+AC_ARG_ENABLE(debugging, [  --disable-debugging             disable debugging code], ac_cv_debugging=$enableval, ac_cv_debugging=yes)
+dnl If we were given "--enable-plugin" without a path, take a stab at where
+dnl the pppd source code might be.
+
+if test "$ac_cv_pluginpath" = "yes" ; then
+    for i in /usr/include /usr/local/include ; do
+	if test -r $i/pppd/pppd.h; then
+	    ac_cv_pluginpath=$i
+	fi
+    done
+fi
+
+if test "$ac_cv_pluginpath" = "yes" ; then
+    echo "*** Could not fined pppd/pppd.h anywhere... not building plugin"
+    ac_cv_pluginpath=no
+fi
+
+AC_ARG_ENABLE(licenses, [  --enable-licenses=lic_path      build commercial version], ac_cv_licpath=$enableval, ac_cv_licpath=no)
+
+LIC_INCDIR=""
+LIC_LIBDIR=""
+LIC_LIB=""
+LIC_DEFINE=""
+LIC_MAKEFILE_INCLUDE=""
+PPPOE_SERVER_DEPS=""
+if test "$ac_cv_licpath" != "no" ; then
+	LIC_INCDIR="-I$ac_cv_licpath"
+	LIC_LIBDIR="-L$ac_cv_licpath -Llicensed-only"
+	if test -d "../../extra-libs" ; then
+	    LIC_LIB="-L../../extra-libs -ltwofish -llicensed-only -ltcl -ldl"
+	else
+	    LIC_LIB="-ltwofish -llicensed-only -ltcl -ldl"
+        fi
+	PPPOE_SERVER_DEPS="licensed-only/liblicensed-only.a ../../SERVPOET-VERSION"
+	LIC_DEFINE="-DHAVE_LICENSE=1 -DSERVPOET_VERSION='\"\$(SERVPOET_VERSION)\"'"
+	LIC_MAKEFILE_INCLUDE="include ../../SERVPOET-VERSION"
+fi
+
+dnl Determine whether or not to build Linux pppd plugin
+LINUX_KERNELMODE_PLUGIN=""
+PPPD_INCDIR=""
+if test "$ac_cv_header_linux_if_pppox_h" = yes ; then
+	if test "$ac_cv_pluginpath" != no ; then
+		LINUX_KERNELMODE_PLUGIN=rp-pppoe.so
+		AC_DEFINE(HAVE_LINUX_KERNEL_PPPOE)
+		PPPD_INCDIR=$ac_cv_pluginpath
+	fi
+fi
+
+if test "$PPPD_INCDIR" = "" ; then
+   PPPD_INCDIR=/usr/include
+fi
+
+if test "$ac_cv_debugging" = "yes" ; then
+   AC_DEFINE(DEBUGGING_ENABLED)
+fi
+
+AC_SUBST(LINUX_KERNELMODE_PLUGIN)
+AC_SUBST(PPPD_INCDIR)
+AC_SUBST(PPPD_H)
+
+dnl Determine whether or not to build PPPoE relay
+PPPOE_RELAY=""
+if test "`uname -s`" = "Linux" ; then
+	PPPOE_RELAY=pppoe-relay
+fi
+AC_SUBST(PPPOE_RELAY)
+
+dnl Checks for library functions.
+AC_FUNC_MEMCMP
+AC_FUNC_SETVBUF_REVERSED
+AC_TYPE_SIGNAL
+AC_CHECK_FUNCS(select socket strerror strtol strlcpy)
+AC_PROG_INSTALL
+
+dnl Integer sizes
+AC_CHECK_SIZEOF(unsigned short)
+AC_CHECK_SIZEOF(unsigned int)
+AC_CHECK_SIZEOF(unsigned long)
+
+dnl Check for location of pppd
+AC_PATH_PROG(PPPD, pppd, NOTFOUND, $PATH:/sbin:/usr/sbin:/usr/local/sbin)
+
+dnl Check for setsid (probably Linux-specific)
+AC_PATH_PROG(SETSID, setsid, "", $PATH:/sbin:/usr/sbin:/usr/local/sbin)
+
+dnl Check for an "id" which accepts "-u" option -- hack for Solaris.
+AC_PATH_PROG(ID, id, "", /usr/xpg4/bin:$PATH)
+
+dnl Check for Linux-specific kernel support for PPPoE
+AC_MSG_CHECKING(for Linux 2.4.X kernel-mode PPPoE support)
+AC_CACHE_VAL(ac_cv_linux_kernel_pppoe,[
+if test "`uname -s`" = "Linux" ; then
+if test $cross_compiling = "no"; then
+
+dnl Do a bunch of modprobes.  Can't hurt; might help.
+modprobe ppp_generic > /dev/null 2>&1
+modprobe ppp_async > /dev/null 2>&1
+modprobe n_hdlc > /dev/null 2>&1
+modprobe ppp_synctty > /dev/null 2>&1
+modprobe pppoe > /dev/null 2>&1
+fi
+AC_TRY_RUN([#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if_pppox.h>
+int main()
+{
+	if (socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE) >= 0) return 0; else return 1;
+}
+],
+ac_cv_linux_kernel_pppoe=yes, ac_cv_linux_kernel_pppoe=no, [ac_cv_linux_kernel_pppoe=no; echo "cross-compiling, default: "])
+else
+	ac_cv_linux_kernel_pppoe=no
+fi
+])
+
+AC_MSG_RESULT($ac_cv_linux_kernel_pppoe)
+
+if test "$ac_cv_linux_kernel_pppoe" != yes ; then
+   if test "$LINUX_KERNELMODE_PLUGIN" = rp-pppoe.so; then
+      echo "*** Your kernel does not appear to have built-in PPPoE support,"
+      echo "*** but I will build the kernel-mode plugin anyway."
+   fi
+fi
+
+dnl GCC warning level
+if test "$GCC" = yes; then
+	CFLAGS="$CFLAGS -fno-strict-aliasing -Wall -Wstrict-prototypes"
+fi
+
+dnl If we couldn't find pppd, die
+if test "$PPPD" = "NOTFOUND"; then
+        AC_MSG_WARN([*** Oops!  I couldn't find pppd, the PPP daemon anywhere.])
+	AC_MSG_WARN([*** You must install pppd, version 2.3.10 or later.])
+	AC_MSG_WARN([*** I will keep going, but it may not work.])
+	PPPD=pppd
+fi
+
+dnl Figure out pppd version.  2.3.7 to 2.3.9 -- issue warning.  Less than
+dnl 2.3.7 -- stop
+
+PPPD_VERSION=`$PPPD --version 2>&1 | awk ' /version/ {print $NF}'`
+
+case "$PPPD_VERSION" in
+1.*|2.0.*|2.1.*|2.2.*|2.3.0|2.3.1|2.3.2|2.3.3|2.3.4|2.3.5|2.3.6)
+	AC_MSG_WARN([*** Oops! Your version of pppd is $PPPD_VERSION, which is too old.])
+	AC_MSG_WARN([*** You need at least 2.3.7 (2.3.10 or newer recommended.])
+	AC_MSG_WARN([*** I will keep going, but it may not work.])
+	;;
+
+2.3.7|2.3.8|2.3.9)
+	AC_MSG_WARN([*** Warning.  Your version of pppd is $PPPD_VERSION.  You will])
+	AC_MSG_WARN([*** not be able to use connect-on-demand.  Upgrade to pppd])
+	AC_MSG_WARN([*** 2.3.10 or newer if you need connect-on-demand.])
+	;;
+
+2*|3*|4*|5*|6*|7*|8*|9*)
+	;;
+
+*)
+	AC_MSG_WARN([*** Oops.  I cannot figure out what version of pppd you have.])
+	AC_MSG_WARN([*** All I got back was '$PPPD_VERSION'])
+	AC_MSG_WARN([*** I will keep going, but it may not work.])
+	;;
+esac
+
+# Sigh... got to fix this up for tcl
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Fully resolve WRAPPER for Tcl script.
+WRAPPER=${sbindir}/pppoe-wrapper
+eval "WRAPPER=${WRAPPER}"
+eval "WRAPPER=${WRAPPER}"
+AC_SUBST(WRAPPER)
+
+# Determine what targets to build
+TARGETS="pppoe pppoe-server"
+
+# pppoe-sniff is built only on Linux and Solaris
+if test "$ac_cv_header_linux_if_packet_h" = "yes" -o "$ac_cv_header_sys_dlpi_h" = "yes" ; then
+	TARGETS="$TARGETS pppoe-sniff"
+fi
+
+# pppoe-relay is built only on Linux
+if test "$ac_cv_header_linux_if_packet_h" = "yes" ; then
+	TARGETS="$TARGETS pppoe-relay"
+fi
+
+# plugin is built only if we have kernel support
+if test -n "$LINUX_KERNELMODE_PLUGIN" ; then
+	TARGETS="$TARGETS $LINUX_KERNELMODE_PLUGIN"
+	mkdir plugin > /dev/null 2>&1
+fi
+
+EXTRACONFIGS=""
+# Licensed stuff only for commercial distro (for now)
+if test -n "$LIC_DEFINE" ; then
+	TARGETS="licensed-only $TARGETS"
+	EXTRACONFIGS="$EXTRACONFIGS licensed-only/Makefile"
+fi
+
+RDYNAMIC=""
+# L2TP is only in commercial distro (for now)
+if test -n "$LIC_DEFINE" ; then
+	TARGETS="l2tp $TARGETS"
+	EXTRACONFIGS="$EXTRACONFIGS l2tp/Makefile l2tp/handlers/Makefile"
+	LIC_DEFINE="$LIC_DEFINE -DHAVE_L2TP=1"
+	LIC_LIB="$LIC_LIB -Ll2tp -ll2tp"
+	PPPOE_SERVER_DEPS="$PPPOE_SERVER_DEPS l2tp/libl2tp.a"
+	RDYNAMIC="-rdynamic"
+fi
+
+LIBEVENT=../libevent
+AC_SUBST(TARGETS)
+AC_SUBST(LIC_INCDIR)
+AC_SUBST(LIC_LIBDIR)
+AC_SUBST(LIC_LIB)
+AC_SUBST(LIC_MAKEFILE_INCLUDE)
+AC_SUBST(LIC_DEFINE)
+AC_SUBST(PPPOE_SERVER_DEPS)
+AC_SUBST(RDYNAMIC)
+AC_SUBST(LIBEVENT)
+AC_SUBST(LDFLAGS)
+
+datadir_evaluated=`eval echo $datadir`
+AC_SUBST(datadir_evaluated)
+
+AC_OUTPUT(Makefile libevent/Makefile ../scripts/pppoe-connect ../scripts/pppoe-start ../scripts/pppoe-stop ../scripts/pppoe-init ../scripts/pppoe-init-suse ../scripts/pppoe-setup ../gui/Makefile ../gui/tkpppoe $EXTRACONFIGS)
+
+AC_MSG_RESULT([On this platform, the following targets will be built:])
+AC_MSG_RESULT([$TARGETS])
+AC_MSG_RESULT([Type 'make' to compile the software.])
Index: create-3.14-plugin-patch/rp-pppoe-3.14-new/src
===================================================================
--- create-3.14-plugin-patch/rp-pppoe-3.14-new/src	(nonexistent)
+++ create-3.14-plugin-patch/rp-pppoe-3.14-new/src	(revision 5)

Property changes on: create-3.14-plugin-patch/rp-pppoe-3.14-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-plugin-patch/rp-pppoe-3.14-new
===================================================================
--- create-3.14-plugin-patch/rp-pppoe-3.14-new	(nonexistent)
+++ create-3.14-plugin-patch/rp-pppoe-3.14-new	(revision 5)

Property changes on: create-3.14-plugin-patch/rp-pppoe-3.14-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-plugin-patch
===================================================================
--- create-3.14-plugin-patch	(nonexistent)
+++ create-3.14-plugin-patch	(revision 5)

Property changes on: create-3.14-plugin-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-plugin-path-patch/create.patch.sh
===================================================================
--- create-3.14-plugin-path-patch/create.patch.sh	(nonexistent)
+++ create-3.14-plugin-path-patch/create.patch.sh	(revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=3.14
+
+tar --files-from=file.list -xzvf ../rp-pppoe-$VERSION.tar.gz
+mv rp-pppoe-$VERSION rp-pppoe-$VERSION-orig
+
+cp -rf ./rp-pppoe-$VERSION-new ./rp-pppoe-$VERSION
+
+diff --unified -Nr  rp-pppoe-$VERSION-orig  rp-pppoe-$VERSION > rp-pppoe-$VERSION-plugin-path.patch
+
+mv rp-pppoe-$VERSION-plugin-path.patch ../patches
+
+rm -rf ./rp-pppoe-$VERSION
+rm -rf ./rp-pppoe-$VERSION-orig

Property changes on: create-3.14-plugin-path-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-3.14-plugin-path-patch/file.list
===================================================================
--- create-3.14-plugin-path-patch/file.list	(nonexistent)
+++ create-3.14-plugin-path-patch/file.list	(revision 5)
@@ -0,0 +1 @@
+rp-pppoe-3.14/src/pppoe-server.c
Index: create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src/pppoe-server.c
===================================================================
--- create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src/pppoe-server.c	(nonexistent)
+++ create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src/pppoe-server.c	(revision 5)
@@ -0,0 +1,2397 @@
+/***********************************************************************
+*
+* pppoe-server.c
+*
+* Implementation of a user-space PPPoE server
+*
+* Copyright (C) 2000-2012 Roaring Penguin Software Inc.
+* Copyright (C) 2018-2020 Dianne Skoll
+*
+* This program may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+* $Id$
+*
+* LIC: GPL
+*
+***********************************************************************/
+
+#include "config.h"
+
+#include <sys/socket.h>
+#if defined(HAVE_LINUX_IF_H)
+#include <linux/if.h>
+#elif defined(HAVE_NET_IF_H)
+#include <net/if.h>
+#endif
+
+#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H)
+#define _POSIX_SOURCE 1 /* For sigaction defines */
+#endif
+
+#include "pppoe-server.h"
+#include "md5.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <time.h>
+
+#include <signal.h>
+
+#ifdef HAVE_LICENSE
+#include "license.h"
+#include "licensed-only/servfuncs.h"
+static struct License const *ServerLicense;
+static struct License const *ClusterLicense;
+#else
+#define control_session_started(x) (void) 0
+#define control_session_terminated(x) (void) 0
+#define control_exit() (void) 0
+#define realpeerip peerip
+#endif
+
+#ifdef HAVE_L2TP
+extern PppoeSessionFunctionTable L2TPSessionFunctionTable;
+extern void pppoe_to_l2tp_add_interface(EventSelector *es,
+					Interface *interface);
+#endif
+
+static void InterfaceHandler(EventSelector *es,
+			int fd, unsigned int flags, void *data);
+static void startPPPD(ClientSession *sess);
+static void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest,
+			  int errorTag, char *errorMsg);
+
+#define CHECK_ROOM(cursor, start, len) \
+do {\
+    if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \
+	syslog(LOG_ERR, "Would create too-long packet"); \
+	return; \
+    } \
+} while(0)
+
+static void PppoeStopSession(ClientSession *ses, char const *reason);
+static int PppoeSessionIsActive(ClientSession *ses);
+
+/* Service-Names we advertise */
+#define MAX_SERVICE_NAMES 64
+static int NumServiceNames = 0;
+static char const *ServiceNames[MAX_SERVICE_NAMES];
+
+PppoeSessionFunctionTable DefaultSessionFunctionTable = {
+    PppoeStopSession,
+    PppoeSessionIsActive,
+    NULL
+};
+
+/* An array of client sessions */
+ClientSession *Sessions = NULL;
+ClientSession *FreeSessions = NULL;
+ClientSession *LastFreeSession = NULL;
+ClientSession *BusySessions = NULL;
+
+/* Interfaces we're listening on */
+Interface interfaces[MAX_INTERFACES];
+int NumInterfaces = 0;
+
+/* The number of session slots */
+size_t NumSessionSlots;
+
+/* Maximum number of sessions per MAC address */
+int MaxSessionsPerMac;
+
+/* Number of active sessions */
+size_t NumActiveSessions = 0;
+
+/* Offset of first session */
+size_t SessOffset = 0;
+
+/* Event Selector */
+EventSelector *event_selector;
+
+/* Use Linux kernel-mode PPPoE? */
+static int UseLinuxKernelModePPPoE = 0;
+
+/* Requested max_ppp_payload */
+static UINT16_t max_ppp_payload = 0;
+
+/* File with PPPD options */
+static char *pppoptfile = NULL;
+
+static char *pppd_path = PPPD_PATH;
+static char *pppoe_path = PPPOE_PATH;
+
+static char *motd_string = NULL;
+static char *hurl_string = NULL;
+
+static int Debug = 0;
+static int CheckPoolSyntax = 0;
+
+/* Synchronous mode */
+static int Synchronous = 0;
+
+/* Ignore PADI if no free sessions */
+static int IgnorePADIIfNoFreeSessions = 0;
+
+static int KidPipe[2] = {-1, -1};
+static int LockFD = -1;
+
+/* Random seed for cookie generation */
+#define SEED_LEN 16
+#define MD5_LEN 16
+#define COOKIE_LEN (MD5_LEN + sizeof(pid_t)) /* Cookie is 16-byte MD5 + PID of server */
+
+static unsigned char CookieSeed[SEED_LEN];
+
+#define MAXLINE 512
+
+/* Default interface if no -I option given */
+#define DEFAULT_IF "eth0"
+
+/* Access concentrator name */
+char *ACName = NULL;
+
+/* Options to pass to pppoe process */
+char PppoeOptions[SMALLBUF] = "";
+
+/* Our local IP address */
+unsigned char LocalIP[IPV4ALEN] = {10, 0, 0, 1}; /* Counter optionally STARTS here */
+unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */
+
+/* Do we increment local IP for each connection? */
+int IncrLocalIP = 0;
+
+/* Do we randomize session numbers? */
+int RandomizeSessionNumbers = 0;
+
+/* Do we pass the "unit" option to pppd?  (2.4 or greater) */
+int PassUnitOptionToPPPD = 0;
+
+static PPPoETag hostUniq;
+static PPPoETag relayId;
+static PPPoETag receivedCookie;
+static PPPoETag requestedService;
+
+#define HOSTNAMELEN 256
+
+static int
+count_sessions_from_mac(unsigned char *eth)
+{
+    int n=0;
+    ClientSession *s = BusySessions;
+    while(s) {
+	if (!memcmp(eth, s->eth, ETH_ALEN)) n++;
+	s = s->next;
+    }
+    return n;
+}
+
+/**********************************************************************
+*%FUNCTION: childHandler
+*%ARGUMENTS:
+* pid -- pid of child
+* status -- exit status
+* ses -- which session terminated
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Called synchronously when a child dies.  Remove from busy list.
+***********************************************************************/
+static void
+childHandler(pid_t pid, int status, void *s)
+{
+    ClientSession *session = s;
+
+    /* Temporary structure for sending PADT's. */
+    PPPoEConnection conn;
+
+#ifdef HAVE_L2TP
+    /* We're acting as LAC, so when child exits, become a PPPoE <-> L2TP
+       relay */
+    if (session->flags & FLAG_ACT_AS_LAC) {
+	syslog(LOG_INFO, "Session %u for client "
+	       "%02x:%02x:%02x:%02x:%02x:%02x handed off to LNS %s",
+	       (unsigned int) ntohs(session->sess),
+	       session->eth[0], session->eth[1], session->eth[2],
+	       session->eth[3], session->eth[4], session->eth[5],
+	       inet_ntoa(session->tunnel_endpoint.sin_addr));
+	session->pid = 0;
+	session->funcs = &L2TPSessionFunctionTable;
+	return;
+    }
+#endif
+
+    memset(&conn, 0, sizeof(conn));
+    conn.hostUniq = NULL;
+
+    syslog(LOG_INFO,
+	   "Session %u closed for client "
+	   "%02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s",
+	   (unsigned int) ntohs(session->sess),
+	   session->eth[0], session->eth[1], session->eth[2],
+	   session->eth[3], session->eth[4], session->eth[5],
+	   (int) session->realpeerip[0], (int) session->realpeerip[1],
+	   (int) session->realpeerip[2], (int) session->realpeerip[3],
+	   session->ethif->name);
+    memcpy(conn.myEth, session->ethif->mac, ETH_ALEN);
+    conn.discoverySocket = session->ethif->sock;
+    conn.session = session->sess;
+    memcpy(conn.peerEth, session->eth, ETH_ALEN);
+    if (!(session->flags & FLAG_SENT_PADT)) {
+	if (session->flags & FLAG_RECVD_PADT) {
+	    sendPADT(&conn, "RP-PPPoE: Received PADT from peer");
+	} else {
+	    sendPADT(&conn, "RP-PPPoE: Child pppd process terminated");
+	}
+	session->flags |= FLAG_SENT_PADT;
+    }
+
+    session->serviceName = "";
+    control_session_terminated(session);
+    if (pppoe_free_session(session) < 0) {
+	return;
+    }
+
+}
+
+/**********************************************************************
+*%FUNCTION: incrementIPAddress (static)
+*%ARGUMENTS:
+* addr -- a 4-byte array representing IP address
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Increments addr in-place
+***********************************************************************/
+static void
+incrementIPAddress(unsigned char ip[IPV4ALEN])
+{
+    ip[3]++;
+    if (!ip[3]) {
+	ip[2]++;
+	if (!ip[2]) {
+	    ip[1]++;
+	    if (!ip[1]) {
+		ip[0]++;
+	    }
+	}
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: killAllSessions
+*%ARGUMENTS:
+* None
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Kills all pppd processes (and hence all PPPoE sessions)
+***********************************************************************/
+void
+killAllSessions(void)
+{
+    ClientSession *sess = BusySessions;
+    while(sess) {
+	sess->funcs->stop(sess, "Shutting Down");
+	sess = sess->next;
+    }
+#ifdef HAVE_L2TP
+    pppoe_close_l2tp_tunnels();
+#endif
+}
+
+/**********************************************************************
+*%FUNCTION: parseAddressPool
+*%ARGUMENTS:
+* fname -- name of file containing IP address pool.
+* install -- if true, install IP addresses in sessions.
+*%RETURNS:
+* Number of valid IP addresses found.
+*%DESCRIPTION:
+* Reads a list of IP addresses from a file.
+***********************************************************************/
+static int
+parseAddressPool(char const *fname, int install)
+{
+    FILE *fp = fopen(fname, "r");
+    int numAddrs = 0;
+    unsigned int a, b, c, d;
+    unsigned int e, f, g, h;
+    char line[MAXLINE];
+
+    if (!fp) {
+	sysErr("Cannot open address pool file");
+	exit(1);
+    }
+
+    while (!feof(fp)) {
+	if (!fgets(line, MAXLINE, fp)) {
+	    break;
+	}
+	if ((sscanf(line, "%u.%u.%u.%u:%u.%u.%u.%u",
+		    &a, &b, &c, &d, &e, &f, &g, &h) == 8) &&
+	    a < 256 && b < 256 && c < 256 && d < 256 &&
+	    e < 256 && f < 256 && g < 256 && h < 256) {
+
+	    /* Both specified (local:remote) */
+	    if (install) {
+		Sessions[numAddrs].myip[0] = (unsigned char) a;
+		Sessions[numAddrs].myip[1] = (unsigned char) b;
+		Sessions[numAddrs].myip[2] = (unsigned char) c;
+		Sessions[numAddrs].myip[3] = (unsigned char) d;
+		Sessions[numAddrs].peerip[0] = (unsigned char) e;
+		Sessions[numAddrs].peerip[1] = (unsigned char) f;
+		Sessions[numAddrs].peerip[2] = (unsigned char) g;
+		Sessions[numAddrs].peerip[3] = (unsigned char) h;
+#ifdef HAVE_LICENSE
+		memcpy(Sessions[numAddrs].realpeerip,
+		       Sessions[numAddrs].peerip, IPV4ALEN);
+#endif
+	    }
+	    numAddrs++;
+	} else if ((sscanf(line, "%u.%u.%u.%u-%u", &a, &b, &c, &d, &e) == 5) &&
+		   a < 256 && b < 256 && c < 256 && d < 256 && e < 256) {
+	    /* Remote specied as a.b.c.d-e.  Example: 1.2.3.4-8 yields:
+	       1.2.3.4, 1.2.3.5, 1.2.3.6, 1.2.3.7, 1.2.3.8 */
+	    /* Swap d and e so that e >= d */
+	    if (e < d) {
+		f = d;
+		d = e;
+		e = f;
+	    }
+	    if (install) {
+		while (d <= e) {
+		    Sessions[numAddrs].peerip[0] = (unsigned char) a;
+		    Sessions[numAddrs].peerip[1] = (unsigned char) b;
+		    Sessions[numAddrs].peerip[2] = (unsigned char) c;
+		    Sessions[numAddrs].peerip[3] = (unsigned char) d;
+#ifdef HAVE_LICENSE
+		    memcpy(Sessions[numAddrs].realpeerip,
+			   Sessions[numAddrs].peerip, IPV4ALEN);
+#endif
+		d++;
+		numAddrs++;
+		}
+	    } else {
+		numAddrs += (e-d) + 1;
+	    }
+	} else if ((sscanf(line, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) &&
+		   a < 256 && b < 256 && c < 256 && d < 256) {
+	    /* Only remote specified */
+	    if (install) {
+		Sessions[numAddrs].peerip[0] = (unsigned char) a;
+		Sessions[numAddrs].peerip[1] = (unsigned char) b;
+		Sessions[numAddrs].peerip[2] = (unsigned char) c;
+		Sessions[numAddrs].peerip[3] = (unsigned char) d;
+#ifdef HAVE_LICENSE
+		memcpy(Sessions[numAddrs].realpeerip,
+		       Sessions[numAddrs].peerip, IPV4ALEN);
+#endif
+	    }
+	    numAddrs++;
+	}
+    }
+    fclose(fp);
+    if (!numAddrs) {
+	rp_fatal("No valid ip addresses found in pool file");
+    }
+    return numAddrs;
+}
+
+/**********************************************************************
+*%FUNCTION: parsePADITags
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data
+* extra -- extra user data.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Picks interesting tags out of a PADI packet
+***********************************************************************/
+void
+parsePADITags(UINT16_t type, UINT16_t len, unsigned char *data,
+	      void *extra)
+{
+    switch(type) {
+    case TAG_PPP_MAX_PAYLOAD:
+	if (len == sizeof(max_ppp_payload)) {
+	    memcpy(&max_ppp_payload, data, sizeof(max_ppp_payload));
+	    max_ppp_payload = ntohs(max_ppp_payload);
+	    if (max_ppp_payload <= ETH_PPPOE_MTU) {
+		max_ppp_payload = 0;
+	    }
+	}
+	break;
+    case TAG_SERVICE_NAME:
+	/* Copy requested service name */
+	requestedService.type = htons(type);
+	requestedService.length = htons(len);
+	memcpy(requestedService.payload, data, len);
+	break;
+    case TAG_RELAY_SESSION_ID:
+	relayId.type = htons(type);
+	relayId.length = htons(len);
+	memcpy(relayId.payload, data, len);
+	break;
+    case TAG_HOST_UNIQ:
+	hostUniq.type = htons(type);
+	hostUniq.length = htons(len);
+	memcpy(hostUniq.payload, data, len);
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: parsePADRTags
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data
+* extra -- extra user data.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Picks interesting tags out of a PADR packet
+***********************************************************************/
+void
+parsePADRTags(UINT16_t type, UINT16_t len, unsigned char *data,
+	      void *extra)
+{
+    switch(type) {
+    case TAG_PPP_MAX_PAYLOAD:
+	if (len == sizeof(max_ppp_payload)) {
+	    memcpy(&max_ppp_payload, data, sizeof(max_ppp_payload));
+	    max_ppp_payload = ntohs(max_ppp_payload);
+	    if (max_ppp_payload <= ETH_PPPOE_MTU) {
+		max_ppp_payload = 0;
+	    }
+	}
+	break;
+    case TAG_RELAY_SESSION_ID:
+	relayId.type = htons(type);
+	relayId.length = htons(len);
+	memcpy(relayId.payload, data, len);
+	break;
+    case TAG_HOST_UNIQ:
+	hostUniq.type = htons(type);
+	hostUniq.length = htons(len);
+	memcpy(hostUniq.payload, data, len);
+	break;
+    case TAG_AC_COOKIE:
+	receivedCookie.type = htons(type);
+	receivedCookie.length = htons(len);
+	memcpy(receivedCookie.payload, data, len);
+	break;
+    case TAG_SERVICE_NAME:
+	requestedService.type = htons(type);
+	requestedService.length = htons(len);
+	memcpy(requestedService.payload, data, len);
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: fatalSys
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message plus the errno value to stderr and syslog and exits.
+***********************************************************************/
+void
+fatalSys(char const *str)
+{
+    char buf[SMALLBUF];
+    snprintf(buf, SMALLBUF, "%s: %s", str, strerror(errno));
+    printErr(buf);
+    control_exit();
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: sysErr
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message plus the errno value to syslog.
+***********************************************************************/
+void
+sysErr(char const *str)
+{
+    char buf[1024];
+    sprintf(buf, "%.256s: %.256s", str, strerror(errno));
+    printErr(buf);
+}
+
+/**********************************************************************
+*%FUNCTION: rp_fatal
+*%ARGUMENTS:
+* str -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints a message to stderr and syslog and exits.
+***********************************************************************/
+void
+rp_fatal(char const *str)
+{
+    printErr(str);
+    control_exit();
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: genCookie
+*%ARGUMENTS:
+* peerEthAddr -- peer Ethernet address (6 bytes)
+* myEthAddr -- my Ethernet address (6 bytes)
+* seed -- random cookie seed to make things tasty (16 bytes)
+* cookie -- buffer which is filled with server PID and
+*           md5 sum of previous items
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Forms the md5 sum of peer MAC address, our MAC address and seed, useful
+* in a PPPoE Cookie tag.
+***********************************************************************/
+void
+genCookie(unsigned char const *peerEthAddr,
+	  unsigned char const *myEthAddr,
+	  unsigned char const *seed,
+	  unsigned char *cookie)
+{
+    struct MD5Context ctx;
+    pid_t pid = getpid();
+
+    MD5Init(&ctx);
+    MD5Update(&ctx, peerEthAddr, ETH_ALEN);
+    MD5Update(&ctx, myEthAddr, ETH_ALEN);
+    MD5Update(&ctx, seed, SEED_LEN);
+    MD5Final(cookie, &ctx);
+    memcpy(cookie+MD5_LEN, &pid, sizeof(pid));
+}
+
+/**********************************************************************
+*%FUNCTION: processPADI
+*%ARGUMENTS:
+* ethif -- Interface
+* packet -- PPPoE PADI packet
+* len -- length of received packet
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADO packet back to client
+***********************************************************************/
+void
+processPADI(Interface *ethif, PPPoEPacket *packet, int len)
+{
+    PPPoEPacket pado;
+    PPPoETag acname;
+    PPPoETag servname;
+    PPPoETag cookie;
+    size_t acname_len;
+    unsigned char *cursor = pado.payload;
+    UINT16_t plen;
+
+    int sock = ethif->sock;
+    int i;
+    int ok = 0;
+    unsigned char *myAddr = ethif->mac;
+
+    /* Ignore PADI's which don't come from a unicast address */
+    if (NOT_UNICAST(packet->ethHdr.h_source)) {
+	syslog(LOG_ERR, "PADI packet from non-unicast source address");
+	return;
+    }
+
+    /* If no free sessions and "-i" flag given, ignore */
+    if (IgnorePADIIfNoFreeSessions && !FreeSessions) {
+	syslog(LOG_INFO, "PADI ignored - No free session slots available");
+	return;
+    }
+
+    /* If number of sessions per MAC is limited, check here and don't
+       send PADO if already max number of sessions. */
+    if (MaxSessionsPerMac) {
+	if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) {
+	    syslog(LOG_INFO, "PADI: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)",
+		   packet->ethHdr.h_source[0],
+		   packet->ethHdr.h_source[1],
+		   packet->ethHdr.h_source[2],
+		   packet->ethHdr.h_source[3],
+		   packet->ethHdr.h_source[4],
+		   packet->ethHdr.h_source[5],
+		   MaxSessionsPerMac);
+	    return;
+	}
+    }
+
+    acname.type = htons(TAG_AC_NAME);
+    acname_len = strlen(ACName);
+    acname.length = htons(acname_len);
+    memcpy(acname.payload, ACName, acname_len);
+
+    relayId.type = 0;
+    hostUniq.type = 0;
+    requestedService.type = 0;
+    max_ppp_payload = 0;
+
+    parsePacket(packet, parsePADITags, NULL);
+
+    /* If PADI specified non-default service name, and we do not offer
+       that service, DO NOT send PADO */
+    if (requestedService.type) {
+	int slen = ntohs(requestedService.length);
+	if (slen) {
+	    for (i=0; i<NumServiceNames; i++) {
+		if (slen == strlen(ServiceNames[i]) &&
+		    !memcmp(ServiceNames[i], &requestedService.payload, slen)) {
+		    ok = 1;
+		    break;
+		}
+	    }
+	} else {
+	    ok = 1;		/* Default service requested */
+	}
+    } else {
+	ok = 1;			/* No Service-Name tag in PADI */
+    }
+
+    if (!ok) {
+	/* PADI asked for unsupported service */
+	return;
+    }
+
+    /* Generate a cookie */
+    cookie.type = htons(TAG_AC_COOKIE);
+    cookie.length = htons(COOKIE_LEN);
+    genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookie.payload);
+
+    /* Construct a PADO packet */
+    memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN);
+    memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN);
+    pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    pado.vertype = PPPOE_VER_TYPE(1, 1);
+    pado.code = CODE_PADO;
+    pado.session = 0;
+    plen = TAG_HDR_SIZE + acname_len;
+
+    CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE);
+    memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE);
+    cursor += acname_len + TAG_HDR_SIZE;
+
+    /* If we asked for an MTU, handle it */
+    if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) {
+	/* Shrink payload to fit */
+	if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_PPPOE_MTU) {
+	    PPPoETag maxPayload;
+	    UINT16_t mru = htons(max_ppp_payload);
+	    maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+	    maxPayload.length = htons(sizeof(mru));
+	    memcpy(maxPayload.payload, &mru, sizeof(mru));
+	    CHECK_ROOM(cursor, pado.payload, sizeof(mru) + TAG_HDR_SIZE);
+	    memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+	    cursor += sizeof(mru) + TAG_HDR_SIZE;
+	    plen += sizeof(mru) + TAG_HDR_SIZE;
+	}
+    }
+    /* If no service-names specified on command-line, just send default
+       zero-length name.  Otherwise, add all service-name tags */
+    servname.type = htons(TAG_SERVICE_NAME);
+    if (!NumServiceNames) {
+	servname.length = 0;
+	CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE);
+	memcpy(cursor, &servname, TAG_HDR_SIZE);
+	cursor += TAG_HDR_SIZE;
+	plen += TAG_HDR_SIZE;
+    } else {
+	for (i=0; i<NumServiceNames; i++) {
+	    int slen = strlen(ServiceNames[i]);
+	    servname.length = htons(slen);
+	    CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE+slen);
+	    memcpy(cursor, &servname, TAG_HDR_SIZE);
+	    memcpy(cursor+TAG_HDR_SIZE, ServiceNames[i], slen);
+	    cursor += TAG_HDR_SIZE+slen;
+	    plen += TAG_HDR_SIZE+slen;
+	}
+    }
+
+    CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + COOKIE_LEN);
+    memcpy(cursor, &cookie, TAG_HDR_SIZE + COOKIE_LEN);
+    cursor += TAG_HDR_SIZE + COOKIE_LEN;
+    plen += TAG_HDR_SIZE + COOKIE_LEN;
+
+    if (relayId.type) {
+	CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE);
+	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
+    }
+    if (hostUniq.type) {
+	CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE);
+	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
+	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+    }
+    pado.length = htons(plen);
+    sendPacket(NULL, sock, &pado, (int) (plen + HDR_SIZE));
+}
+
+/**********************************************************************
+*%FUNCTION: processPADT
+*%ARGUMENTS:
+* ethif -- interface
+* packet -- PPPoE PADT packet
+* len -- length of received packet
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Kills session whose session-ID is in PADT packet.
+***********************************************************************/
+void
+processPADT(Interface *ethif, PPPoEPacket *packet, int len)
+{
+    size_t i;
+
+    unsigned char *myAddr = ethif->mac;
+
+    /* Ignore PADT's not directed at us */
+    if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return;
+
+    /* Get session's index */
+    i = ntohs(packet->session) - 1 - SessOffset;
+    if (i >= NumSessionSlots) return;
+    if (Sessions[i].sess != packet->session) {
+	syslog(LOG_ERR, "Session index %u doesn't match session number %u",
+	       (unsigned int) i, (unsigned int) ntohs(packet->session));
+	return;
+    }
+
+
+    /* If source MAC does not match, do not kill session */
+    if (memcmp(packet->ethHdr.h_source, Sessions[i].eth, ETH_ALEN)) {
+	syslog(LOG_WARNING, "PADT for session %u received from "
+	       "%02X:%02X:%02X:%02X:%02X:%02X; should be from "
+	       "%02X:%02X:%02X:%02X:%02X:%02X",
+	       (unsigned int) ntohs(packet->session),
+	       packet->ethHdr.h_source[0],
+	       packet->ethHdr.h_source[1],
+	       packet->ethHdr.h_source[2],
+	       packet->ethHdr.h_source[3],
+	       packet->ethHdr.h_source[4],
+	       packet->ethHdr.h_source[5],
+	       Sessions[i].eth[0],
+	       Sessions[i].eth[1],
+	       Sessions[i].eth[2],
+	       Sessions[i].eth[3],
+	       Sessions[i].eth[4],
+	       Sessions[i].eth[5]);
+	return;
+    }
+    Sessions[i].flags |= FLAG_RECVD_PADT;
+    parsePacket(packet, parseLogErrs, NULL);
+    Sessions[i].funcs->stop(&Sessions[i], "Received PADT");
+}
+
+/**********************************************************************
+*%FUNCTION: processPADR
+*%ARGUMENTS:
+* ethif -- Ethernet interface
+* packet -- PPPoE PADR packet
+* len -- length of received packet
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADS packet back to client and starts a PPP session if PADR
+* packet is OK.
+***********************************************************************/
+void
+processPADR(Interface *ethif, PPPoEPacket *packet, int len)
+{
+    unsigned char cookieBuffer[COOKIE_LEN];
+    ClientSession *cliSession;
+    pid_t child;
+    PPPoEPacket pads;
+    unsigned char *cursor = pads.payload;
+    UINT16_t plen;
+    int i;
+    int sock = ethif->sock;
+    unsigned char *myAddr = ethif->mac;
+    int slen = 0;
+    char const *serviceName = NULL;
+
+    /* Temporary structure for sending PADM's. */
+    PPPoEConnection conn;
+
+
+#ifdef HAVE_LICENSE
+    int freemem;
+#endif
+
+    /* Initialize some globals */
+    relayId.type = 0;
+    hostUniq.type = 0;
+    receivedCookie.type = 0;
+    requestedService.type = 0;
+
+    /* Ignore PADR's not directed at us */
+    if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return;
+
+    /* Ignore PADR's from non-unicast addresses */
+    if (NOT_UNICAST(packet->ethHdr.h_source)) {
+	syslog(LOG_ERR, "PADR packet from non-unicast source address");
+	return;
+    }
+
+    /* If number of sessions per MAC is limited, check here and don't
+       send PADS if already max number of sessions. */
+    if (MaxSessionsPerMac) {
+	if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) {
+	    syslog(LOG_INFO, "PADR: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)",
+		   packet->ethHdr.h_source[0],
+		   packet->ethHdr.h_source[1],
+		   packet->ethHdr.h_source[2],
+		   packet->ethHdr.h_source[3],
+		   packet->ethHdr.h_source[4],
+		   packet->ethHdr.h_source[5],
+		   MaxSessionsPerMac);
+	    return;
+	}
+    }
+
+    max_ppp_payload = 0;
+    parsePacket(packet, parsePADRTags, NULL);
+
+    /* Check that everything's cool */
+    if (!receivedCookie.type) {
+	/* Drop it -- do not send error PADS */
+	return;
+    }
+
+    /* Is cookie kosher? */
+    if (receivedCookie.length != htons(COOKIE_LEN)) {
+	/* Drop it -- do not send error PADS */
+	return;
+    }
+
+    genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookieBuffer);
+    if (memcmp(receivedCookie.payload, cookieBuffer, COOKIE_LEN)) {
+	/* Drop it -- do not send error PADS */
+	return;
+    }
+
+    /* Check service name */
+    if (!requestedService.type) {
+	syslog(LOG_ERR, "Received PADR packet with no SERVICE_NAME tag");
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: No service name tag");
+	return;
+    }
+
+    slen = ntohs(requestedService.length);
+    if (slen) {
+	/* Check supported services */
+	for(i=0; i<NumServiceNames; i++) {
+	    if (slen == strlen(ServiceNames[i]) &&
+		!memcmp(ServiceNames[i], &requestedService.payload, slen)) {
+		serviceName = ServiceNames[i];
+		break;
+	    }
+	}
+
+	if (!serviceName) {
+	    syslog(LOG_ERR, "Received PADR packet asking for unsupported service %.*s", (int) ntohs(requestedService.length), requestedService.payload);
+	    sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+			  TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: Invalid service name tag");
+	    return;
+	}
+    } else {
+	serviceName = "";
+    }
+
+
+#ifdef HAVE_LICENSE
+    /* Are we licensed for this many sessions? */
+    if (License_NumLicenses("PPPOE-SESSIONS") <= NumActiveSessions) {
+	syslog(LOG_ERR, "Insufficient session licenses (%02x:%02x:%02x:%02x:%02x:%02x)",
+	       (unsigned int) packet->ethHdr.h_source[0],
+	       (unsigned int) packet->ethHdr.h_source[1],
+	       (unsigned int) packet->ethHdr.h_source[2],
+	       (unsigned int) packet->ethHdr.h_source[3],
+	       (unsigned int) packet->ethHdr.h_source[4],
+	       (unsigned int) packet->ethHdr.h_source[5]);
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No session licenses available");
+	return;
+    }
+#endif
+    /* Enough free memory? */
+#ifdef HAVE_LICENSE
+    freemem = getFreeMem();
+    if (freemem < MIN_FREE_MEMORY) {
+	syslog(LOG_WARNING,
+	       "Insufficient free memory to create session: Want %d, have %d",
+	       MIN_FREE_MEMORY, freemem);
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Insufficient free RAM");
+	return;
+    }
+#endif
+    /* Looks cool... find a slot for the session */
+    cliSession = pppoe_alloc_session();
+    if (!cliSession) {
+	syslog(LOG_ERR, "No client slots available (%02x:%02x:%02x:%02x:%02x:%02x)",
+	       (unsigned int) packet->ethHdr.h_source[0],
+	       (unsigned int) packet->ethHdr.h_source[1],
+	       (unsigned int) packet->ethHdr.h_source[2],
+	       (unsigned int) packet->ethHdr.h_source[3],
+	       (unsigned int) packet->ethHdr.h_source[4],
+	       (unsigned int) packet->ethHdr.h_source[5]);
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No client slots available");
+	return;
+    }
+
+    /* Set up client session peer Ethernet address */
+    memcpy(cliSession->eth, packet->ethHdr.h_source, ETH_ALEN);
+    cliSession->ethif = ethif;
+    cliSession->flags = 0;
+    cliSession->funcs = &DefaultSessionFunctionTable;
+    cliSession->startTime = time(NULL);
+    cliSession->serviceName = serviceName;
+
+    /* Create child process, send PADS packet back */
+    child = fork();
+    if (child < 0) {
+	sendErrorPADS(sock, myAddr, packet->ethHdr.h_source,
+		      TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: Unable to start session process");
+	pppoe_free_session(cliSession);
+	return;
+    }
+    if (child != 0) {
+	/* In the parent process.  Mark pid in session slot */
+	cliSession->pid = child;
+	Event_HandleChildExit(event_selector, child,
+			      childHandler, cliSession);
+	control_session_started(cliSession);
+	return;
+    }
+
+    /* In the child process */
+
+    /* Reset signal handlers to default */
+    signal(SIGTERM, SIG_DFL);
+    signal(SIGINT, SIG_DFL);
+
+    /* Close all file descriptors except for socket */
+    closelog();
+    if (LockFD >= 0) close(LockFD);
+    for (i=0; i<CLOSEFD; i++) {
+	if (i != sock) {
+	    close(i);
+	}
+    }
+
+    openlog("pppoe-server", LOG_PID, LOG_DAEMON);
+    /* pppd has a nasty habit of killing all processes in its process group.
+       Start a new session to stop pppd from killing us! */
+    setsid();
+
+    /* Send PADS and Start pppd */
+    memcpy(pads.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN);
+    memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN);
+    pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    pads.vertype = PPPOE_VER_TYPE(1, 1);
+    pads.code = CODE_PADS;
+
+    pads.session = cliSession->sess;
+    plen = 0;
+
+    /* Copy requested service name tag back in.  If requested-service name
+       length is zero, and we have non-zero services, use first service-name
+       as default */
+    if (!slen && NumServiceNames) {
+	slen = strlen(ServiceNames[0]);
+	memcpy(&requestedService.payload, ServiceNames[0], slen);
+	requestedService.length = htons(slen);
+    }
+    memcpy(cursor, &requestedService, TAG_HDR_SIZE+slen);
+    cursor += TAG_HDR_SIZE+slen;
+    plen += TAG_HDR_SIZE+slen;
+
+    /* If we asked for an MTU, handle it */
+    if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) {
+	/* Shrink payload to fit */
+	if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) {
+	    max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD;
+	}
+	if (max_ppp_payload > ETH_PPPOE_MTU) {
+	    PPPoETag maxPayload;
+	    UINT16_t mru = htons(max_ppp_payload);
+	    maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+	    maxPayload.length = htons(sizeof(mru));
+	    memcpy(maxPayload.payload, &mru, sizeof(mru));
+	    CHECK_ROOM(cursor, pads.payload, sizeof(mru) + TAG_HDR_SIZE);
+	    memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+	    cursor += sizeof(mru) + TAG_HDR_SIZE;
+	    plen += sizeof(mru) + TAG_HDR_SIZE;
+	    cliSession->requested_mtu = max_ppp_payload;
+	}
+    }
+
+    if (relayId.type) {
+	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
+    }
+    if (hostUniq.type) {
+	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
+	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+    }
+    pads.length = htons(plen);
+    sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE));
+
+    if (hurl_string || motd_string) {
+	memset(&conn, 0, sizeof(conn));
+	conn.hostUniq = NULL;
+
+	memcpy(conn.myEth, cliSession->ethif->mac, ETH_ALEN);
+	conn.discoverySocket = sock;
+	conn.session = cliSession->sess;
+	memcpy(conn.peerEth, cliSession->eth, ETH_ALEN);
+	if (hurl_string != NULL)
+	    sendHURLorMOTM(&conn, hurl_string, TAG_HURL);
+	if (motd_string != NULL)
+	    sendHURLorMOTM(&conn, motd_string, TAG_MOTM);
+    }
+    /* Close sock; don't need it any more */
+    close(sock);
+
+    startPPPD(cliSession);
+}
+
+/**********************************************************************
+*%FUNCTION: termHandler
+*%ARGUMENTS:
+* sig -- signal number
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Called by SIGTERM or SIGINT.  Causes all sessions to be killed!
+***********************************************************************/
+static void
+termHandler(int sig)
+{
+    syslog(LOG_INFO,
+	   "Terminating on signal %d -- killing all PPPoE sessions",
+	   sig);
+    killAllSessions();
+    control_exit();
+    exit(0);
+}
+
+/**********************************************************************
+*%FUNCTION: usage
+*%ARGUMENTS:
+* argv0 -- argv[0] from main
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Prints usage instructions
+***********************************************************************/
+void
+usage(char const *argv0)
+{
+    fprintf(stderr, "Usage: %s [options]\n", argv0);
+    fprintf(stderr, "Options:\n");
+#ifdef USE_BPF
+    fprintf(stderr, "   -I if_name     -- Specify interface (REQUIRED)\n");
+#else
+    fprintf(stderr, "   -I if_name     -- Specify interface (default %s.)\n",
+	    DEFAULT_IF);
+#endif
+    fprintf(stderr, "   -T timeout     -- Specify inactivity timeout in seconds.\n");
+    fprintf(stderr, "   -C name        -- Set access concentrator name.\n");
+    fprintf(stderr, "   -m MSS         -- Clamp incoming and outgoing MSS options.\n");
+    fprintf(stderr, "   -L ip          -- Set local IP address.\n");
+    fprintf(stderr, "   -l             -- Increment local IP address for each session.\n");
+    fprintf(stderr, "   -R ip          -- Set start address of remote IP pool.\n");
+    fprintf(stderr, "   -S name        -- Advertise specified service-name.\n");
+    fprintf(stderr, "   -O fname       -- Use PPPD options from specified file\n");
+    fprintf(stderr, "                     (default %s).\n", PPPOE_SERVER_OPTIONS);
+    fprintf(stderr, "   -p fname       -- Optain IP address pool from specified file.\n");
+    fprintf(stderr, "   -N num         -- Allow 'num' concurrent sessions.\n");
+    fprintf(stderr, "   -o offset      -- Assign session numbers starting at offset+1.\n");
+    fprintf(stderr, "   -f disc:sess   -- Set Ethernet frame types (hex).\n");
+    fprintf(stderr, "   -s             -- Use synchronous PPP mode.\n");
+    fprintf(stderr, "   -X pidfile     -- Write PID and lock pidfile.\n");
+    fprintf(stderr, "   -q /path/pppd  -- Specify full path to pppd.\n");
+    fprintf(stderr, "   -Q /path/pppoe -- Specify full path to pppoe.\n");
+#ifdef HAVE_LINUX_KERNEL_PPPOE
+    fprintf(stderr, "   -k             -- Use kernel-mode PPPoE.\n");
+#endif
+    fprintf(stderr, "   -u             -- Pass 'unit' option to pppd.\n");
+    fprintf(stderr, "   -r             -- Randomize session numbers.\n");
+    fprintf(stderr, "   -d             -- Debug session creation.\n");
+    fprintf(stderr, "   -x n           -- Limit to 'n' sessions/MAC address.\n");
+    fprintf(stderr, "   -P             -- Check pool file for correctness and exit.\n");
+#ifdef HAVE_LICENSE
+    fprintf(stderr, "   -c secret:if:port -- Enable clustering on interface 'if'.\n");
+    fprintf(stderr, "   -1             -- Allow only one session per user.\n");
+#endif
+
+    fprintf(stderr, "   -i             -- Ignore PADI if no free sessions.\n");
+    fprintf(stderr, "   -M msg         -- Send MSG in a MOTM tag in PADM packet after PADS.\n");
+    fprintf(stderr, "   -H url         -- Send URL in a HURL tag in PADM packet after PADS.\n");
+    fprintf(stderr, "   -h             -- Print usage information.\n\n");
+    fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001-2009 Roaring Penguin Software Inc.\n", RP_VERSION);
+    fprintf(stderr, "                     %*s  Copyright (C) 2018-2020 Dianne Skoll\n", (int) strlen(RP_VERSION), "");
+
+#ifndef HAVE_LICENSE
+    fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n");
+    fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
+    fprintf(stderr, "under the terms of the GNU General Public License, version 2\n");
+    fprintf(stderr, "or (at your option) any later version.\n");
+#endif
+    fprintf(stderr, "https://dianne.skoll.ca/projects/rp-pppoe/\n");
+}
+
+/**********************************************************************
+*%FUNCTION: main
+*%ARGUMENTS:
+* argc, argv -- usual suspects
+*%RETURNS:
+* Exit status
+*%DESCRIPTION:
+* Main program of PPPoE server
+***********************************************************************/
+int
+main(int argc, char **argv)
+{
+
+    FILE *fp;
+    int i, j;
+    int opt;
+    int d[IPV4ALEN];
+    int beDaemon = 1;
+    int found;
+    unsigned int discoveryType, sessionType;
+    char *addressPoolFname = NULL;
+    char *pidfile = NULL;
+    char c;
+
+#ifdef HAVE_LICENSE
+    int use_clustering = 0;
+#endif
+
+#ifndef HAVE_LINUX_KERNEL_PPPOE
+    char *options = "X:ix:hI:C:L:R:T:m:FN:f:O:o:sp:lrudPc:S:1q:Q:H:M:";
+#else
+    char *options = "X:ix:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPc:S:1q:Q:H:M:";
+#endif
+
+    if (getuid() != geteuid() ||
+	getgid() != getegid()) {
+	fprintf(stderr, "SECURITY WARNING: pppoe-server will NOT run suid or sgid.  Fix your installation.\n");
+	exit(1);
+    }
+
+    memset(interfaces, 0, sizeof(interfaces));
+
+    /* Initialize syslog */
+    openlog("pppoe-server", LOG_PID, LOG_DAEMON);
+
+    /* Default number of session slots */
+    NumSessionSlots = DEFAULT_MAX_SESSIONS;
+    MaxSessionsPerMac = 0; /* No limit */
+    NumActiveSessions = 0;
+
+    /* Parse command-line options */
+    while((opt = getopt(argc, argv, options)) != -1) {
+	switch(opt) {
+	case 'i':
+	    IgnorePADIIfNoFreeSessions = 1;
+	    break;
+	case 'x':
+	    if (sscanf(optarg, "%d", &MaxSessionsPerMac) != 1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (MaxSessionsPerMac < 0) {
+		MaxSessionsPerMac = 0;
+	    }
+	    break;
+
+#ifdef HAVE_LINUX_KERNEL_PPPOE
+	case 'k':
+	    UseLinuxKernelModePPPoE = 1;
+	    break;
+#endif
+	case 'S':
+	    if (NumServiceNames == MAX_SERVICE_NAMES) {
+		fprintf(stderr, "Too many '-S' options (%d max)",
+			MAX_SERVICE_NAMES);
+		exit(1);
+	    }
+	    ServiceNames[NumServiceNames] = strdup(optarg);
+	    if (!ServiceNames[NumServiceNames]) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    NumServiceNames++;
+	    break;
+	case 'q':
+	    pppd_path = strdup(optarg);
+	    if (!pppd_path) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+	case 'Q':
+	    pppoe_path = strdup(optarg);
+	    if (!pppoe_path) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+
+	case 'M':
+	    if (motd_string) {
+		free(motd_string);
+		motd_string = NULL;
+	    }
+	    motd_string = strdup(optarg);
+	    if (!motd_string) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+
+	case 'H':
+	    if (hurl_string) {
+		free(hurl_string);
+		hurl_string = NULL;
+	    }
+	    hurl_string = strdup(optarg);
+	    if (!hurl_string) {
+		fprintf(stderr, "Out of memory");
+		exit(1);
+	    }
+	    break;
+	case 'c':
+#ifndef HAVE_LICENSE
+	    fprintf(stderr, "Clustering capability not available.\n");
+	    exit(1);
+#else
+	    cluster_handle_option(optarg);
+	    use_clustering = 1;
+	    break;
+#endif
+
+	case 'd':
+	    Debug = 1;
+	    break;
+	case 'P':
+	    CheckPoolSyntax = 1;
+	    break;
+	case 'u':
+	    PassUnitOptionToPPPD = 1;
+	    break;
+
+	case 'r':
+	    RandomizeSessionNumbers = 1;
+	    break;
+
+	case 'l':
+	    IncrLocalIP = 1;
+	    break;
+
+	case 'p':
+	    SET_STRING(addressPoolFname, optarg);
+	    break;
+
+	case 'X':
+	    SET_STRING(pidfile, optarg);
+	    break;
+	case 's':
+	    Synchronous = 1;
+	    /* Pass the Synchronous option on to pppoe */
+	    snprintf(PppoeOptions + strlen(PppoeOptions),
+		     SMALLBUF-strlen(PppoeOptions),
+		     " -s");
+	    break;
+
+	case 'f':
+	    if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) {
+		fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n");
+		exit(EXIT_FAILURE);
+	    }
+	    Eth_PPPOE_Discovery = (UINT16_t) discoveryType;
+	    Eth_PPPOE_Session   = (UINT16_t) sessionType;
+	    /* This option gets passed to pppoe */
+	    snprintf(PppoeOptions + strlen(PppoeOptions),
+		     SMALLBUF-strlen(PppoeOptions),
+		     " -%c %s", opt, optarg);
+	    break;
+
+	case 'F':
+	    beDaemon = 0;
+	    break;
+
+	case 'N':
+	    if (sscanf(optarg, "%d", &opt) != 1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (opt <= 0) {
+		fprintf(stderr, "-N: Value must be positive\n");
+		exit(EXIT_FAILURE);
+	    }
+	    NumSessionSlots = opt;
+	    break;
+
+	case 'O':
+	    SET_STRING(pppoptfile, optarg);
+	    break;
+
+	case 'o':
+	    if (sscanf(optarg, "%d", &opt) != 1) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (opt < 0) {
+		fprintf(stderr, "-o: Value must be non-negative\n");
+		exit(EXIT_FAILURE);
+	    }
+	    SessOffset = (size_t) opt;
+	    break;
+
+	case 'I':
+	    if (NumInterfaces >= MAX_INTERFACES) {
+		fprintf(stderr, "Too many -I options (max %d)\n",
+			MAX_INTERFACES);
+		exit(EXIT_FAILURE);
+	    }
+	    found = 0;
+	    for (i=0; i<NumInterfaces; i++) {
+		if (!strncmp(interfaces[i].name, optarg, IFNAMSIZ)) {
+		    found = 1;
+		    break;
+		}
+	    }
+	    if (!found) {
+		strncpy(interfaces[NumInterfaces].name, optarg, IFNAMSIZ);
+		NumInterfaces++;
+	    }
+	    break;
+
+	case 'C':
+	    SET_STRING(ACName, optarg);
+	    break;
+
+	case 'L':
+	case 'R':
+	    /* Get local/remote IP address */
+	    if (sscanf(optarg, "%d.%d.%d.%d", &d[0], &d[1], &d[2], &d[3]) != 4) {
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	    }
+	    for (i=0; i<IPV4ALEN; i++) {
+		if (d[i] < 0 || d[i] > 255) {
+		    usage(argv[0]);
+		    exit(EXIT_FAILURE);
+		}
+		if (opt == 'L') {
+		    LocalIP[i] = (unsigned char) d[i];
+		} else {
+		    RemoteIP[i] = (unsigned char) d[i];
+		}
+	    }
+	    break;
+
+	case 'T':
+	case 'm':
+	    /* These just get passed to pppoe */
+	    snprintf(PppoeOptions + strlen(PppoeOptions),
+		     SMALLBUF-strlen(PppoeOptions),
+		     " -%c %s", opt, optarg);
+	    break;
+
+	case 'h':
+	    usage(argv[0]);
+	    exit(EXIT_SUCCESS);
+	case '1':
+#ifdef HAVE_LICENSE
+	    MaxSessionsPerUser = 1;
+#else
+	    fprintf(stderr, "-1 option not valid.\n");
+	    exit(1);
+#endif
+	    break;
+	}
+    }
+
+    if (!pppoptfile) {
+	pppoptfile = PPPOE_SERVER_OPTIONS;
+    }
+
+#ifdef HAVE_LICENSE
+    License_SetVersion(SERVPOET_VERSION);
+    License_ReadBundleFile("/etc/rp/bundle.txt");
+    License_ReadFile("/etc/rp/license.txt");
+    ServerLicense = License_GetFeature("PPPOE-SERVER");
+    if (!ServerLicense) {
+	fprintf(stderr, "License: GetFeature failed: %s\n",
+		License_ErrorMessage());
+	exit(1);
+    }
+#endif
+
+#ifdef USE_LINUX_PACKET
+#ifndef HAVE_STRUCT_SOCKADDR_LL
+    fprintf(stderr, "The PPPoE server does not work on Linux 2.0 kernels.\n");
+    exit(EXIT_FAILURE);
+#endif
+#endif
+
+    if (!NumInterfaces) {
+	strcpy(interfaces[0].name, DEFAULT_IF);
+	NumInterfaces = 1;
+    }
+
+    if (!ACName) {
+	ACName = malloc(HOSTNAMELEN);
+	if (gethostname(ACName, HOSTNAMELEN) < 0) {
+	    fatalSys("gethostname");
+	}
+    }
+
+    /* If address pool filename given, count number of addresses */
+    if (addressPoolFname) {
+	NumSessionSlots = parseAddressPool(addressPoolFname, 0);
+	if (CheckPoolSyntax) {
+	    printf("%lu\n", (unsigned long) NumSessionSlots);
+	    exit(0);
+	}
+    }
+
+    /* Max 65534 - SessOffset sessions */
+    if (NumSessionSlots + SessOffset > 65534) {
+	fprintf(stderr, "-N and -o options must add up to at most 65534\n");
+	exit(EXIT_FAILURE);
+    }
+
+    /* Allocate memory for sessions */
+    Sessions = calloc(NumSessionSlots, sizeof(ClientSession));
+    if (!Sessions) {
+	rp_fatal("Cannot allocate memory for session slots");
+    }
+
+    /* Fill in local addresses first (let pool file override later */
+    for (i=0; i<NumSessionSlots; i++) {
+	memcpy(Sessions[i].myip, LocalIP, sizeof(LocalIP));
+	if (IncrLocalIP) {
+	    incrementIPAddress(LocalIP);
+	}
+    }
+
+    /* Fill in remote IP addresses from pool (may also overwrite local ips) */
+    if (addressPoolFname) {
+	(void) parseAddressPool(addressPoolFname, 1);
+    }
+
+    /* For testing -- generate sequential remote IP addresses */
+    for (i=0; i<NumSessionSlots; i++) {
+	Sessions[i].pid = 0;
+	Sessions[i].funcs = &DefaultSessionFunctionTable;
+	Sessions[i].sess = htons(i+1+SessOffset);
+
+	if (!addressPoolFname) {
+	    memcpy(Sessions[i].peerip, RemoteIP, sizeof(RemoteIP));
+#ifdef HAVE_LICENSE
+	    memcpy(Sessions[i].realpeerip, RemoteIP, sizeof(RemoteIP));
+#endif
+	    incrementIPAddress(RemoteIP);
+	}
+    }
+
+    /* Initialize our random cookie.  Try /dev/urandom; if that fails,
+       use PID and rand() */
+    fp = fopen("/dev/urandom", "r");
+    if (fp) {
+	unsigned int x;
+	fread(&x, 1, sizeof(x), fp);
+	srand(x);
+	fread(&CookieSeed, 1, SEED_LEN, fp);
+	fclose(fp);
+    } else {
+	srand((unsigned int) getpid() * (unsigned int) time(NULL));
+	CookieSeed[0] = getpid() & 0xFF;
+	CookieSeed[1] = (getpid() >> 8) & 0xFF;
+	for (i=2; i<SEED_LEN; i++) {
+	    CookieSeed[i] = (rand() >> (i % 9)) & 0xFF;
+	}
+    }
+
+    if (RandomizeSessionNumbers) {
+	int *permutation;
+	int tmp;
+	permutation = malloc(sizeof(int) * NumSessionSlots);
+	if (!permutation) {
+	    fprintf(stderr, "Could not allocate memory to randomize session numbers\n");
+	    exit(EXIT_FAILURE);
+	}
+	for (i=0; i<NumSessionSlots; i++) {
+	    permutation[i] = i;
+	}
+	for (i=0; i<NumSessionSlots-1; i++) {
+	    j = i + rand() % (NumSessionSlots - i);
+	    if (j != i) {
+		tmp = permutation[j];
+		permutation[j] = permutation[i];
+		permutation[i] = tmp;
+	    }
+	}
+	/* Link sessions together */
+	FreeSessions = &Sessions[permutation[0]];
+	LastFreeSession = &Sessions[permutation[NumSessionSlots-1]];
+	for (i=0; i<NumSessionSlots-1; i++) {
+	    Sessions[permutation[i]].next = &Sessions[permutation[i+1]];
+	}
+	Sessions[permutation[NumSessionSlots-1]].next = NULL;
+	free(permutation);
+    } else {
+	/* Link sessions together */
+	FreeSessions = &Sessions[0];
+	LastFreeSession = &Sessions[NumSessionSlots - 1];
+	for (i=0; i<NumSessionSlots-1; i++) {
+	    Sessions[i].next = &Sessions[i+1];
+	}
+	Sessions[NumSessionSlots-1].next = NULL;
+    }
+
+    if (Debug) {
+	/* Dump session array and exit */
+	ClientSession *ses = FreeSessions;
+	while(ses) {
+	    printf("Session %u local %d.%d.%d.%d remote %d.%d.%d.%d\n",
+		   (unsigned int) (ntohs(ses->sess)),
+		   ses->myip[0], ses->myip[1],
+		   ses->myip[2], ses->myip[3],
+		   ses->peerip[0], ses->peerip[1],
+		   ses->peerip[2], ses->peerip[3]);
+	    ses = ses->next;
+	}
+	exit(0);
+    }
+
+    /* Open all the interfaces */
+    for (i=0; i<NumInterfaces; i++) {
+	interfaces[i].mtu = 0;
+	interfaces[i].sock = openInterface(interfaces[i].name, Eth_PPPOE_Discovery, interfaces[i].mac, &interfaces[i].mtu);
+    }
+
+    /* Ignore SIGPIPE */
+    signal(SIGPIPE, SIG_IGN);
+
+    /* Create event selector */
+    event_selector = Event_CreateSelector();
+    if (!event_selector) {
+	rp_fatal("Could not create EventSelector -- probably out of memory");
+    }
+
+    /* Control channel */
+#ifdef HAVE_LICENSE
+    if (control_init(argc, argv, event_selector)) {
+	rp_fatal("control_init failed");
+    }
+#endif
+
+    /* Create event handler for each interface */
+    for (i = 0; i<NumInterfaces; i++) {
+	interfaces[i].eh = Event_AddHandler(event_selector,
+					    interfaces[i].sock,
+					    EVENT_FLAG_READABLE,
+					    InterfaceHandler,
+					    &interfaces[i]);
+#ifdef HAVE_L2TP
+	interfaces[i].session_sock = -1;
+#endif
+	if (!interfaces[i].eh) {
+	    rp_fatal("Event_AddHandler failed");
+	}
+    }
+
+#ifdef HAVE_LICENSE
+    if (use_clustering) {
+	ClusterLicense = License_GetFeature("PPPOE-CLUSTER");
+	if (!ClusterLicense) {
+	    fprintf(stderr, "License: GetFeature failed: %s\n",
+		    License_ErrorMessage());
+	    exit(1);
+	}
+	if (!License_Expired(ClusterLicense)) {
+	    if (cluster_init(event_selector) < 0) {
+		rp_fatal("cluster_init failed");
+	    }
+	}
+    }
+#endif
+
+#ifdef HAVE_L2TP
+    for (i=0; i<NumInterfaces; i++) {
+	pppoe_to_l2tp_add_interface(event_selector,
+				    &interfaces[i]);
+    }
+#endif
+
+    /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */
+    if (beDaemon) {
+	if (pipe(KidPipe) < 0) {
+	    fatalSys("pipe");
+	}
+	i = fork();
+	if (i < 0) {
+	    fatalSys("fork");
+	} else if (i != 0) {
+	    /* parent */
+	    close(KidPipe[1]);
+	    KidPipe[1] = -1;
+	    /* Wait for child to give the go-ahead */
+	    while(1) {
+		int r = read(KidPipe[0], &c, 1);
+		if (r == 0) {
+		    fprintf(stderr, "EOF from child - something went wrong; please check logs.\n");
+		    exit(EXIT_FAILURE);
+		}
+		if (r < 0) {
+		    if (errno == EINTR) continue;
+		    fatalSys("read");
+		}
+		break;
+	    }
+
+	    if (c == 'X') {
+		exit(EXIT_SUCCESS);
+	    }
+
+	    /* Read error message from child */
+	    while (1) {
+		int r = read(KidPipe[0], &c, 1);
+		if (r == 0) exit(EXIT_FAILURE);
+		if (r < 0) {
+		    if (errno == EINTR) continue;
+		    fatalSys("read");
+		}
+		fprintf(stderr, "%c", c);
+	    }
+	    exit(EXIT_FAILURE);
+	}
+	setsid();
+	signal(SIGHUP, SIG_IGN);
+	i = fork();
+	if (i < 0) {
+	    fatalSys("fork");
+	} else if (i != 0) {
+	    exit(EXIT_SUCCESS);
+	}
+
+	chdir("/");
+
+	if (KidPipe[0] >= 0) {
+	    close(KidPipe[0]);
+	    KidPipe[0] = -1;
+	}
+
+	/* Point stdin/stdout/stderr to /dev/null */
+	for (i=0; i<3; i++) {
+	    close(i);
+	}
+	i = open("/dev/null", O_RDWR);
+	if (i >= 0) {
+	    dup2(i, 0);
+	    dup2(i, 1);
+	    dup2(i, 2);
+	    if (i > 2) close(i);
+	}
+    }
+
+    if (pidfile) {
+	FILE *foo = NULL;
+	if (KidPipe[1] >= 0) foo = fdopen(KidPipe[1], "w");
+	struct flock fl;
+	char buf[64];
+	fl.l_type = F_WRLCK;
+	fl.l_whence = SEEK_SET;
+	fl.l_start = 0;
+	fl.l_len = 0;
+	LockFD = open(pidfile, O_RDWR|O_CREAT, 0666);
+	if (LockFD < 0) {
+	    syslog(LOG_INFO, "Could not open PID file %s: %s", pidfile, strerror(errno));
+	    if (foo) fprintf(foo, "ECould not open PID file %s: %s\n", pidfile, strerror(errno));
+	    exit(1);
+	}
+	if (fcntl(LockFD, F_SETLK, &fl) < 0) {
+	    syslog(LOG_INFO, "Could not lock PID file %s: Is another process running?", pidfile);
+	    if (foo) fprintf(foo, "ECould not lock PID file %s: Is another process running?\n", pidfile);
+	    exit(1);
+	}
+	ftruncate(LockFD, 0);
+	snprintf(buf, sizeof(buf), "%lu\n", (unsigned long) getpid());
+	write(LockFD, buf, strlen(buf));
+	/* Do not close fd... use it to retain lock */
+    }
+
+    /* Set signal handlers for SIGTERM and SIGINT */
+    if (Event_HandleSignal(event_selector, SIGTERM, termHandler) < 0 ||
+	Event_HandleSignal(event_selector, SIGINT, termHandler) < 0) {
+	fatalSys("Event_HandleSignal");
+    }
+
+    /* Tell parent all is cool */
+    if (KidPipe[1] >= 0) {
+	write(KidPipe[1], "X", 1);
+	close(KidPipe[1]);
+	KidPipe[1] = -1;
+    }
+
+    for(;;) {
+	i = Event_HandleEvent(event_selector);
+	if (i < 0) {
+	    fatalSys("Event_HandleEvent");
+	}
+
+#ifdef HAVE_LICENSE
+	if (License_Expired(ServerLicense)) {
+	    syslog(LOG_INFO, "Server license has expired -- killing all PPPoE sessions");
+	    killAllSessions();
+	    control_exit();
+	    exit(0);
+	}
+#endif
+    }
+    return 0;
+}
+
+void
+serverProcessPacket(Interface *i)
+{
+    int len;
+    PPPoEPacket packet;
+    int sock = i->sock;
+
+    if (receivePacket(sock, &packet, &len) < 0) {
+	return;
+    }
+
+    if (len < HDR_SIZE) {
+	/* Impossible - ignore */
+	return;
+    }
+
+    /* Sanity check on packet */
+    if (PPPOE_VER(packet.vertype) != 1 || PPPOE_TYPE(packet.vertype) != 1) {
+	/* Syslog an error */
+	return;
+    }
+
+    /* Check length */
+    if (ntohs(packet.length) + HDR_SIZE > len) {
+	syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	       (unsigned int) ntohs(packet.length));
+	return;
+    }
+
+    switch(packet.code) {
+    case CODE_PADI:
+	processPADI(i, &packet, len);
+	break;
+    case CODE_PADR:
+	processPADR(i, &packet, len);
+	break;
+    case CODE_PADT:
+	/* Kill the child */
+	processPADT(i, &packet, len);
+	break;
+    case CODE_SESS:
+	/* Ignore SESS -- children will handle them */
+	break;
+    case CODE_PADO:
+    case CODE_PADS:
+	/* Ignore PADO and PADS totally */
+	break;
+    default:
+	/* Syslog an error */
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: sendErrorPADS
+*%ARGUMENTS:
+* sock -- socket to write to
+* source -- source Ethernet address
+* dest -- destination Ethernet address
+* errorTag -- error tag
+* errorMsg -- error message
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADS packet with an error message
+***********************************************************************/
+void
+sendErrorPADS(int sock,
+	      unsigned char *source,
+	      unsigned char *dest,
+	      int errorTag,
+	      char *errorMsg)
+{
+    PPPoEPacket pads;
+    unsigned char *cursor = pads.payload;
+    UINT16_t plen;
+    PPPoETag err;
+    int elen = strlen(errorMsg);
+
+    memcpy(pads.ethHdr.h_dest, dest, ETH_ALEN);
+    memcpy(pads.ethHdr.h_source, source, ETH_ALEN);
+    pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    pads.vertype = PPPOE_VER_TYPE(1, 1);
+    pads.code = CODE_PADS;
+
+    pads.session = htons(0);
+    plen = 0;
+
+    err.type = htons(errorTag);
+    err.length = htons(elen);
+
+    memcpy(err.payload, errorMsg, elen);
+    memcpy(cursor, &err, TAG_HDR_SIZE+elen);
+    cursor += TAG_HDR_SIZE + elen;
+    plen += TAG_HDR_SIZE + elen;
+
+    if (relayId.type) {
+	memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(relayId.length) + TAG_HDR_SIZE;
+    }
+    if (hostUniq.type) {
+	memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE);
+	cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+	plen += ntohs(hostUniq.length) + TAG_HDR_SIZE;
+    }
+    pads.length = htons(plen);
+    sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE));
+}
+
+
+/**********************************************************************
+*%FUNCTION: startPPPDUserMode
+*%ARGUMENTS:
+* session -- client session record
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Starts PPPD for user-mode PPPoE
+***********************************************************************/
+void
+startPPPDUserMode(ClientSession *session)
+{
+    /* Leave some room */
+    char *argv[64];
+
+    char buffer[2 * SMALLBUF];
+
+    int c = 0;
+
+    argv[c++] = "pppd";
+    argv[c++] = "pty";
+
+    /* Let's hope service-name does not have ' in it... */
+    snprintf(buffer, sizeof(buffer), "%s -n -I %s -e %u:%02x:%02x:%02x:%02x:%02x:%02x%s -S '%s'",
+	     pppoe_path, session->ethif->name,
+	     (unsigned int) ntohs(session->sess),
+	     session->eth[0], session->eth[1], session->eth[2],
+	     session->eth[3], session->eth[4], session->eth[5],
+	     PppoeOptions, session->serviceName);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+
+    argv[c++] = "file";
+    argv[c++] = pppoptfile;
+
+    snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d:%d.%d.%d.%d",
+	    (int) session->myip[0], (int) session->myip[1],
+	    (int) session->myip[2], (int) session->myip[3],
+	    (int) session->peerip[0], (int) session->peerip[1],
+	    (int) session->peerip[2], (int) session->peerip[3]);
+    syslog(LOG_INFO,
+	   "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'",
+	   (unsigned int) ntohs(session->sess),
+	   session->eth[0], session->eth[1], session->eth[2],
+	   session->eth[3], session->eth[4], session->eth[5],
+	   (int) session->peerip[0], (int) session->peerip[1],
+	   (int) session->peerip[2], (int) session->peerip[3],
+	   session->ethif->name,
+	   session->serviceName);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+    argv[c++] = "nodetach";
+    argv[c++] = "noaccomp";
+    argv[c++] = "nopcomp";
+    argv[c++] = "default-asyncmap";
+    if (Synchronous) {
+	argv[c++] = "sync";
+    }
+    if (PassUnitOptionToPPPD) {
+	argv[c++] = "unit";
+	sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1));
+	argv[c++] = buffer;
+    }
+    if (session->requested_mtu > 1492) {
+	sprintf(buffer, "%u", (unsigned int) session->requested_mtu);
+	argv[c++] = "mru";
+	argv[c++] = buffer;
+	argv[c++] = "mtu";
+	argv[c++] = buffer;
+    } else {
+	argv[c++] = "mru";
+	argv[c++] = "1492";
+	argv[c++] = "mtu";
+	argv[c++] = "1492";
+    }
+
+    argv[c++] = NULL;
+
+    execv(pppd_path, argv);
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: startPPPDLinuxKernelMode
+*%ARGUMENTS:
+* session -- client session record
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Starts PPPD for kernel-mode PPPoE on Linux
+***********************************************************************/
+void
+startPPPDLinuxKernelMode(ClientSession *session)
+{
+    /* Leave some room */
+    char *argv[32];
+
+    int c = 0;
+
+    char buffer[SMALLBUF];
+
+    argv[c++] = "pppd";
+    argv[c++] = "plugin";
+    argv[c++] = "rp-pppoe.so";
+
+    /* Add "nic-" to interface name */
+    snprintf(buffer, SMALLBUF, "nic-%s", session->ethif->name);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	exit(EXIT_FAILURE);
+    }
+
+    snprintf(buffer, SMALLBUF, "%u:%02x:%02x:%02x:%02x:%02x:%02x",
+	     (unsigned int) ntohs(session->sess),
+	     session->eth[0], session->eth[1], session->eth[2],
+	     session->eth[3], session->eth[4], session->eth[5]);
+    argv[c++] = "rp_pppoe_sess";
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+    argv[c++] = "rp_pppoe_service";
+    argv[c++] = (char *) session->serviceName;
+    argv[c++] = "file";
+    argv[c++] = pppoptfile;
+
+    snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d",
+	    (int) session->myip[0], (int) session->myip[1],
+	    (int) session->myip[2], (int) session->myip[3],
+	    (int) session->peerip[0], (int) session->peerip[1],
+	    (int) session->peerip[2], (int) session->peerip[3]);
+    syslog(LOG_INFO,
+	   "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'",
+	   (unsigned int) ntohs(session->sess),
+	   session->eth[0], session->eth[1], session->eth[2],
+	   session->eth[3], session->eth[4], session->eth[5],
+	   (int) session->peerip[0], (int) session->peerip[1],
+	   (int) session->peerip[2], (int) session->peerip[3],
+	   session->ethif->name,
+	   session->serviceName);
+    argv[c++] = strdup(buffer);
+    if (!argv[c-1]) {
+	/* TODO: Send a PADT */
+	exit(EXIT_FAILURE);
+    }
+    argv[c++] = "nodetach";
+    argv[c++] = "noaccomp";
+    argv[c++] = "nopcomp";
+    argv[c++] = "default-asyncmap";
+    if (PassUnitOptionToPPPD) {
+	argv[c++] = "unit";
+	sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset));
+	argv[c++] = buffer;
+    }
+    if (session->requested_mtu > 1492) {
+	sprintf(buffer, "%u", (unsigned int) session->requested_mtu);
+	argv[c++] = "mru";
+	argv[c++] = buffer;
+	argv[c++] = "mtu";
+	argv[c++] = buffer;
+    } else {
+	argv[c++] = "mru";
+	argv[c++] = "1492";
+	argv[c++] = "mtu";
+	argv[c++] = "1492";
+    }
+    argv[c++] = NULL;
+    execv(pppd_path, argv);
+    exit(EXIT_FAILURE);
+}
+
+/**********************************************************************
+*%FUNCTION: startPPPD
+*%ARGUMENTS:
+* session -- client session record
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Starts PPPD
+***********************************************************************/
+void
+startPPPD(ClientSession *session)
+{
+    if (UseLinuxKernelModePPPoE) startPPPDLinuxKernelMode(session);
+    else startPPPDUserMode(session);
+}
+
+/**********************************************************************
+* %FUNCTION: InterfaceHandler
+* %ARGUMENTS:
+*  es -- event selector (ignored)
+*  fd -- file descriptor which is readable
+*  flags -- ignored
+*  data -- Pointer to the Interface structure
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Handles a packet ready at an interface
+***********************************************************************/
+void
+InterfaceHandler(EventSelector *es,
+		 int fd,
+		 unsigned int flags,
+		 void *data)
+{
+    serverProcessPacket((Interface *) data);
+}
+
+/**********************************************************************
+* %FUNCTION: PppoeStopSession
+* %ARGUMENTS:
+*  ses -- the session
+*  reason -- reason session is being stopped.
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Kills pppd.
+***********************************************************************/
+static void
+PppoeStopSession(ClientSession *ses,
+		 char const *reason)
+{
+    /* Temporary structure for sending PADT's. */
+    PPPoEConnection conn;
+
+    memset(&conn, 0, sizeof(conn));
+    conn.hostUniq = NULL;
+
+    memcpy(conn.myEth, ses->ethif->mac, ETH_ALEN);
+    conn.discoverySocket = ses->ethif->sock;
+    conn.session = ses->sess;
+    memcpy(conn.peerEth, ses->eth, ETH_ALEN);
+    sendPADT(&conn, reason);
+    ses->flags |= FLAG_SENT_PADT;
+
+    if (ses->pid) {
+	kill(ses->pid, SIGTERM);
+    }
+    ses->funcs = &DefaultSessionFunctionTable;
+}
+
+/**********************************************************************
+* %FUNCTION: PppoeSessionIsActive
+* %ARGUMENTS:
+*  ses -- the session
+* %RETURNS:
+*  True if session is active, false if not.
+***********************************************************************/
+static int
+PppoeSessionIsActive(ClientSession *ses)
+{
+    return (ses->pid != 0);
+}
+
+#ifdef HAVE_LICENSE
+/**********************************************************************
+* %FUNCTION: getFreeMem
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  The amount of free RAM in kilobytes, or -1 if it could not be
+*  determined
+* %DESCRIPTION:
+*  Reads Linux-specific /proc/meminfo file and extracts free RAM
+***********************************************************************/
+int
+getFreeMem(void)
+{
+    char buf[512];
+    int memfree=0, buffers=0, cached=0;
+    FILE *fp = fopen("/proc/meminfo", "r");
+    if (!fp) return -1;
+
+    while (fgets(buf, sizeof(buf), fp)) {
+	if (!strncmp(buf, "MemFree:", 8)) {
+	    if (sscanf(buf, "MemFree: %d", &memfree) != 1) {
+		fclose(fp);
+		return -1;
+	    }
+	} else if (!strncmp(buf, "Buffers:", 8)) {
+	    if (sscanf(buf, "Buffers: %d", &buffers) != 1) {
+		fclose(fp);
+		return -1;
+	    }
+	} else if (!strncmp(buf, "Cached:", 7)) {
+	    if (sscanf(buf, "Cached: %d", &cached) != 1) {
+		fclose(fp);
+		return -1;
+	    }
+	}
+    }
+    fclose(fp);
+    /* return memfree + buffers + cached; */
+    return memfree;
+}
+#endif
+
+/**********************************************************************
+* %FUNCTION: pppoe_alloc_session
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  NULL if no session is available, otherwise a ClientSession structure.
+* %DESCRIPTION:
+*  Allocates a ClientSession structure and removes from free list, puts
+*  on busy list
+***********************************************************************/
+ClientSession *
+pppoe_alloc_session(void)
+{
+    ClientSession *ses = FreeSessions;
+    if (!ses) return NULL;
+
+    /* Remove from free sessions list */
+    if (ses == LastFreeSession) {
+	LastFreeSession = NULL;
+    }
+    FreeSessions = ses->next;
+
+    /* Put on busy sessions list */
+    ses->next = BusySessions;
+    BusySessions = ses;
+
+    /* Initialize fields to sane values */
+    ses->funcs = &DefaultSessionFunctionTable;
+    ses->pid = 0;
+    ses->ethif = NULL;
+    memset(ses->eth, 0, ETH_ALEN);
+    ses->flags = 0;
+    ses->startTime = time(NULL);
+    ses->serviceName = "";
+    ses->requested_mtu = 0;
+#ifdef HAVE_LICENSE
+    memset(ses->user, 0, MAX_USERNAME_LEN+1);
+    memset(ses->realm, 0, MAX_USERNAME_LEN+1);
+    memset(ses->realpeerip, 0, IPV4ALEN);
+#endif
+#ifdef HAVE_L2TP
+    ses->l2tp_ses = NULL;
+#endif
+    NumActiveSessions++;
+    return ses;
+}
+
+/**********************************************************************
+* %FUNCTION: pppoe_free_session
+* %ARGUMENTS:
+*  ses -- session to free
+* %RETURNS:
+*  0 if OK, -1 if error
+* %DESCRIPTION:
+*  Places a ClientSession on the free list.
+***********************************************************************/
+int
+pppoe_free_session(ClientSession *ses)
+{
+    ClientSession *cur, *prev;
+
+    cur = BusySessions;
+    prev = NULL;
+    while (cur) {
+	if (ses == cur) break;
+	prev = cur;
+	cur = cur->next;
+    }
+
+    if (!cur) {
+	syslog(LOG_ERR, "pppoe_free_session: Could not find session %p on busy list", (void *) ses);
+	return -1;
+    }
+
+    /* Remove from busy sessions list */
+    if (prev) {
+	prev->next = ses->next;
+    } else {
+	BusySessions = ses->next;
+    }
+
+    /* Add to end of free sessions */
+    ses->next = NULL;
+    if (LastFreeSession) {
+	LastFreeSession->next = ses;
+	LastFreeSession = ses;
+    } else {
+	FreeSessions = ses;
+	LastFreeSession = ses;
+    }
+
+    /* Initialize fields to sane values */
+    ses->funcs = &DefaultSessionFunctionTable;
+    ses->pid = 0;
+    memset(ses->eth, 0, ETH_ALEN);
+    ses->flags = 0;
+#ifdef HAVE_L2TP
+    ses->l2tp_ses = NULL;
+#endif
+    NumActiveSessions--;
+    return 0;
+}
+
+/**********************************************************************
+* %FUNCTION: sendHURLorMOTM
+* %ARGUMENTS:
+*  conn -- PPPoE connection
+*  url -- a URL, which *MUST* begin with "http://" or it won't be sent, or
+*         a message.
+*  tag -- one of TAG_HURL or TAG_MOTM
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Sends a PADM packet contaning a HURL or MOTM tag to the victim...er, peer.
+***********************************************************************/
+void
+sendHURLorMOTM(PPPoEConnection *conn, char const *url, UINT16_t tag)
+{
+    PPPoEPacket packet;
+    PPPoETag hurl;
+    size_t elen;
+    unsigned char *cursor = packet.payload;
+    UINT16_t plen = 0;
+
+    if (!conn->session) return;
+    if (conn->discoverySocket < 0) return;
+
+    if (tag == TAG_HURL) {
+	if (strncmp(url, "http://", 7) && strncmp(url, "https://", 8)) {
+	    syslog(LOG_WARNING, "sendHURL(%s): URL must begin with http:// or https://", url);
+	    return;
+	}
+    } else {
+	tag = TAG_MOTM;
+    }
+
+    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
+    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
+
+    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
+    packet.code = CODE_PADM;
+    packet.session = conn->session;
+
+    elen = strlen(url);
+    if (elen > 256) {
+	syslog(LOG_WARNING, "MOTM or HURL too long: %d", (int) elen);
+	return;
+    }
+
+    hurl.type = htons(tag);
+    hurl.length = htons(elen);
+    strcpy((char *) hurl.payload, url);
+    memcpy(cursor, &hurl, elen + TAG_HDR_SIZE);
+    cursor += elen + TAG_HDR_SIZE;
+    plen += elen + TAG_HDR_SIZE;
+
+    packet.length = htons(plen);
+
+    sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
+#ifdef DEBUGGING_ENABLED
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, &packet, "SENT");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+#endif
+}
Index: create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src
===================================================================
--- create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src	(nonexistent)
+++ create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src	(revision 5)

Property changes on: create-3.14-plugin-path-patch/rp-pppoe-3.14-new/src
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-plugin-path-patch/rp-pppoe-3.14-new
===================================================================
--- create-3.14-plugin-path-patch/rp-pppoe-3.14-new	(nonexistent)
+++ create-3.14-plugin-path-patch/rp-pppoe-3.14-new	(revision 5)

Property changes on: create-3.14-plugin-path-patch/rp-pppoe-3.14-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-3.14-plugin-path-patch
===================================================================
--- create-3.14-plugin-path-patch	(nonexistent)
+++ create-3.14-plugin-path-patch	(revision 5)

Property changes on: create-3.14-plugin-path-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: patches/README
===================================================================
--- patches/README	(nonexistent)
+++ patches/README	(revision 5)
@@ -0,0 +1,6 @@
+
+/* begin *
+
+   TODO: Leave some comment here.
+
+ * end */
Index: patches
===================================================================
--- patches	(nonexistent)
+++ patches	(revision 5)

Property changes on: patches
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- .	(nonexistent)
+++ .	(revision 5)

Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~