Submitted By:     DJ Lucas
Date:             2003-09-24
Initial Package Version:  2.4.1
Origin:           Backported from ppp-cvs.  Unfortunaltely, the original 
                  author who ripped the code out of CVS is unknown at 
                  this time.
Description:      Updated for gcc-3.3.1.  Adds PPPoE module support to
                  ppp-2.4.1 (for kernel mode PPPoE).

diff -Naur ppp-2.4.1-orig/configure ppp-2.4.1/configure
--- ppp-2.4.1-orig/configure	2000-04-24 02:41:40.000000000 -0500
+++ ppp-2.4.1/configure	2003-09-19 23:47:33.000000000 -0500
@@ -88,7 +88,7 @@
 fi
 
 case $state in
-  neolithic) 
+  neolithic)
     echo "This is a newer release on an outdated OS ($system)."
     echo " This software may or may not work on this OS."
     echo " You may want to download an older version of PPP for this OS.";;
@@ -120,18 +120,18 @@
   echo "  Makefile -> $ksrc/Makefile.top"
   if [ "$ksrc" = solaris ]; then
     # Point to 64-bit Makefile extension
-    if [ "$lp64" = y ]; then 
-      makext=$makext-64 
+    if [ "$lp64" = y ]; then
+      makext=$makext-64
     fi
     rm -f $ksrc/Makefile
     ln -s Makefile.$makext $ksrc/Makefile
     echo "  $ksrc/Makefile -> Makefile.$makext"
     # Restore extension
-    if [ "$lp64" = y ]; then 
-      makext=$orig_makext 
+    if [ "$lp64" = y ]; then
+      makext=$orig_makext
     fi
   fi
-  for dir in pppd pppstats chat pppdump; do
+  for dir in pppd pppstats chat pppdump pppd/plugins pppd/plugins/pppoe; do
     rm -f $dir/Makefile
     if [ -f $dir/Makefile.$makext ]; then
       ln -s Makefile.$makext $dir/Makefile
diff -Naur ppp-2.4.1-orig/linux/Makefile.top ppp-2.4.1/linux/Makefile.top
--- ppp-2.4.1-orig/linux/Makefile.top	2000-04-17 05:39:26.000000000 -0500
+++ ppp-2.4.1/linux/Makefile.top	2003-09-19 23:47:33.000000000 -0500
@@ -10,6 +10,7 @@
 
 all:
 	cd chat; $(MAKE) $(MFLAGS) all
+	cd pppd/plugins; $(MAKE) $(MFLAGS) all
 	cd pppd; $(MAKE) $(MFLAGS) all
 	cd pppstats; $(MAKE) $(MFLAGS) all
 	cd pppdump; $(MAKE) $(MFLAGS) all
@@ -18,6 +19,7 @@
 
 install-progs:
 	cd chat; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
+	cd pppd/plugins; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
 	cd pppd; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
 	cd pppstats; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
 	cd pppdump; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
@@ -44,6 +46,7 @@
 	rm -f `find . -name 'core' -print`
 	rm -f `find . -name '*~' -print`
 	cd chat; $(MAKE) clean
+	cd pppd/plugins; $(MAKE) clean
 	cd pppd; $(MAKE) clean
 	cd pppstats; $(MAKE) clean
 	cd pppdump; $(MAKE) clean
diff -Naur ppp-2.4.1-orig/pppd/Makefile.linux ppp-2.4.1/pppd/Makefile.linux
--- ppp-2.4.1-orig/pppd/Makefile.linux	2001-03-07 23:00:35.000000000 -0600
+++ ppp-2.4.1/pppd/Makefile.linux	2003-09-19 23:47:33.000000000 -0500
@@ -122,7 +122,7 @@
 	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8
 
 pppd: $(PPPDOBJS)
-	$(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
+	$(CC)  $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
 
 clean:
 	rm -f $(PPPDOBJS) pppd *~ #* core
diff -Naur ppp-2.4.1-orig/pppd/main.c ppp-2.4.1/pppd/main.c
--- ppp-2.4.1-orig/pppd/main.c	2001-03-12 23:56:19.000000000 -0600
+++ ppp-2.4.1/pppd/main.c	2003-09-19 23:47:33.000000000 -0500
@@ -291,6 +291,7 @@
      * Parse, in order, the system options file, the user's options file,
      * and the command line arguments.
      */
+
     if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
 	|| !options_from_user()
 	|| !parse_args(argc-1, argv+1))
@@ -510,7 +511,7 @@
 	 */
 	if (!demand) {
 	    if (pidfilename[0] != 0
-		&& unlink(pidfilename) < 0 && errno != ENOENT) 
+		&& unlink(pidfilename) < 0 && errno != ENOENT)
 		warn("unable to delete pid file %s: %m", pidfilename);
 	    pidfilename[0] = 0;
 	}
@@ -543,7 +544,7 @@
 
 	if (!demand) {
 	    if (pidfilename[0] != 0
-		&& unlink(pidfilename) < 0 && errno != ENOENT) 
+		&& unlink(pidfilename) < 0 && errno != ENOENT)
 		warn("unable to delete pid file %s: %m", pidfilename);
 	    pidfilename[0] = 0;
 	}
@@ -1039,10 +1040,10 @@
     if (the_channel->cleanup)
 	(*the_channel->cleanup)();
 
-    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) 
+    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
 	warn("unable to delete pid file %s: %m", pidfilename);
     pidfilename[0] = 0;
-    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT) 
+    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
 	warn("unable to delete pid file %s: %m", linkpidfile);
     linkpidfile[0] = 0;
 
@@ -1098,10 +1099,10 @@
     int secs, usecs;
 {
     struct callout *newp, *p, **pp;
-  
+
     MAINDEBUG(("Timeout %p:%p in %d.%03d seconds.", func, arg,
 	       time / 1000, time % 1000));
-  
+
     /*
      * Allocate timeout.
      */
@@ -1139,9 +1140,9 @@
     void *arg;
 {
     struct callout **copp, *freep;
-  
+
     MAINDEBUG(("Untimeout %p:%p.", func, arg));
-  
+
     /*
      * Find first matching timeout and remove it from the list.
      */
@@ -1495,7 +1496,7 @@
 #ifdef BSD
 	/* Force the priority back to zero if pppd is running higher. */
 	if (setpriority (PRIO_PROCESS, 0, 0) < 0)
-	    warn("can't reset priority to 0: %m"); 
+	    warn("can't reset priority to 0: %m");
 #endif
 
 	/* SysV recommends a second fork at this point. */
diff -Naur ppp-2.4.1-orig/pppd/options.c ppp-2.4.1/pppd/options.c
--- ppp-2.4.1-orig/pppd/options.c	2001-03-12 23:56:19.000000000 -0600
+++ ppp-2.4.1/pppd/options.c	2003-09-19 23:47:33.000000000 -0500
@@ -789,6 +789,23 @@
 }
 
 /*
+ * remove_option - permanently remove an option from consideration...
+ * for use by modules to remove choices which no longer make sense.
+ * returns true if found an option
+ */
+int
+remove_option(name)
+    char *name;
+{
+    option_t *o;
+    o = find_option(name);
+    if (o == NULL)
+	return 0;
+    o->name = "";
+    return 1;
+}
+
+/*
  * check_options - check that options are valid and consistent.
  */
 void
@@ -1385,7 +1402,7 @@
 {
     pc.linktype = DLT_PPP;
     pc.snapshot = PPP_HDRLEN;
- 
+
     if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
 	return 1;
     option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
@@ -1401,7 +1418,7 @@
 {
     pc.linktype = DLT_PPP;
     pc.snapshot = PPP_HDRLEN;
- 
+
     if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
 	return 1;
     option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
@@ -1410,7 +1427,7 @@
 #endif
 
 /*
- * setdomain - Set domain name to append to hostname 
+ * setdomain - Set domain name to append to hostname
  */
 static int
 setdomain(argv)
diff -Naur ppp-2.4.1-orig/pppd/plugins/Makefile.linux ppp-2.4.1/pppd/plugins/Makefile.linux
--- ppp-2.4.1-orig/pppd/plugins/Makefile.linux	2001-03-06 22:21:18.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/Makefile.linux	2003-09-19 23:47:33.000000000 -0500
@@ -3,7 +3,11 @@
 LDFLAGS	= -shared
 INSTALL	= install
 
-all:	minconn.so passprompt.so
+all:	minconn.so passprompt.so pppoe/pppoe.so
+	$(MAKE) -C pppoe $(MFLAGS) all
+
+pppoe/pppoe.so:
+	$(MAKE) -C pppoe $(MFLAGS) pppoe.so
 
 minconn.so: minconn.c
 	$(CC) -o $@ $(LDFLAGS) $(CFLAGS) minconn.c
@@ -13,7 +17,11 @@
 
 LIBDIR	= /usr/lib/pppd
 
-install: minconn.so passprompt.so
+install: minconn.so passprompt.so pppoe/pppoe.so
 	version=`awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h`; \
 	$(INSTALL) -d $(LIBDIR)/$$version; \
-	$(INSTALL) $? $(LIBDIR)/$$version
\ No newline at end of file
+	$(INSTALL) $? $(LIBDIR)/$$version
+
+clean:
+	$(MAKE) -C pppoe clean
+	rm -rf *.o *.so
\ No newline at end of file
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/Makefile.linux ppp-2.4.1/pppd/plugins/pppoe/Makefile.linux
--- ppp-2.4.1-orig/pppd/plugins/pppoe/Makefile.linux	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/Makefile.linux	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,43 @@
+CC	= gcc
+CFLAGS	= -g  -I.. -I../.. -I../../../include -D_linux_=1 -fPIC
+LDFLAGS	= -shared
+
+all: pppoe.so pppoed
+
+
+PLUGINDIR = $(LIBDIR)/plugins
+PLUGINSRCS= pppoe.c libpppoe.c utils.c pppoehash.c pppoe_client.c \
+		pppoe_relay.c pppoe_server.c pppd_utils.c
+#
+# include dependancies if present and backup if as a header file
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+
+
+pppoefwd: pppoefwd.o libpppoe.a 
+	$(CC) -o $@ $^
+
+pppoed: pppoed.o pppd_utils.o libpppoe.a 
+	$(CC) -o $@ $^
+
+libpppoe.a: pppoehash.o pppoe_client.o pppoe_relay.o pppoe_server.o \
+	utils.o libpppoe.o 
+	ar -rc $@ $^
+
+pppoe.so: pppoe.o libpppoe.a
+	$(CC) -o $@ $(LDFLAGS) $^
+
+%.so: %.c
+	$(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^
+
+clean: 
+	rm -f *.o *.so *.a pppoefwd pppoed
+
+
+$(PLUGINDIR):
+	$(INSTALL) -d -m 755 $@
+
+depend:
+	$(CPP) -M $(CFLAGS) $(PLUGINSRCS) >.depend
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/libpppoe.c ppp-2.4.1/pppd/plugins/pppoe/libpppoe.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/libpppoe.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/libpppoe.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,631 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+int disc_sock=-1;
+
+int verify_packet( struct session *ses, struct pppoe_packet *p);
+
+#define TAG_DATA(type,tag_ptr) ((type *) ((struct pppoe_tag*)tag_ptr)->tag_data)
+
+
+/***************************************************************************
+ *
+ * Return the location where the next tag can be pu
+ *
+ **************************************************************************/
+static  struct pppoe_tag *next_tag(struct pppoe_hdr *ph)
+{
+    return (struct pppoe_tag *)
+	(((char *) &ph->tag) + ntohs(ph->length));
+}
+
+/**************************************************************************
+ *
+ * Update header to reflect the addition of a new tag
+ *
+ **************************************************************************/
+static  void add_tag(struct pppoe_hdr *ph, struct pppoe_tag *pt)
+{
+    int len = (ntohs(ph->length) +
+	       ntohs(pt->tag_len) +
+	       sizeof(struct pppoe_tag));
+
+    if (pt != next_tag(ph))
+	printf("PPPoE add_tag caller is buggy\n");
+
+    ph->length = htons(len);
+}
+
+/*************************************************************************
+ *
+ * Look for a tag of a specific type
+ *
+ ************************************************************************/
+struct pppoe_tag *get_tag(struct pppoe_hdr *ph, u_int16_t idx)
+{
+    char *end = (char *) next_tag(ph);
+    char *ptn = NULL;
+    struct pppoe_tag *pt = &ph->tag[0];
+
+    /*
+     * Keep processing tags while a tag header will still fit.
+     *
+     * This check will ensure that the entire tag header pointed
+     * to by pt will fit inside the message, and thus it will be
+     * valid to check the tag_type and tag_len fields.
+     */
+    while ((char *)(pt + 1) <= end) {
+	/*
+	 * If the tag data would go past the end of the packet, abort.
+	 */
+	ptn = (((char *) (pt + 1)) + ntohs(pt->tag_len));
+	if (ptn > end)
+	    return NULL;
+
+	if (pt->tag_type == idx)
+	    return pt;
+
+	pt = (struct pppoe_tag *) ptn;
+    }
+
+    return NULL;
+}
+
+/* We want to use tag names to reference into arrays  containing the tag data.
+   This takes an RFC 2516 tag identifier and maps it into a local one.
+   The reverse mapping is accomplished via the tag_map array */
+#define UNMAP_TAG(x) case PTT_##x : return TAG_##x
+static inline int tag_index(int tag){
+    switch(tag){
+	UNMAP_TAG(SRV_NAME);
+	UNMAP_TAG(AC_NAME);
+	UNMAP_TAG(HOST_UNIQ);
+	UNMAP_TAG(AC_COOKIE);
+	UNMAP_TAG(VENDOR);
+	UNMAP_TAG(RELAY_SID);
+	UNMAP_TAG(SRV_ERR);
+	UNMAP_TAG(SYS_ERR);
+	UNMAP_TAG(GEN_ERR);
+	UNMAP_TAG(EOL);
+    };
+    return -1;
+}
+
+/*************************************************************************
+ *
+ * Makes a copy of a tag into a PPPoE packe
+ *
+ ************************************************************************/
+void copy_tag(struct pppoe_packet *dest, struct pppoe_tag *pt)
+{
+    struct pppoe_tag *end_tag = get_tag(dest->hdr, PTT_EOL);
+    int tagid;
+    int tag_len;
+    if( !pt ) {
+	return;
+    }
+    tagid = tag_index(pt->tag_type);
+
+    tag_len = sizeof(struct pppoe_tag) + ntohs(pt->tag_len);
+
+    if( end_tag ){
+	memcpy(((char*)end_tag)+tag_len ,
+	       end_tag, sizeof(struct pppoe_tag));
+
+	dest->tags[tagid]=end_tag;
+	dest->tags[TAG_EOL] = (struct pppoe_tag*)((char*)dest->tags[TAG_EOL] + tag_len);
+	memcpy(end_tag, pt, tag_len);
+	dest->hdr->length = htons(ntohs(dest->hdr->length) + tag_len);
+
+    }else{
+	memcpy(next_tag(dest->hdr),pt, tag_len);
+	dest->tags[tagid]=next_tag(dest->hdr);
+	add_tag(dest->hdr,next_tag(dest->hdr));
+    }
+
+
+}
+
+
+/*************************************************************************
+ *
+ * Put tags from a packet into a nice array
+ *
+ ************************************************************************/
+static void extract_tags(struct pppoe_hdr *ph, struct pppoe_tag** buf){
+    int i=0;
+    for(;i<MAX_TAGS;++i){
+	buf[i] = get_tag(ph,tag_map[i]);
+    }
+}
+
+
+/*************************************************************************
+ *
+ * Verify that a packet has a tag containint a specific value
+ *
+ ************************************************************************/
+static int verify_tag(struct session* ses,
+		      struct pppoe_packet* p,
+		      unsigned short id,
+		      char* data,
+		      int data_len)
+{
+    int len;
+    struct pppoe_tag *pt = p->tags[id];
+
+    if( !pt ){
+	poe_info(ses,"Missing tag %d. Expected %s\n",
+		 id,data);
+	return 0;
+    }
+    len = ntohs(pt->tag_len);
+    if(len != data_len){
+	poe_info(ses,"Length mismatch on tag %d: expect: %d got: %d\n",
+		 id, data_len, len);
+	return 0;
+    }
+
+    if( 0!=memcmp(pt->tag_data,data,data_len)){
+	poe_info(ses,"Tag data mismatch on tag %d: expect: %s vs %s\n",
+		 id, data,pt->tag_data);
+	return 0;
+    }
+    return 1;
+}
+
+
+/*************************************************************************
+ *
+ * Verify the existence of an ethernet device.
+ * Construct an AF_PACKET address struct to match.
+ *
+ ************************************************************************/
+int get_sockaddr_ll(const char *devnam,struct sockaddr_ll* sll){
+    struct ifreq ifr;
+    int retval;
+
+    if(disc_sock<0){
+
+	disc_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if( disc_sock < 0 ){
+	    return -1;
+	}
+    }
+
+    strncpy(ifr.ifr_name, devnam, sizeof(ifr.ifr_name));
+
+    retval = ioctl( disc_sock , SIOCGIFINDEX, &ifr);
+
+    if( retval < 0 ){
+//	error("Bad device name: %s  (%m)",devnam);
+	return 0;
+    }
+
+    if(sll) sll->sll_ifindex = ifr.ifr_ifindex;
+
+    retval = ioctl (disc_sock, SIOCGIFHWADDR, &ifr);
+    if( retval < 0 ){
+//	error("Bad device name: %s  (%m)",devnam);
+	return 0;
+    }
+
+    if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+	error("Interface %s is not Ethernet!", devnam);
+	return 0;
+    }
+    if(sll){
+	sll->sll_family	= AF_PACKET;
+	sll->sll_protocol= ntohs(ETH_P_PPP_DISC);
+	sll->sll_hatype	= ARPHRD_ETHER;
+	sll->sll_pkttype = PACKET_BROADCAST;
+	sll->sll_hatype	= ETH_ALEN;
+	memcpy( sll->sll_addr , ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+    }
+    return 1;
+}
+
+
+
+
+/*************************************************************************
+ *
+ * Construct and send a discovery message.
+ *
+ ************************************************************************/
+int send_disc(struct session *ses, struct pppoe_packet *p)
+{
+    char buf[MAX_PAYLOAD + sizeof(struct pppoe_hdr)];
+    int data_len = sizeof(struct pppoe_hdr);
+
+    struct pppoe_hdr *ph = NULL;
+    struct pppoe_tag *tag = NULL;
+    int i, error = 0;
+    int got_host_uniq = 0;
+    int got_srv_name = 0;
+    int got_ac_name = 0;
+
+    for (i = 0; i < MAX_TAGS; i++) {
+	if (!p->tags[i])
+	    continue;
+
+	got_host_uniq |= (p->tags[i]->tag_type == PTT_HOST_UNIQ);
+
+	/* Relay identifiers qualify as HOST_UNIQ's:
+	   we need HOST_UNIQ to uniquely identify the packet,
+	   PTT_RELAY_SID is sufficient for us for outgoing packets */
+	got_host_uniq |= (p->tags[i]->tag_type == PTT_RELAY_SID);
+
+	got_srv_name |= (p->tags[i]->tag_type == PTT_SRV_NAME);
+	got_ac_name  |= (p->tags[i]->tag_type == PTT_AC_NAME);
+
+	data_len += (ntohs(p->tags[i]->tag_len) +
+		     sizeof(struct pppoe_tag));
+    }
+
+    ph = (struct pppoe_hdr *) buf;
+
+
+    memcpy(ph, p->hdr, sizeof(struct pppoe_hdr));
+    ph->length = __constant_htons(0);
+
+    /* if no HOST_UNIQ tags --- add one with process id */
+    if (!got_host_uniq){
+	data_len += (sizeof(struct pppoe_tag) +
+		     sizeof(struct session *));
+	tag = next_tag(ph);
+	tag->tag_type = PTT_HOST_UNIQ;
+	tag->tag_len = htons(sizeof(struct session *));
+	memcpy(tag->tag_data,
+	       &ses,
+	       sizeof(struct session *));
+
+	add_tag(ph, tag);
+    }
+
+    if( !got_srv_name ){
+	data_len += sizeof(struct pppoe_tag);
+	tag = next_tag(ph);
+	tag->tag_type = PTT_SRV_NAME;
+	tag->tag_len = 0;
+	add_tag(ph, tag);
+    }
+
+    if(!got_ac_name && ph->code==PADO_CODE){
+	data_len += sizeof(struct pppoe_tag);
+	tag = next_tag(ph);
+	tag->tag_type = PTT_AC_NAME;
+	tag->tag_len = 0;
+	add_tag(ph, tag);
+    }
+
+    for (i = 0; i < MAX_TAGS; i++) {
+	if (!p->tags[i])
+	    continue;
+
+	tag = next_tag(ph);
+	memcpy(tag, p->tags[i],
+	       sizeof(struct pppoe_tag) + ntohs(p->tags[i]->tag_len));
+
+	add_tag(ph, tag);
+    }
+
+    /* Now fixup the packet struct to make sure all of its pointers
+       are self-contained */
+    memcpy( p->hdr , ph, data_len );
+    extract_tags( p->hdr, p->tags);
+
+    error = sendto(disc_sock, buf, data_len, 0,
+		   (struct sockaddr*) &p->addr,
+		   sizeof(struct sockaddr_ll));
+
+    if(error < 0)
+	poe_error(ses,"sendto returned: %m\n");
+
+    return error;
+}
+
+/*************************************************************************
+ *
+ * Verify that a packet is legal
+ *
+ *************************************************************************/
+int verify_packet( struct session *ses, struct pppoe_packet *p){
+    struct session * hu_val;
+
+    /* This code here should do all of the error checking and
+       validation on the incoming packet */
+
+
+    /* If we receive any error tags, abort */
+#define CHECK_TAG(name, val)					\
+    if((NULL==p->tags[name])== val){				\
+	poe_error(ses,"Tag error: " #name );			\
+	return -1;						\
+    }
+
+
+
+    CHECK_TAG(TAG_SRV_ERR,0);
+    CHECK_TAG(TAG_SYS_ERR,0);
+    CHECK_TAG(TAG_GEN_ERR,0);
+
+    /* A HOST_UNIQ must be present */
+    CHECK_TAG(TAG_HOST_UNIQ,1);
+
+    hu_val = *TAG_DATA(struct session* ,p->tags[TAG_HOST_UNIQ]);
+
+    if( hu_val != ses ){
+	poe_info(ses,"HOST_UNIQ mismatch: %08x %i\n",(int)hu_val,getpid());
+	return -1;
+    }
+
+    if(ses->filt->htag &&
+       !verify_tag(ses,p,TAG_HOST_UNIQ,ses->filt->htag->tag_data,(int)ntohs(ses->filt->htag->tag_len)))
+	return -1;
+    else
+	poe_info(ses,"HOST_UNIQ successful match\n");
+
+
+    if(ses->filt->ntag &&
+       !verify_tag(ses,p,TAG_AC_NAME,ses->filt->ntag->tag_data,(int)ntohs(ses->filt->ntag->tag_len))){
+	poe_info(ses,"AC_NAME failure");
+	return -1;
+    }
+
+    if(ses->filt->stag &&
+       !verify_tag(ses,p,TAG_SRV_NAME,ses->filt->stag->tag_data,(int)ntohs(ses->filt->stag->tag_len))){
+	poe_info(ses,"SRV_NAME failure");
+	return -1;
+    }
+
+}
+
+
+/*************************************************************************
+ *
+ * Receive and verify an incoming packet.
+ *
+ *************************************************************************/
+static int recv_disc( struct session *ses,
+		      struct pppoe_packet *p){
+    int error = 0;
+    unsigned int from_len = sizeof(struct sockaddr_ll);
+    struct session* hu_val;
+    struct pppoe_tag *pt;
+
+    p->hdr = (struct pppoe_hdr*)p->buf;
+
+    error = recvfrom( disc_sock, p->buf, 1500, 0,
+		      (struct sockaddr*)&p->addr, &from_len);
+
+    if(error < 0) return error;
+
+    extract_tags(p->hdr,p->tags);
+
+    return 1;
+}
+
+
+/*************************************************************************
+ *
+ * Send a PADT
+ *
+ *************************************************************************/
+int session_disconnect(struct session *ses){
+    struct pppoe_packet padt;
+
+    memset(&padt,0,sizeof(struct pppoe_packet));
+    memcpy(&padt.addr, &ses->remote, sizeof(struct sockaddr_ll));
+
+    padt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
+    padt.hdr->ver  = 1;
+    padt.hdr->type = 1;
+    padt.hdr->code = PADT_CODE;
+    padt.hdr->sid  = ses->sp.sa_addr.pppoe.sid;
+
+    send_disc(ses,&padt);
+    ses->sp.sa_addr.pppoe.sid = 0 ;
+    ses->state = PADO_CODE;
+    return 0;
+
+}
+
+
+/*************************************************************************
+ *
+ * Make a connection -- behaviour depends on callbacks specified in "ses"
+ *
+ *************************************************************************/
+int session_connect(struct session *ses)
+{
+
+    int pkt_size=0;
+    int ret_pkt_size=0;
+    struct pppoe_tag *tags = NULL;
+    struct pppoe_packet *p_out=NULL;
+    struct pppoe_packet rcv_packet;
+    int ret;
+
+
+    if(ses->init_disc){
+	ret = (*ses->init_disc)(ses, NULL, &p_out);
+	if( ret != 0 ) return ret;
+    }
+
+    /* main discovery loop */
+
+
+    while(ses->retransmits < ses->retries || ses->retries==-1 ){
+
+	fd_set in;
+	struct timeval tv;
+	FD_ZERO(&in);
+
+	FD_SET(disc_sock,&in);
+
+	if(ses->retransmits>=0){
+	    ++ses->retransmits;
+	    tv.tv_sec = 1 << ses->retransmits;
+	    tv.tv_usec = 0;
+	    ret = select(disc_sock+1, &in, NULL, NULL, &tv);
+	}else{
+	    ret = select(disc_sock+1, &in, NULL, NULL, NULL);
+	}
+
+	if( ret == 0 ){
+	    if( DEB_DISC ){
+		poe_dbglog(ses, "Re-sending ...");
+	    }
+
+	    if( ses->timeout ){
+		ret = (*ses->timeout)(ses, NULL, &p_out);
+		if( ret != 0 )
+		    return ret;
+
+	    }else if(p_out){
+		send_disc(ses,p_out);
+	    }
+	    continue;
+	}
+
+
+	ret = recv_disc(ses, &rcv_packet);
+
+	/* Should differentiate between system errors and
+	   bad packets and the like... */
+	if( ret < 0 && errno != EINTR){
+
+	    return -1;
+	}
+
+
+
+
+	switch (rcv_packet.hdr->code) {
+
+	case PADI_CODE:
+	{
+	    if(ses->rcv_padi){
+		ret = (*ses->rcv_padi)(ses,&rcv_packet,&p_out);
+
+		if( ret != 0){
+		    return ret;
+		}
+	    }
+	    break;
+	}
+
+	case PADO_CODE:		/* wait for PADO */
+	{
+	    if(ses->rcv_pado){
+		ret = (*ses->rcv_pado)(ses,&rcv_packet,&p_out);
+
+		if( ret != 0){
+		    return ret;
+		}
+	    }
+	    break;
+	}
+
+	case PADR_CODE:
+	{
+	    if(ses->rcv_padr){
+		ret = (*ses->rcv_padr)(ses,&rcv_packet,&p_out);
+
+		if( ret != 0){
+		    return ret;
+		}
+	    }
+	    break;
+	}
+
+	case PADS_CODE:
+	{
+	    if(ses->rcv_pads){
+		ret = (*ses->rcv_pads)(ses,&rcv_packet,&p_out);
+
+		if( ret != 0){
+		    return ret;
+		}
+	    }
+	    break;
+	}
+
+	case PADT_CODE:
+	{
+	    if( rcv_packet.hdr->sid != ses->sp.sa_addr.pppoe.sid ){
+		--ses->retransmits;
+		continue;
+	    }
+	    if(ses->rcv_padt){
+		ret = (*ses->rcv_padt)(ses,&rcv_packet,&p_out);
+
+		if( ret != 0){
+		    return ret;
+		}
+	    }else{
+		poe_error (ses,"connection terminated");
+		return (-1);
+	    }
+	    break;
+	}
+	default:
+	    poe_error(ses,"invalid packet %P",&rcv_packet);
+	    return (-1);
+	}
+    }
+    return (0);
+}
+
+
+/*************************************************************************
+ *
+ * Register an ethernet address as a client of relaying services.
+ *
+ *************************************************************************/
+int add_client(char *addr)
+{
+    struct pppoe_con* pc = (struct pppoe_con*)malloc(sizeof(struct pppoe_con));
+    int ret;
+    if(!pc)
+	return -ENOMEM;
+
+    memset(pc, 0 , sizeof(struct pppoe_con));
+
+    memcpy(pc->client,addr, ETH_ALEN);
+    memcpy(pc->key, addr, ETH_ALEN);
+
+    pc->key_len = ETH_ALEN;
+
+    if( (ret=store_con(pc)) < 0 ){
+	free(pc);
+    }
+    return ret;
+
+}
+
+struct pppoe_tag *make_filter_tag(short type, short length, char* data){
+    struct pppoe_tag *pt =
+	(struct pppoe_tag* )malloc( sizeof(struct pppoe_tag) + length );
+
+    if(pt == NULL) return NULL;
+
+    pt->tag_len=htons(length);
+    pt->tag_type=type;
+
+    if(length>0 && data){
+	memcpy( pt+1, data, length);
+    }
+    return pt;
+}
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppd_utils.c ppp-2.4.1/pppd/plugins/pppoe/pppd_utils.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppd_utils.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppd_utils.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,162 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include "pppoe.h"
+
+/*
+ *
+ */
+int build_ppp_opts(char *args[],struct session *ses)
+{
+    char buf[256];
+    int retval=0,i=0;
+    
+    memset(buf,0,256);
+    
+/* pppds path */
+    if ( NULL != ses->filt->pppd){
+	args[0]=(char *)malloc(strlen(ses->filt->pppd));
+        strcpy (args[0],ses->filt->pppd);
+    } else {
+	args[0]=(char *)malloc(strlen(_PATH_PPPD));
+        strcpy (args[0],_PATH_PPPD);
+    }
+    
+/*  long device name */
+    snprintf(buf, 256,"%02x:%02x:%02x:%02x:%02x:%02x/%04x/%s",
+	     ses->remote.sll_addr[0],
+	     ses->remote.sll_addr[1],
+	     ses->remote.sll_addr[2],
+	     ses->remote.sll_addr[3],
+	     ses->remote.sll_addr[4],
+	     ses->remote.sll_addr[5],
+	     ses->sp.sa_addr.pppoe.sid,
+	     ses->name);
+    args[1]=(char *)malloc(strlen(buf));
+    strcpy(args[1],buf);
+    
+    i=2;
+    
+/* override options file */
+    if (NULL != ses->filt->fname ) {
+	
+	if (!ses->filt->peermode) {
+	    args[i]=(char *)malloc(strlen("file"));
+	    strcpy (args[i],"file");
+	    i++;
+	    args[i]=(char *)malloc(strlen(ses->filt->fname)+1);
+	    strcpy (args[i],ses->filt->fname);
+	    i++;
+	} else{ /* peermode */
+	    args[i]=(char *)malloc(strlen("call"));
+	    strcpy (args[i],"call");
+	    i++;
+	    args[i]=(char *)malloc(strlen(ses->filt->fname)+1);
+	    strcpy (args[i],ses->filt->fname);
+	    i++;
+	}
+    }
+    
+/* user requested for a specific name */
+    if (NULL != ses->filt->ntag) {
+	if ( NULL != ses->filt->ntag->tag_data) {
+	    args[i]=(char *)malloc(strlen("pppoe_ac_name"));
+	    strcpy(args[i],"pppoe_ac_name");
+	    i++;
+	    args[i]=(char *)malloc(ntohs(ses->filt->ntag->tag_len));
+	    strcpy(args[i],ses->filt->ntag->tag_data);
+	    i++;
+	}
+    }
+/* user requested for a specific service name */
+    if (NULL != ses->filt->stag) {
+	if ( NULL != ses->filt->stag->tag_data) {
+	    args[i]=(char *)malloc(strlen("pppoe_srv_name"));
+	    strcpy(args[i],"pppoe_srv_name");
+	    i++;
+	    args[i]=(char *)malloc(ntohs(ses->filt->stag->tag_len));
+	    strcpy(args[i],ses->filt->stag->tag_data);
+	    i++;
+	}
+    }
+    
+/*
+ */
+    if (ses->opt_daemonize) {
+	args[i]=(char *)malloc(strlen("nodetach"));
+	strcpy(args[i],"nodetach");
+	i++;
+    }
+    
+    args[i]=NULL;
+    {
+	int j;
+	poe_info(ses,"calling pppd with %d args\n",i);
+	j=i;
+	for (i=0; i<j,NULL !=args[i]; i++) {
+	    poe_info(ses," <%d: %s > \n",i,args[i]);
+	}
+    }
+    return retval;
+}
+
+
+/*
+ *
+ */
+int ppp_connect (struct session *ses)
+{
+    int ret,pid;
+    char *args[32];
+    
+    
+    poe_info(ses,"calling ses_connect\n");
+    do{
+	ret = session_connect(ses);
+    }while(ret == 0);
+
+    if (ret > 0 )
+	if (ses->np == 1 && ret == 1)
+	    return ses->np; /* -G */
+    if (ses->np == 2)
+	return ses->np; /* -H */
+
+    if( ret <= 0){
+	return ret;
+    }
+    
+    poe_info(ses,"DONE calling ses_connect np is %d \n",ses->np);
+    
+    
+    pid = fork ();
+    if (pid < 0) {
+	poe_error (ses,"unable to fork() for pppd: %m");
+	poe_die (-1);
+    }
+    
+    
+    if(!pid) {
+	poe_info(ses,"calling build_ppp_opts\n");
+	if (0> build_ppp_opts(args,ses)) {
+	    poe_error(ses,"ppp_connect: failed to build ppp_opts\n");
+	    return -1;
+	}
+	execvp(args[0],args);
+	poe_info (ses," child got killed");
+    } else if( ses->type == SESSION_CLIENT) {
+	if (!ses->opt_daemonize)
+	    return 1;
+	pause();
+	poe_info (ses," OK we got killed");
+	return -1;
+    }
+    return 1;
+}
+
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe.c ppp-2.4.1/pppd/plugins/pppoe/pppoe.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoe.c	2003-09-19 23:47:51.000000000 -0500
@@ -0,0 +1,386 @@
+/* pppoe.c - pppd plugin to implement PPPoE protocol.
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
+ * which is based in part on work from Jens Axboe and Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include "pppoe.h"
+#if _linux_
+extern int new_style_driver;    /* From sys-linux.c */
+#include <linux/ppp_defs.h>
+#include <linux/if_pppox.h>
+#include <linux/if_ppp.h>
+#else
+#error this module meant for use with linux only at this time
+#endif
+
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "ccp.h"
+#include "pathnames.h"
+
+const char pppd_version[] = VERSION;
+
+#define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
+
+#define PPPOE_MTU	1492
+extern int kill_link;
+static char *bad_options[] = {
+    "noaccomp",
+    "-ac",
+    "default-asyncmap",
+    "-am",
+    "asyncmap",
+    "-as",
+    "escape",
+    "multilink",
+    "receive-all",
+    "crtscts",
+    "-crtscts",
+    "nocrtscts",
+    "cdtrcts",
+    "nocdtrcts",
+    "xonxoff",
+    "modem",
+    "local",
+    "sync",
+    "deflate",
+    "nodeflate",
+    "vj",
+    "novj",
+    "nobsdcomp",
+    "bsdcomp",
+    "-bsdcomp",
+    NULL
+};
+
+bool	pppoe_server=0;
+char	*pppoe_srv_name=NULL;
+char	*pppoe_ac_name=NULL;
+char    *hostuniq = NULL;
+int     retries = 0;
+
+int setdevname_pppoe(const char *cp);
+
+static option_t pppoe_options[] = {
+	{ "device name", o_wild, (void *) &setdevname_pppoe,
+	  "Serial port device name",
+	  OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+	  devnam},
+	{ "pppoe_srv_name", o_string, &pppoe_srv_name,
+	  "PPPoE service name"},
+	{ "pppoe_ac_name", o_string, &pppoe_ac_name,
+	  "PPPoE access concentrator name"},
+	{ "pppoe_hostuniq", o_string, &hostuniq,
+	  "PPPoE client uniq hostid "},
+	{ "pppoe_retransmit", o_int, &retries,
+	  "PPPoE client number of retransmit tries"},
+	{ "pppoe_server", o_bool, &pppoe_server,
+	  "PPPoE listen for incoming requests",1},
+	{ NULL }
+};
+
+
+
+struct session *ses = NULL;
+static int connect_pppoe_ses(void)
+{
+    int i,err=-1;
+    if( pppoe_server == 1 ){
+	srv_init_ses(ses,devnam);
+    }else{
+	client_init_ses(ses,devnam);
+    }
+#if 0
+    ses->np=1;  /* jamal debug the discovery portion */
+#endif
+    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+
+    err= session_connect ( ses );
+
+    if(err < 0){
+	poe_fatal(ses,"Failed to negotiate PPPoE connection: %d %m",errno,errno);
+    }
+
+
+    poe_info(ses,"Connecting PPPoE socket: %E %04x %s %p",
+	     ses->sp.sa_addr.pppoe.remote,
+	     ses->sp.sa_addr.pppoe.sid,
+	     ses->sp.sa_addr.pppoe.dev,ses);
+
+    err = connect(ses->fd, (struct sockaddr*)&ses->sp,
+		  sizeof(struct sockaddr_pppox));
+
+
+    if( err < 0 ){
+	poe_fatal(ses,"Failed to connect PPPoE socket: %d %m",errno,errno);
+	return err;
+    }
+#if 0
+    if (ses->np)
+     	fatal("discovery complete\n");
+#endif
+    /* Once the logging is fixed, print a message here indicating
+       connection parameters */
+
+    return ses->fd;
+}
+
+static void disconnect_pppoe_ses(void)
+{
+    int ret;
+    warn("Doing disconnect");
+    session_disconnect(ses);
+    ses->sp.sa_addr.pppoe.sid = 0;
+    ret = connect(ses->fd, (struct sockaddr*)&ses->sp,
+	    sizeof(struct sockaddr_pppox));
+
+}
+
+
+static int setspeed_pppoe(const char *cp)
+{
+    return 0;
+}
+
+static void init_device_pppoe(void)
+{
+    struct filter *filt;
+    unsigned int size=0;
+    ses=(void *)malloc(sizeof(struct session));
+    if(!ses){
+	fatal("No memory for new PPPoE session");
+    }
+    memset(ses,0,sizeof(struct session));
+
+    if ((ses->filt=malloc(sizeof(struct filter))) == NULL) {
+	poe_error (ses,"failed to malloc for Filter ");
+	poe_die (-1);
+    }
+
+    filt=ses->filt;  /* makes the code more readable */
+    memset(filt,0,sizeof(struct filter));
+
+    if (pppoe_ac_name !=NULL) {
+	if (strlen (pppoe_ac_name) > 255) {
+	    poe_error (ses," AC name too long (maximum allowed 256 chars)");
+	    poe_die(-1);
+	}
+	ses->filt->ntag = make_filter_tag(PTT_AC_NAME,
+					  strlen(pppoe_ac_name),
+					  pppoe_ac_name);
+
+	if ( ses->filt->ntag== NULL) {
+	    poe_error (ses,"failed to malloc for AC name");
+	    poe_die(-1);
+	}
+
+    }
+
+
+    if (pppoe_srv_name !=NULL) {
+	if (strlen (pppoe_srv_name) > 255) {
+	    poe_error (ses," Service name too long (maximum allowed 256 chars)");
+	    poe_die(-1);
+	}
+	ses->filt->stag = make_filter_tag(PTT_SRV_NAME,
+					  strlen(pppoe_srv_name),
+					  pppoe_srv_name);
+	if ( ses->filt->stag == NULL) {
+	    poe_error (ses,"failed to malloc for service name");
+	    poe_die(-1);
+	}
+    }
+
+    if (hostuniq) {
+	ses->filt->htag = make_filter_tag(PTT_HOST_UNIQ,
+					  strlen(hostuniq),
+					  hostuniq);
+	if ( ses->filt->htag == NULL) {
+	    poe_error (ses,"failed to malloc for Uniq Host Id ");
+	    poe_die(-1);
+	}
+    }
+
+    if (retries) {
+	ses->retries=retries;
+    }
+
+    memcpy( ses->name, devnam, IFNAMSIZ);
+    ses->opt_debug=1;
+}
+
+static void pppoe_extra_options()
+{
+    int ret;
+    char buf[256];
+    snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
+    if(!options_from_file(buf, 0, 0, 1))
+	exit(EXIT_OPTION_ERROR);
+
+}
+
+
+
+static void send_config_pppoe(int mtu,
+			      u_int32_t asyncmap,
+			      int pcomp,
+			      int accomp)
+{
+    int sock;
+    struct ifreq ifr;
+
+    if (mtu > PPPOE_MTU) {
+	warn("Couldn't increase MTU to %d.", mtu);
+	mtu = PPPOE_MTU;
+    }
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0)
+	fatal("Couldn't create IP socket: %m");
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_mtu = mtu;
+    if (ioctl(sock, SIOCSIFMTU, (caddr_t) &ifr) < 0)
+	fatal("ioctl(SIOCSIFMTU): %m");
+    (void) close (sock);
+}
+
+
+static void recv_config_pppoe(int mru,
+			      u_int32_t asyncmap,
+			      int pcomp,
+			      int accomp)
+{
+    if (mru > PPPOE_MTU)
+	error("Couldn't increase MRU to %d", mru);
+}
+
+static void set_xaccm_pppoe(int unit, ext_accm accm)
+{
+    /* NOTHING */
+}
+
+
+
+struct channel pppoe_channel;
+/* Check is cp is a valid ethernet device
+ * return either 1 if "cp" is a reasonable thing to name a device
+ * or die.
+ * Note that we don't actually open the device at this point
+ * We do need to fill in:
+ *   devnam: a string representation of the device
+ */
+
+int (*old_setdevname_hook)(const char* cp) = NULL;
+int setdevname_pppoe(const char *cp)
+{
+    int ret;
+    char dev[IFNAMSIZ+1];
+    int addr[ETH_ALEN];
+    int sid;
+
+    char **a;
+    ret =sscanf(cp, FMTSTRING(IFNAMSIZ),addr, addr+1, addr+2,
+		addr+3, addr+4, addr+5,&sid,dev);
+    if( ret != 8 ){
+
+	ret = get_sockaddr_ll(cp,NULL);
+        if (ret < 0)
+	    fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
+	if (ret == 1)
+	    strncpy(devnam, cp, sizeof(devnam));
+    }else{
+	/* long form parsed */
+	ret = get_sockaddr_ll(dev,NULL);
+        if (ret < 0)
+	    fatal("PPPoE: Cannot create PF_PACKET socket for PPPoE discovery\n");
+
+	strncpy(devnam, cp, sizeof(devnam));
+	ret = 1;
+    }
+
+
+    if( ret == 1 && the_channel != &pppoe_channel ){
+
+	the_channel = &pppoe_channel;
+
+	{
+	    char **a;
+	    for (a = bad_options; *a != NULL; a++)
+		remove_option(*a);
+	}
+	modem = 0;
+
+	lcp_allowoptions[0].neg_accompression = 0;
+	lcp_wantoptions[0].neg_accompression = 0;
+
+	lcp_allowoptions[0].neg_asyncmap = 0;
+	lcp_wantoptions[0].neg_asyncmap = 0;
+
+	lcp_allowoptions[0].neg_pcompression = 0;
+	lcp_wantoptions[0].neg_pcompression = 0;
+
+	ccp_allowoptions[0].deflate = 0 ;
+	ccp_wantoptions[0].deflate = 0 ;
+
+	ipcp_allowoptions[0].neg_vj=0;
+	ipcp_wantoptions[0].neg_vj=0;
+
+	ccp_allowoptions[0].bsd_compress = 0;
+	ccp_wantoptions[0].bsd_compress = 0;
+
+	init_device_pppoe();
+    }
+    return ret;
+}
+
+
+
+void plugin_init(void)
+{
+/*
+  fatal("PPPoE plugin loading...");
+*/
+
+#if _linux_
+    if (!ppp_available() && !new_style_driver)
+	fatal("Kernel doesn't support ppp_generic needed for PPPoE");
+#else
+    fatal("No PPPoE support on this OS");
+#endif
+    add_options(pppoe_options);
+
+
+    info("PPPoE Plugin Initialized");
+}
+
+struct channel pppoe_channel = {
+    options: pppoe_options,
+    process_extra_options: &pppoe_extra_options,
+    check_options: NULL,
+    connect: &connect_pppoe_ses,
+    disconnect: &disconnect_pppoe_ses,
+    establish_ppp: &generic_establish_ppp,
+    disestablish_ppp: &generic_disestablish_ppp,
+    send_config: &send_config_pppoe,
+    recv_config: &recv_config_pppoe,
+    close: NULL,
+    cleanup: NULL
+};
+
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe.h ppp-2.4.1/pppd/plugins/pppoe/pppoe.h
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe.h	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoe.h	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,273 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#ifndef PPPOE_H
+#define PPPOE_H	1
+#include <stdio.h>		/* stdio               */
+#include <stdlib.h>		/* strtoul(), realloc() */
+#include <unistd.h>		/* STDIN_FILENO,exec    */
+#include <string.h>		/* memcpy()             */
+#include <errno.h>		/* errno                */
+#include <signal.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <paths.h>
+
+#include <sys/types.h>		/* socket types         */
+#include <asm/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>		/* ioctl()              */
+#include <sys/select.h>
+#include <sys/socket.h>		/* socket()             */
+#include <net/if.h>		/* ifreq struct         */
+#include <net/if_arp.h>
+#include <netinet/in.h>
+
+#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+//#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+
+#include <asm/byteorder.h>
+
+/*
+  jamal: we really have to change this
+  to make it compatible to the 2.2 and
+  2.3 kernel
+*/
+
+#include <linux/if_pppox.h>
+
+
+#define CONNECTED 1
+#define DISCONNECTED 0
+
+#ifndef _PATH_PPPD
+#define _PATH_PPPD "/usr/sbin/pppd"
+#endif
+
+#ifndef LOG_PPPOE
+#define LOG_PPPOE LOG_DAEMON
+#endif
+
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 4
+#define VERSION_DATE 991120
+
+/* Bigger than the biggest ethernet packet we'll ever see, in bytes */
+#define MAX_PACKET      2000
+
+/* references: RFC 2516 */
+/* ETHER_TYPE fields for PPPoE */
+
+#define ETH_P_PPPOE_DISC 0x8863	/* discovery stage */
+#define ETH_P_PPPOE_SESS 0x8864
+
+/* ethernet broadcast address */
+#define MAC_BCAST_ADDR "\xff\xff\xff\xff\xff\xff"
+
+/* Format for parsing long device-name */
+#define _STR(x) #x
+#define FMTSTRING(size) "%x:%x:%x:%x:%x:%x/%x/%" _STR(size) "s"
+
+/* maximum payload length */
+#define MAX_PAYLOAD 1484
+
+
+
+/* PPPoE tag types */
+#define MAX_TAGS		11
+
+
+/* PPPoE packet; includes Ethernet headers and such */
+struct pppoe_packet{
+	struct sockaddr_ll addr;
+	struct pppoe_tag *tags[MAX_TAGS];
+	struct pppoe_hdr *hdr;
+	char buf[MAX_PAYLOAD];		/* buffer in which tags are held */
+};
+/* Defines meaning of each "tags" element */
+
+#define TAG_SRV_NAME	0
+#define TAG_AC_NAME	1
+#define TAG_HOST_UNIQ	2
+#define TAG_AC_COOKIE	3
+#define TAG_VENDOR 	4
+#define TAG_RELAY_SID	5
+#define TAG_SRV_ERR     6
+#define TAG_SYS_ERR  	7
+#define TAG_GEN_ERR  	8
+#define TAG_EOL		9
+
+static int tag_map[] = { PTT_SRV_NAME,
+			 PTT_AC_NAME,
+			 PTT_HOST_UNIQ,
+			 PTT_AC_COOKIE,
+			 PTT_VENDOR,
+			 PTT_RELAY_SID,
+			 PTT_SRV_ERR,
+			 PTT_SYS_ERR,
+			 PTT_GEN_ERR,
+			 PTT_EOL
+};
+
+
+/* Debug flags */
+int DEB_DISC,DEB_DISC2;
+/*
+  #define DEB_DISC		(opt_debug & 0x0002)
+  #define DEB_DISC2		(opt_debug & 0x0004)
+*/
+#define MAX_FNAME		256
+
+
+struct session;
+
+/* return <0 --> fatal error; abor
+   return =0 --> ok, proceed
+   return >0 --> ok, qui
+*/
+typedef int (*packet_cb_t)(struct session* ses,
+			   struct pppoe_packet *p_in,
+			   struct pppoe_packet **p_out);
+
+/* various override filter tags */
+struct filter {
+	struct pppoe_tag *stag;  /* service name tag override */
+	struct pppoe_tag *ntag;  /*AC name override */
+	struct pppoe_tag *htag;  /* hostuniq override */
+	int num_restart;
+	int peermode;
+	char *fname;
+	char *pppd;
+} __attribute__ ((packed));
+
+
+struct pppoe_tag *make_filter_tag(short type, short length, char* data);
+
+/* Session type definitions */
+#define SESSION_CLIENT	0
+#define SESSION_SERVER	1
+#define SESSION_RELAY	2
+
+struct session {
+
+	/* Administrative */
+	int type;
+	int opt_debug;
+	int detached;
+	int np;
+	int log_to_fd;
+	int ifindex;			/* index of device */
+	char name[IFNAMSIZ];		/*dev name */
+	struct pppoe_packet curr_pkt;
+
+	packet_cb_t init_disc;
+	packet_cb_t rcv_pado;
+	packet_cb_t rcv_padi;
+	packet_cb_t rcv_pads;
+	packet_cb_t rcv_padr;
+	packet_cb_t rcv_padt;
+	packet_cb_t timeout;
+
+
+	/* Generic */
+	struct filter *filt;
+	struct sockaddr_ll local;
+	struct sockaddr_ll remote;
+	struct sockaddr_pppox sp;
+	int fd;				/* fd of PPPoE socket */
+
+
+	/* For client */
+	int retransmits;		/* Number of retransmission performed
+					   if < 0 , retransmissions disabled */
+	int retries;
+	int state;
+	int opt_daemonize;
+
+	/* For server */
+	int fork;
+
+	/* For forwarding */
+	int fwd_sock;
+	char fwd_name[IFNAMSIZ];	/* Name of device to forward to */
+} __attribute__ ((packed));
+
+/*
+  retransmit retries for the PADR and PADI packets
+  during discovery
+*/
+int PADR_ret;
+int PADI_ret;
+
+int log_to_fd;
+int ctrl_fd;
+int opt_debug;
+int opt_daemonize;
+
+
+/* Structure for keeping track of connection relays */
+struct pppoe_con{
+	struct pppoe_con *next;
+	int id;
+	int connected;
+	int  cl_sock;
+	int  sv_sock;
+	int ref_count;
+	char client[ETH_ALEN];
+	char server[ETH_ALEN];
+	char key_len;
+	char key[32];
+};
+
+/* Functions exported from utils.c. */
+
+/* Functions exported from pppoehash.c */
+struct pppoe_con *get_con(int len, char *key);
+int store_con(struct pppoe_con *pc);
+struct pppoe_con *delete_con(unsigned long len, char *key);
+
+/* exported by lib.c */
+
+extern int init_lib();
+
+extern int get_sockaddr_ll(const char *devnam,struct sockaddr_ll* sll);
+
+extern int client_init_ses (struct session *ses, char* devnam);
+extern int relay_init_ses(struct session *ses, char* from, char* to);
+extern int srv_init_ses(struct session *ses, char* from);
+extern int session_connect(struct session *ses);
+extern int session_disconnect(struct session*ses);
+
+extern int verify_packet( struct session *ses, struct pppoe_packet *p);
+
+extern void copy_tag(struct pppoe_packet *dest, struct pppoe_tag *pt);
+extern struct pppoe_tag *get_tag(struct pppoe_hdr *ph, u_int16_t idx);
+extern int send_disc(struct session *ses, struct pppoe_packet *p);
+
+
+extern int add_client(char *addr);
+
+/* Make connections (including spawning pppd) as server/client */
+extern ppp_connect(struct session *ses);
+
+
+#endif
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe_client.c ppp-2.4.1/pppd/plugins/pppoe/pppoe_client.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe_client.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoe_client.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,232 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+
+
+static int std_rcv_pado(struct session* ses,
+			struct pppoe_packet *p_in,
+			struct pppoe_packet **p_out){
+    
+    if( verify_packet(ses, p_in) < 0)
+	return -1;
+    
+    if(ses->state != PADO_CODE ){
+	poe_error(ses,"Unexpected packet: %P",p_in);
+	return 0;
+    }
+    
+    
+    if (DEB_DISC2) {
+	poe_dbglog (ses,"PADO received: %P", p_in);
+    }
+    
+    memcpy(&ses->remote, &p_in->addr, sizeof(struct sockaddr_ll));
+    memcpy( &ses->curr_pkt.addr, &ses->remote , sizeof(struct sockaddr_ll));
+    
+    ses->curr_pkt.hdr->code = PADR_CODE;
+    
+    /* The HOST_UNIQ has been verified already... there's no "if" about this */
+    /* if(ses->filt->htag) */
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_HOST_UNIQ));	
+    
+    if (ses->filt->ntag) {
+    	ses->curr_pkt.tags[TAG_AC_NAME]=NULL;
+    }
+//    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_AC_NAME));
+    
+    if(ses->filt->stag) {
+    	ses->curr_pkt.tags[TAG_SRV_NAME]=NULL;
+    }
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_SRV_NAME));
+    
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_AC_COOKIE));
+    copy_tag(&ses->curr_pkt,get_tag(p_in->hdr,PTT_RELAY_SID));
+    
+    ses->state = PADS_CODE;
+    
+    ses->retransmits = 0;
+    
+    send_disc(ses, &ses->curr_pkt);
+    (*p_out) = &ses->curr_pkt;
+    
+    if (ses->np)
+	return 1;
+    
+    return 0;
+}
+
+static int std_init_disc(struct session* ses,
+			 struct pppoe_packet *p_in,
+			 struct pppoe_packet **p_out){
+    
+    memset(&ses->curr_pkt,0, sizeof(struct pppoe_packet));
+
+    
+    /* Check if already connected */
+    if( ses->state != PADO_CODE ){
+	return 1;
+    }
+    
+    ses->curr_pkt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
+    ses->curr_pkt.hdr->ver  = 1;
+    ses->curr_pkt.hdr->type = 1;
+    ses->curr_pkt.hdr->code = PADI_CODE;
+    
+    
+    memcpy( &ses->curr_pkt.addr, &ses->remote , sizeof(struct sockaddr_ll));
+    
+    poe_info (ses,"Sending PADI");
+    if (DEB_DISC)
+	poe_dbglog (ses,"Sending PADI");
+    
+    ses->retransmits = 0 ;
+    
+    if(ses->filt->ntag) {
+	ses->curr_pkt.tags[TAG_AC_NAME]=ses->filt->ntag;
+	poe_info(ses,"overriding AC name\n");
+    }
+    
+    if(ses->filt->stag)
+	ses->curr_pkt.tags[TAG_SRV_NAME]=ses->filt->stag;
+    
+    if(ses->filt->htag)
+	ses->curr_pkt.tags[TAG_HOST_UNIQ]=ses->filt->htag;
+    
+    send_disc(ses, &ses->curr_pkt);
+    (*p_out)= &ses->curr_pkt;
+    return 0;
+}
+
+
+static int std_rcv_pads(struct session* ses,
+			struct pppoe_packet *p_in,
+			struct pppoe_packet **p_out){
+    if( verify_packet(ses, p_in) < 0)
+	return -1;
+    
+    if (DEB_DISC)
+	poe_dbglog (ses,"Got connection: %x",
+		    ntohs(p_in->hdr->sid));
+    poe_info (ses,"Got connection: %x", ntohs(p_in->hdr->sid));
+    
+    ses->sp.sa_family = AF_PPPOX;
+    ses->sp.sa_protocol = PX_PROTO_OE;
+    ses->sp.sa_addr.pppoe.sid = p_in->hdr->sid;
+    memcpy(ses->sp.sa_addr.pppoe.dev,ses->name, IFNAMSIZ);
+    memcpy(ses->sp.sa_addr.pppoe.remote, p_in->addr.sll_addr, ETH_ALEN);
+    
+    
+    return 1;
+}
+
+static int std_rcv_padt(struct session* ses,
+			struct pppoe_packet *p_in,
+			struct pppoe_packet **p_out){
+    ses->state = PADO_CODE;
+    return 0;
+}
+
+
+extern int disc_sock;
+int client_init_ses (struct session *ses, char* devnam)
+{
+    int i=0;
+    int retval;
+    char dev[IFNAMSIZ+1];
+    int addr[ETH_ALEN];
+    int sid;
+    
+    /* do error checks here; session name etc are valid */
+//    poe_info (ses,"init_ses: creating socket");
+    
+    /* Make socket if necessary */
+    if( disc_sock < 0 ){
+	
+	disc_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if( disc_sock < 0 ){
+	    poe_fatal(ses,
+		      "Cannot create PF_PACKET socket for PPPoE discovery\n");
+	}
+	
+    }
+    
+    /* Check for long format */
+    retval =sscanf(devnam, FMTSTRING(IFNAMSIZ),addr, addr+1, addr+2,
+		   addr+3, addr+4, addr+5,&sid,dev);
+    if( retval != 8 ){
+	/* Verify the device name , construct ses->local */
+	retval = get_sockaddr_ll(devnam,&ses->local);
+	if (retval < 0)
+	    poe_fatal(ses, "client_init_ses: "
+		      "Cannot create PF_PACKET socket for PPPoE discovery\n");
+	
+	
+	ses->state = PADO_CODE;
+	memcpy(&ses->remote, &ses->local, sizeof(struct sockaddr_ll) );
+	
+	memset( ses->remote.sll_addr, 0xff, ETH_ALEN);
+    }else{
+	/* long form parsed */
+
+	/* Verify the device name , construct ses->local */
+	retval = get_sockaddr_ll(dev,&ses->local);
+	if (retval < 0)
+	    poe_fatal(ses,"client_init_ses(2): "
+		      "Cannot create PF_PACKET socket for PPPoE discovery\n");
+	ses->state = PADS_CODE;
+	ses->sp.sa_family = AF_PPPOX;
+	ses->sp.sa_protocol = PX_PROTO_OE;
+	ses->sp.sa_addr.pppoe.sid = sid;
+	
+	memcpy(&ses->remote, &ses->local, sizeof(struct sockaddr_ll) );
+	
+	for(; i < ETH_ALEN ; ++i ){
+	    ses->sp.sa_addr.pppoe.remote[i] = addr[i];
+	    ses->remote.sll_addr[i]=addr[i];
+	}
+	memcpy(ses->sp.sa_addr.pppoe.dev, dev, IFNAMSIZ);
+	
+	
+	
+    }
+    if( retval < 0 )
+	error("bad device name: %s",devnam);
+    
+    
+    retval = bind( disc_sock ,
+		   (struct sockaddr*)&ses->local,
+		   sizeof(struct sockaddr_ll));
+    
+    
+    if( retval < 0 ){
+	error("bind to PF_PACKET socket failed: %m");
+    }
+    
+    ses->fd = socket(AF_PPPOX,SOCK_STREAM,PX_PROTO_OE);
+    if(ses->fd < 0)
+    {
+	poe_fatal(ses,"Failed to create PPPoE socket: %m");
+    }
+    
+    
+    ses->init_disc = std_init_disc;
+    ses->rcv_pado  = std_rcv_pado;
+    ses->rcv_pads  = std_rcv_pads;
+    ses->rcv_padt  = std_rcv_padt;
+    
+    /* this should be filter overridable */
+    ses->retries = 10;
+    
+    return ses->fd;
+}
+
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe_relay.c ppp-2.4.1/pppd/plugins/pppoe/pppoe_relay.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe_relay.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoe_relay.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,260 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+static int relay_init_disc(struct session* ses,
+			   struct pppoe_packet *p_in,
+			   struct pppoe_packet **p_out){
+
+    ses->state = 0;
+    ses->retransmits = -1 ;
+    ses->retries = -1;
+
+    (*p_out) = NULL;
+    return 0;
+}
+
+static int pcid=0;
+static int relay_rcv_padi(struct session* ses,
+			  struct pppoe_packet *p_in,
+			  struct pppoe_packet **p_out){
+    char tag_buf[32];
+    struct pppoe_con *newpc = NULL;
+    struct pppoe_tag *tag = (struct pppoe_tag *) tag_buf;
+
+
+    tag->tag_type = PTT_RELAY_SID;
+    tag->tag_len  = htons(ETH_ALEN + sizeof(struct session *));
+
+    memcpy(tag->tag_data, p_in->addr.sll_addr, ETH_ALEN);
+    memcpy(tag->tag_data + ETH_ALEN, &ses, sizeof(struct session *));
+
+    if(! p_in->tags[TAG_RELAY_SID] ){
+	copy_tag(p_in, tag);
+    }
+
+
+    poe_dbglog(ses, "Recv'd PADI: %P",p_in);
+    poe_dbglog(ses, "Recv'd packet: %P",p_in);
+    newpc = get_con( ntohs(tag->tag_len), tag->tag_data );
+    if(!newpc){
+	
+	newpc = (struct pppoe_con *) malloc(sizeof(struct pppoe_con));
+	memset(newpc , 0, sizeof(struct pppoe_con));
+	
+	newpc->id = pcid++;
+	
+	newpc->key_len = ntohs(p_in->tags[TAG_RELAY_SID]->tag_len);
+	memcpy(newpc->key, p_in->tags[TAG_RELAY_SID]->tag_data, newpc->key_len);
+	memcpy(newpc->client, p_in->addr.sll_addr, ETH_ALEN);
+	
+	memcpy(newpc->server, MAC_BCAST_ADDR, ETH_ALEN);
+	
+	store_con(newpc);
+	
+    }
+
+    ++newpc->ref_count;
+
+    memset(p_in->addr.sll_addr, 0xff, ETH_ALEN);
+
+    p_in->addr.sll_ifindex = ses->remote.sll_ifindex;
+
+    send_disc(ses, p_in);
+    return 0;
+}
+
+static int relay_rcv_pkt(struct session* ses,
+			 struct pppoe_packet *p_in,
+			 struct pppoe_packet **p_out){
+    struct pppoe_con *pc;
+    char tag_buf[32];
+    struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
+
+    if( !tag ) return 0;
+
+    pc = get_con(ntohs(tag->tag_len),tag->tag_data);
+
+    if( !pc ) return 0;
+
+    poe_dbglog(ses, "Recv'd packet: %P",p_in);
+
+    if( memcmp(pc->client , p_in->addr.sll_addr , ETH_ALEN ) == 0 ){
+	
+	memcpy(p_in->addr.sll_addr, pc->server, ETH_ALEN);
+	p_in->addr.sll_ifindex = ses->remote.sll_ifindex;
+	
+    }else{
+	if( memcmp(pc->server, MAC_BCAST_ADDR, ETH_ALEN) == 0 ){
+	    memcpy(pc->server, p_in->addr.sll_addr, ETH_ALEN);
+	
+	}else if( memcmp(pc->server, p_in->addr.sll_addr, ETH_ALEN) !=0){
+	    return 0;
+	}
+	
+	memcpy(p_in->addr.sll_addr, pc->client, ETH_ALEN);
+	p_in->addr.sll_ifindex = ses->local.sll_ifindex;
+	
+	
+    }
+
+
+    send_disc(ses, p_in);
+    return 0;
+}
+
+static int relay_rcv_pads(struct session* ses,
+			  struct pppoe_packet *p_in,
+			  struct pppoe_packet **p_out){
+
+    struct pppoe_con *pc;
+    char tag_buf[32];
+    struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
+    struct sockaddr_pppox sp_cl= { AF_PPPOX, PX_PROTO_OE,
+				   { p_in->hdr->sid, {0,},{0,}}};
+
+    struct sockaddr_pppox sp_sv= { AF_PPPOX, PX_PROTO_OE,
+				   { p_in->hdr->sid, {0,},{0,}}};
+
+    int ret;
+
+
+    if( !tag ) return 0;
+
+    pc = get_con(ntohs(tag->tag_len),tag->tag_data);
+
+    if( !pc ) return 0;
+
+
+    if(!pc->connected){
+	
+	pc->sv_sock = socket( AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE);
+	if( pc->sv_sock < 0){
+	    poe_fatal(ses,"Cannot open PPPoE socket: %i",errno);
+	}
+	
+	pc->cl_sock = socket( AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE);
+	if( pc->cl_sock < 0){
+	    poe_fatal(ses,"Cannot open PPPoE socket: %i",errno);
+	}
+	
+	memcpy( sp_sv.sa_addr.pppoe.dev, ses->fwd_name, IFNAMSIZ);
+	memcpy( sp_sv.sa_addr.pppoe.remote, pc->server, ETH_ALEN);
+	
+	ret = connect( pc->sv_sock,
+		       (struct sockaddr*)&sp_sv,
+		       sizeof(struct sockaddr_pppox));
+	if( ret < 0){
+	    poe_fatal(ses,"Cannot connect PPPoE socket: %i",errno);
+	}
+	
+	memcpy( sp_cl.sa_addr.pppoe.dev, ses->name, IFNAMSIZ);
+	memcpy( sp_cl.sa_addr.pppoe.remote, pc->client, ETH_ALEN);
+	
+	ret = connect( pc->cl_sock,
+		       (struct sockaddr*)&sp_cl,
+		       sizeof(struct sockaddr_pppox));
+	if( ret < 0){
+	    poe_fatal(ses,"Cannot connect PPPoE socket: %i",errno);
+	}
+	
+	
+	ret = ioctl( pc->sv_sock, PPPOEIOCSFWD, &sp_cl);
+	if( ret < 0){
+	    poe_fatal(ses,"Cannot set forwarding on PPPoE socket: %i",errno);
+	}
+	
+	ret = ioctl( pc->cl_sock, PPPOEIOCSFWD, &sp_sv);
+	if( ret < 0){
+	    poe_fatal(ses,"Cannot set forwarding on PPPoE socket: %i",errno);
+	}
+	
+	pc->connected = 1;
+    }
+
+    poe_info(ses,"PPPoE relay for %E established to %E (sid=%04x)\n",
+	     pc->client,pc->server, p_in->hdr->sid);
+
+    return relay_rcv_pkt(ses,p_in,p_out);
+}
+
+
+static int relay_rcv_padt(struct session* ses,
+			  struct pppoe_packet *p_in,
+			  struct pppoe_packet **p_out){
+
+    int ret;
+    struct pppoe_con *pc;
+    char tag_buf[32];
+    struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
+
+    if( !tag ) return 0;
+
+    pc = get_con(ntohs(tag->tag_len),tag->tag_data);
+
+    if( !pc ) return 0;
+
+    ret = relay_rcv_pkt(ses,p_in,p_out);
+
+
+    if(pc->cl_sock>0){
+	close(pc->cl_sock);
+    }
+
+    if(pc->sv_sock>0){
+	close(pc->sv_sock);
+    }
+
+    --pc->ref_count;
+    if( pc->ref_count == 0 ){
+	delete_con(pc->key_len, pc->key);
+	
+	free(pc);
+    }
+}
+
+
+int relay_init_ses(struct session *ses, char* from, char* to)
+{
+    int retval = client_init_ses(ses, from);
+
+    if(retval<0) return retval;
+
+    ses->fwd_sock =  socket(PF_PACKET, SOCK_DGRAM, 0);
+    if( ses->fwd_sock < 0 ) {
+	poe_fatal(ses,"Cannot create PF_PACKET socket for PPPoE forwarding\n");
+    }
+
+    /* Verify the device name , construct ses->local */
+    retval = get_sockaddr_ll(to, &ses->remote);
+    if (retval < 0)
+	poe_fatal(ses,"relay_init_ses:get_sockaddr_ll failed %m");
+
+    retval = bind( ses->fwd_sock ,
+		   (struct sockaddr*)&ses->remote,
+		   sizeof(struct sockaddr_ll));
+
+    if( retval < 0 ){
+	poe_fatal(ses,"bind to PF_PACKET socket failed: %m");
+    }
+
+    memcpy(ses->fwd_name, to, IFNAMSIZ);
+    memcpy(ses->name, from, IFNAMSIZ);
+
+
+    ses->init_disc = relay_init_disc;
+    ses->rcv_padi  = relay_rcv_padi;
+    ses->rcv_pado  = relay_rcv_pkt;
+    ses->rcv_padr  = relay_rcv_pkt;
+    ses->rcv_pads  = relay_rcv_pads;
+    ses->rcv_padt  = relay_rcv_padt;
+}
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe_server.c ppp-2.4.1/pppd/plugins/pppoe/pppoe_server.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoe_server.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoe_server.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,143 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include "pppoe.h"
+#include <unistd.h>
+
+static unsigned int pcid=1111;
+static int srv_rcv_padi(struct session* ses, 
+			struct pppoe_packet *p_in,
+			struct pppoe_packet **p_out){
+    struct pppoe_con *newpc = NULL;
+    struct pppoe_tag *tag;
+    
+    poe_dbglog(ses,"Srv Recv'd packet: %P\n",p_in);
+    
+    
+    ses->curr_pkt.hdr = (struct pppoe_hdr*) ses->curr_pkt.buf;
+    ses->curr_pkt.hdr->ver  = 1;
+    ses->curr_pkt.hdr->type = 1;
+
+    tag = get_tag(p_in->hdr,PTT_SRV_NAME);
+
+    if(!tag )
+	return 0;
+
+    if( ntohs(tag->tag_len)==0 ){
+	ses->curr_pkt.tags[TAG_SRV_NAME] = ses->filt->stag ;
+    }else if( tag->tag_len != ses->filt->stag->tag_len
+	      || !memcmp( tag+1, ses->filt->stag, ntohs(tag->tag_len)) ){
+	return 0;
+    }else{
+	ses->curr_pkt.tags[TAG_SRV_NAME] = tag;
+    }
+
+    ses->curr_pkt.tags[ TAG_AC_NAME] = ses->filt->ntag ;
+    ses->curr_pkt.tags[ TAG_HOST_UNIQ ] = get_tag(p_in->hdr,PTT_HOST_UNIQ);
+    
+    memcpy(&ses->remote, &p_in->addr, sizeof(struct sockaddr_ll));
+    memcpy( &ses->curr_pkt.addr, &ses->remote , sizeof(struct sockaddr_ll));
+    
+    ses->curr_pkt.hdr->code =  PADO_CODE;
+    
+    
+    ses->curr_pkt.tags[ TAG_RELAY_SID ] = get_tag(p_in->hdr,PTT_RELAY_SID);
+
+    send_disc(ses, &ses->curr_pkt);
+    poe_dbglog(ses,"Srv Sent packet: %P\n",&ses->curr_pkt);
+    
+    return 0;
+}
+
+
+static int srv_rcv_padr(struct session* ses, 
+			struct pppoe_packet *p_in,
+			struct pppoe_packet **p_out){
+    struct pppoe_tag *tag;
+
+    poe_dbglog(ses,"Recv'd packet: %P\n",p_in);
+    
+
+
+    /* Run checks to ensure this packets asks for 
+       what we're willing to offer */
+
+    tag = get_tag(p_in->hdr,PTT_SRV_NAME);
+
+    if(!tag || tag->tag_len == 0 ){
+	p_in->tags[TAG_SRV_NAME] = ses->filt->stag;
+
+    }else if( tag->tag_len != ses->filt->stag->tag_len
+	     || !memcmp(tag + 1 , ses->filt->stag, ntohs(tag->tag_len)) ){
+	return 0;
+    }else{
+	p_in->tags[TAG_SRV_NAME] = tag;
+    }
+
+    tag = get_tag(p_in->hdr,PTT_AC_NAME);
+    if( !tag || tag->tag_len==0 ){
+	p_in->tags[TAG_AC_NAME] = ses->filt->ntag;
+    }else if( tag->tag_len != ses->filt->ntag->tag_len
+	  || !memcmp(tag + 1, ses->filt->ntag, ntohs(tag->tag_len)) ){
+	return 0;
+    }else{
+	p_in->tags[TAG_AC_NAME] = tag;
+    }
+
+    
+    
+    
+    pcid = ++pcid & 0x0000ffff ;
+    if(pcid == 0 ){
+	pcid = 1111;
+    }
+    
+    p_in->hdr->sid  = ntohs(pcid);
+    
+    p_in->hdr->code = PADS_CODE;
+    send_disc(ses, p_in);
+    
+    poe_dbglog(ses,"Sent packet: %P\n",p_in);
+    
+    ses->sp.sa_family = AF_PPPOX;
+    ses->sp.sa_protocol=PX_PROTO_OE;
+    ses->sp.sa_addr.pppoe.sid = p_in->hdr->sid;
+    memcpy(ses->sp.sa_addr.pppoe.dev, ses->name, IFNAMSIZ);
+    memcpy(ses->sp.sa_addr.pppoe.remote, p_in->addr.sll_addr, ETH_ALEN);
+    memcpy(&ses->remote, &p_in->addr, sizeof(struct sockaddr_ll));
+    return 1;
+}
+
+static int srv_rcv_padt(struct session* ses, 
+			struct pppoe_packet *p_in,
+			struct pppoe_packet **p_out){
+    return 0;
+}
+
+
+
+int srv_init_ses(struct session *ses, char* from)
+{
+    int retval;
+    retval = client_init_ses(ses, from);
+    ses->init_disc = NULL;
+    ses->rcv_pado  = NULL;
+    ses->rcv_pads  = NULL;
+    ses->rcv_padi  = srv_rcv_padi;
+    ses->rcv_padr  = srv_rcv_padr;
+    ses->rcv_padt  = srv_rcv_padt;
+
+    /* retries forever */
+    ses->retries   = -1;
+
+    return retval;
+}
+
+
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoed.c ppp-2.4.1/pppd/plugins/pppoe/pppoed.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoed.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoed.c	2003-09-19 23:47:54.000000000 -0500
@@ -0,0 +1,281 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+#include "pppoe.h"
+
+int detached=1;
+void
+sigproc (int src)
+{
+    int i;
+    fprintf (stderr,"Received signal %d", src);
+}
+
+void
+sigchild (int src)
+{
+    pid_t pid;
+    int status;
+    int i;
+    pid = waitpid (-1, &status, WNOHANG);
+    
+    if (!detached)
+	fprintf (stderr,"Child received signal %d PID %d, status %d", src, pid, status);
+    if (pid < 1) {
+	return;
+    }
+}
+
+void
+print_help ()
+{
+    
+    fprintf (stdout,"\npppoe version %d.%d build %d", VERSION_MAJOR, VERSION_MINOR,
+	     VERSION_DATE);
+    fprintf (stdout,"\nrecognized options are:");
+    fprintf (stdout,"\n -I <interface> : overrides the default interface of eth0");
+    fprintf (stdout,"\n -S : starts pppoed in server mode");
+    fprintf (stdout,"\n -R <num_retries>: forces pppoed to be restarted num_retries");
+    fprintf (stdout,"\n                   should the other end be detected to be dead.");
+    fprintf (stdout,"\n                   Needs lcp_echo. Read the INSTALL file instructions");
+    fprintf (stdout,"\n -F <filename> : specifies additional ppp options file");
+    fprintf (stdout,"\n -C <filename> : ppp options file in /etc/ppp/peers/");
+    fprintf (stdout,"\n -d <level> : sets debug level");
+    fprintf (stdout,"\n -D : prevents pppoed from detaching itself and running in the background");
+    fprintf (stdout,"\n -P <path to pppd> : selects a different pppd. Defaults to " _PATH_PPPD);
+    fprintf (stdout,"\n -A <AC name> to select a specific AC by name");
+    fprintf (stdout,"\n -E <AC service name> to select a specific AC service by name");
+    fprintf (stdout,"\n -G Do service discovery only");
+    fprintf (stdout,"\n -H Do service discovery and connection (no pppd)\n");
+}
+
+
+int 
+get_args (int argc, char **argv,struct session *sess)
+{
+    struct filter *filt;
+    struct host_tag *tg;
+    int opt;
+    
+
+    sess->opt_debug = 0;
+    DEB_DISC=0;
+    DEB_DISC2=0;
+    sess->log_to_fd = 1;
+    sess->np = 0;
+    sess->opt_daemonize = 0;
+    
+    sess->log_to_fd = fileno (stdout);
+    
+/* defaults to eth0 */
+    strcpy (sess->name, "eth0");
+    
+    
+    if ((sess->filt=malloc(sizeof(struct filter))) == NULL) {
+        poe_error (sess,"failed to malloc for Filter ");
+        poe_die (-1);
+    }
+    
+    filt=sess->filt;  /* makes the code more readable */
+    memset(filt,0,sizeof(struct filter));
+    
+    filt->num_restart=1;
+    
+/* set default filters; move this to routine */
+    /* parse options */
+    
+    while ((opt = getopt (argc, argv, "A:C:E:d:DR:I:F:L:V:P:SN:GH")) != -1)
+	
+	switch (opt) {
+	case 'R':			/* sets number of retries */
+	    filt->num_restart = strtol (optarg, (char **) NULL, 10);
+	    filt->num_restart += 1;
+	    break;
+	case 'I':			/* sets interface */
+	    if (strlen (optarg) >= IFNAMSIZ) {
+		poe_error (sess,"interface name cannot exceed %d characters", IFNAMSIZ - 1);
+		return (-1);
+	    }
+	    strncpy (sess->name, optarg, strlen(optarg)+1);
+	    break;
+	case 'C':			/* name of the file in /etc/ppp/peers */
+	    if (NULL != filt->fname) {
+		poe_error (sess,"-F can not be used with -C");
+		return (-1);
+	    }
+	    if (strlen(optarg) > MAX_FNAME) {
+		poe_error (sess,"file name cannot exceed %d characters", MAX_FNAME - 1);
+		return (-1);
+	    }
+	    filt->fname=malloc(strlen(optarg));
+	    strncpy (filt->fname, optarg, strlen(optarg));
+	    filt->peermode=1;
+	    break;
+	case 'F':			/* sets the options file */
+	    if (NULL != filt->fname) {
+		poe_error (sess,"-F can not be used with -C");
+		return (-1);
+	    }
+	    
+	    if (strlen(optarg) > MAX_FNAME) {
+		poe_error (sess,"file name cannot exceed %d characters", MAX_FNAME - 1);
+		return (-1);
+	    }
+	    filt->fname=malloc(strlen(optarg)+1);
+	    strncpy (filt->fname, optarg, strlen(optarg)+1);
+	    
+	    poe_info (sess,"selected %s as filename\n",filt->fname);
+	    break;
+	case 'D':			/* don't daemonize */
+	    sess->opt_daemonize = 1;
+	    detached=0;
+	    break;
+	case 'd':			/* debug level */
+	    sess->opt_debug = strtol (optarg, (char **) NULL, 10);
+	    if (sess->opt_debug & 0x0002)
+		DEB_DISC=1;
+	    if (sess->opt_debug & 0x0004)
+		DEB_DISC2=1;
+	    break;
+	case 'P':			/* sets the pppd binary */
+	    if (strlen(optarg) > MAX_FNAME) {
+		poe_error (sess,"pppd binary cant exceed %d characters", MAX_FNAME - 1);
+		return (-1);
+	    }
+	    filt->pppd=malloc(strlen(optarg));
+	    strncpy (filt->pppd, optarg, strlen(optarg));
+	    break;
+	case 'H':			
+	    sess->np = 2;
+	    break;
+	case 'G':			
+	    sess->np = 1;
+	    break;
+	case 'V':			/* version */
+	    fprintf (stdout,"pppoe version %d.%d build %d", VERSION_MAJOR,
+		     VERSION_MINOR, VERSION_DATE);
+	    return (0);
+	case 'S':			/* server mode */
+	    sess->type = SESSION_SERVER;
+	    break;
+	case 'A':			/* AC override */
+	    poe_info (sess,"AC name override to %s", optarg);
+	    if (strlen (optarg) > 255) {
+		poe_error (sess," AC name to long (maximum allowed 256 chars)");
+		poe_die(-1);
+	    }
+	    if ((sess->filt->ntag= malloc (sizeof (struct pppoe_tag) + 
+					   strlen (optarg)))== NULL) {
+		poe_error (sess,"failed to malloc for AC name");
+		poe_die(-1);
+	    }
+	    sess->filt->ntag->tag_len=htons(strlen(optarg));
+	    sess->filt->ntag->tag_type=PTT_AC_NAME;
+	    poe_error (sess," pppoe_ac_name: AC name Override %p\n",
+		       sess->filt->ntag);
+	    strcpy(sess->filt->ntag->tag_data,optarg);
+	    break;
+	case 'E':			/* AC service name override */
+	    poe_info (sess,"AC service name override to %s", optarg);
+	    if (strlen (optarg) > 255) {
+		poe_error (sess," Service name too long (maximum allowed 256 chars)");
+		poe_die(-1);
+	    }
+	    
+	    if ((filt->stag = malloc (strlen (optarg) + sizeof (struct pppoe_tag))) == NULL) {
+		poe_error (sess,"failed to malloc for service name: %m");
+		return (-1);
+	    }
+	    
+	    filt->stag->tag_len = htons (strlen (optarg));
+	    filt->stag->tag_type = PTT_SRV_NAME;
+	    strcpy ((char *) (filt->stag->tag_data), optarg);
+	    break;
+	default:
+	    poe_error (sess,"Unknown option '%c'", optopt);
+	    print_help ();
+	    return (-1);
+	}
+    
+    
+    return (1);
+    
+}
+
+
+int main(int argc, char** argv){
+    int ret;
+    struct filter *filt;
+    struct session *ses = (struct session *)malloc(sizeof(struct session));
+    char buf[256];
+    ses=(void *)malloc(sizeof(struct session));
+    
+    if(!ses){
+	return -1;
+    }
+    memset(ses,0,sizeof(struct session));
+    
+    
+    
+    openlog ("pppoed", LOG_PID | LOG_NDELAY, LOG_PPPOE);
+    setlogmask (LOG_UPTO (ses->opt_debug ? LOG_DEBUG : LOG_INFO));
+    
+    
+    if ((get_args (argc,(char **) argv,ses)) <1)
+        poe_die(-1);
+    
+    filt=ses->filt;  /* makes the code more readable */
+    
+    if (!ses->np) {
+	poe_create_pidfile (ses);
+//	signal (SIGINT, &sigproc);
+//	signal (SIGTERM, &sigproc);
+	signal (SIGCHLD, &sigchild);
+    }
+    
+    if(ses->type == SESSION_CLIENT){
+
+	poe_info(ses,"calling client_init_ses\n");
+	ret = client_init_ses(ses,ses->name);
+    
+	if( ret < 0 ){
+	    return -1;
+	}
+
+	while (ses->filt->num_restart > 0)
+	{
+	    poe_info(ses,"Restart number %d ",ses->filt->num_restart);
+	    ppp_connect (ses);
+	    ses->filt->num_restart--;
+	}
+
+    }else if( ses->type == SESSION_SERVER ){
+
+	poe_info(ses,"calling srv_init_ses\n");
+	ret = srv_init_ses(ses,ses->name);
+
+	if( ret < 0 ){
+	    return -1;
+	}
+
+	ret = 1;
+	while(ret>=0)
+	    ret = ppp_connect(ses);
+    
+    }
+
+    
+    
+    
+    poe_info(ses,"ppp_connect came back! %d",ret);
+    
+    exit(0);
+    
+}
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoefwd.c ppp-2.4.1/pppd/plugins/pppoe/pppoefwd.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoefwd.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoefwd.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,61 @@
+#include "pppoe.h"
+
+void fatal (char *fmt, ...)
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    vprintf( fmt, pvar);
+    va_end(pvar);
+
+    exit(1);			/* as promised */
+}
+
+void info (char *fmt, ...)
+{
+    va_list pvar;
+
+#if defined(__STDC__)
+    va_start(pvar, fmt);
+#else
+    char *fmt;
+    va_start(pvar);
+    fmt = va_arg(pvar, char *);
+#endif
+
+    vprintf( fmt, pvar);
+    va_end(pvar);
+
+}
+
+
+int main(int argc, char** argv){
+    int ret;
+    struct session *ses = (struct session *)malloc(sizeof(struct session));
+
+    if(!ses) return -1;
+
+    ret = relay_init_ses(ses,argv[1],argv[2]);
+    
+    if( ret < 0 ){
+	return -1;
+    }
+
+    ses->log_to_fd = 1;
+    ses->opt_debug=1;
+    while(1)
+	ret = session_connect(ses);
+    
+    
+    
+    return ret;
+
+
+}
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/pppoehash.c ppp-2.4.1/pppd/plugins/pppoe/pppoehash.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/pppoehash.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/pppoehash.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,91 @@
+/* PPPoE support library "libpppoe"
+ *
+ * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+ *		  Jamal Hadi Salim <hadi@cyberus.ca>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include "pppoe.h"
+
+
+#define PPPOE_HASH_SIZE 16
+
+
+static inline int keycmp(char *a, char *b, int x, int y){
+    return x==y && !memcmp(a,b,x);
+}
+
+static int hash_con(int key_len, char* key)
+{
+    int i = 0;
+    char hash[sizeof(int)]={0,};
+
+    for (i = 0; i < key_len ; ++i)
+	hash[i% sizeof(int)] = hash[i%sizeof(int)] ^ key[i];
+
+    i = (*((int*)hash)) ;
+    i &= PPPOE_HASH_SIZE - 1;
+
+    return i;
+}	
+
+static struct pppoe_con *con_ht[PPPOE_HASH_SIZE] = { 0, };
+
+struct pppoe_con *get_con(int len, char *key)
+{
+    int hash = hash_con(len, key);
+    struct pppoe_con *ret;
+
+    ret = con_ht[hash];
+
+    while (ret && !keycmp(ret->key,key, ret->key_len, len))
+	ret = ret->next;
+
+    return ret;
+}
+
+int store_con(struct pppoe_con *pc)
+{
+    int hash = hash_con(pc->key_len, pc->key);
+    struct pppoe_con *ret;
+
+    ret = con_ht[hash];
+    while (ret) {
+	if (!keycmp(ret->key, pc->key, ret->key_len, pc->key_len))
+	    return -EALREADY;
+	
+	ret = ret->next;
+    }
+
+    if (!ret) {
+	pc->next = con_ht[hash];
+	con_ht[hash] = pc;
+    }
+
+    return 0;
+}
+
+struct pppoe_con *delete_con(unsigned long len, char *key)
+{
+    int hash = hash_con(len, key);
+    struct pppoe_con *ret, **src;
+
+    ret = con_ht[hash];
+    src = &con_ht[hash];
+
+    while (ret) {
+	if (keycmp(ret->key,key, ret->key_len, len)) {
+	    *src = ret->next;
+	    break;
+	}
+	
+	src = &ret->next;
+	ret = ret->next;
+    }
+
+    return ret;
+}
+
diff -Naur ppp-2.4.1-orig/pppd/plugins/pppoe/utils.c ppp-2.4.1/pppd/plugins/pppoe/utils.c
--- ppp-2.4.1-orig/pppd/plugins/pppoe/utils.c	1969-12-31 18:00:00.000000000 -0600
+++ ppp-2.4.1/pppd/plugins/pppoe/utils.c	2003-09-19 23:47:33.000000000 -0500
@@ -0,0 +1,667 @@
+
+/*
+ * utils.c - various utility functions used in pppoed.
+ *
+ * mostly stolen from ppp-2.3.10 by Marc Boucher <marc@mbsi.ca>
+ *
+ * Feb 18/2000 Made fully re-entrant (JHS)
+ *
+ * Copyright (c) 1999 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright poe_notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University.  The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>		/* stdio               */
+#include <stdlib.h>		/* strtoul(), realloc() */
+#include <string.h>		/* memcpy()             */
+#include <unistd.h>		/* STDIN_FILENO,exec    */
+#include <errno.h>		/* errno                */
+
+#include <sys/time.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <limits.h>
+#include <paths.h>
+
+#include "pppoe.h"
+
+static char pidfilename[PATH_MAX];	/* name of pid file */
+
+/*
+static int detached = 0;
+   log_to_fd = -1;
+ */
+
+static void vslp_printer (void *, char *,...);
+static void format_packet (struct pppoe_packet *, int, void (*)(void *, char *,...), void *);
+static void format_tag (struct pppoe_tag *, void (*)(void *, char *,...), void *);
+struct buffer_poe_info {
+  char *ptr;
+  int len;
+};
+
+void poe_die (int status);
+
+
+/*
+ * vpoe_slprintf - like vsprintf, except we
+ * also specify the length of the output buffer, and we handle
+ * %r (recursive format), %m (poe_error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %E (Ether address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+#define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vpoe_slprintf (char *buf, int buflen, char *fmt, va_list args)
+{
+  int c, i, n;
+  int width, prec, fillch;
+  int base, len, neg, quoted;
+  unsigned long val = 0;
+  char *str, *f, *buf0;
+  unsigned char *p;
+  char num[32];
+  time_t t;
+  static char hexchars[] = "0123456789abcdef";
+  struct buffer_poe_info bufpoe_info;
+
+  buf0 = buf;
+  --buflen;
+  while (buflen > 0) {
+    for (f = fmt; *f != '%' && *f != 0; ++f);
+    if (f > fmt) {
+      len = f - fmt;
+      if (len > buflen)
+	len = buflen;
+      memcpy (buf, fmt, len);
+      buf += len;
+      buflen -= len;
+      fmt = f;
+    }
+    if (*fmt == 0)
+      break;
+    c = *++fmt;
+    width = 0;
+    prec = -1;
+    fillch = ' ';
+    if (c == '0') {
+      fillch = '0';
+      c = *++fmt;
+    }
+    if (c == '*') {
+      width = va_arg (args, int);
+      c = *++fmt;
+    }
+    else {
+      while (isdigit (c)) {
+	width = width * 10 + c - '0';
+	c = *++fmt;
+      }
+    }
+    if (c == '.') {
+      c = *++fmt;
+      if (c == '*') {
+	prec = va_arg (args, int);
+	c = *++fmt;
+      }
+      else {
+	prec = 0;
+	while (isdigit (c)) {
+	  prec = prec * 10 + c - '0';
+	  c = *++fmt;
+	}
+      }
+    }
+    str = 0;
+    base = 0;
+    neg = 0;
+    ++fmt;
+    switch (c) {
+    case 'd':
+      i = va_arg (args, int);
+      if (i < 0) {
+	neg = 1;
+	val = -i;
+      }
+      else
+	val = i;
+      base = 10;
+      break;
+    case 'o':
+      val = va_arg (args, unsigned int);
+      base = 8;
+      break;
+    case 'x':
+    case 'X':
+      val = va_arg (args, unsigned int);
+      base = 16;
+      break;
+    case 'p':
+      val = (unsigned long) va_arg (args, void *);
+      base = 16;
+      neg = 2;
+      break;
+    case 's':
+      str = va_arg (args, char *);
+      break;
+    case 'c':
+      num[0] = va_arg (args, int);
+      num[1] = 0;
+      str = num;
+      break;
+    case 'm':
+      str = strerror (errno);
+      break;
+    case 'E':
+      p = va_arg (args, unsigned char *);
+      for (n = ETH_ALEN; n > 0; --n) {
+	c = *p++;
+	OUTCHAR (hexchars[(c >> 4) & 0xf]);
+	OUTCHAR (hexchars[c & 0xf]);
+	if (n > 1)
+	  OUTCHAR (':');
+      }
+      continue;
+    case 'r':
+      f = va_arg (args, char *);
+#ifndef __powerpc__
+      n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, va_list));
+#else
+      /* On the powerpc, a va_list is an array of 1 structure */
+      n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, void *));
+#endif
+      buf += n;
+      buflen -= n;
+      continue;
+    case 't':
+      time (&t);
+      str = ctime (&t);
+      str += 4;			/* chop off the day name */
+      str[15] = 0;		/* chop off year and newline */
+      break;
+    case 'v':			/* "visible" string */
+    case 'q':			/* quoted string */
+      quoted = c == 'q';
+      p = va_arg (args, unsigned char *);
+      if (fillch == '0' && prec >= 0) {
+	n = prec;
+      }
+      else {
+	n = strlen ((char *) p);
+	if (prec >= 0 && n > prec)
+	  n = prec;
+      }
+      while (n > 0 && buflen > 0) {
+	c = *p++;
+	--n;
+	if (!quoted && c >= 0x80) {
+	  OUTCHAR ('M');
+	  OUTCHAR ('-');
+	  c -= 0x80;
+	}
+	if (quoted && (c == '"' || c == '\\'))
+	  OUTCHAR ('\\');
+	if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+	  if (quoted) {
+	    OUTCHAR ('\\');
+	    switch (c) {
+	    case '\t':
+	      OUTCHAR ('t');
+	      break;
+	    case '\n':
+	      OUTCHAR ('n');
+	      break;
+	    case '\b':
+	      OUTCHAR ('b');
+	      break;
+	    case '\f':
+	      OUTCHAR ('f');
+	      break;
+	    default:
+	      OUTCHAR ('x');
+	      OUTCHAR (hexchars[c >> 4]);
+	      OUTCHAR (hexchars[c & 0xf]);
+	    }
+	  }
+	  else {
+	    if (c == '\t')
+	      OUTCHAR (c);
+	    else {
+	      OUTCHAR ('^');
+	      OUTCHAR (c ^ 0x40);
+	    }
+	  }
+	}
+	else
+	  OUTCHAR (c);
+      }
+      continue;
+    case 'P':			/* print PPPoE packet */
+      bufpoe_info.ptr = buf;
+      bufpoe_info.len = buflen + 1;
+      p = va_arg (args, unsigned char *);
+      n = va_arg (args, int);
+      format_packet ((struct pppoe_packet *) p, n, vslp_printer, &bufpoe_info);
+      buf = bufpoe_info.ptr;
+      buflen = bufpoe_info.len - 1;
+      continue;
+    case 'T':			/* print PPPoE tag */
+      bufpoe_info.ptr = buf;
+      bufpoe_info.len = buflen + 1;
+      p = va_arg (args, unsigned char *);
+      format_tag ((struct pppoe_tag *) p, vslp_printer, &bufpoe_info);
+      buf = bufpoe_info.ptr;
+      buflen = bufpoe_info.len - 1;
+      continue;
+    case 'B':
+      p = va_arg (args, unsigned char *);
+      for (n = prec; n > 0; --n) {
+	c = *p++;
+	if (fillch == ' ')
+	  OUTCHAR (' ');
+	OUTCHAR (hexchars[(c >> 4) & 0xf]);
+	OUTCHAR (hexchars[c & 0xf]);
+      }
+      continue;
+    default:
+      *buf++ = '%';
+      if (c != '%')
+	--fmt;			/* so %z outputs %z etc. */
+      --buflen;
+      continue;
+    }
+    if (base != 0) {
+      str = num + sizeof (num);
+      *--str = 0;
+      while (str > num + neg) {
+	*--str = hexchars[val % base];
+	val = val / base;
+	if (--prec <= 0 && val == 0)
+	  break;
+      }
+      switch (neg) {
+      case 1:
+	*--str = '-';
+	break;
+      case 2:
+	*--str = 'x';
+	*--str = '0';
+	break;
+      }
+      len = num + sizeof (num) - 1 - str;
+    }
+    else {
+      len = strlen (str);
+      if (prec >= 0 && len > prec)
+	len = prec;
+    }
+    if (width > 0) {
+      if (width > buflen)
+	width = buflen;
+      if ((n = width - len) > 0) {
+	buflen -= n;
+	for (; n > 0; --n)
+	  *buf++ = fillch;
+      }
+    }
+    if (len > buflen)
+      len = buflen;
+    memcpy (buf, str, len);
+    buf += len;
+    buflen -= len;
+  }
+  *buf = 0;
+  return buf - buf0;
+}
+
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void
+vslp_printer (void *arg, char *fmt,...)
+{
+  int n;
+  va_list pvar;
+  struct buffer_poe_info *bi;
+
+  va_start (pvar, fmt);
+
+  bi = (struct buffer_poe_info *) arg;
+  n = vpoe_slprintf (bi->ptr, bi->len, fmt, pvar);
+  va_end (pvar);
+
+  bi->ptr += n;
+  bi->len -= n;
+}
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_packet (struct pppoe_packet *p,
+	       int len,
+	       void (*printer) (void *, char *,...),
+	       void *arg)
+{
+  struct pppoe_tag *t;
+
+  printer (arg, "Ether addr: %E\n", p->addr.sll_addr);
+
+  switch ((unsigned) ntohs (p->addr.sll_protocol)) {
+  case ETH_P_PPPOE_DISC:
+    printer (arg, " (PPPOE Discovery)\n");
+    break;
+  case ETH_P_PPPOE_SESS:
+    printer (arg, " (PPPOE Session)\n");
+    break;
+  }
+
+  printer (arg, " PPPoE hdr: ver=0x%01x type=0x%01x code=0x%02x "
+	   "sid=0x%04x length=0x%04x ", (unsigned) p->hdr->ver,
+	   (unsigned) p->hdr->type, (unsigned) p->hdr->code, (unsigned) p->hdr->sid,
+	   (unsigned) ntohs (p->hdr->length));
+
+  switch (p->hdr->code) {
+  case PADI_CODE:
+    printer (arg, "(PADI)\n");
+    break;
+  case PADO_CODE:
+    printer (arg, "(PADO)\n");
+    break;
+  case PADR_CODE:
+    printer (arg, "(PADR)\n");
+    break;
+  case PADS_CODE:
+    printer (arg, "(PADS)\n");
+    break;
+  case PADT_CODE:
+    printer (arg, "(PADT)\n");
+    break;
+  default:
+    printer (arg, "(Unknown)\n");
+  }
+
+#if 0
+  if (ntohs (p->addr.sll_protocol) != ETH_P_PPPOE_DISC) {
+      len = ntohs (p->length);
+
+    if (len > 64)
+      printer (arg, " %.64B ...", (p + 1));
+    else
+      printer (arg, " %.*B", len, p + 1);
+
+    return;
+  }
+#endif
+
+  for(t = (struct pppoe_tag *) (&p->hdr->tag);
+      (t < (struct pppoe_tag *) ((char *) (&p->hdr->tag) + ntohs (p->hdr->length))) &&
+	  ntohs (t->tag_type) != PTT_EOL;
+      t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->tag_len))) {
+      format_tag (t, printer, arg);
+  }
+}
+
+/*
+ * format_tag - make a readable representation of a tag,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_tag (struct pppoe_tag *t,
+	       void (*printer) (void *, char *,...),
+	       void *arg)
+{
+    printer (arg, " PPPoE tag: type=%04x length=%04x ",
+	     ntohs (t->tag_type), ntohs (t->tag_len));
+    switch ( t->tag_type ) {
+    case PTT_EOL:
+      printer (arg, "(End of list)");
+      break;
+    case PTT_SRV_NAME:
+      printer (arg, "(Service name)");
+      break;
+    case PTT_AC_NAME:
+      printer (arg, "(AC Name)");
+      break;
+    case PTT_HOST_UNIQ:
+      printer (arg, "(Host Uniq)");
+      break;
+    case PTT_AC_COOKIE:
+      printer (arg, "(AC Cookie)");
+      break;
+    case PTT_VENDOR:
+      printer (arg, "(Vendor Specific)");
+      break;
+    case PTT_RELAY_SID:
+      printer (arg, "(Relay Session ID)");
+      break;
+    case PTT_SRV_ERR:
+      printer (arg, "(Service Name Error)");
+      break;
+    case PTT_SYS_ERR:
+      printer (arg, "(AC System Error)");
+      break;
+    case PTT_GEN_ERR:
+      printer (arg, "(Generic Error)");
+      break;
+    default:
+      printer (arg, "(Unknown)");
+    }
+    if (ntohs (t->tag_len) > 0)
+      switch ( t->tag_type ) {
+      case PTT_SRV_NAME:
+      case PTT_AC_NAME:
+      case PTT_SRV_ERR:
+      case PTT_SYS_ERR:
+      case PTT_GEN_ERR:	/* ascii data */
+	{
+	  char *buf;
+	  buf = malloc (ntohs (t->tag_len) + 1);
+	  memset (buf, 0, ntohs (t->tag_len) + 1);
+	  strncpy (buf, (char *) (t + 1), ntohs (t->tag_len));
+//	  buf[ntohs (t->tag_len)] = '\0';
+	  printer (arg, " data (UTF-8): %s", buf);
+	  free (buf);
+	  break;
+	}
+
+      case PTT_HOST_UNIQ:
+      case PTT_AC_COOKIE:
+      case PTT_RELAY_SID:
+	printer (arg, " data (bin): %.*B", ntohs (t->tag_len), (char *) (t + 1));
+	break;
+
+      default:
+	printer (arg, " unrecognized data");
+      }
+}
+
+/*
+ * poe_logit - does the hard work for poe_fatal et al.
+ */
+static void
+poe_logit (struct session *ses,int level, char *fmt, va_list args)
+{
+  int n;
+  char buf[256];
+
+  n = vpoe_slprintf (buf, sizeof (buf), fmt, args);
+  syslog (level, "%s", buf);
+  if (log_to_fd >= 0 && (level != LOG_DEBUG || ses->opt_debug)) {
+    if (buf[n - 1] != '\n')
+      buf[n++] = '\n';
+    if (write (log_to_fd, buf, n) != n)
+      log_to_fd = -1;
+  }
+}
+
+/*
+ * poe_fatal - log an poe_error message and poe_die horribly.
+ */
+void
+poe_fatal (struct session *ses, char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_ERR, fmt, pvar);
+  va_end (pvar);
+
+  poe_die(1);			/* as promised */
+}
+
+/*
+ * poe_error - log an poe_error message.
+ */
+void
+poe_error (struct session *ses,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_ERR, fmt, pvar);
+  va_end (pvar);
+}
+
+/*
+ * poe_warn - log a poe_warning message.
+ */
+void
+poe_warn (struct session *ses,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_WARNING, fmt, pvar);
+  va_end (pvar);
+}
+
+#if 0
+/*
+ * poe_notice - log a poe_notice-level message.
+ */
+void
+poe_notice (int log_to_fd ,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (log_to_fd,LOG_NOTICE, fmt, pvar);
+  va_end (pvar);
+}
+
+#endif
+/*
+ * poe_info - log an poe_informational message.
+ */
+void
+poe_info (struct session *ses,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_INFO, fmt, pvar);
+  va_end (pvar);
+}
+
+/*
+ * poe_dbglog - log a debug message.
+ */
+void
+poe_dbglog (struct session *ses ,char *fmt,...)
+{
+  va_list pvar;
+
+  va_start (pvar, fmt);
+
+  poe_logit (ses,LOG_DEBUG, fmt, pvar);
+  va_end (pvar);
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+void
+poe_create_pidfile (struct session *ses)
+{
+  FILE *pidfile;
+
+  sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "pppoed");
+  if ((pidfile = fopen (pidfilename, "w")) != NULL) {
+    fprintf (pidfile, "%d\n", getpid ());
+    (void) fclose (pidfile);
+  }
+  else {
+    poe_error (ses,"Failed to create pid file %s: %m", pidfilename);
+    pidfilename[0] = 0;
+  }
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+poe_detach (struct session *ses)
+{
+  if (ses->detached)
+    return;
+
+  if ((daemon (0, 0)) < 0) {
+    poe_error (ses,"Couldn't detach (daemon failed: %m)");
+#if 0
+    poe_die (1);			/* or just return? */
+#endif
+  }
+  ses->detached = 1;
+  ses->log_to_fd = -1;
+  /* update pid files if they have been written already */
+  if (pidfilename[0])
+    poe_create_pidfile (ses);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup ()
+{
+  if (pidfilename[0] != 0 && unlink (pidfilename) < 0 && errno != ENOENT)
+    syslog (LOG_INFO,"unable to delete pid file ");
+  pidfilename[0] = 0;
+}
+
+/*
+ * poe_die - clean up state and exit with the specified status.
+ */
+void
+poe_die (int status)
+{
+  cleanup ();
+  syslog (LOG_INFO, "Exit.");
+  exit (status);
+}
diff -Naur ppp-2.4.1-orig/pppd/pppd.h ppp-2.4.1/pppd/pppd.h
--- ppp-2.4.1-orig/pppd/pppd.h	2001-03-12 23:54:37.000000000 -0600
+++ ppp-2.4.1/pppd/pppd.h	2003-09-19 23:47:33.000000000 -0500
@@ -496,6 +496,8 @@
 int  open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
 int  tty_establish_ppp __P((int));  /* Turn serial port into a ppp interface */
 void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */
+void generic_disestablish_ppp __P((int dev_fd)); /* Restore device setting */
+int  generic_establish_ppp __P((int dev_fd)); /* Make a ppp interface */
 void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
 int  bundle_attach __P((int));	/* Attach link to existing bundle */
 void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
@@ -587,6 +589,7 @@
 				/* Simplified number_option for decimal ints */
 void add_options __P((option_t *)); /* Add extra options */
 void check_options __P((void));	/* check values after all options parsed */
+int remove_option __P((char *)); /* Disable the specified option */
 int  override_value __P((const char *, int, const char *));
 				/* override value if permitted by priority */
 void print_options __P((void (*) __P((void *, char *, ...)), void *));
diff -Naur ppp-2.4.1-orig/pppd/sys-linux.c ppp-2.4.1/pppd/sys-linux.c
--- ppp-2.4.1-orig/pppd/sys-linux.c	2001-03-12 23:54:41.000000000 -0600
+++ ppp-2.4.1/pppd/sys-linux.c	2003-09-19 23:47:33.000000000 -0500
@@ -141,7 +141,7 @@
 static int restore_term        = 0;	/* 1 => we've munged the terminal */
 static struct termios inittermios;	/* Initial TTY termios */
 
-static int new_style_driver = 0;
+int new_style_driver = 0;
 
 static char loop_name[20];
 static unsigned char inbuf[512]; /* buffer for chars read from loopback */
@@ -229,7 +229,7 @@
  */
 
 static int get_flags (int fd)
-{    
+{
     int flags;
 
     if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
@@ -246,7 +246,7 @@
 /********************************************************************/
 
 static void set_flags (int fd, int flags)
-{    
+{
     SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags));
 
     if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) {
@@ -352,6 +352,7 @@
     return (1);
 }
 
+
 /********************************************************************
  *
  * tty_establish_ppp - Turn the serial port into a ppp interface.
@@ -359,9 +360,7 @@
 
 int tty_establish_ppp (int tty_fd)
 {
-    int x;
-    int fd = -1;
-
+    int ret_fd;
 /*
  * Ensure that the tty device is in exclusive mode.
  */
@@ -370,14 +369,6 @@
 	    warn("Couldn't make tty exclusive: %m");
     }
 /*
- * Demand mode - prime the old ppp device to relinquish the unit.
- */
-    if (!new_style_driver && looped
-	&& ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
-	error("ioctl(transfer ppp unit): %m");
-	return -1;
-    }
-/*
  * Set the current tty to the PPP discpline
  */
 
@@ -392,11 +383,39 @@
 	}
     }
 
+    ret_fd = generic_establish_ppp(tty_fd);
+#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+		 | SC_LOG_FLUSH)
+
+    set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
+		       | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
+
+    return ret_fd;
+}
+
+/********************************************************************
+ *
+ * generic_establish_ppp - Turn the fd into a ppp interface.
+ */
+int generic_establish_ppp (int fd)
+{
+    int x;
+/*
+ * Demand mode - prime the old ppp device to relinquish the unit.
+ */
+    if (!new_style_driver && looped
+	&& ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
+	error("ioctl(transfer ppp unit): %m");
+	return -1;
+    }
+
+
     if (new_style_driver) {
 	/* Open another instance of /dev/ppp and connect the channel to it */
 	int flags;
 
-	if (ioctl(tty_fd, PPPIOCGCHAN, &chindex) == -1) {
+	if (ioctl(fd, PPPIOCGCHAN, &chindex) == -1) {
 	    error("Couldn't get channel number: %m");
 	    goto err;
 	}
@@ -437,11 +456,12 @@
 	}
 
     } else {
+
 	/*
 	 * Old-style driver: find out which interface we were given.
 	 */
-	set_ppp_fd (tty_fd);
-	if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {	
+	set_ppp_fd (fd);
+	if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
 	    if (ok_error (errno))
 		goto err;
 	    fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
@@ -454,14 +474,15 @@
 	/*
 	 * Fetch the initial file flags and reset blocking mode on the file.
 	 */
-	initfdflags = fcntl(tty_fd, F_GETFL);
+	initfdflags = fcntl(fd, F_GETFL);
 	if (initfdflags == -1 ||
-	    fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+	    fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
 	    if ( ! ok_error (errno))
 		warn("Couldn't set device to non-blocking mode: %m");
 	}
     }
 
+
     looped = 0;
 
     /*
@@ -470,13 +491,6 @@
     if (!looped)
 	set_kdebugflag (kdebugflag);
 
-#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
-#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
-		 | SC_LOG_FLUSH)
-
-    set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
-		       | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
-
     SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
 	    driver_version, driver_modification, driver_patch));
 
@@ -485,22 +499,21 @@
  err_close:
     close(fd);
  err:
-    if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+    if (ioctl(fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
 	warn("Couldn't reset tty to normal line discipline: %m");
     return -1;
 }
 
 /********************************************************************
  *
- * tty_disestablish_ppp - Restore the serial port to normal operation,
- * and reconnect the ppp unit to the loopback if in demand mode.
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
  * This shouldn't call die() because it's called from die().
  */
 
 void tty_disestablish_ppp(int tty_fd)
 {
-    if (demand)
-	restore_loop();
+    generic_disestablish_ppp(tty_fd);
+
     if (!hungup) {
 /*
  * Flush the tty output buffer so that the TIOCSETD doesn't hang.
@@ -514,7 +527,7 @@
 	    if ( ! ok_error (errno))
 		error("ioctl(TIOCSETD, N_TTY): %m");
 	}
-	
+
 	if (ioctl(tty_fd, TIOCNXCL, 0) < 0) {
 	    if ( ! ok_error (errno))
 		warn("ioctl(TIOCNXCL): %m(%d)", errno);
@@ -526,6 +539,20 @@
 		warn("Couldn't restore device fd flags: %m");
 	}
     }
+}
+
+/********************************************************************
+ *
+ * generic_disestablish_ppp - Restore device components to normal
+ * operation, and reconnect the ppp unit to the loopback if in demand
+ * mode.  This shouldn't call die() because it's called from die().
+*/
+void generic_disestablish_ppp(int dev_fd){
+    /* Restore loop if needed */
+    if(demand)
+	restore_loop();
+
+    /* Finally detach the device */
     initfdflags = -1;
 
     if (new_style_driver) {
@@ -538,6 +565,7 @@
     }
 }
 
+
 /*
  * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
  * Assumes new_style_driver.
@@ -644,20 +672,20 @@
 	    case SC_RCV_B7_0:
 		s = "all had bit 7 set to 1";
 		break;
-		
+
 	    case SC_RCV_B7_1:
 		s = "all had bit 7 set to 0";
 		break;
-		
+
 	    case SC_RCV_EVNP:
 		s = "all had odd parity";
 		break;
-		
+
 	    case SC_RCV_ODDP:
 		s = "all had even parity";
 		break;
 	    }
-	    
+
 	    if (s != NULL) {
 		warn("Receive serial link is not 8-bit clean:");
 		warn("Problem: %s", s);
@@ -665,7 +693,7 @@
 	}
     }
 }
-	
+
 
 /*
  * List of valid speeds.
@@ -782,7 +810,7 @@
 static int baud_rate_of (int speed)
 {
     struct speed *speedp;
-    
+
     if (speed != 0) {
 	for (speedp = speeds; speedp->speed_int; speedp++) {
 	    if (speed == speedp->speed_val)
@@ -810,10 +838,10 @@
 	    fatal("tcgetattr: %m(%d)", errno);
 	return;
     }
-    
+
     if (!restore_term)
 	inittermios = tios;
-    
+
     tios.c_cflag     &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
     tios.c_cflag     |= CS8 | CREAD | HUPCL;
 
@@ -822,7 +850,7 @@
     tios.c_lflag      = 0;
     tios.c_cc[VMIN]   = 1;
     tios.c_cc[VTIME]  = 0;
-    
+
     if (local || !modem)
 	tios.c_cflag ^= (CLOCAL | HUPCL);
 
@@ -844,7 +872,7 @@
     default:
 	break;
     }
-    
+
     speed = translate_speed(inspeed);
     if (speed) {
 	cfsetospeed (&tios, speed);
@@ -863,7 +891,7 @@
     if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0)
 	if (!ok_error(errno))
 	    fatal("tcsetattr: %m");
-    
+
     baud_rate    = baud_rate_of(speed);
     restore_term = 1;
 }
@@ -898,7 +926,7 @@
  */
 	if (!default_device)
 	    inittermios.c_lflag &= ~(ECHO | ECHONL);
-	
+
 	if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) {
 	    if (! ok_error (errno))
 		warn("tcsetattr: %m");
@@ -1054,7 +1082,7 @@
     memset (&ifr, '\0', sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
     ifr.ifr_mtu = mtu;
-	
+
     if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
 	fatal("ioctl(SIOCSIFMTU): %m");
 }
@@ -1081,7 +1109,7 @@
 	    fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno);
 	return;
     }
-    
+
     x = get_flags(ppp_fd);
     x = pcomp  ? x | SC_COMP_PROT : x & ~SC_COMP_PROT;
     x = accomp ? x | SC_COMP_AC   : x & ~SC_COMP_AC;
@@ -1213,7 +1241,7 @@
     struct ppp_idle *ip;
 {
     return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
-} 
+}
 
 /********************************************************************
  *
@@ -1380,7 +1408,7 @@
 {
     char *cols[ROUTE_MAX_COLS], *p;
     int col;
-	
+
     memset (rt, '\0', sizeof (struct rtentry));
 
     if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
@@ -1488,7 +1516,7 @@
     }
 
     SIN_ADDR(rt.rt_gateway) = gateway;
-    
+
     rt.rt_flags = RTF_UP | RTF_GATEWAY;
     if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
 	if ( ! ok_error ( errno ))
@@ -1521,7 +1549,7 @@
     }
 
     SIN_ADDR(rt.rt_gateway) = gateway;
-    
+
     rt.rt_flags = RTF_UP | RTF_GATEWAY;
     if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
 	if (still_ppp()) {
@@ -1546,7 +1574,7 @@
 
     if (has_proxy_arp == 0) {
 	memset (&arpreq, '\0', sizeof(arpreq));
-    
+
 	SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
 	SIN_ADDR(arpreq.arp_pa) = his_adr;
 	arpreq.arp_flags = ATF_PERM | ATF_PUBL;
@@ -1610,7 +1638,7 @@
     }
     return 1;
 }
-     
+
 /********************************************************************
  *
  * get_ether_addr - get the hardware address of an interface on the
@@ -1627,7 +1655,7 @@
     struct ifreq ifreq;
     struct ifconf ifc;
     struct ifreq ifs[MAX_IFS];
-    
+
     ifc.ifc_len = sizeof(ifs);
     ifc.ifc_req = ifs;
     if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
@@ -1673,7 +1701,7 @@
 	    break;
 	}
     }
-    
+
     if (ifr >= ifend)
         return 0;
 
@@ -1761,14 +1789,14 @@
     struct ifreq ifs[MAX_IFS];
 
     addr = ntohl(addr);
-    
+
     if (IN_CLASSA(addr))	/* determine network mask for address class */
 	nmask = IN_CLASSA_NET;
     else if (IN_CLASSB(addr))
 	    nmask = IN_CLASSB_NET;
     else
 	    nmask = IN_CLASSC_NET;
-    
+
     /* class D nets are disallowed by bad_ip_adrs */
     mask = netmask | htonl(nmask);
 /*
@@ -1781,7 +1809,7 @@
 	    warn("ioctl(SIOCGIFCONF): %m(%d)", errno);
 	return mask;
     }
-    
+
     ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
     for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
 /*
@@ -1798,7 +1826,7 @@
 	strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
 	if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
 	    continue;
-	
+
 	if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
 	    continue;
 /*
@@ -1825,7 +1853,7 @@
     *version      = (int) strtoul (buf, &endp, 10);
     *modification = 0;
     *patch        = 0;
-    
+
     if (endp != buf && *endp == '.') {
 	buf = endp + 1;
 	*modification = (int) strtoul (buf, &endp, 10);
@@ -1868,7 +1896,7 @@
 	error("ioctl(TIOCSETD(PPP)): %m(%d)", errno);
     } else
 	ret = 1;
-    
+
     close(local_fd);
     close(mfd);
     return ret;
@@ -1888,7 +1916,7 @@
     int    my_version, my_modification, my_patch;
     int osmaj, osmin, ospatch;
 
-    no_ppp_msg = 
+    no_ppp_msg =
 	"This system lacks kernel support for PPP.  This could be because\n"
 	"the PPP kernel module could not be loaded, or because PPP was not\n"
 	"included in the kernel configuration.  If PPP was included as a\n"
@@ -1940,11 +1968,11 @@
 
 /*
  * Open a socket for doing the ioctl operations.
- */    
+ */
     s = socket(AF_INET, SOCK_DGRAM, 0);
     if (s < 0)
 	return 0;
-    
+
     strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
     ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
 /*
@@ -1997,7 +2025,7 @@
 	    /* The version numbers must match */
 	    if (driver_version != my_version)
 		ok = 0;
-      
+
 	    /* The modification levels must be legal */
 	    if (driver_modification < 3) {
 		if (driver_modification >= 2) {
@@ -2045,7 +2073,7 @@
 
     /* Is this call really necessary? There is another one after the 'put' */
     endutent();
-    
+
     if (utp)
 	memcpy(&ut, utp, sizeof(ut));
     else
@@ -2054,7 +2082,7 @@
 
     if (ut.ut_id[0] == 0)
 	strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
-	
+
     strncpy(ut.ut_user, name, sizeof(ut.ut_user));
     strncpy(ut.ut_line, line, sizeof(ut.ut_line));
 
@@ -2071,7 +2099,7 @@
     if (ipcp_protent.enabled_flag && ipcp_hisoptions[0].neg_addr)
 	memcpy(&ut.ut_addr, (char *) &ipcp_hisoptions[0].hisaddr,
 		 sizeof(ut.ut_addr));
-	
+
     /* CL: Makes sure that the logout works */
     if (*host == 0 && *name==0)
 	ut.ut_host[0]=0;
@@ -2190,15 +2218,15 @@
 int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
 	     u_int32_t net_mask)
 {
-    struct ifreq   ifr; 
+    struct ifreq   ifr;
     struct rtentry rt;
-    
+
     memset (&ifr, '\0', sizeof (ifr));
     memset (&rt,  '\0', sizeof (rt));
-    
-    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET); 
-    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET); 
-    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET); 
+
+    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET);
+    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
 
     strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
 /*
@@ -2221,9 +2249,9 @@
     SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
     if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno))
-	    error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno); 
+	    error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno);
 	return (0);
-    } 
+    }
 /*
  *  Set the netmask.
  *  For recent kernels, force the netmask to 255.255.255.255.
@@ -2234,9 +2262,9 @@
 	SIN_ADDR(ifr.ifr_netmask) = net_mask;
 	if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
 	    if (! ok_error (errno))
-		error("ioctl(SIOCSIFNETMASK): %m(%d)", errno); 
+		error("ioctl(SIOCSIFNETMASK): %m(%d)", errno);
 	    return (0);
-	} 
+	}
     }
 /*
  *  Add the device route
@@ -2323,7 +2351,7 @@
     memset(&ifr, 0, sizeof(ifr));
     SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    
+
     if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno)) {
 	    error("ioctl(SIOCSIFADDR): %m(%d)", errno);
@@ -2338,7 +2366,7 @@
 
 #ifdef INET6
 /********************************************************************
- * 
+ *
  * sif6addr - Config the interface with an IPv6 link-local address
  */
 int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
@@ -2358,7 +2386,7 @@
 	error("sif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
 	return 0;
     }
-    
+
     /* Local interface */
     memset(&ifr6, 0, sizeof(ifr6));
     IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
@@ -2369,7 +2397,7 @@
 	error("sif6addr: ioctl(SIOCSIFADDR): %m (%d)", errno);
 	return 0;
     }
-    
+
     /* Route to remote host */
     memset(&rt6, 0, sizeof(rt6));
     IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
@@ -2377,7 +2405,7 @@
     rt6.rtmsg_dst_len = 10;
     rt6.rtmsg_ifindex = ifr.ifr_ifindex;
     rt6.rtmsg_metric = 1;
-    
+
     if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
 	error("sif6addr: ioctl(SIOCADDRT): %m (%d)", errno);
 	return 0;
@@ -2407,7 +2435,7 @@
 	error("cif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
 	return 0;
     }
-    
+
     memset(&ifr6, 0, sizeof(ifr6));
     IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
     ifr6.ifr6_ifindex = ifr.ifr_ifindex;
@@ -2567,7 +2595,7 @@
  * Just to be sure, set the real serial port to the normal discipline.
  */
 
-static void
+void
 restore_loop(void)
 {
     looped = 1;
@@ -2616,12 +2644,12 @@
     int    result = 1;
 
 #ifdef IPX_CHANGE
-    int    skfd; 
+    int    skfd;
     struct ifreq         ifr;
     struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
 
     skfd = socket (AF_IPX, SOCK_DGRAM, 0);
-    if (skfd < 0) { 
+    if (skfd < 0) {
 	if (! ok_error (errno))
 	    dbglog("socket(AF_IPX): %m (%d)", errno);
 	result = 0;
@@ -2667,12 +2695,12 @@
     int    result = 1;
 
 #ifdef IPX_CHANGE
-    int    skfd; 
+    int    skfd;
     struct ifreq         ifr;
     struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
 
     skfd = socket (AF_IPX, SOCK_DGRAM, 0);
-    if (skfd < 0) { 
+    if (skfd < 0) {
 	if (! ok_error (errno))
 	    dbglog("socket(AF_IPX): %m (%d)", errno);
 	result = 0;
