Sync to upstream head.

This change syncs external/ppp with upstream. Specifically it's based on
upstream's 6e8eaa7a78b31cdab2edf140a9c8afdb02ffaca5.

In order to do this without blowing away the Android-specific changes, I
ran a script to try and find a matching revision in upstream's git tree
to the initial commit in external/ppp.

Based on the contents of *.c and *.h files in pppd/, upstream's
f8583676904957554c5a8bd14ede415688835f6c matches with the local revision
82c907af479178801a7a8701341b22c9d20fdb7e.

I diffed upstream's f8583676 with upstream's HEAD and removed chunks
from the result that applied to files that aren't present in Android. In
some places, the diff didn't apply cleanly because of Android-specific
changes:

  1) Local change 8ad0dd2a added two "return UPAP_AUTHNAK;" statements
     in "#if 1" blocks in auth.c. They have been reflected into
     session.c/session_start by always returning SESSION_FAILED.
     (Additionally, new code to query the local password db has been
     disabled via #if !defined(__ANDROID__).)

  2) Upstream now always defines INET6 for Linux so I've pulled IPv6
     support in. (Otherwise sys-linux.c has compile errors.)

  3) linux/ipv6_route.h needed to be included explicitly.

  4) I defined HAVE_LOGWTMP to avoid calling some wtmp functions that
     aren't implemented in Android. A dummy logwtmp is defined.

All the above changes are in __ANDROID__ tags to highlight them.
Additionally I've ensured that all the pre-existing changes are also now
in __ANDROID__ tags.

Change-Id: Ia5abe677d6ba4d5e9f4fbcd4fa03d8911b8bed12
diff --git a/pppd/Android.mk b/pppd/Android.mk
index 6552f11..0a564d9 100644
--- a/pppd/Android.mk
+++ b/pppd/Android.mk
@@ -2,27 +2,30 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-	main.c \
-	magic.c \
-	fsm.c \
-	lcp.c \
-	ipcp.c \
-	upap.c \
-	chap-new.c \
-	ccp.c \
-	ecp.c \
 	auth.c \
-	options.c \
-	sys-linux.c \
+	ccp.c \
+	chap-md5.c \
+	chap-new.c \
 	chap_ms.c \
 	demand.c \
-	utils.c \
-	tty.c \
 	eap.c \
-	chap-md5.c \
-	pppcrypt.c \
+	ecp.c \
+	eui64.c \
+	fsm.c \
+	ipcp.c \
+	ipv6cp.c \
+	lcp.c \
+	magic.c \
+	main.c \
 	openssl-hash.c \
-	pppox.c
+	options.c \
+	pppcrypt.c \
+	pppox.c \
+	session.c \
+	sys-linux.c \
+	tty.c \
+	upap.c \
+	utils.c
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils liblog libcrypto
@@ -30,7 +33,7 @@
 LOCAL_C_INCLUDES := \
 	$(LOCAL_PATH)/include
 
-LOCAL_CFLAGS := -DANDROID_CHANGES -DCHAPMS=1 -DMPPE=1 -Iexternal/openssl/include
+LOCAL_CFLAGS := -DCHAPMS=1 -DMPPE=1 -DINET6=1 -Iexternal/openssl/include -Wno-unused-parameter -Wno-empty-body -Wno-missing-field-initializers -Wno-attributes -Wno-sign-compare -Wno-pointer-sign -Werror
 
 LOCAL_MODULE:= pppd
 
diff --git a/pppd/Makefile.linux b/pppd/Makefile.linux
index e7e52b8..a74c914 100644
--- a/pppd/Makefile.linux
+++ b/pppd/Makefile.linux
@@ -1,10 +1,10 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.66 2004/11/13 12:02:22 paulus Exp $
+# $Id: Makefile.linux,v 1.70 2007/06/19 02:08:34 carlsonj Exp $
 #
 
 # Default installation locations
-DESTDIR = @DESTDIR@
+DESTDIR = $(INSTROOT)@DESTDIR@
 BINDIR = $(DESTDIR)/sbin
 MANDIR = $(DESTDIR)/share/man/man8
 INCDIR = $(DESTDIR)/include
@@ -13,16 +13,16 @@
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap-new.c md5.c ccp.c \
 	   ecp.c ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c \
-	   demand.c utils.c tty.c eap.c chap-md5.c
+	   demand.c utils.c tty.c eap.c chap-md5.c session.c
 
-HEADERS = ccp.h chap-new.h ecp.h fsm.h ipcp.h \
+HEADERS = ccp.h session.h chap-new.h ecp.h fsm.h ipcp.h \
 	ipxcp.h lcp.h magic.h md5.h patchlevel.h pathnames.h pppd.h \
 	upap.h eap.h
 
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o md5.o ccp.o \
 	   ecp.o auth.o options.o demand.o utils.o sys-linux.o ipxcp.o tty.o \
-	   eap.o chap-md5.o
+	   eap.o chap-md5.o session.o
 
 #
 # include dependencies if present
@@ -62,7 +62,7 @@
 
 HAS_SHADOW=y
 #USE_PAM=y
-#HAVE_INET6=y
+HAVE_INET6=y
 
 # Enable plugins
 PLUGIN=y
@@ -73,13 +73,16 @@
 # Enable EAP SRP-SHA1 authentication (requires libsrp)
 #USE_SRP=y
 
+# Use libutil
+USE_LIBUTIL=y
+
 MAXOCTETS=y
 
 INCLUDE_DIRS= -I../include
 
 COMPILE_FLAGS= -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MMAP
 
-CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) '-DDESTDIR="@DESTDIR@"'
 
 ifdef CHAPMS
 CFLAGS   += -DCHAPMS=1
@@ -118,12 +121,15 @@
 endif
 
 ifneq ($(wildcard /usr/include/crypt.h),)
-CFLAGS   += -DHAVE_CRYPT_H=1
-endif
-ifneq ($(wildcard /usr/lib/libcrypt.*),)
+CFLAGS  += -DHAVE_CRYPT_H=1
 LIBS	+= -lcrypt
 endif
 
+ifdef USE_LIBUTIL
+CFLAGS	+= -DHAVE_LOGWTMP=1
+LIBS	+= -lutil
+endif
+
 ifdef NEEDDES
 ifndef USE_CRYPT
 LIBS     += -ldes $(LIBS)
diff --git a/pppd/Makefile.sol2 b/pppd/Makefile.sol2
index 75c2922..45b6b62 100644
--- a/pppd/Makefile.sol2
+++ b/pppd/Makefile.sol2
@@ -1,19 +1,22 @@
 #
 # Makefile for pppd under Solaris 2.
-# $Id: Makefile.sol2,v 1.26 2004/04/14 02:39:39 carlsonj Exp $
+# $Id: Makefile.sol2,v 1.30 2008/01/30 14:26:52 carlsonj Exp $
 #
 
 include ../Makedefs.com
 
-CFLAGS	=  -I../include -DSVR4 -DSOL2 $(COPTS)
+CFLAGS	=  -I../include -DSVR4 -DSOL2 $(COPTS) '-DDESTDIR="@DESTDIR@"'
 LIBS	= -lsocket -lnsl
 
 OBJS	=  main.o magic.o fsm.o lcp.o ipcp.o upap.o chap-new.o eap.o md5.o \
 	tty.o ccp.o ecp.o auth.o options.o demand.o utils.o sys-solaris.o \
-	chap-md5.o
+	chap-md5.o session.o
+
+# Solaris uses shadow passwords
+CFLAGS	+= -DHAS_SHADOW
 
 #
-# uncomment the following to enable plugins
+# Comment the following out to disable plugins
 #
 CFLAGS	+= -DPLUGIN
 LIBS	+= -ldl
@@ -29,17 +32,21 @@
 #
 # Solaris 8 and on includes support for IPv6
 #
-#CFLAGS	+= -DINET6
-#OBJS	+= ipv6cp.o eui64.o
+CFLAGS	+= -DINET6
+OBJS	+= ipv6cp.o eui64.o
 
 # Uncomment to enable MS-CHAP
-#CFLAGS += -DUSE_CRYPT -DCHAPMS -DHAVE_CRYPT_H
-#OBJS += chap_ms.o pppcrypt.o md4.o sha1.o
+CFLAGS += -DUSE_CRYPT -DCHAPMS -DMSLANMAN -DHAVE_CRYPT_H
+OBJS += chap_ms.o pppcrypt.o md4.o sha1.o
 
 # Uncomment for CBCP
 #CFLAGS += -DCBCP_SUPPORT
 #OBJS += cbcp.o
 
+# Uncomment for PAM
+#CFLAGS += -DUSE_PAM
+#LIBS += -lpam
+
 #
 # Make targets
 #
diff --git a/pppd/auth.c b/pppd/auth.c
index 07f0b31..f932cf5 100644
--- a/pppd/auth.c
+++ b/pppd/auth.c
@@ -68,7 +68,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: auth.c,v 1.101 2004/11/12 10:30:51 paulus Exp $"
+#define RCSID	"$Id: auth.c,v 1.117 2008/07/01 12:27:56 paulus Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -91,9 +91,6 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#ifdef USE_PAM
-#include <security/pam_appl.h>
-#endif
 
 #ifdef HAS_SHADOW
 #include <shadow.h>
@@ -116,6 +113,7 @@
 #include "cbcp.h"
 #endif
 #include "pathnames.h"
+#include "session.h"
 
 static const char rcsid[] = RCSID;
 
@@ -134,9 +132,6 @@
 /* Records which authentication operations have been completed. */
 int auth_done[NUM_PPP];
 
-/* Set if we have successfully called plogin() */
-static int logged_in;
-
 /* List of addresses which the peer may use. */
 static struct permitted_ip *addresses[NUM_PPP];
 
@@ -195,6 +190,11 @@
 
 int (*allowed_address_hook) __P((u_int32_t addr)) = NULL;
 
+#ifdef HAVE_MULTILINK
+/* Hook for plugin to hear when an interface joins a multilink bundle */
+void (*multilink_join_hook) __P((void)) = NULL;
+#endif
+
 /* A notifier for when the peer has authenticated itself,
    and we are proceeding to the network phase. */
 struct notifier *auth_up_notifier = NULL;
@@ -215,12 +215,11 @@
 static enum script_state auth_script_state = s_down;
 static pid_t auth_script_pid = 0;
 
-static int used_login;		/* peer authenticated against login database */
-
 /*
  * Option variables.
  */
 bool uselogin = 0;		/* Use /etc/passwd for checking PAP */
+bool session_mgmt = 0;		/* Do session management (login records) */
 bool cryptpap = 0;		/* Passwords in pap-secrets are encrypted */
 bool refuse_pap = 0;		/* Don't wanna auth. ourselves with PAP */
 bool refuse_chap = 0;		/* Don't wanna auth. ourselves with CHAP */
@@ -236,6 +235,8 @@
 bool auth_required = 0;		/* Always require authentication from peer */
 bool allow_any_ip = 0;		/* Allow peer to use any IP address */
 bool explicit_remote = 0;	/* User specified explicit remote name */
+bool explicit_user = 0;		/* Set if "user" option supplied */
+bool explicit_passwd = 0;	/* Set if "password" option supplied */
 char remote_name[MAXNAMELEN];	/* Peer's name for authentication */
 
 static char *uafname;		/* name of most recent +ua file */
@@ -247,8 +248,6 @@
 static void network_phase __P((int));
 static void check_idle __P((void *));
 static void connect_time_expired __P((void *));
-static int  plogin __P((char *, char *, char **));
-static void plogout __P((void));
 static int  null_login __P((int));
 static int  get_pap_passwd __P((char *));
 static int  have_pap_secret __P((int *));
@@ -363,11 +362,13 @@
       OPT_PRIO | OPT_A2STRVAL, &uafname },
 
     { "user", o_string, user,
-      "Set name for auth with peer", OPT_PRIO | OPT_STATIC, NULL, MAXNAMELEN },
+      "Set name for auth with peer", OPT_PRIO | OPT_STATIC,
+      &explicit_user, MAXNAMELEN },
 
     { "password", o_string, passwd,
       "Password for authenticating us to the peer",
-      OPT_PRIO | OPT_STATIC | OPT_HIDE, NULL, MAXSECRETLEN },
+      OPT_PRIO | OPT_STATIC | OPT_HIDE,
+      &explicit_passwd, MAXSECRETLEN },
 
     { "usehostname", o_bool, &usehostname,
       "Must use hostname for authentication", 1 },
@@ -377,7 +378,10 @@
       &explicit_remote, MAXNAMELEN },
 
     { "login", o_bool, &uselogin,
-      "Use system password database for PAP", 1 },
+      "Use system password database for PAP", OPT_A2COPY | 1 ,
+      &session_mgmt },
+    { "enable-session", o_bool, &session_mgmt,
+      "Enable session accounting for remote peers", OPT_PRIV | 1 },
 
     { "papcrypt", o_bool, &cryptpap,
       "PAP passwords are encrypted", 1 },
@@ -409,6 +413,7 @@
 {
     FILE *ufile;
     int l;
+    uid_t euid;
     char u[MAXNAMELEN], p[MAXSECRETLEN];
     char *fname;
 
@@ -418,9 +423,14 @@
     fname = strdup(*argv);
     if (fname == NULL)
 	novm("+ua file name");
-    seteuid(getuid());
+    euid = geteuid();
+    if (seteuid(getuid()) == -1) {
+	option_error("unable to reset uid before opening %s: %m", fname);
+	return 0;
+    }
     ufile = fopen(fname, "r");
-    seteuid(0);
+    if (seteuid(euid) == -1)
+	fatal("unable to regain privileges: %m");
     if (ufile == NULL) {
 	option_error("unable to open user login data file %s", fname);
 	return 0;
@@ -445,10 +455,14 @@
     if (l > 0 && p[l-1] == '\n')
 	p[l-1] = 0;
 
-    if (override_value("user", option_priority, fname))
+    if (override_value("user", option_priority, fname)) {
 	strlcpy(user, u, sizeof(user));
-    if (override_value("passwd", option_priority, fname))
+	explicit_user = 1;
+    }
+    if (override_value("passwd", option_priority, fname)) {
 	strlcpy(passwd, p, sizeof(passwd));
+	explicit_passwd = 1;
+    }
 
     return (1);
 }
@@ -526,14 +540,23 @@
 
 /*
  * An Open on LCP has requested a change from Dead to Establish phase.
- * Do what's necessary to bring the physical layer up.
  */
 void
 link_required(unit)
     int unit;
 {
+}
+
+/*
+ * Bring the link up to the point of being able to do ppp.
+ */
+void start_link(unit)
+    int unit;
+{
+    status = EXIT_CONNECT_FAILED;
     new_phase(PHASE_SERIALCONN);
 
+    hungup = 0;
     devfd = the_channel->connect();
     if (devfd < 0)
 	goto fail;
@@ -580,7 +603,6 @@
     new_phase(PHASE_DEAD);
     if (the_channel->cleanup)
 	(*the_channel->cleanup)();
-
 }
 
 /*
@@ -597,10 +619,8 @@
 
     if (pap_logout_hook) {
 	pap_logout_hook();
-    } else {
-	if (logged_in)
-	    plogout();
     }
+    session_end(devnam);
 
     if (!doing_multilink) {
 	notice("Connection terminated.");
@@ -642,11 +662,15 @@
 	the_channel->disconnect();
 	devfd = -1;
     }
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
 
     if (doing_multilink && multilink_master) {
-	if (!bundle_terminating)
+	if (!bundle_terminating) {
 	    new_phase(PHASE_MASTER);
-	else
+	    if (master_detach && !detached)
+		detach();
+	} else
 	    mp_bundle_terminated();
     } else
 	new_phase(PHASE_DEAD);
@@ -734,14 +758,13 @@
 	    set_allowed_addrs(unit, NULL, NULL);
 	} else if (!wo->neg_upap || uselogin || !null_login(unit)) {
 	    warn("peer refused to authenticate: terminating link");
-	    lcp_close(unit, "peer refused to authenticate");
 	    status = EXIT_PEER_AUTH_FAILED;
+	    lcp_close(unit, "peer refused to authenticate");
 	    return;
 	}
     }
 
     new_phase(PHASE_AUTHENTICATE);
-    used_login = 0;
     auth = 0;
     if (go->neg_eap) {
 	eap_authpeer(unit, our_name);
@@ -760,7 +783,9 @@
 	chap_auth_with_peer(unit, user, CHAP_DIGEST(ho->chap_mdtype));
 	auth |= CHAP_WITHPEER;
     } else if (ho->neg_upap) {
-	if (passwd[0] == 0) {
+	/* If a blank password was explicitly given as an option, trust
+	   the user and don't try to look up one. */
+	if (passwd[0] == 0 && !explicit_passwd) {
 	    passwd_from_file = 1;
 	    if (!get_pap_passwd(passwd))
 		error("No secret found for PAP login");
@@ -835,6 +860,8 @@
 #ifdef HAVE_MULTILINK
     if (multilink) {
 	if (mp_join_bundle()) {
+	    if (multilink_join_hook)
+		(*multilink_join_hook)();
 	    if (updetach && !nodetach)
 		detach();
 	    return;
@@ -894,8 +921,8 @@
     /*
      * Authentication failure: take the link down
      */
-    lcp_close(unit, "Authentication failed");
     status = EXIT_PEER_AUTH_FAILED;
+    lcp_close(unit, "Authentication failed");
 }
 
 /*
@@ -972,8 +999,8 @@
      * is no point in persisting without any way to get updated
      * authentication secrets.
      */
-    lcp_close(unit, "Failed to authenticate ourselves to peer");
     status = EXIT_AUTH_TOPEER_FAILED;
+    lcp_close(unit, "Failed to authenticate ourselves to peer");
 }
 
 /*
@@ -984,10 +1011,12 @@
     int unit, protocol, prot_flavor;
 {
     int bit;
+    const char *prot = "";
 
     switch (protocol) {
     case PPP_CHAP:
 	bit = CHAP_WITHPEER;
+	prot = "CHAP";
 	switch (prot_flavor) {
 	case CHAP_MD5:
 	    bit |= CHAP_MD5_WITHPEER;
@@ -1006,15 +1035,19 @@
 	if (passwd_from_file)
 	    BZERO(passwd, MAXSECRETLEN);
 	bit = PAP_WITHPEER;
+	prot = "PAP";
 	break;
     case PPP_EAP:
 	bit = EAP_WITHPEER;
+	prot = "EAP";
 	break;
     default:
 	warn("auth_withpeer_success: unknown protocol %x", protocol);
 	bit = 0;
     }
 
+    notice("%s authentication succeeded", prot);
+
     /* Save the authentication method for later. */
     auth_done[unit] |= bit;
 
@@ -1107,7 +1140,6 @@
 check_maxoctets(arg)
     void *arg;
 {
-    int diff;
     unsigned int used;
 
     update_link_stats(ifunit);
@@ -1128,12 +1160,11 @@
 	    used = link_stats.bytes_in+link_stats.bytes_out;
 	    break;
     }
-    diff = maxoctets - used;
-    if(diff < 0) {
+    if (used > maxoctets) {
 	notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used);
+	status = EXIT_TRAFFIC_LIMIT;
 	lcp_close(0, "Traffic limit");
 	need_holdoff = 0;
-	status = EXIT_TRAFFIC_LIMIT;
     } else {
         TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
     }
@@ -1163,9 +1194,9 @@
     if (tlim <= 0) {
 	/* link is idle: shut it down. */
 	notice("Terminating connection due to lack of activity.");
+	status = EXIT_IDLE_TIMEOUT;
 	lcp_close(0, "Link inactive");
 	need_holdoff = 0;
-	status = EXIT_IDLE_TIMEOUT;
     } else {
 	TIMEOUT(check_idle, NULL, tlim);
     }
@@ -1196,7 +1227,9 @@
     /* Default our_name to hostname, and user to our_name */
     if (our_name[0] == 0 || usehostname)
 	strlcpy(our_name, hostname, sizeof(our_name));
-    if (user[0] == 0)
+    /* If a blank username was explicitly given as an option, trust
+       the user and don't use our_name */
+    if (user[0] == 0 && !explicit_user)
 	strlcpy(user, our_name, sizeof(user));
 
     /*
@@ -1337,7 +1370,7 @@
     int passwdlen;
     char **msg;
 {
-#if 1
+#if defined(__ANDROID__)
     return UPAP_AUTHNAK;
 #else
     int ret;
@@ -1400,14 +1433,22 @@
 	    ret = UPAP_AUTHACK;
 	    if (uselogin || login_secret) {
 		/* login option or secret is @login */
-		if ((ret = plogin(user, passwd, msg)) == UPAP_AUTHACK)
-		    used_login = 1;
+		if (session_full(user, passwd, devnam, msg) == 0) {
+		    ret = UPAP_AUTHNAK;
+		}
+	    } else if (session_mgmt) {
+		if (session_check(user, NULL, devnam, NULL) == 0) {
+		    warn("Peer %q failed PAP Session verification", user);
+		    ret = UPAP_AUTHNAK;
+		}
 	    }
 	    if (secret[0] != 0 && !login_secret) {
 		/* password given in pap-secrets - must match */
-		if ((cryptpap || strcmp(passwd, secret) != 0)
-		    && strcmp(crypt(passwd, secret), secret) != 0)
-		    ret = UPAP_AUTHNAK;
+		if (cryptpap || strcmp(passwd, secret) != 0) {
+		    char *cbuf = crypt(passwd, secret);
+		    if (!cbuf || strcmp(cbuf, secret) != 0)
+			ret = UPAP_AUTHNAK;
+		}
 	    }
 	}
 	fclose(f);
@@ -1448,237 +1489,6 @@
 }
 
 /*
- * This function is needed for PAM.
- */
-
-#ifdef USE_PAM
-/* Static variables used to communicate between the conversation function
- * and the server_login function 
- */
-static char *PAM_username;
-static char *PAM_password;
-static int PAM_error = 0;
-static pam_handle_t *pamh = NULL;
-
-/* PAM conversation function
- * Here we assume (for now, at least) that echo on means login name, and
- * echo off means password.
- */
-
-static int PAM_conv (int num_msg,
-#ifndef SOL2
-    const
-#endif
-    struct pam_message **msg,
-    struct pam_response **resp, void *appdata_ptr)
-{
-    int replies = 0;
-    struct pam_response *reply = NULL;
-
-#define COPY_STRING(s) (s) ? strdup(s) : NULL
-
-    reply = malloc(sizeof(struct pam_response) * num_msg);
-    if (!reply) return PAM_CONV_ERR;
-
-    for (replies = 0; replies < num_msg; replies++) {
-        switch (msg[replies]->msg_style) {
-            case PAM_PROMPT_ECHO_ON:
-                reply[replies].resp_retcode = PAM_SUCCESS;
-                reply[replies].resp = COPY_STRING(PAM_username);
-                /* PAM frees resp */
-                break;
-            case PAM_PROMPT_ECHO_OFF:
-                reply[replies].resp_retcode = PAM_SUCCESS;
-                reply[replies].resp = COPY_STRING(PAM_password);
-                /* PAM frees resp */
-                break;
-            case PAM_TEXT_INFO:
-                /* fall through */
-            case PAM_ERROR_MSG:
-                /* ignore it, but pam still wants a NULL response... */
-                reply[replies].resp_retcode = PAM_SUCCESS;
-                reply[replies].resp = NULL;
-                break;
-            default:       
-                /* Must be an error of some sort... */
-                free (reply);
-                PAM_error = 1;
-                return PAM_CONV_ERR;
-        }
-    }
-    *resp = reply;     
-    return PAM_SUCCESS;
-}
-
-static struct pam_conv PAM_conversation = {
-    &PAM_conv,
-    NULL
-};
-#endif  /* USE_PAM */
-
-/*
- * plogin - Check the user name and password against the system
- * password database, and login the user if OK.
- *
- * returns:
- *	UPAP_AUTHNAK: Login failed.
- *	UPAP_AUTHACK: Login succeeded.
- * In either case, msg points to an appropriate message.
- */
-
-static int
-plogin(user, passwd, msg)
-    char *user;
-    char *passwd;
-    char **msg;
-{
-    char *tty;
-
-#if 1
-    return UPAP_AUTHNAK;
-#else
-#ifdef USE_PAM
-    int pam_error;
-
-    pam_error = pam_start ("ppp", user, &PAM_conversation, &pamh);
-    if (pam_error != PAM_SUCCESS) {
-        *msg = (char *) pam_strerror (pamh, pam_error);
-	reopen_log();
-	return UPAP_AUTHNAK;
-    }
-    /*
-     * Define the fields for the credential validation
-     */
-     
-    PAM_username = user;
-    PAM_password = passwd;
-    PAM_error = 0;
-    pam_set_item (pamh, PAM_TTY, devnam); /* this might be useful to some modules */
-
-    /*
-     * Validate the user
-     */
-    pam_error = pam_authenticate (pamh, PAM_SILENT);
-    if (pam_error == PAM_SUCCESS && !PAM_error) {    
-        pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
-        if (pam_error == PAM_SUCCESS)
-	    pam_error = pam_open_session (pamh, PAM_SILENT);
-    }
-
-    *msg = (char *) pam_strerror (pamh, pam_error);
-
-    /*
-     * Clean up the mess
-     */
-    reopen_log();	/* apparently the PAM stuff does closelog() */
-    PAM_username = NULL;
-    PAM_password = NULL;
-    if (pam_error != PAM_SUCCESS)
-        return UPAP_AUTHNAK;
-#else /* #ifdef USE_PAM */
-
-/*
- * Use the non-PAM methods directly
- */
-
-#ifdef HAS_SHADOW
-    struct spwd *spwd;
-    struct spwd *getspnam();
-#endif
-    struct passwd *pw = getpwnam(user);
-
-    endpwent();
-    if (pw == NULL)
-	return (UPAP_AUTHNAK);
-
-#ifdef HAS_SHADOW
-    spwd = getspnam(user);
-    endspent();
-    if (spwd) {
-	/* check the age of the password entry */
-	long now = time(NULL) / 86400L;
-
-	if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
-	    || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
-		&& spwd->sp_lstchg >= 0
-		&& now >= spwd->sp_lstchg + spwd->sp_max)) {
-	    warn("Password for %s has expired", user);
-	    return (UPAP_AUTHNAK);
-	}
-	pw->pw_passwd = spwd->sp_pwdp;
-    }
-#endif
-
-    /*
-     * If no passwd, don't let them login.
-     */
-    if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2
-	|| strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
-	return (UPAP_AUTHNAK);
-
-#endif /* #ifdef USE_PAM */
-
-    /*
-     * Write a wtmp entry for this user.
-     */
-
-    tty = devnam;
-    if (strncmp(tty, "/dev/", 5) == 0)
-	tty += 5;
-    logwtmp(tty, user, ifname);		/* Add wtmp login entry */
-
-#if defined(_PATH_LASTLOG) && !defined(USE_PAM)
-    if (pw != (struct passwd *)NULL) {
-	    struct lastlog ll;
-	    int fd;
-
-	    if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
-		(void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
-		memset((void *)&ll, 0, sizeof(ll));
-		(void)time(&ll.ll_time);
-		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
-		(void)write(fd, (char *)&ll, sizeof(ll));
-		(void)close(fd);
-	    }
-    }
-#endif /* _PATH_LASTLOG and not USE_PAM */
-
-    info("user %s logged in", user);
-    logged_in = 1;
-
-    return (UPAP_AUTHACK);
-#endif
-}
-
-/*
- * plogout - Logout the user.
- */
-static void
-plogout()
-{
-#ifdef USE_PAM
-    int pam_error;
-
-    if (pamh != NULL) {
-	pam_error = pam_close_session (pamh, PAM_SILENT);
-	pam_end (pamh, pam_error);
-	pamh = NULL;
-    }
-    /* Apparently the pam stuff does closelog(). */
-    reopen_log();
-#else /* ! USE_PAM */   
-    char *tty;
-
-    tty = devnam;
-    if (strncmp(tty, "/dev/", 5) == 0)
-	tty += 5;
-    logwtmp(tty, "", "");		/* Wipe out utmp logout entry */
-#endif /* ! USE_PAM */
-    logged_in = 0;
-}
-
-
-/*
  * null_login - Check if a username of "" and a password of "" are
  * acceptable, and iff so, set the list of acceptable IP addresses
  * and return 1.
@@ -2551,5 +2361,5 @@
     argv[5] = strspeed;
     argv[6] = NULL;
 
-    auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+    auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL, 0);
 }
diff --git a/pppd/cbcp.c b/pppd/cbcp.c
index ab069d4..7f2f787 100644
--- a/pppd/cbcp.c
+++ b/pppd/cbcp.c
@@ -33,7 +33,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: cbcp.c,v 1.16 2004/10/28 00:15:36 paulus Exp $"
+#define RCSID	"$Id: cbcp.c,v 1.17 2006/05/22 00:04:07 paulus Exp $"
 
 #include <stdio.h>
 #include <string.h>
@@ -483,6 +483,6 @@
     cbcp_state *us;
 {
     persist = 0;
-    lcp_close(0, "Call me back, please");
     status = EXIT_CALLBACK;
+    lcp_close(0, "Call me back, please");
 }
diff --git a/pppd/ccp.c b/pppd/ccp.c
index fd51952..5814f35 100644
--- a/pppd/ccp.c
+++ b/pppd/ccp.c
@@ -28,7 +28,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: ccp.c,v 1.48 2004/11/13 02:28:15 paulus Exp $"
+#define RCSID	"$Id: ccp.c,v 1.50 2005/06/26 19:34:41 carlsonj Exp $"
 
 #include <stdlib.h>
 #include <string.h>
@@ -898,6 +898,7 @@
     fsm *f;
     u_char *p;
     int len;
+    int treat_as_reject;
 {
     ccp_options *go = &ccp_gotoptions[f->unit];
     ccp_options no;		/* options we've seen already */
@@ -1158,8 +1159,11 @@
 		    }
 		} else {
 		    /* Neither are set. */
-		    newret = CONFREJ;
-		    break;
+		    /* We cannot accept this.  */
+		    newret = CONFNAK;
+		    /* Give the peer our idea of what can be used,
+		       so it can choose and confirm */
+		    ho->mppe = ao->mppe;
 		}
 
 		/* rebuild the opts */
diff --git a/pppd/chap-md5.c b/pppd/chap-md5.c
index bc69778..77dd4ec 100644
--- a/pppd/chap-md5.c
+++ b/pppd/chap-md5.c
@@ -95,7 +95,7 @@
 
 	MD5_Init(&ctx);
 	MD5_Update(&ctx, &idbyte, 1);
-	MD5_Update(&ctx, secret, secret_len);
+	MD5_Update(&ctx, (u_char *)secret, secret_len);
 	MD5_Update(&ctx, challenge, challenge_len);
 	MD5_Final(&response[1], &ctx);
 	response[0] = MD5_HASH_SIZE;
diff --git a/pppd/chap-new.c b/pppd/chap-new.c
index 7d1aecd..a3b7937 100644
--- a/pppd/chap-new.c
+++ b/pppd/chap-new.c
@@ -28,14 +28,15 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: chap-new.c,v 1.6 2004/11/04 10:02:26 paulus Exp $"
+#define RCSID	"$Id: chap-new.c,v 1.9 2007/06/19 02:08:35 carlsonj Exp $"
 
 #include <stdlib.h>
 #include <string.h>
 #include "pppd.h"
+#include "session.h"
 #include "chap-new.h"
 #include "chap-md5.h"
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
 #include "openssl-hash.h"
 #endif
 
@@ -99,6 +100,7 @@
 	int challenge_xmits;
 	int challenge_pktlen;
 	unsigned char challenge[CHAL_MAX_PKTLEN];
+	char message[256];
 } server;
 
 /* Values for flags in chap_client_state and chap_server_state */
@@ -144,7 +146,7 @@
 	memset(&client, 0, sizeof(client));
 	memset(&server, 0, sizeof(server));
 
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
 	openssl_hash_init();
 #endif
 	chap_md5_init();
@@ -316,15 +318,12 @@
 	int (*verifier)(char *, char *, int, struct chap_digest_type *,
 		unsigned char *, unsigned char *, char *, int);
 	char rname[MAXNAMELEN+1];
-	char message[256];
 
 	if ((ss->flags & LOWERUP) == 0)
 		return;
 	if (id != ss->challenge[PPP_HDRLEN+1] || len < 2)
 		return;
-	if ((ss->flags & AUTH_DONE) == 0) {
-		if ((ss->flags & CHALLENGE_VALID) == 0)
-			return;
+	if (ss->flags & CHALLENGE_VALID) {
 		response = pkt;
 		GETCHAR(response_len, pkt);
 		len -= response_len + 1;	/* length of name */
@@ -332,7 +331,6 @@
 		if (len < 0)
 			return;
 
-		ss->flags &= ~CHALLENGE_VALID;
 		if (ss->flags & TIMEOUT_PENDING) {
 			ss->flags &= ~TIMEOUT_PENDING;
 			UNTIMEOUT(chap_timeout, ss);
@@ -352,39 +350,59 @@
 			verifier = chap_verify_response;
 		ok = (*verifier)(name, ss->name, id, ss->digest,
 				 ss->challenge + PPP_HDRLEN + CHAP_HDRLEN,
-				 response, message, sizeof(message));
+				 response, ss->message, sizeof(ss->message));
 		if (!ok || !auth_number()) {
 			ss->flags |= AUTH_FAILED;
 			warn("Peer %q failed CHAP authentication", name);
 		}
-	}
+	} else if ((ss->flags & AUTH_DONE) == 0)
+		return;
 
 	/* send the response */
 	p = outpacket_buf;
 	MAKEHEADER(p, PPP_CHAP);
-	mlen = strlen(message);
+	mlen = strlen(ss->message);
 	len = CHAP_HDRLEN + mlen;
 	p[0] = (ss->flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
 	p[1] = id;
 	p[2] = len >> 8;
 	p[3] = len;
 	if (mlen > 0)
-		memcpy(p + CHAP_HDRLEN, message, mlen);
+		memcpy(p + CHAP_HDRLEN, ss->message, mlen);
 	output(0, outpacket_buf, PPP_HDRLEN + len);
 
-	if ((ss->flags & AUTH_DONE) == 0) {
-		ss->flags |= AUTH_DONE;
+	if (ss->flags & CHALLENGE_VALID) {
+		ss->flags &= ~CHALLENGE_VALID;
+		if (!(ss->flags & AUTH_DONE) && !(ss->flags & AUTH_FAILED)) {
+		    /*
+		     * Auth is OK, so now we need to check session restrictions
+		     * to ensure everything is OK, but only if we used a
+		     * plugin, and only if we're configured to check.  This
+		     * allows us to do PAM checks on PPP servers that
+		     * authenticate against ActiveDirectory, and use AD for
+		     * account info (like when using Winbind integrated with
+		     * PAM).
+		     */
+		    if (session_mgmt &&
+			session_check(name, NULL, devnam, NULL) == 0) {
+			ss->flags |= AUTH_FAILED;
+			warn("Peer %q failed CHAP Session verification", name);
+		    }
+		}
 		if (ss->flags & AUTH_FAILED) {
 			auth_peer_fail(0, PPP_CHAP);
 		} else {
-			auth_peer_success(0, PPP_CHAP, ss->digest->code,
-					  name, strlen(name));
+			if ((ss->flags & AUTH_DONE) == 0)
+				auth_peer_success(0, PPP_CHAP,
+						  ss->digest->code,
+						  name, strlen(name));
 			if (chap_rechallenge_time) {
 				ss->flags |= TIMEOUT_PENDING;
 				TIMEOUT(chap_timeout, ss,
 					chap_rechallenge_time);
 			}
 		}
+		ss->flags |= AUTH_DONE;
 	}
 }
 
@@ -486,7 +504,7 @@
 	if (code == CHAP_SUCCESS) {
 		/* used for MS-CHAP v2 mutual auth, yuck */
 		if (cs->digest->check_success != NULL) {
-			if (!(*cs->digest->check_success)(pkt, len, cs->priv))
+			if (!(*cs->digest->check_success)(id, pkt, len))
 				code = CHAP_FAILURE;
 		} else
 			msg = "CHAP authentication succeeded";
@@ -506,6 +524,7 @@
 		auth_withpeer_success(0, PPP_CHAP, cs->digest->code);
 	else {
 		cs->flags |= AUTH_FAILED;
+		error("CHAP authentication failed");
 		auth_withpeer_fail(0, PPP_CHAP);
 	}
 }
@@ -557,6 +576,7 @@
 	}
 	if ((cs->flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
 		cs->flags &= ~AUTH_STARTED;
+		error("CHAP authentication failed due to protocol-reject");
 		auth_withpeer_fail(0, PPP_CHAP);
 	}
 }
diff --git a/pppd/chap-new.h b/pppd/chap-new.h
index 48235d4..665e78f 100644
--- a/pppd/chap-new.h
+++ b/pppd/chap-new.h
@@ -105,7 +105,7 @@
 	void (*make_response)(unsigned char *response, int id, char *our_name,
 		unsigned char *challenge, char *secret, int secret_len,
 		unsigned char *priv);
-	int (*check_success)(unsigned char *pkt, int len, unsigned char *priv);
+	int (*check_success)(int id, unsigned char *pkt, int len);
 	void (*handle_failure)(unsigned char *pkt, int len);
 
 	struct chap_digest_type *next;
diff --git a/pppd/chap_ms.c b/pppd/chap_ms.c
index 5f2c0e2..3c0e52c 100644
--- a/pppd/chap_ms.c
+++ b/pppd/chap_ms.c
@@ -46,7 +46,7 @@
  *   implementation in RFC 2759.  Implemented MPPE functionality,
  *   heavily based on sample implementation in RFC 3079.
  *
- * Copyright (c) 2002 The Android Open Source Project
+ * Copyright (c) 2002 Google, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -74,7 +74,7 @@
  *
  */
 
-#define RCSID	"$Id: chap_ms.c,v 1.33 2004/11/12 09:57:43 paulus Exp $"
+#define RCSID	"$Id: chap_ms.c,v 1.38 2007/12/01 20:10:51 carlsonj Exp $"
 
 #ifdef CHAPMS
 
@@ -89,7 +89,7 @@
 #include "pppd.h"
 #include "chap-new.h"
 #include "chap_ms.h"
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
 #include "openssl-hash.h"
 #else
 #include "md4.h"
@@ -102,16 +102,16 @@
 
 
 static void	ascii2unicode __P((char[], int, u_char[]));
-static void	NTPasswordHash __P((char *, int, u_char[MD4_SIGNATURE_SIZE]));
+static void	NTPasswordHash __P((u_char *, int, u_char[MD4_SIGNATURE_SIZE]));
 static void	ChallengeResponse __P((u_char *, u_char *, u_char[24]));
 static void	ChapMS_NT __P((u_char *, char *, int, u_char[24]));
-static void	ChapMS2_NT __P((char *, u_char[16], char *, char *, int,
+static void	ChapMS2_NT __P((u_char *, u_char[16], char *, char *, int,
 				u_char[24]));
 static void	GenerateAuthenticatorResponsePlain
 			__P((char*, int, u_char[24], u_char[16], u_char *,
 			     char *, u_char[41]));
 #ifdef MSLANMAN
-static void	ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
+static void	ChapMS_LANMan __P((u_char *, char *, int, u_char *));
 #endif
 
 #ifdef MPPE
@@ -195,8 +195,7 @@
 		       unsigned char *challenge, unsigned char *response,
 		       char *message, int message_space)
 {
-	MS_ChapResponse *rmd;
-	MS_ChapResponse md;
+	unsigned char md[MS_CHAP_RESPONSE_LEN];
 	int diff;
 	int challenge_len, response_len;
 
@@ -205,10 +204,8 @@
 	if (response_len != MS_CHAP_RESPONSE_LEN)
 		goto bad;
 
-	rmd = (MS_ChapResponse *) response;
-
 #ifndef MSLANMAN
-	if (!rmd->UseNT[0]) {
+	if (!response[MS_CHAP_USENT]) {
 		/* Should really propagate this into the error packet. */
 		notice("Peer request for LANMAN auth not supported");
 		goto bad;
@@ -216,16 +213,17 @@
 #endif
 
 	/* Generate the expected response. */
-	ChapMS(challenge, (char *)secret, secret_len, &md);
+	ChapMS(challenge, (char *)secret, secret_len, md);
 
 #ifdef MSLANMAN
 	/* Determine which part of response to verify against */
-	if (!rmd->UseNT[0])
-		diff = memcmp(&rmd->LANManResp, &md.LANManResp,
-			      sizeof(md.LANManResp));
+	if (!response[MS_CHAP_USENT])
+		diff = memcmp(&response[MS_CHAP_LANMANRESP],
+			      &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
 	else
 #endif
-		diff = memcmp(&rmd->NTResp, &md.NTResp, sizeof(md.NTResp));
+		diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
+			      MS_CHAP_NTRESP_LEN);
 
 	if (diff == 0) {
 		slprintf(message, message_space, "Access granted");
@@ -245,8 +243,7 @@
 			unsigned char *challenge, unsigned char *response,
 			char *message, int message_space)
 {
-	MS_Chap2Response *rmd;
-	MS_Chap2Response md;
+	unsigned char md[MS_CHAP2_RESPONSE_LEN];
 	char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
 	int challenge_len, response_len;
 
@@ -255,11 +252,9 @@
 	if (response_len != MS_CHAP2_RESPONSE_LEN)
 		goto bad;	/* not even the right length */
 
-	rmd = (MS_Chap2Response *) response;
-
 	/* Generate the expected response and our mutual auth. */
-	ChapMS2(challenge, rmd->PeerChallenge, name,
-		(char *)secret, secret_len, &md,
+	ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
+		(char *)secret, secret_len, md,
 		(unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
 
 	/* compare MDs and send the appropriate status */
@@ -282,8 +277,9 @@
 	 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
 	 * help debugging this.
 	 */
-	if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0) {
-		if (rmd->Flags[0])
+	if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
+		   MS_CHAP2_NTRESP_LEN) == 0) {
+		if (response[MS_CHAP2_FLAGS])
 			slprintf(message, message_space, "S=%s", saresponse);
 		else
 			slprintf(message, message_space, "S=%s M=%s",
@@ -325,7 +321,63 @@
 {
 	challenge++;	/* skip length, should be 8 */
 	*response++ = MS_CHAP_RESPONSE_LEN;
-	ChapMS(challenge, secret, secret_len, (MS_ChapResponse *) response);
+	ChapMS(challenge, secret, secret_len, response);
+}
+
+struct chapms2_response_cache_entry {
+	int id;
+	unsigned char challenge[16];
+	unsigned char response[MS_CHAP2_RESPONSE_LEN];
+	unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
+};
+
+#define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
+static struct chapms2_response_cache_entry
+    chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
+static int chapms2_response_cache_next_index = 0;
+static int chapms2_response_cache_size = 0;
+
+static void
+chapms2_add_to_response_cache(int id, unsigned char *challenge,
+			      unsigned char *response,
+			      unsigned char *auth_response)
+{
+	int i = chapms2_response_cache_next_index;
+
+	chapms2_response_cache[i].id = id;
+	memcpy(chapms2_response_cache[i].challenge, challenge, 16);
+	memcpy(chapms2_response_cache[i].response, response,
+	       MS_CHAP2_RESPONSE_LEN);
+	memcpy(chapms2_response_cache[i].auth_response,
+	       auth_response, MS_AUTH_RESPONSE_LENGTH);
+	chapms2_response_cache_next_index =
+		(i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
+	if (chapms2_response_cache_next_index > chapms2_response_cache_size)
+		chapms2_response_cache_size = chapms2_response_cache_next_index;
+	dbglog("added response cache entry %d", i);
+}
+
+static struct chapms2_response_cache_entry*
+chapms2_find_in_response_cache(int id, unsigned char *challenge,
+		      unsigned char *auth_response)
+{
+	int i;
+
+	for (i = 0; i < chapms2_response_cache_size; i++) {
+		if (id == chapms2_response_cache[i].id
+		    && (!challenge
+			|| memcmp(challenge,
+				  chapms2_response_cache[i].challenge,
+				  16) == 0)
+		    && (!auth_response
+			|| memcmp(auth_response,
+				  chapms2_response_cache[i].auth_response,
+				  MS_AUTH_RESPONSE_LENGTH) == 0)) {
+			dbglog("response found in cache (entry %d)", i);
+			return &chapms2_response_cache[i];
+		}
+	}
+	return NULL;  /* not found */
 }
 
 static void
@@ -333,21 +385,29 @@
 		      unsigned char *challenge, char *secret, int secret_len,
 		      unsigned char *private)
 {
+	const struct chapms2_response_cache_entry *cache_entry;
+	unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH+1];
+
 	challenge++;	/* skip length, should be 16 */
 	*response++ = MS_CHAP2_RESPONSE_LEN;
+	cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
+	if (cache_entry) {
+		memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
+		return;
+	}
 	ChapMS2(challenge,
 #ifdef DEBUGMPPEKEY
 		mschap2_peer_challenge,
 #else
 		NULL,
 #endif
-		our_name, secret, secret_len,
-		(MS_Chap2Response *) response, private,
+		our_name, secret, secret_len, response, auth_response,
 		MS_CHAP2_AUTHENTICATEE);
+	chapms2_add_to_response_cache(id, challenge, response, auth_response);
 }
 
 static int
-chapms2_check_success(unsigned char *msg, int len, unsigned char *private)
+chapms2_check_success(int id, unsigned char *msg, int len)
 {
 	if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
 	    strncmp((char *)msg, "S=", 2) != 0) {
@@ -358,7 +418,7 @@
 	msg += 2;
 	len -= 2;
 	if (len < MS_AUTH_RESPONSE_LENGTH
-	    || memcmp(msg, private, MS_AUTH_RESPONSE_LENGTH)) {
+	    || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
 		/* Authenticator Response did not match expected. */
 		error("MS-CHAPv2 mutual authentication failed.");
 		return 0;
@@ -399,7 +459,7 @@
 	 * chapms[2]_verify_response.
 	 */
 	if (!strncmp(p, "E=", 2))
-		err = strtol(p, NULL, 10); /* Remember the error code. */
+		err = strtol(p+2, NULL, 10); /* Remember the error code. */
 	else
 		goto print_msg; /* Message is badly formatted. */
 
@@ -516,9 +576,9 @@
 }
 
 static void
-NTPasswordHash(char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
+NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
 {
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
     /* We link with MD4 routines in openssl, we have to take bytes instead */
     int			mdlen = secret_len;
 #else
@@ -532,7 +592,13 @@
     MD4_CTX		md4Context;
 
     MD4Init(&md4Context);
-    MD4Update(&md4Context, (unsigned char *)secret, mdlen);
+    /* MD4Update can take at most 64 bytes at a time */
+    while (mdlen > 512) {
+	MD4Update(&md4Context, secret, 512);
+	secret += 64;
+	mdlen -= 512;
+    }
+    MD4Update(&md4Context, secret, mdlen);
     MD4Final(hash, &md4Context);
 
 }
@@ -546,25 +612,24 @@
 
     /* Hash the Unicode version of the secret (== password). */
     ascii2unicode(secret, secret_len, unicodePassword);
-    NTPasswordHash((char *)unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
 
     ChallengeResponse(rchallenge, PasswordHash, NTResponse);
 }
 
 static void
-ChapMS2_NT(char *rchallenge, u_char PeerChallenge[16], char *username,
+ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
 	   char *secret, int secret_len, u_char NTResponse[24])
 {
     u_char	unicodePassword[MAX_NT_PASSWORD * 2];
     u_char	PasswordHash[MD4_SIGNATURE_SIZE];
     u_char	Challenge[8];
 
-    ChallengeHash(PeerChallenge, (unsigned char *)rchallenge, username,
-		  Challenge);
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
 
     /* Hash the Unicode version of the secret (== password). */
     ascii2unicode(secret, secret_len, unicodePassword);
-    NTPasswordHash((char *)unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
 
     ChallengeResponse(Challenge, PasswordHash, NTResponse);
 }
@@ -574,7 +639,7 @@
 
 static void
 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
-	      MS_ChapResponse *response)
+	      unsigned char *response)
 {
     int			i;
     u_char		UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
@@ -588,7 +653,7 @@
     DesEncrypt( StdText, PasswordHash + 0 );
     (void) DesSetkey(UcasePassword + 7);
     DesEncrypt( StdText, PasswordHash + 8 );
-    ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+    ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
 }
 #endif
 
@@ -652,8 +717,8 @@
 
     /* Hash (x2) the Unicode version of the secret (== password). */
     ascii2unicode(secret, secret_len, unicodePassword);
-    NTPasswordHash((char *)unicodePassword, secret_len * 2, PasswordHash);
-    NTPasswordHash((char *)PasswordHash, sizeof(PasswordHash),
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash),
 		   PasswordHashHash);
 
     GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
@@ -821,19 +886,20 @@
 
 void
 ChapMS(u_char *rchallenge, char *secret, int secret_len,
-       MS_ChapResponse *response)
+       unsigned char *response)
 {
-    BZERO(response, sizeof(*response));
+    BZERO(response, MS_CHAP_RESPONSE_LEN);
 
-    ChapMS_NT(rchallenge, secret, secret_len, response->NTResp);
+    ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
 
 #ifdef MSLANMAN
-    ChapMS_LANMan(rchallenge, secret, secret_len, response);
+    ChapMS_LANMan(rchallenge, secret, secret_len,
+		  &response[MS_CHAP_LANMANRESP]);
 
     /* preferred method is set by option  */
-    response->UseNT[0] = !ms_lanman;
+    response[MS_CHAP_USENT] = !ms_lanman;
 #else
-    response->UseNT[0] = 1;
+    response[MS_CHAP_USENT] = 1;
 #endif
 
 #ifdef MPPE
@@ -843,45 +909,47 @@
 
 
 /*
- * If PeerChallenge is NULL, one is generated and response->PeerChallenge
- * is filled in.  Call this way when generating a response.
- * If PeerChallenge is supplied, it is copied into response->PeerChallenge.
+ * If PeerChallenge is NULL, one is generated and the PeerChallenge
+ * field of response is filled in.  Call this way when generating a response.
+ * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
  * Call this way when verifying a response (or debugging).
- * Do not call with PeerChallenge = response->PeerChallenge.
+ * Do not call with PeerChallenge = response.
  *
- * response->PeerChallenge is then used for calculation of the
+ * The PeerChallenge field of response is then used for calculation of the
  * Authenticator Response.
  */
 void
 ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
-	char *user, char *secret, int secret_len, MS_Chap2Response *response,
+	char *user, char *secret, int secret_len, unsigned char *response,
 	u_char authResponse[], int authenticator)
 {
     /* ARGSUSED */
-    u_char *p = response->PeerChallenge;
+    u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
     int i;
 
-    BZERO(response, sizeof(*response));
+    BZERO(response, MS_CHAP2_RESPONSE_LEN);
 
     /* Generate the Peer-Challenge if requested, or copy it if supplied. */
     if (!PeerChallenge)
-	for (i = 0; i < sizeof(response->PeerChallenge); i++)
+	for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
 	    *p++ = (u_char) (drand48() * 0xff);
     else
-	BCOPY(PeerChallenge, response->PeerChallenge,
-	      sizeof(response->PeerChallenge));
+	BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
+	      MS_CHAP2_PEER_CHAL_LEN);
 
     /* Generate the NT-Response */
-    ChapMS2_NT((char *)rchallenge, response->PeerChallenge, user,
-	       secret, secret_len, response->NTResp);
+    ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
+	       secret, secret_len, &response[MS_CHAP2_NTRESP]);
 
     /* Generate the Authenticator Response. */
-    GenerateAuthenticatorResponsePlain(secret, secret_len, response->NTResp,
-				       response->PeerChallenge, rchallenge,
-				       user, authResponse);
+    GenerateAuthenticatorResponsePlain(secret, secret_len,
+				       &response[MS_CHAP2_NTRESP],
+				       &response[MS_CHAP2_PEER_CHALLENGE],
+				       rchallenge, user, authResponse);
 
 #ifdef MPPE
-    SetMasterKeys(secret, secret_len, response->NTResp, authenticator);
+    SetMasterKeys(secret, secret_len,
+		  &response[MS_CHAP2_NTRESP], authenticator);
 #endif
 }
 
diff --git a/pppd/chap_ms.h b/pppd/chap_ms.h
index 168c0be..040d80a 100644
--- a/pppd/chap_ms.h
+++ b/pppd/chap_ms.h
@@ -27,7 +27,7 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Id: chap_ms.h,v 1.12 2004/11/09 22:49:05 paulus Exp $
+ * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $
  */
 
 #ifndef __CHAPMS_INCLUDE__
@@ -49,39 +49,26 @@
 #define MS_CHAP_ERROR_CHANGING_PASSWORD		709
 
 /*
- * Apparently gcc on ARM gives all structures 4-byte alignment
- * by default.  This tells gcc that these structures may be
- * unaligned and may not have extra padding inside them.
+ * Offsets within the response field for MS-CHAP
  */
-#ifdef __GNUC__
-#define PACKED	__attribute__((__packed__))
-#else
-#define PACKED
-#endif
+#define MS_CHAP_LANMANRESP	0
+#define MS_CHAP_LANMANRESP_LEN	24
+#define MS_CHAP_NTRESP		24
+#define MS_CHAP_NTRESP_LEN	24
+#define MS_CHAP_USENT		48
 
 /*
- * Use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
- * in case this struct gets padded.
+ * Offsets within the response field for MS-CHAP2
  */
-typedef struct {
-    u_char LANManResp[24];
-    u_char NTResp[24];
-    u_char UseNT[1];		/* If 1, ignore the LANMan response field */
-} MS_ChapResponse PACKED;
-
-/*
- * Use MS_CHAP2_RESPONSE_LEN, rather than sizeof(MS_Chap2Response),
- * in case this struct gets padded.
- */
-typedef struct {
-    u_char PeerChallenge[16];
-    u_char Reserved[8];		/* Must be zero */
-    u_char NTResp[24];
-    u_char Flags[1];		/* Must be zero */
-} MS_Chap2Response PACKED;
+#define MS_CHAP2_PEER_CHALLENGE	0
+#define MS_CHAP2_PEER_CHAL_LEN	16
+#define MS_CHAP2_RESERVED_LEN	8
+#define MS_CHAP2_NTRESP		24
+#define MS_CHAP2_NTRESP_LEN	24
+#define MS_CHAP2_FLAGS		48
 
 #ifdef MPPE
-#include <net/ppp-comp.h>	/* MPPE_MAX_KEY_LEN */
+#include "mppe.h"	/* MPPE_MAX_KEY_LEN */
 extern u_char mppe_send_key[MPPE_MAX_KEY_LEN];
 extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
 extern int mppe_keys_set;
@@ -100,9 +87,9 @@
 #define MS_CHAP2_AUTHENTICATEE 0
 #define MS_CHAP2_AUTHENTICATOR 1
 
-void ChapMS __P((u_char *, char *, int, MS_ChapResponse *));
+void ChapMS __P((u_char *, char *, int, u_char *));
 void ChapMS2 __P((u_char *, u_char *, char *, char *, int,
-		  MS_Chap2Response *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int));
+		  u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int));
 #ifdef MPPE
 void mppe_set_keys __P((u_char *, u_char[MD4_SIGNATURE_SIZE]));
 void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
diff --git a/pppd/demand.c b/pppd/demand.c
index 8bf96d0..5e57658 100644
--- a/pppd/demand.c
+++ b/pppd/demand.c
@@ -28,7 +28,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: demand.c,v 1.19 2004/11/04 10:02:26 paulus Exp $"
+#define RCSID	"$Id: demand.c,v 1.20 2005/08/25 12:14:18 paulus Exp $"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -341,12 +341,15 @@
 	return 0;
     proto = PPP_PROTOCOL(p);
 #ifdef PPP_FILTER
-    if (pass_filter.bf_len != 0
-	&& bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
+    p[0] = 1;		/* outbound packet indicator */
+    if ((pass_filter.bf_len != 0
+	 && bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
+	|| (active_filter.bf_len != 0
+	    && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) {
+	p[0] = 0xff;
 	return 0;
-    if (active_filter.bf_len != 0
-	&& bpf_filter(active_filter.bf_insns, p, len, len) == 0)
-	return 0;
+    }
+    p[0] = 0xff;
 #endif
     for (i = 0; (protp = protocols[i]) != NULL; ++i) {
 	if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
diff --git a/pppd/eap.c b/pppd/eap.c
index 6203f94..6ea6c1f 100644
--- a/pppd/eap.c
+++ b/pppd/eap.c
@@ -1448,7 +1448,7 @@
 		MD5_Init(&mdContext);
 		typenum = id;
 		MD5_Update(&mdContext, &typenum, 1);
-		MD5_Update(&mdContext, secret, secret_len);
+		MD5_Update(&mdContext, (u_char *)secret, secret_len);
 		BZERO(secret, sizeof (secret));
 		MD5_Update(&mdContext, inp, vallen);
 		MD5_Final(hash, &mdContext);
@@ -1873,7 +1873,7 @@
 		}
 		MD5_Init(&mdContext);
 		MD5_Update(&mdContext, &esp->es_server.ea_id, 1);
-		MD5_Update(&mdContext, secret, secret_len);
+		MD5_Update(&mdContext, (u_char *)secret, secret_len);
 		BZERO(secret, sizeof (secret));
 		MD5_Update(&mdContext, esp->es_challenge, esp->es_challen);
 		MD5_Final(hash, &mdContext);
diff --git a/pppd/ecp.c b/pppd/ecp.c
index 43964fc..e5754e5 100644
--- a/pppd/ecp.c
+++ b/pppd/ecp.c
@@ -1,7 +1,7 @@
 /*
  * ecp.c - PPP Encryption Control Protocol.
  *
- * Copyright (c) 2002 The Android Open Source Project
+ * Copyright (c) 2002 Google, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/pppd/ecp.h b/pppd/ecp.h
index f02eae2..df6e3ca 100644
--- a/pppd/ecp.h
+++ b/pppd/ecp.h
@@ -1,7 +1,7 @@
 /*
  * ecp.h - Definitions for PPP Encryption Control Protocol.
  *
- * Copyright (c) 2002 The Android Open Source Project
+ * Copyright (c) 2002 Google, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/pppd/include/net/ppp-comp.h b/pppd/include/net/ppp-comp.h
index 894bf12..088c73e 100644
--- a/pppd/include/net/ppp-comp.h
+++ b/pppd/include/net/ppp-comp.h
@@ -168,93 +168,6 @@
 #define CI_MPPE			18	/* config option for MPPE */
 #define CILEN_MPPE		6	/* length of config option */
 
-#define MPPE_PAD		4	/* MPPE growth per frame */
-#define MPPE_MAX_KEY_LEN	16	/* largest key length (128-bit) */
-
-/* option bits for ccp_options.mppe */
-#define MPPE_OPT_40		0x01	/* 40 bit */
-#define MPPE_OPT_128		0x02	/* 128 bit */
-#define MPPE_OPT_STATEFUL	0x04	/* stateful mode */
-/* unsupported opts */
-#define MPPE_OPT_56		0x08	/* 56 bit */
-#define MPPE_OPT_MPPC		0x10	/* MPPC compression */
-#define MPPE_OPT_D		0x20	/* Unknown */
-#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
-#define MPPE_OPT_UNKNOWN	0x40	/* Bits !defined in RFC 3078 were set */
-
-/*
- * This is not nice ... the alternative is a bitfield struct though.
- * And unfortunately, we cannot share the same bits for the option
- * names above since C and H are the same bit.  We could do a u_int32
- * but then we have to do a htonl() all the time and/or we still need
- * to know which octet is which.
- */
-#define MPPE_C_BIT		0x01	/* MPPC */
-#define MPPE_D_BIT		0x10	/* Obsolete, usage unknown */
-#define MPPE_L_BIT		0x20	/* 40-bit */
-#define MPPE_S_BIT		0x40	/* 128-bit */
-#define MPPE_M_BIT		0x80	/* 56-bit, not supported */
-#define MPPE_H_BIT		0x01	/* Stateless (in a different byte) */
-
-/* Does not include H bit; used for least significant octet only. */
-#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
-
-/* Build a CI from mppe opts (see RFC 3078) */
-#define MPPE_OPTS_TO_CI(opts, ci)		\
-    do {					\
-	u_char *ptr = ci; /* u_char[4] */	\
-						\
-	/* H bit */				\
-	if (opts & MPPE_OPT_STATEFUL)		\
-	    *ptr++ = 0x0;			\
-	else					\
-	    *ptr++ = MPPE_H_BIT;		\
-	*ptr++ = 0;				\
-	*ptr++ = 0;				\
-						\
-	/* S,L bits */				\
-	*ptr = 0;				\
-	if (opts & MPPE_OPT_128)		\
-	    *ptr |= MPPE_S_BIT;			\
-	if (opts & MPPE_OPT_40)			\
-	    *ptr |= MPPE_L_BIT;			\
-	/* M,D,C bits not supported */		\
-    } while (/* CONSTCOND */ 0)
-
-/* The reverse of the above */
-#define MPPE_CI_TO_OPTS(ci, opts)		\
-    do {					\
-	u_char *ptr = ci; /* u_char[4] */	\
-						\
-	opts = 0;				\
-						\
-	/* H bit */				\
-	if (!(ptr[0] & MPPE_H_BIT))		\
-	    opts |= MPPE_OPT_STATEFUL;		\
-						\
-	/* S,L bits */				\
-	if (ptr[3] & MPPE_S_BIT)		\
-	    opts |= MPPE_OPT_128;		\
-	if (ptr[3] & MPPE_L_BIT)		\
-	    opts |= MPPE_OPT_40;		\
-						\
-	/* M,D,C bits */			\
-	if (ptr[3] & MPPE_M_BIT)		\
-	    opts |= MPPE_OPT_56;		\
-	if (ptr[3] & MPPE_D_BIT)		\
-	    opts |= MPPE_OPT_D;			\
-	if (ptr[3] & MPPE_C_BIT)		\
-	    opts |= MPPE_OPT_MPPC;		\
-						\
-	/* Other bits */			\
-	if (ptr[0] & ~MPPE_H_BIT)		\
-	    opts |= MPPE_OPT_UNKNOWN;		\
-	if (ptr[1] || ptr[2])			\
-	    opts |= MPPE_OPT_UNKNOWN;		\
-	if (ptr[3] & ~MPPE_ALL_BITS)		\
-	    opts |= MPPE_OPT_UNKNOWN;		\
-    } while (/* CONSTCOND */ 0)
-
 /*
  * Definitions for other, as yet unsupported, compression methods.
  */
diff --git a/pppd/ipcp.c b/pppd/ipcp.c
index 09aebe3..0237396 100644
--- a/pppd/ipcp.c
+++ b/pppd/ipcp.c
@@ -40,7 +40,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: ipcp.c,v 1.69 2004/11/13 12:03:26 paulus Exp $"
+#define RCSID	"$Id: ipcp.c,v 1.73 2008/05/26 08:33:22 paulus Exp $"
 
 /*
  * TODO:
@@ -72,6 +72,7 @@
 u_int32_t netmask = 0;		/* IP netmask to set on interface */
 
 bool	disable_defaultip = 0;	/* Don't use hostname for default IP adrs */
+bool	noremoteip = 0;		/* Let him have no IP address */
 
 /* Hook for a plugin to know when IP protocol has come up */
 void (*ip_up_hook) __P((void)) = NULL;
@@ -218,6 +219,13 @@
     { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr,
       "Disable IP-Address usage", OPT_A2CLR,
       &ipcp_allowoptions[0].neg_addr },
+#ifdef __linux__
+    { "noremoteip", o_bool, &noremoteip,
+      "Allow peer to have no IP address", 1 },
+#endif
+    { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr,
+      "Don't send our IP address to peer", OPT_A2CLR,
+      &ipcp_wantoptions[0].old_addrs},
 
     { "IP addresses", o_wild, (void *) &setipaddr,
       "set local and remote IP addresses",
@@ -264,7 +272,7 @@
 };
 
 static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t));
-static void ipcp_script __P((char *));		/* Run an up/down script */
+static void ipcp_script __P((char *, int));	/* Run an up/down script */
 static void ipcp_script_done __P((void *));
 
 /*
@@ -567,6 +575,14 @@
     f->callbacks = &ipcp_callbacks;
     fsm_init(&ipcp_fsm[unit]);
 
+    /*
+     * Some 3G modems use repeated IPCP NAKs as a way of stalling
+     * until they can contact a server on the network, so we increase
+     * the default number of NAKs we accept before we start treating
+     * them as rejects.
+     */
+    f->maxnakloops = 100;
+
     memset(wo, 0, sizeof(*wo));
     memset(ao, 0, sizeof(*ao));
 
@@ -715,7 +731,8 @@
 #define LENCIADDRS(neg)		(neg ? CILEN_ADDRS : 0)
 #define LENCIVJ(neg, old)	(neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
 #define LENCIADDR(neg)		(neg ? CILEN_ADDR : 0)
-#define LENCIDNS(neg)		(neg ? (CILEN_ADDR) : 0)
+#define LENCIDNS(neg)		LENCIADDR(neg)
+#define LENCIWINS(neg)		LENCIADDR(neg)
 
     /*
      * First see if we want to change our options to the old
@@ -737,7 +754,9 @@
 	    LENCIVJ(go->neg_vj, go->old_vj) +
 	    LENCIADDR(go->neg_addr) +
 	    LENCIDNS(go->req_dns1) +
-	    LENCIDNS(go->req_dns2)) ;
+	    LENCIDNS(go->req_dns2) +
+	    LENCIWINS(go->winsaddr[0]) +
+	    LENCIWINS(go->winsaddr[1])) ;
 }
 
 
@@ -811,6 +830,19 @@
 	    neg = 0; \
     }
 
+#define ADDCIWINS(opt, addr) \
+    if (addr) { \
+	if (len >= CILEN_ADDR) { \
+	    u_int32_t l; \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_ADDR, ucp); \
+	    l = ntohl(addr); \
+	    PUTLONG(l, ucp); \
+	    len -= CILEN_ADDR; \
+	} else \
+	    addr = 0; \
+    }
+
     ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
 	       go->hisaddr);
 
@@ -823,6 +855,10 @@
 
     ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
 
+    ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+    
     *lenp -= len;
 }
 
@@ -926,6 +962,21 @@
 	    goto bad; \
     }
 
+#define ACKCIWINS(opt, addr) \
+    if (addr) { \
+	u_int32_t l; \
+	if ((len -= CILEN_ADDR) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_ADDR || citype != opt) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (addr != cilong) \
+	    goto bad; \
+    }
+
     ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
 	       go->hisaddr);
 
@@ -938,6 +989,10 @@
 
     ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
 
+    ACKCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    ACKCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+
     /*
      * If there are any remaining CIs, then this packet is bad.
      */
@@ -1117,6 +1172,8 @@
      * on an option that we didn't include in our request packet.
      * If they want to negotiate about IP addresses, we comply.
      * If they want us to ask for compression, we refuse.
+     * If they want us to ask for ms-dns, we do that, since some
+     * peers get huffy if we don't.
      */
     while (len >= CILEN_VOID) {
 	GETCHAR(citype, p);
@@ -1159,6 +1216,31 @@
 		try.neg_addr = 1;
 	    no.neg_addr = 1;
 	    break;
+	case CI_MS_DNS1:
+	    if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR)
+		goto bad;
+	    GETLONG(l, p);
+	    try.dnsaddr[0] = htonl(l);
+	    try.req_dns1 = 1;
+	    no.req_dns1 = 1;
+	    break;
+	case CI_MS_DNS2:
+	    if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR)
+		goto bad;
+	    GETLONG(l, p);
+	    try.dnsaddr[1] = htonl(l);
+	    try.req_dns2 = 1;
+	    no.req_dns2 = 1;
+	    break;
+	case CI_MS_WINS1:
+	case CI_MS_WINS2:
+	    if (cilen != CILEN_ADDR)
+		goto bad;
+	    GETLONG(l, p);
+	    ciaddr1 = htonl(l);
+	    if (ciaddr1)
+		try.winsaddr[citype == CI_MS_WINS2] = ciaddr1;
+	    break;
 	}
 	p = next;
     }
@@ -1275,6 +1357,21 @@
 	try.neg = 0; \
     }
 
+#define REJCIWINS(opt, addr) \
+    if (addr && \
+	((cilen = p[1]) == CILEN_ADDR) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	u_int32_t l; \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != addr) \
+	    goto bad; \
+	try.winsaddr[opt == CI_MS_WINS2] = 0; \
+    }
 
     REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
 	       go->ouraddr, go->hisaddr);
@@ -1288,6 +1385,10 @@
 
     REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
 
+    REJCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+    REJCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+
     /*
      * If there are any remaining CIs, then this packet is bad.
      */
@@ -1581,7 +1682,7 @@
      * option safely.
      */
     if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs &&
-	wo->req_addr && !reject_if_disagree) {
+	wo->req_addr && !reject_if_disagree && !noremoteip) {
 	if (rc == CONFACK) {
 	    rc = CONFNAK;
 	    ucp = inp;			/* reset pointer */
@@ -1641,7 +1742,7 @@
 {
     ipcp_options *wo = &ipcp_wantoptions[u];
 
-    if (wo->hisaddr == 0) {
+    if (wo->hisaddr == 0 && !noremoteip) {
 	/* make up an arbitrary address for the peer */
 	wo->hisaddr = htonl(0x0a707070 + ifunit);
 	wo->accept_remote = 1;
@@ -1654,6 +1755,7 @@
     }
     if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
 	return 0;
+    ipcp_script(_PATH_IPPREUP, 1);
     if (!sifup(u))
 	return 0;
     if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
@@ -1666,7 +1768,8 @@
 	    proxy_arp_set[u] = 1;
 
     notice("local  IP address %I", wo->ouraddr);
-    notice("remote IP address %I", wo->hisaddr);
+    if (wo->hisaddr)
+	notice("remote IP address %I", wo->hisaddr);
 
     return 1;
 }
@@ -1705,14 +1808,19 @@
 	ipcp_close(f->unit, "Could not determine local IP address");
 	return;
     }
-    if (ho->hisaddr == 0) {
+    if (ho->hisaddr == 0 && !noremoteip) {
 	ho->hisaddr = htonl(0x0a404040 + ifunit);
 	warn("Could not determine remote IP address: defaulting to %I",
 	     ho->hisaddr);
     }
     script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
-    script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
+    if (ho->hisaddr != 0)
+	script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
 
+    if (!go->req_dns1)
+	    go->dnsaddr[0] = 0;
+    if (!go->req_dns2)
+	    go->dnsaddr[1] = 0;
     if (go->dnsaddr[0])
 	script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
     if (go->dnsaddr[1])
@@ -1725,7 +1833,7 @@
     /*
      * Check that the peer is allowed to use the IP address it wants.
      */
-    if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+    if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) {
 	error("Peer is not authorized to use remote address %I", ho->hisaddr);
 	ipcp_close(f->unit, "Unauthorized remote IP address");
 	return;
@@ -1748,7 +1856,7 @@
 		wo->ouraddr = go->ouraddr;
 	    } else
 		script_unsetenv("OLDIPLOCAL");
-	    if (ho->hisaddr != wo->hisaddr) {
+	    if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) {
 		warn("Remote IP address changed to %I", ho->hisaddr);
 		script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
 		wo->hisaddr = ho->hisaddr;
@@ -1770,7 +1878,7 @@
 		    default_route_set[f->unit] = 1;
 
 	    /* Make a proxy ARP entry if requested. */
-	    if (ipcp_wantoptions[f->unit].proxy_arp)
+	    if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp)
 		if (sifproxyarp(f->unit, ho->hisaddr))
 		    proxy_arp_set[f->unit] = 1;
 
@@ -1793,6 +1901,9 @@
 	}
 #endif
 
+	/* run the pre-up script, if any, and wait for it to finish */
+	ipcp_script(_PATH_IPPREUP, 1);
+
 	/* bring the interface up for IP */
 	if (!sifup(f->unit)) {
 	    if (debug)
@@ -1817,14 +1928,15 @@
 		default_route_set[f->unit] = 1;
 
 	/* Make a proxy ARP entry if requested. */
-	if (ipcp_wantoptions[f->unit].proxy_arp)
+	if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp)
 	    if (sifproxyarp(f->unit, ho->hisaddr))
 		proxy_arp_set[f->unit] = 1;
 
 	ipcp_wantoptions[0].ouraddr = go->ouraddr;
 
 	notice("local  IP address %I", go->ouraddr);
-	notice("remote IP address %I", ho->hisaddr);
+	if (ho->hisaddr != 0)
+	    notice("remote IP address %I", ho->hisaddr);
 	if (go->dnsaddr[0])
 	    notice("primary   DNS address %I", go->dnsaddr[0]);
 	if (go->dnsaddr[1])
@@ -1846,7 +1958,7 @@
      */
     if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
 	ipcp_script_state = s_up;
-	ipcp_script(_PATH_IPUP);
+	ipcp_script(_PATH_IPUP, 0);
     }
 }
 
@@ -1896,7 +2008,7 @@
     /* Execute the ip-down script */
     if (ipcp_script_state == s_up && ipcp_script_pid == 0) {
 	ipcp_script_state = s_down;
-	ipcp_script(_PATH_IPDOWN);
+	ipcp_script(_PATH_IPDOWN, 0);
     }
 }
 
@@ -1950,13 +2062,13 @@
     case s_up:
 	if (ipcp_fsm[0].state != OPENED) {
 	    ipcp_script_state = s_down;
-	    ipcp_script(_PATH_IPDOWN);
+	    ipcp_script(_PATH_IPDOWN, 0);
 	}
 	break;
     case s_down:
 	if (ipcp_fsm[0].state == OPENED) {
 	    ipcp_script_state = s_up;
-	    ipcp_script(_PATH_IPUP);
+	    ipcp_script(_PATH_IPUP, 0);
 	}
 	break;
     }
@@ -1968,8 +2080,9 @@
  * interface-name tty-name speed local-IP remote-IP.
  */
 static void
-ipcp_script(script)
+ipcp_script(script, wait)
     char *script;
+    int wait;
 {
     char strspeed[32], strlocal[32], strremote[32];
     char *argv[8];
@@ -1986,7 +2099,11 @@
     argv[5] = strremote;
     argv[6] = ipparam;
     argv[7] = NULL;
-    ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL);
+    if (wait)
+	run_program(script, argv, 0, NULL, NULL, 1);
+    else
+	ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done,
+				      NULL, 0);
 }
 
 /*
@@ -1996,7 +2113,7 @@
 create_resolv(peerdns1, peerdns2)
     u_int32_t peerdns1, peerdns2;
 {
-#ifndef ANDROID_CHANGES
+#if !defined(__ANDROID__)
     FILE *f;
 
     f = fopen(_PATH_RESOLV, "w");
@@ -2107,7 +2224,7 @@
 	    case CI_MS_DNS2:
 	        p += 2;
 		GETLONG(cilong, p);
-		printer(arg, "ms-dns%d %I", code - CI_MS_DNS1 + 1,
+		printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2),
 			htonl(cilong));
 		break;
 	    case CI_MS_WINS1:
diff --git a/pppd/ipv6cp.c b/pppd/ipv6cp.c
index ce9b138..356ff84 100644
--- a/pppd/ipv6cp.c
+++ b/pppd/ipv6cp.c
@@ -135,10 +135,10 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Id: ipv6cp.c,v 1.20 2004/11/13 02:28:15 paulus Exp $ 
+ * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ 
  */
 
-#define RCSID	"$Id: ipv6cp.c,v 1.20 2004/11/13 02:28:15 paulus Exp $"
+#define RCSID	"$Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $"
 
 /*
  * TODO: 
@@ -151,6 +151,7 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <netdb.h>
@@ -179,6 +180,16 @@
 /* local vars */
 static int ipv6cp_is_up;
 
+/* Hook for a plugin to know when IPv6 protocol has come up */
+void (*ipv6_up_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to know when IPv6 protocol has come down */
+void (*ipv6_down_hook) __P((void)) = NULL;
+
+/* Notifiers for when IPCPv6 goes up and down */
+struct notifier *ipv6_up_notifier = NULL;
+struct notifier *ipv6_down_notifier = NULL;
+
 /*
  * Callbacks for fsm code.  (CI = Configuration Information)
  */
@@ -238,10 +249,8 @@
     { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
       "Use (default) IPv4 address as interface identifier", 1 },
 
-#if defined(SOL2) || defined(__linux__)
     { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
       "Use uniquely-available persistent value for link local address", 1 },
-#endif /* defined(SOL2) */
 
     { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
       "Set timeout for IPv6CP", OPT_PRIO },
@@ -1077,7 +1086,6 @@
     if (!ipv6cp_protent.enabled_flag)
 	return;
 
-#if defined(SOL2) || defined(__linux__)
     /*
      * Persistent link-local id is only used when user has not explicitly
      * configure/hard-code the id
@@ -1097,7 +1105,6 @@
 	    wo->opt_local = 1;
 	}
     }
-#endif
 
     if (!wo->opt_local) {	/* init interface identifier */
 	if (wo->use_ip && eui64_iszero(wo->ourid)) {
@@ -1120,7 +1127,7 @@
 
     if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
 	option_error("local/remote LL address required for demand-dialling\n");
-	exit(1);
+	exit(EXIT_OPTION_ERROR);
     }
 }
 
@@ -1135,15 +1142,8 @@
 {
     ipv6cp_options *wo = &ipv6cp_wantoptions[u];
 
-#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-#if defined(SOL2)
     if (!sif6up(u))
 	return 0;
-#else
-    if (!sifup(u))
-	return 0;
-#endif /* defined(SOL2) */
-#endif    
     if (!sif6addr(u, wo->ourid, wo->hisid))
 	return 0;
 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
@@ -1236,43 +1236,20 @@
 	sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
 
     } else {
-	/*
-	 * Set LL addresses
-	 */
-#if !defined(__linux__) && !defined(SOL2) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-	if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
-	    if (debug)
-		warn("sif6addr failed");
-	    ipv6cp_close(f->unit, "Interface configuration failed");
-	    return;
-	}
-#endif
-
 	/* bring the interface up for IPv6 */
-#if defined(SOL2)
 	if (!sif6up(f->unit)) {
 	    if (debug)
-		warn("sifup failed (IPV6)");
+		warn("sif6up failed (IPV6)");
 	    ipv6cp_close(f->unit, "Interface configuration failed");
 	    return;
 	}
-#else
-	if (!sifup(f->unit)) {
-	    if (debug)
-		warn("sifup failed (IPV6)");
-	    ipv6cp_close(f->unit, "Interface configuration failed");
-	    return;
-	}
-#endif /* defined(SOL2) */
 
-#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
 	if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
 	    if (debug)
 		warn("sif6addr failed");
 	    ipv6cp_close(f->unit, "Interface configuration failed");
 	    return;
 	}
-#endif
 	sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
 
 	notice("local  LL address %s", llv6_ntoa(go->ourid));
@@ -1282,6 +1259,10 @@
     np_up(f->unit, PPP_IPV6);
     ipv6cp_is_up = 1;
 
+    notify(ipv6_up_notifier, 0);
+    if (ipv6_up_hook)
+       ipv6_up_hook();
+
     /*
      * Execute the ipv6-up script, like this:
      *	/etc/ppp/ipv6-up interface tty speed local-LL remote-LL
@@ -1305,6 +1286,9 @@
 {
     IPV6CPDEBUG(("ipv6cp: down"));
     update_link_stats(f->unit);
+    notify(ipv6_down_notifier, 0);
+    if (ipv6_down_hook)
+       ipv6_down_hook();
     if (ipv6cp_is_up) {
 	ipv6cp_is_up = 0;
 	np_down(f->unit, PPP_IPV6);
@@ -1322,16 +1306,14 @@
     } else {
 	sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
-#if defined(SOL2)
 	sif6down(f->unit);
-#else
-	sifdown(f->unit);
-#endif /* defined(SOL2) */
 #endif
 	ipv6cp_clear_addrs(f->unit, 
 			   ipv6cp_gotoptions[f->unit].ourid,
 			   ipv6cp_hisoptions[f->unit].hisid);
-#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(__linux__)
+	sif6down(f->unit);
+#elif defined(SVR4) && (defined(SNI) || defined(__USLC))
 	sifdown(f->unit);
 #endif
     }
@@ -1419,7 +1401,8 @@
     argv[6] = ipparam;
     argv[7] = NULL;
 
-    ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
+    ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done,
+				    NULL, 0);
 }
 
 /*
diff --git a/pppd/ipv6cp.h b/pppd/ipv6cp.h
index cc4568d..2f4c06d 100644
--- a/pppd/ipv6cp.h
+++ b/pppd/ipv6cp.h
@@ -154,9 +154,7 @@
     int opt_local;		/* ourtoken set by option */
     int opt_remote;		/* histoken set by option */
     int use_ip;			/* use IP as interface identifier */
-#if defined(SOL2) || defined(__linux__)
     int use_persistent;		/* use uniquely persistent value for address */
-#endif /* defined(SOL2) */
     int neg_vj;			/* Van Jacobson Compression? */
     u_short vj_protocol;	/* protocol value to use in VJ option */
     eui64_t ourid, hisid;	/* Interface identifiers */
diff --git a/pppd/ipxcp.c b/pppd/ipxcp.c
index a78456d..7b2343e 100644
--- a/pppd/ipxcp.c
+++ b/pppd/ipxcp.c
@@ -42,7 +42,7 @@
 
 #ifdef IPX_CHANGE
 
-#define RCSID	"$Id: ipxcp.c,v 1.23 2004/11/13 02:28:15 paulus Exp $"
+#define RCSID	"$Id: ipxcp.c,v 1.24 2005/08/25 23:59:34 paulus Exp $"
 
 /*
  * TODO:
@@ -289,7 +289,7 @@
 setipxnode(argv)
     char **argv;
 {
-    char *end;
+    u_char *end;
     int have_his = 0;
     u_char our_node[6];
     u_char his_node[6];
@@ -343,7 +343,7 @@
 setipxname (argv)
     char **argv;
 {
-    char *dest = ipxcp_wantoptions[0].name;
+    u_char *dest = ipxcp_wantoptions[0].name;
     char *src  = *argv;
     int  count;
     char ch;
@@ -593,7 +593,7 @@
 
     len	 = go->neg_nn	    ? CILEN_NETN     : 0;
     len += go->neg_node	    ? CILEN_NODEN    : 0;
-    len += go->neg_name	    ? CILEN_NAME + strlen (go->name) - 1 : 0;
+    len += go->neg_name	    ? CILEN_NAME + strlen ((char *)go->name) - 1 : 0;
 
     /* RFC says that defaults should not be included. */
     if (go->neg_router && to_external(go->router) != RIP_SAP)
@@ -630,7 +630,7 @@
     }
 
     if (go->neg_name) {
-	int cilen = strlen (go->name);
+	    int cilen = strlen ((char *)go->name);
 	int indx;
 	PUTCHAR (IPX_ROUTER_NAME, ucp);
 	PUTCHAR (CILEN_NAME + cilen - 1, ucp);
@@ -699,7 +699,7 @@
     }
 
 #define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val))
-#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen(val))
+#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen((char *)val))
 
 #define ACKCINETWORK(opt, neg, val) \
     if (neg) { \
@@ -923,7 +923,7 @@
     }
 
 #define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val))
-#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen(val))
+#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen((char *)val))
 
 #define REJCIVOID(opt, neg) \
     if (neg && p[0] == opt) { \
@@ -1453,12 +1453,12 @@
     argv[6]  = strremote;
     argv[7]  = strproto_lcl;
     argv[8]  = strproto_rmt;
-    argv[9]  = go->name;
-    argv[10] = ho->name;
+    argv[9]  = (char *)go->name;
+    argv[10] = (char *)ho->name;
     argv[11] = ipparam;
     argv[12] = strpid;
     argv[13] = NULL;
-    run_program(script, argv, 0, NULL, NULL);
+    run_program(script, argv, 0, NULL, NULL, 0);
 }
 
 /*
@@ -1580,7 +1580,7 @@
     case TERMREQ:
 	if (len > 0 && *p >= ' ' && *p < 0x7f) {
 	    printer(arg, " ");
-	    print_string(p, len, printer, arg);
+	    print_string((char *)p, len, printer, arg);
 	    p += len;
 	    len = 0;
 	}
diff --git a/pppd/lcp.c b/pppd/lcp.c
index 23f69fe..8ed2778 100644
--- a/pppd/lcp.c
+++ b/pppd/lcp.c
@@ -40,7 +40,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: lcp.c,v 1.74 2004/11/13 02:28:15 paulus Exp $"
+#define RCSID	"$Id: lcp.c,v 1.76 2006/05/22 00:04:07 paulus Exp $"
 
 /*
  * TODO:
@@ -397,21 +397,29 @@
     char *reason;
 {
     fsm *f = &lcp_fsm[unit];
+    int oldstate;
 
     if (phase != PHASE_DEAD && phase != PHASE_MASTER)
 	new_phase(PHASE_TERMINATE);
-    if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+
+    if (f->flags & DELAYED_UP) {
+	untimeout(lcp_delayed_up, f);
+	f->state = STOPPED;
+    }
+    oldstate = f->state;
+
+    fsm_close(f, reason);
+    if (oldstate == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT|DELAYED_UP)) {
 	/*
 	 * This action is not strictly according to the FSM in RFC1548,
 	 * but it does mean that the program terminates if you do a
-	 * lcp_close() in passive/silent mode when a connection hasn't
-	 * been established.
+	 * lcp_close() when a connection hasn't been established
+	 * because we are in passive/silent mode or because we have
+	 * delayed the fsm_lowerup() call and it hasn't happened yet.
 	 */
-	f->state = CLOSED;
+	f->flags &= ~DELAYED_UP;
 	lcp_finished(f);
-
-    } else
-	fsm_close(f, reason);
+    }
 }
 
 
@@ -453,9 +461,10 @@
 {
     fsm *f = &lcp_fsm[unit];
 
-    if (f->flags & DELAYED_UP)
+    if (f->flags & DELAYED_UP) {
 	f->flags &= ~DELAYED_UP;
-    else
+	untimeout(lcp_delayed_up, f);
+    } else
 	fsm_lowerdown(&lcp_fsm[unit]);
 }
 
@@ -489,12 +498,12 @@
 
     if (f->flags & DELAYED_UP) {
 	f->flags &= ~DELAYED_UP;
+	untimeout(lcp_delayed_up, f);
 	fsm_lowerup(f);
     }
     fsm_input(f, p, len);
 }
 
-
 /*
  * lcp_extcode - Handle a LCP-specific code.
  */
@@ -525,6 +534,8 @@
 	break;
 
     case DISCREQ:
+    case IDENTIF:
+    case TIMEREM:
 	break;
 
     default:
@@ -548,6 +559,7 @@
     int i;
     struct protent *protp;
     u_short prot;
+    const char *pname;
 
     if (len < 2) {
 	LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
@@ -565,16 +577,27 @@
 	return;
     }
 
+    pname = protocol_name(prot);
+
     /*
      * Upcall the proper Protocol-Reject routine.
      */
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
 	if (protp->protocol == prot && protp->enabled_flag) {
+	    if (pname == NULL)
+		dbglog("Protocol-Reject for 0x%x received", prot);
+	    else
+		dbglog("Protocol-Reject for '%s' (0x%x) received", pname,
+		       prot);
 	    (*protp->protrej)(f->unit);
 	    return;
 	}
 
-    warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+    if (pname == NULL)
+	warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+    else
+	warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname,
+	     prot);
 }
 
 
@@ -1292,8 +1315,8 @@
 	if (looped_back) {
 	    if (++try.numloops >= lcp_loopbackfail) {
 		notice("Serial line is looped back.");
-		lcp_close(f->unit, "Loopback detected");
 		status = EXIT_LOOPBACK;
+		lcp_close(f->unit, "Loopback detected");
 	    }
 	} else
 	    try.numloops = 0;
@@ -1964,7 +1987,8 @@
 static char *lcp_codenames[] = {
     "ConfReq", "ConfAck", "ConfNak", "ConfRej",
     "TermReq", "TermAck", "CodeRej", "ProtRej",
-    "EchoReq", "EchoRep", "DiscReq"
+    "EchoReq", "EchoRep", "DiscReq", "Ident",
+    "TimeRem"
 };
 
 static int
@@ -2042,7 +2066,6 @@
 				printer(arg, " MD5");
 				++p;
 				break;
-#ifdef CHAPMS
 			    case CHAP_MICROSOFT:
 				printer(arg, " MS");
 				++p;
@@ -2052,7 +2075,6 @@
 				printer(arg, " MS-v2");
 				++p;
 				break;
-#endif
 			    }
 			}
 			break;
@@ -2168,10 +2190,31 @@
 	if (len >= 4) {
 	    GETLONG(cilong, p);
 	    printer(arg, " magic=0x%x", cilong);
-	    p += 4;
 	    len -= 4;
 	}
 	break;
+
+    case IDENTIF:
+    case TIMEREM:
+	if (len >= 4) {
+	    GETLONG(cilong, p);
+	    printer(arg, " magic=0x%x", cilong);
+	    len -= 4;
+	}
+	if (code == TIMEREM) {
+	    if (len < 4)
+		break;
+	    GETLONG(cilong, p);
+	    printer(arg, " seconds=%u", cilong);
+	    len -= 4;
+	}
+	if (len > 0) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
     }
 
     /* print the rest of the bytes in the packet */
@@ -2198,8 +2241,8 @@
     if (f->state == OPENED) {
 	info("No response to %d echo-requests", lcp_echos_pending);
         notice("Serial link appears to be disconnected.");
-        lcp_close(f->unit, "Peer not responding");
 	status = EXIT_PEER_DEAD;
+	lcp_close(f->unit, "Peer not responding");
     }
 }
 
diff --git a/pppd/lcp.h b/pppd/lcp.h
index 23f3c84..d5f8aee 100644
--- a/pppd/lcp.h
+++ b/pppd/lcp.h
@@ -39,12 +39,13 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Id: lcp.h,v 1.19 2002/12/04 23:03:32 paulus Exp $
+ * $Id: lcp.h,v 1.20 2004/11/14 22:53:42 carlsonj Exp $
  */
 
 /*
  * Options.
  */
+#define CI_VENDOR	0	/* Vendor Specific */
 #define CI_MRU		1	/* Maximum Receive Unit */
 #define CI_ASYNCMAP	2	/* Async Control Character Map */
 #define CI_AUTHTYPE	3	/* Authentication Type */
@@ -52,18 +53,33 @@
 #define CI_MAGICNUMBER	5	/* Magic Number */
 #define CI_PCOMPRESSION	7	/* Protocol Field Compression */
 #define CI_ACCOMPRESSION 8	/* Address/Control Field Compression */
+#define CI_FCSALTERN	9	/* FCS-Alternatives */
+#define CI_SDP		10	/* Self-Describing-Pad */
+#define CI_NUMBERED	11	/* Numbered-Mode */
 #define CI_CALLBACK	13	/* callback */
 #define CI_MRRU		17	/* max reconstructed receive unit; multilink */
 #define CI_SSNHF	18	/* short sequence numbers for multilink */
 #define CI_EPDISC	19	/* endpoint discriminator */
+#define CI_MPPLUS	22	/* Multi-Link-Plus-Procedure */
+#define CI_LDISC	23	/* Link-Discriminator */
+#define CI_LCPAUTH	24	/* LCP Authentication */
+#define CI_COBS		25	/* Consistent Overhead Byte Stuffing */
+#define CI_PREFELIS	26	/* Prefix Elision */
+#define CI_MPHDRFMT	27	/* MP Header Format */
+#define CI_I18N		28	/* Internationalization */
+#define CI_SDL		29	/* Simple Data Link */
 
 /*
- * LCP-specific packet types.
+ * LCP-specific packet types (code numbers).
  */
 #define PROTREJ		8	/* Protocol Reject */
 #define ECHOREQ		9	/* Echo Request */
 #define ECHOREP		10	/* Echo Reply */
 #define DISCREQ		11	/* Discard Request */
+#define IDENTIF		12	/* Identification */
+#define TIMEREM		13	/* Time Remaining */
+
+/* Value used as data for CI_CALLBACK option */
 #define CBCP_OPT	6	/* Use callback control protocol */
 
 /*
diff --git a/pppd/main.c b/pppd/main.c
index 64efefe..6c2a376 100644
--- a/pppd/main.c
+++ b/pppd/main.c
@@ -66,7 +66,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: main.c,v 1.148 2004/11/13 12:05:48 paulus Exp $"
+#define RCSID	"$Id: main.c,v 1.156 2008/06/23 11:47:18 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -90,7 +90,9 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#if defined(__ANDROID__)
 #include <cutils/properties.h>
+#endif
 
 #include "pppd.h"
 #include "magic.h"
@@ -212,10 +214,6 @@
 bool bundle_eof;
 bool bundle_terminating;
 
-int sent_since_received = 0;
-int sent_total = 0;
-int received_total = 0;
-
 /*
  * We maintain a list of child process pids and
  * functions to call when they exit.
@@ -225,6 +223,7 @@
     char	*prog;
     void	(*done) __P((void *));
     void	*arg;
+    int		killable;
     struct subprocess *next;
 };
 
@@ -247,6 +246,7 @@
 static void open_ccp __P((int));
 static void bad_signal __P((int));
 static void holdoff_end __P((void *));
+static void forget_child __P((int pid, int status));
 static int reap_kids __P((void));
 static void childwait_end __P((void *));
 
@@ -362,7 +362,7 @@
 
     progname = *argv;
 
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
     {
         extern void pppox_init();
         pppox_init();
@@ -397,11 +397,11 @@
      * Parse, in order, the system options file, the user's options file,
      * and the command line arguments.
      */
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
     /* Android: only take options from commandline */
     if (!parse_args(argc-1, argv+1))
 	exit(EXIT_OPTION_ERROR);
-    
+
 #else
     if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
 	|| !options_from_user()
@@ -422,7 +422,7 @@
     if (debug)
 	setlogmask(LOG_UPTO(LOG_DEBUG));
 
-#ifndef ANDROID_CHANGES
+#if !defined(__ANDROID__)
     /*
      * Check that we are running as root.
      */
@@ -580,6 +580,7 @@
 	script_unsetenv("BYTES_RCVD");
 
 	lcp_open(0);		/* Start protocol */
+	start_link(0);
 	while (phase != PHASE_DEAD) {
 	    handle_events();
 	    get_input();
@@ -597,6 +598,8 @@
 		}
 	    }
 	}
+	/* restore FSMs to original state */
+	lcp_close(0, "");
 
 	if (!persist || asked_to_quit || (maxfail > 0 && unsuccess >= maxfail))
 	    break;
@@ -838,10 +841,8 @@
 void
 reopen_log()
 {
-#ifndef ANDROID_CHANGES
     openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
     setlogmask(LOG_UPTO(LOG_INFO));
-#endif
 }
 
 /*
@@ -851,7 +852,7 @@
 create_pidfile(pid)
     int pid;
 {
-#ifndef ANDROID_CHANGES
+#if !defined(__ANDROID__)
     FILE *pidfile;
 
     slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
@@ -870,7 +871,7 @@
 create_linkpidfile(pid)
     int pid;
 {
-#ifndef ANDROID_CHANGES
+#if !defined(__ANDROID__)
     FILE *pidfile;
 
     if (linkname[0] == 0)
@@ -895,7 +896,7 @@
  */
 void remove_pidfiles()
 {
-#ifndef ANDROID_CHANGES
+#if !defined(__ANDROID__)
     if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
 	warn("unable to delete pid file %s: %m", pidfilename);
     pidfilename[0] = 0;
@@ -943,14 +944,54 @@
     { 0x4b,	"SNA over 802.2" },
     { 0x4d,	"SNA" },
     { 0x4f,	"IP6 Header Compression" },
+    { 0x51,	"KNX Bridging Data" },
+    { 0x53,	"Encryption" },
+    { 0x55,	"Individual Link Encryption" },
+    { 0x57,	"IPv6" },
+    { 0x59,	"PPP Muxing" },
+    { 0x5b,	"Vendor-Specific Network Protocol" },
+    { 0x61,	"RTP IPHC Full Header" },
+    { 0x63,	"RTP IPHC Compressed TCP" },
+    { 0x65,	"RTP IPHC Compressed non-TCP" },
+    { 0x67,	"RTP IPHC Compressed UDP 8" },
+    { 0x69,	"RTP IPHC Compressed RTP 8" },
     { 0x6f,	"Stampede Bridging" },
+    { 0x73,	"MP+" },
+    { 0xc1,	"NTCITS IPI" },
     { 0xfb,	"single-link compression" },
-    { 0xfd,	"1st choice compression" },
+    { 0xfd,	"Compressed Datagram" },
     { 0x0201,	"802.1d Hello Packets" },
     { 0x0203,	"IBM Source Routing BPDU" },
     { 0x0205,	"DEC LANBridge100 Spanning Tree" },
+    { 0x0207,	"Cisco Discovery Protocol" },
+    { 0x0209,	"Netcs Twin Routing" },
+    { 0x020b,	"STP - Scheduled Transfer Protocol" },
+    { 0x020d,	"EDP - Extreme Discovery Protocol" },
+    { 0x0211,	"Optical Supervisory Channel Protocol" },
+    { 0x0213,	"Optical Supervisory Channel Protocol" },
     { 0x0231,	"Luxcom" },
     { 0x0233,	"Sigma Network Systems" },
+    { 0x0235,	"Apple Client Server Protocol" },
+    { 0x0281,	"MPLS Unicast" },
+    { 0x0283,	"MPLS Multicast" },
+    { 0x0285,	"IEEE p1284.4 standard - data packets" },
+    { 0x0287,	"ETSI TETRA Network Protocol Type 1" },
+    { 0x0289,	"Multichannel Flow Treatment Protocol" },
+    { 0x2063,	"RTP IPHC Compressed TCP No Delta" },
+    { 0x2065,	"RTP IPHC Context State" },
+    { 0x2067,	"RTP IPHC Compressed UDP 16" },
+    { 0x2069,	"RTP IPHC Compressed RTP 16" },
+    { 0x4001,	"Cray Communications Control Protocol" },
+    { 0x4003,	"CDPD Mobile Network Registration Protocol" },
+    { 0x4005,	"Expand accelerator protocol" },
+    { 0x4007,	"ODSICP NCP" },
+    { 0x4009,	"DOCSIS DLL" },
+    { 0x400B,	"Cetacean Network Detection Protocol" },
+    { 0x4021,	"Stacker LZS" },
+    { 0x4023,	"RefTek Protocol" },
+    { 0x4025,	"Fibre Channel" },
+    { 0x4027,	"EMIT Protocols" },
+    { 0x405b,	"Vendor-Specific Protocol (VSP)" },
     { 0x8021,	"Internet Protocol Control Protocol" },
     { 0x8023,	"OSI Network Layer Control Protocol" },
     { 0x8025,	"Xerox NS IDP Control Protocol" },
@@ -970,17 +1011,43 @@
     { 0x804b,	"SNA over 802.2 Control Protocol" },
     { 0x804d,	"SNA Control Protocol" },
     { 0x804f,	"IP6 Header Compression Control Protocol" },
-    { 0x006f,	"Stampede Bridging Control Protocol" },
+    { 0x8051,	"KNX Bridging Control Protocol" },
+    { 0x8053,	"Encryption Control Protocol" },
+    { 0x8055,	"Individual Link Encryption Control Protocol" },
+    { 0x8057,	"IPv6 Control Protocol" },
+    { 0x8059,	"PPP Muxing Control Protocol" },
+    { 0x805b,	"Vendor-Specific Network Control Protocol (VSNCP)" },
+    { 0x806f,	"Stampede Bridging Control Protocol" },
+    { 0x8073,	"MP+ Control Protocol" },
+    { 0x80c1,	"NTCITS IPI Control Protocol" },
     { 0x80fb,	"Single Link Compression Control Protocol" },
     { 0x80fd,	"Compression Control Protocol" },
+    { 0x8207,	"Cisco Discovery Protocol Control" },
+    { 0x8209,	"Netcs Twin Routing" },
+    { 0x820b,	"STP - Control Protocol" },
+    { 0x820d,	"EDPCP - Extreme Discovery Protocol Ctrl Prtcl" },
+    { 0x8235,	"Apple Client Server Protocol Control" },
+    { 0x8281,	"MPLSCP" },
+    { 0x8285,	"IEEE p1284.4 standard - Protocol Control" },
+    { 0x8287,	"ETSI TETRA TNP1 Control Protocol" },
+    { 0x8289,	"Multichannel Flow Treatment Protocol" },
     { 0xc021,	"Link Control Protocol" },
     { 0xc023,	"Password Authentication Protocol" },
     { 0xc025,	"Link Quality Report" },
     { 0xc027,	"Shiva Password Authentication Protocol" },
     { 0xc029,	"CallBack Control Protocol (CBCP)" },
+    { 0xc02b,	"BACP Bandwidth Allocation Control Protocol" },
+    { 0xc02d,	"BAP" },
+    { 0xc05b,	"Vendor-Specific Authentication Protocol (VSAP)" },
     { 0xc081,	"Container Control Protocol" },
     { 0xc223,	"Challenge Handshake Authentication Protocol" },
+    { 0xc225,	"RSA Authentication Protocol" },
+    { 0xc227,	"Extensible Authentication Protocol" },
+    { 0xc229,	"Mitsubishi Security Info Exch Ptcl (SIEP)" },
+    { 0xc26f,	"Stampede Bridging Authorization Protocol" },
     { 0xc281,	"Proprietary Authentication Protocol" },
+    { 0xc283,	"Proprietary Authentication Protocol" },
+    { 0xc481,	"Proprietary Node ID Authentication Protocol" },
     { 0,	NULL },
 };
 
@@ -1367,7 +1434,21 @@
     int sig;
 {
     struct sigaction act, oldact;
+    struct subprocess *chp;
 
+    if (!detached) {
+	/*
+	 * There might be other things in our process group that we
+	 * didn't start that would get hit if we did a kill(0), so
+	 * just send the signal individually to our children.
+	 */
+	for (chp = children; chp != NULL; chp = chp->next)
+	    if (chp->killable)
+		kill(chp->pid, sig);
+	return;
+    }
+
+    /* We've done a setsid(), so we can just use a kill(0) */
     sigemptyset(&act.sa_mask);		/* unnecessary in fact */
     act.sa_handler = SIG_IGN;
     act.sa_flags = 0;
@@ -1551,6 +1632,8 @@
 	if (errfd == 0 || errfd == 1)
 		errfd = dup(errfd);
 
+	closelog();
+
 	/* dup the in, out, err fds to 0, 1, 2 */
 	if (infd != 0)
 		dup2(infd, 0);
@@ -1559,9 +1642,6 @@
 	if (errfd != 2)
 		dup2(errfd, 2);
 
-#ifndef ANDROID_CHANGES
-	closelog();
-#endif
 	if (log_to_fd > 2)
 		close(log_to_fd);
 	if (the_channel->close)
@@ -1585,6 +1665,52 @@
 	return 0;
 }
 
+static bool
+add_script_env(pos, newstring)
+    int pos;
+    char *newstring;
+{
+    if (pos + 1 >= s_env_nalloc) {
+	int new_n = pos + 17;
+	char **newenv = realloc(script_env, new_n * sizeof(char *));
+	if (newenv == NULL) {
+	    free(newstring - 1);
+	    return 0;
+	}
+	script_env = newenv;
+	s_env_nalloc = new_n;
+    }
+    script_env[pos] = newstring;
+    script_env[pos + 1] = NULL;
+    return 1;
+}
+
+static void
+remove_script_env(pos)
+    int pos;
+{
+    free(script_env[pos] - 1);
+    while ((script_env[pos] = script_env[pos + 1]) != NULL)
+	pos++;
+}
+
+/*
+ * update_system_environment - process the list of set/unset options
+ * and update the system environment.
+ */
+static void
+update_system_environment()
+{
+    struct userenv *uep;
+
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+	if (uep->ue_isset)
+	    setenv(uep->ue_name, uep->ue_value, 1);
+	else
+	    unsetenv(uep->ue_name);
+    }
+}
+
 /*
  * device_script - run a program to talk to the specified fds
  * (e.g. to run the connector or disconnector script).
@@ -1618,15 +1744,15 @@
     }
 
     if (pid != 0) {
-	if (dont_wait) {
-	    record_child(pid, program, NULL, NULL);
-	    status = 0;
-	} else {
+	record_child(pid, program, NULL, NULL, 1);
+	status = 0;
+	if (!dont_wait) {
 	    while (waitpid(pid, &status, 0) < 0) {
 		if (errno == EINTR)
 		    continue;
 		fatal("error waiting for (dis)connection process: %m");
 	    }
+	    forget_child(pid, status);
 	    --conn_running;
 	}
 	return (status == 0 ? 0 : -1);
@@ -1640,16 +1766,59 @@
 	fprintf(stderr, "pppd: setuid failed\n");
 	exit(1);
     }
+    update_system_environment();
+#if defined(__ANDROID__)
     execl("/system/bin/sh", "sh", "-c", program, NULL);
+#else
+    execl("/bin/sh", "sh", "-c", program, (char *)0);
+#endif
     perror("pppd: could not exec /bin/sh");
-    exit(99);
+    _exit(99);
     /* NOTREACHED */
 }
 
 
 /*
- * run-program - execute a program with given arguments,
- * but don't wait for it.
+ * update_script_environment - process the list of set/unset options
+ * and update the script environment.  Note that we intentionally do
+ * not update the TDB.  These changes are layered on top right before
+ * exec.  It is not possible to use script_setenv() or
+ * script_unsetenv() safely after this routine is run.
+ */
+static void
+update_script_environment()
+{
+    struct userenv *uep;
+
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+	int i;
+	char *p, *newstring;
+	int nlen = strlen(uep->ue_name);
+
+	for (i = 0; (p = script_env[i]) != NULL; i++) {
+	    if (strncmp(p, uep->ue_name, nlen) == 0 && p[nlen] == '=')
+		break;
+	}
+	if (uep->ue_isset) {
+	    nlen += strlen(uep->ue_value) + 2;
+	    newstring = malloc(nlen + 1);
+	    if (newstring == NULL)
+		continue;
+	    *newstring++ = 0;
+	    slprintf(newstring, nlen, "%s=%s", uep->ue_name, uep->ue_value);
+	    if (p != NULL)
+		script_env[i] = newstring;
+	    else
+		add_script_env(i, newstring);
+	} else {
+	    remove_script_env(i);
+	}
+    }
+}
+
+/*
+ * run_program - execute a program with given arguments,
+ * but don't wait for it unless wait is non-zero.
  * If the program can't be executed, logs an error unless
  * must_exist is 0 and the program file doesn't exist.
  * Returns -1 if it couldn't fork, 0 if the file doesn't exist
@@ -1658,17 +1827,18 @@
  * reap_kids) iff the return value is > 0.
  */
 pid_t
-run_program(prog, args, must_exist, done, arg)
+run_program(prog, args, must_exist, done, arg, wait)
     char *prog;
     char **args;
     int must_exist;
     void (*done) __P((void *));
     void *arg;
+    int wait;
 {
-    int pid;
+    int pid, status;
     struct stat sbuf;
 
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
     /* Originally linkname is used to create named pid files, which is
     * meaningless to android. Here we use it as a suffix of program names,
     * so different users can run their own program by specifying it. For
@@ -1705,7 +1875,15 @@
     if (pid != 0) {
 	if (debug)
 	    dbglog("Script %s started (pid %d)", prog, pid);
-	record_child(pid, prog, done, arg);
+	record_child(pid, prog, done, arg, 0);
+	if (wait) {
+	    while (waitpid(pid, &status, 0) < 0) {
+		if (errno == EINTR)
+		    continue;
+		fatal("error waiting for script %s: %m", prog);
+	    }
+	    forget_child(pid, status);
+	}
 	return pid;
     }
 
@@ -1723,19 +1901,16 @@
 #endif
 
     /* run the program */
+    update_script_environment();
     execve(prog, args, script_env);
     if (must_exist || errno != ENOENT) {
-#ifndef ANDROID_CHANGES
 	/* have to reopen the log, there's nowhere else
 	   for the message to go. */
 	reopen_log();
 	syslog(LOG_ERR, "Can't execute %s: %m", prog);
 	closelog();
-#else
-	error("Can't execute %s: %m", prog);
-#endif
     }
-    _exit(-1);
+    _exit(99);
 }
 
 
@@ -1744,11 +1919,12 @@
  * to use.
  */
 void
-record_child(pid, prog, done, arg)
+record_child(pid, prog, done, arg, killable)
     int pid;
     char *prog;
     void (*done) __P((void *));
     void *arg;
+    int killable;
 {
     struct subprocess *chp;
 
@@ -1763,6 +1939,7 @@
 	chp->done = done;
 	chp->arg = arg;
 	chp->next = children;
+	chp->killable = killable;
 	children = chp;
     }
 }
@@ -1786,6 +1963,35 @@
 }
 
 /*
+ * forget_child - clean up after a dead child
+ */
+static void
+forget_child(pid, status)
+    int pid, status;
+{
+    struct subprocess *chp, **prevp;
+
+    for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
+        if (chp->pid == pid) {
+	    --n_children;
+	    *prevp = chp->next;
+	    break;
+	}
+    }
+    if (WIFSIGNALED(status)) {
+        warn("Child process %s (pid %d) terminated with signal %d",
+	     (chp? chp->prog: "??"), pid, WTERMSIG(status));
+    } else if (debug)
+        dbglog("Script %s finished (pid %d), status = 0x%x",
+	       (chp? chp->prog: "??"), pid,
+	       WIFEXITED(status) ? WEXITSTATUS(status) : status);
+    if (chp && chp->done)
+        (*chp->done)(chp->arg);
+    if (chp)
+        free(chp);
+}
+
+/*
  * reap_kids - get status from any dead child processes,
  * and log a message for abnormal terminations.
  */
@@ -1793,29 +1999,11 @@
 reap_kids()
 {
     int pid, status;
-    struct subprocess *chp, **prevp;
 
     if (n_children == 0)
 	return 0;
     while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
-	for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
-	    if (chp->pid == pid) {
-		--n_children;
-		*prevp = chp->next;
-		break;
-	    }
-	}
-	if (WIFSIGNALED(status)) {
-	    warn("Child process %s (pid %d) terminated with signal %d",
-		 (chp? chp->prog: "??"), pid, WTERMSIG(status));
-	} else if (debug)
-	    dbglog("Script %s finished (pid %d), status = 0x%x",
-		   (chp? chp->prog: "??"), pid,
-		   WIFEXITED(status) ? WEXITSTATUS(status) : status);
-	if (chp && chp->done)
-	    (*chp->done)(chp->arg);
-	if (chp)
-	    free(chp);
+        forget_child(pid, status);
     }
     if (pid == -1) {
 	if (errno == ECHILD)
@@ -1924,9 +2112,11 @@
 		free(p-1);
 		script_env[i] = newstring;
 #ifdef USE_TDB
-		if (iskey && pppdb != NULL)
-		    add_db_key(newstring);
-		update_db_entry();
+		if (pppdb != NULL) {
+		    if (iskey)
+			add_db_key(newstring);
+		    update_db_entry();
+		}
 #endif
 		return;
 	    }
@@ -1934,25 +2124,16 @@
     } else {
 	/* no space allocated for script env. ptrs. yet */
 	i = 0;
-	script_env = (char **) malloc(16 * sizeof(char *));
-	if (script_env == 0)
+	script_env = malloc(16 * sizeof(char *));
+	if (script_env == 0) {
+	    free(newstring - 1);
 	    return;
+	}
 	s_env_nalloc = 16;
     }
 
-    /* reallocate script_env with more space if needed */
-    if (i + 1 >= s_env_nalloc) {
-	int new_n = i + 17;
-	char **newenv = (char **) realloc((void *)script_env,
-					  new_n * sizeof(char *));
-	if (newenv == 0)
-	    return;
-	script_env = newenv;
-	s_env_nalloc = new_n;
-    }
-
-    script_env[i] = newstring;
-    script_env[i+1] = 0;
+    if (!add_script_env(i, newstring))
+	return;
 
 #ifdef USE_TDB
     if (pppdb != NULL) {
@@ -1983,9 +2164,7 @@
 	    if (p[-1] && pppdb != NULL)
 		delete_db_key(p);
 #endif
-	    free(p-1);
-	    while ((script_env[i] = script_env[i+1]) != 0)
-		++i;
+	    remove_script_env(i);
 	    break;
 	}
     }
@@ -2058,7 +2237,7 @@
     dbuf.dptr = vbuf;
     dbuf.dsize = vlen;
     if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
-	error("tdb_store failed: %s", tdb_error(pppdb));
+	error("tdb_store failed: %s", tdb_errorstr(pppdb));
 
     if (vbuf)
         free(vbuf);
@@ -2079,7 +2258,7 @@
     dbuf.dptr = db_key;
     dbuf.dsize = strlen(db_key);
     if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
-	error("tdb_store key failed: %s", tdb_error(pppdb));
+	error("tdb_store key failed: %s", tdb_errorstr(pppdb));
 }
 
 /*
diff --git a/pppd/md4.c b/pppd/md4.c
index cda9f94..d943e88 100644
--- a/pppd/md4.c
+++ b/pppd/md4.c
@@ -249,7 +249,8 @@
     byte = count >> 3;
     bit =  count & 7;
     /* Copy X into XX since we need to modify it */
-    for (i=0;i<=byte;i++)   XX[i] = X[i];
+    if (count)
+      for (i=0;i<=byte;i++) XX[i] = X[i];
     for (i=byte+1;i<64;i++) XX[i] = 0;
     /* Add padding '1' bit and low-order zeros in last byte */
     mask = 1 << (7 - bit);
diff --git a/pppd/md5.c b/pppd/md5.c
index 2039760..f1291ce 100644
--- a/pppd/md5.c
+++ b/pppd/md5.c
@@ -48,7 +48,7 @@
  */
 
 /* forward declaration */
-static void Transform ();
+static void Transform (UINT4 *buf, UINT4 *in);
 
 static unsigned char PADDING[64] = {
   0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/pppd/md5.h b/pppd/md5.h
index f7a0c96..71e8b00 100644
--- a/pppd/md5.h
+++ b/pppd/md5.h
@@ -57,9 +57,9 @@
   unsigned char digest[16];     /* actual digest after MD5Final call */
 } MD5_CTX;
 
-void MD5_Init ();
-void MD5_Update ();
-void MD5_Final ();
+void MD5_Init (MD5_CTX *mdContext);
+void MD5_Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
+void MD5_Final (unsigned char hash[], MD5_CTX *mdContext);
 
 #define __MD5_INCLUDE__
 #endif /* __MD5_INCLUDE__ */
diff --git a/pppd/mppe.h b/pppd/mppe.h
new file mode 100644
index 0000000..5eb3b37
--- /dev/null
+++ b/pppd/mppe.h
@@ -0,0 +1,121 @@
+/*
+ * mppe.h - Definitions for MPPE
+ *
+ * Copyright (c) 2008 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MPPE_PAD		4	/* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN	16	/* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40		0x01	/* 40 bit */
+#define MPPE_OPT_128		0x02	/* 128 bit */
+#define MPPE_OPT_STATEFUL	0x04	/* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56		0x08	/* 56 bit */
+#define MPPE_OPT_MPPC		0x10	/* MPPC compression */
+#define MPPE_OPT_D		0x20	/* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN	0x40	/* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit.  We could do a u_int32
+ * but then we have to do a htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT		0x01	/* MPPC */
+#define MPPE_D_BIT		0x10	/* Obsolete, usage unknown */
+#define MPPE_L_BIT		0x20	/* 40-bit */
+#define MPPE_S_BIT		0x40	/* 128-bit */
+#define MPPE_M_BIT		0x80	/* 56-bit, not supported */
+#define MPPE_H_BIT		0x01	/* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci)		\
+    do {					\
+	u_char *ptr = ci; /* u_char[4] */	\
+						\
+	/* H bit */				\
+	if (opts & MPPE_OPT_STATEFUL)		\
+	    *ptr++ = 0x0;			\
+	else					\
+	    *ptr++ = MPPE_H_BIT;		\
+	*ptr++ = 0;				\
+	*ptr++ = 0;				\
+						\
+	/* S,L bits */				\
+	*ptr = 0;				\
+	if (opts & MPPE_OPT_128)		\
+	    *ptr |= MPPE_S_BIT;			\
+	if (opts & MPPE_OPT_40)			\
+	    *ptr |= MPPE_L_BIT;			\
+	/* M,D,C bits not supported */		\
+    } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts)		\
+    do {					\
+	u_char *ptr = ci; /* u_char[4] */	\
+						\
+	opts = 0;				\
+						\
+	/* H bit */				\
+	if (!(ptr[0] & MPPE_H_BIT))		\
+	    opts |= MPPE_OPT_STATEFUL;		\
+						\
+	/* S,L bits */				\
+	if (ptr[3] & MPPE_S_BIT)		\
+	    opts |= MPPE_OPT_128;		\
+	if (ptr[3] & MPPE_L_BIT)		\
+	    opts |= MPPE_OPT_40;		\
+						\
+	/* M,D,C bits */			\
+	if (ptr[3] & MPPE_M_BIT)		\
+	    opts |= MPPE_OPT_56;		\
+	if (ptr[3] & MPPE_D_BIT)		\
+	    opts |= MPPE_OPT_D;			\
+	if (ptr[3] & MPPE_C_BIT)		\
+	    opts |= MPPE_OPT_MPPC;		\
+						\
+	/* Other bits */			\
+	if (ptr[0] & ~MPPE_H_BIT)		\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+	if (ptr[1] || ptr[2])			\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+	if (ptr[3] & ~MPPE_ALL_BITS)		\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+    } while (/* CONSTCOND */ 0)
diff --git a/pppd/openssl-hash.c b/pppd/openssl-hash.c
index 840a68c..76b2af2 100644
--- a/pppd/openssl-hash.c
+++ b/pppd/openssl-hash.c
@@ -16,6 +16,8 @@
 
 #include <openssl/evp.h>
 
+#include "pppd.h"
+
 const EVP_MD *sha1_md;
 const EVP_MD *md4_md;
 const EVP_MD *md5_md;
diff --git a/pppd/options.c b/pppd/options.c
index f8a836b..f66b765 100644
--- a/pppd/options.c
+++ b/pppd/options.c
@@ -40,7 +40,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: options.c,v 1.95 2004/11/09 22:33:35 paulus Exp $"
+#define RCSID	"$Id: options.c,v 1.102 2008/06/15 06:53:06 paulus Exp $"
 
 #include <ctype.h>
 #include <stdio.h>
@@ -57,16 +57,20 @@
 
 #ifdef PPP_FILTER
 #include <pcap.h>
-
 /*
- * DLT_PPP_WITH_DIRECTION is in current libpcap cvs, and should be in
- * libpcap-0.8.4.  Until that is released, use DLT_PPP - but that means
+ * There have been 3 or 4 different names for this in libpcap CVS, but
+ * this seems to be what they have settled on...
+ * For older versions of libpcap, use DLT_PPP - but that means
  * we lose the inbound and outbound qualifiers.
  */
-#ifndef DLT_PPP_WITH_DIRECTION
-#define DLT_PPP_WITH_DIRECTION	DLT_PPP
+#ifndef DLT_PPP_PPPD
+#ifdef DLT_PPP_WITHDIRECTION
+#define DLT_PPP_PPPD	DLT_PPP_WITHDIRECTION
+#else
+#define DLT_PPP_PPPD	DLT_PPP
 #endif
 #endif
+#endif /* PPP_FILTER */
 
 #include "pppd.h"
 #include "pathnames.h"
@@ -92,6 +96,7 @@
 char	devnam[MAXPATHLEN];	/* Device name */
 bool	nodetach = 0;		/* Don't detach from controlling tty */
 bool	updetach = 0;		/* Detach once link is up */
+bool	master_detach;		/* Detach when we're (only) multilink master */
 int	maxconnect = 0;		/* Maximum connect time */
 char	user[MAXNAMELEN];	/* Username for PAP */
 char	passwd[MAXSECRETLEN];	/* Password for PAP */
@@ -115,6 +120,7 @@
 bool	dryrun;			/* print out option values and exit */
 char	*domain;		/* domain name set by domain option */
 int	child_wait = 5;		/* # seconds to wait for children at exit */
+struct userenv *userenv_list;	/* user environment variables */
 
 #ifdef MAXOCTETS
 unsigned int  maxoctets = 0;    /* default - no limit */
@@ -131,6 +137,7 @@
 struct	bpf_program active_filter; /* Filter program for link-active pkts */
 #endif
 
+static option_t *curopt;	/* pointer to option being processed */
 char *current_option;		/* the name of the option being parsed */
 int  privileged_option;		/* set iff the current option came from root */
 char *option_source;		/* string saying where the option came from */
@@ -163,6 +170,11 @@
 static int setmodir __P((char **));
 #endif
 
+static int user_setenv __P((char **));
+static void user_setprint __P((option_t *, printer_func, void *));
+static int user_unsetenv __P((char **));
+static void user_unsetprint __P((option_t *, printer_func, void *));
+
 static option_t *find_option __P((const char *name));
 static int process_option __P((option_t *, char *, char **));
 static int n_arguments __P((option_t *));
@@ -199,6 +211,9 @@
       "Detach from controlling tty once link is up",
       OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
 
+    { "master_detach", o_bool, &master_detach,
+      "Detach when we're multilink master but have no link", 1 },
+
     { "holdoff", o_int, &holdoff,
       "Set time in seconds before retrying connection",
       OPT_PRIO, &holdoff_specified },
@@ -277,6 +292,13 @@
       "Number of seconds to wait for child processes at exit",
       OPT_PRIO },
 
+    { "set", o_special, (void *)user_setenv,
+      "Set user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_setprint },
+    { "unset", o_special, (void *)user_unsetenv,
+      "Unset user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_unsetprint },
+
 #ifdef HAVE_MULTILINK
     { "multilink", o_bool, &multilink,
       "Enable multilink operation", OPT_PRIO | 1 },
@@ -297,10 +319,10 @@
 #endif
 
 #ifdef PPP_FILTER
-    { "pass-filter", 1, setpassfilter,
+    { "pass-filter", o_special, setpassfilter,
       "set filter for packets to pass", OPT_PRIO },
 
-    { "active-filter", 1, setactivefilter,
+    { "active-filter", o_special, setactivefilter,
       "set filter for active pkts", OPT_PRIO },
 #endif
 
@@ -395,16 +417,20 @@
     option_t *opt;
     int oldpriv, n;
     char *oldsource;
+    uid_t euid;
     char *argv[MAXARGS];
     char args[MAXARGS][MAXWORDLEN];
     char cmd[MAXWORDLEN];
 
-    if (check_prot)
-	seteuid(getuid());
+    euid = geteuid();
+    if (check_prot && seteuid(getuid()) == -1) {
+	option_error("unable to drop privileges to open %s: %m", filename);
+	return 0;
+    }
     f = fopen(filename, "r");
     err = errno;
-    if (check_prot)
-	seteuid(0);
+    if (check_prot && seteuid(euid) == -1)
+	fatal("unable to regain privileges");
     if (f == NULL) {
 	errno = err;
 	if (!must_exist) {
@@ -750,30 +776,37 @@
 	if (opt->flags & OPT_STATIC) {
 	    strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
 	} else {
+	    char **optptr = (char **)(opt->addr);
 	    sv = strdup(*argv);
 	    if (sv == NULL)
 		novm("option argument");
-	    *(char **)(opt->addr) = sv;
+	    if (*optptr)
+		free(*optptr);
+	    *optptr = sv;
 	}
 	break;
 
     case o_special_noarg:
     case o_special:
 	parser = (int (*) __P((char **))) opt->addr;
+	curopt = opt;
 	if (!(*parser)(argv))
 	    return 0;
 	if (opt->flags & OPT_A2LIST) {
-	    struct option_value *ovp, **pp;
+	    struct option_value *ovp, *pp;
 
 	    ovp = malloc(sizeof(*ovp) + strlen(*argv));
 	    if (ovp != 0) {
 		strcpy(ovp->value, *argv);
 		ovp->source = option_source;
 		ovp->next = NULL;
-		pp = (struct option_value **) &opt->addr2;
-		while (*pp != 0)
-		    pp = &(*pp)->next;
-		*pp = ovp;
+		if (opt->addr2 == NULL) {
+		    opt->addr2 = ovp;
+		} else {
+		    for (pp = opt->addr2; pp->next != NULL; pp = pp->next)
+			;
+		    pp->next = ovp;
+		}
 	    }
 	}
 	break;
@@ -785,6 +818,10 @@
 	break;
     }
 
+    /*
+     * If addr2 wasn't used by any flag (OPT_A2COPY, etc.) but is set,
+     * treat it as a bool and set/clear it based on the OPT_A2CLR bit.
+     */
     if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
 		|OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0)
 	*(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
@@ -866,7 +903,7 @@
 static void
 print_option(opt, mainopt, printer, arg)
     option_t *opt, *mainopt;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
 	int i, v;
@@ -929,11 +966,8 @@
 			printer(arg, " ");
 		}
 		if (opt->flags & OPT_A2PRINTER) {
-			void (*oprt) __P((option_t *,
-					  void ((*)__P((void *, char *, ...))),
-					  void *));
-			oprt = (void (*) __P((option_t *,
-					 void ((*)__P((void *, char *, ...))),
+			void (*oprt) __P((option_t *, printer_func, void *));
+			oprt = (void (*) __P((option_t *, printer_func,
 					 void *)))opt->addr2;
 			(*oprt)(opt, printer, arg);
 		} else if (opt->flags & OPT_A2STRVAL) {
@@ -971,7 +1005,7 @@
 static void
 print_option_list(opt, printer, arg)
     option_t *opt;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
 	while (opt->name != NULL) {
@@ -989,7 +1023,7 @@
  */
 void
 print_options(printer, arg)
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
 	struct option_list *list;
@@ -1065,11 +1099,7 @@
     va_end(args);
     if (phase == PHASE_INITIALIZE)
 	fprintf(stderr, "%s: %s\n", progname, buf);
-#ifndef ANDROID_CHANGES
     syslog(LOG_ERR, "%s", buf);
-#else
-    error("%s", buf);
-#endif    
 }
 
 #if 0
@@ -1123,6 +1153,7 @@
     len = 0;
     escape = 0;
     comment = 0;
+    quoted = 0;
 
     /*
      * First skip white-space and comments.
@@ -1181,15 +1212,6 @@
     }
 
     /*
-     * Save the delimiter for quoted strings.
-     */
-    if (!escape && (c == '"' || c == '\'')) {
-        quoted = c;
-	c = getc(f);
-    } else
-        quoted = 0;
-
-    /*
      * Process characters until the end of the word.
      */
     while (c != EOF) {
@@ -1270,31 +1292,18 @@
 	    /*
 	     * Store the resulting character for the escape sequence.
 	     */
-	    if (len < MAXWORDLEN-1)
+	    if (len < MAXWORDLEN) {
 		word[len] = value;
-	    ++len;
+		++len;
+	    }
 
 	    if (!got)
 		c = getc(f);
 	    continue;
-
 	}
 
 	/*
-	 * Not escaped: see if we've reached the end of the word.
-	 */
-	if (quoted) {
-	    if (c == quoted)
-		break;
-	} else {
-	    if (isspace(c) || c == '#') {
-		ungetc (c, f);
-		break;
-	    }
-	}
-
-	/*
-	 * Backslash starts an escape sequence.
+	 * Backslash starts a new escape sequence.
 	 */
 	if (c == '\\') {
 	    escape = 1;
@@ -1303,11 +1312,31 @@
 	}
 
 	/*
+	 * Not escaped: check for the start or end of a quoted
+	 * section and see if we've reached the end of the word.
+	 */
+	if (quoted) {
+	    if (c == quoted) {
+		quoted = 0;
+		c = getc(f);
+		continue;
+	    }
+	} else if (c == '"' || c == '\'') {
+	    quoted = c;
+	    c = getc(f);
+	    continue;
+	} else if (isspace(c) || c == '#') {
+	    ungetc (c, f);
+	    break;
+	}
+
+	/*
 	 * An ordinary character: store it in the word and get another.
 	 */
-	if (len < MAXWORDLEN-1)
+	if (len < MAXWORDLEN) {
 	    word[len] = c;
-	++len;
+	    ++len;
+	}
 
 	c = getc(f);
     }
@@ -1328,6 +1357,9 @@
 	 */
 	if (len == 0)
 	    return 0;
+	if (quoted)
+	    option_error("warning: quoted word runs to end of file (%.20s...)",
+			 filename, word);
     }
 
     /*
@@ -1452,13 +1484,13 @@
     char **argv;
 {
     pcap_t *pc;
-    int ret = 0;
+    int ret = 1;
 
-    pc = pcap_open_dead(DLT_PPP_WITH_DIRECTION, 65535);
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
     if (pcap_compile(pc, &pass_filter, *argv, 1, netmask) == -1) {
 	option_error("error in pass-filter expression: %s\n",
 		     pcap_geterr(pc));
-	ret = 1;
+	ret = 0;
     }
     pcap_close(pc);
 
@@ -1473,13 +1505,13 @@
     char **argv;
 {
     pcap_t *pc;
-    int ret = 0;
+    int ret = 1;
 
-    pc = pcap_open_dead(DLT_PPP_WITH_DIRECTION, 65535);
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
     if (pcap_compile(pc, &active_filter, *argv, 1, netmask) == -1) {
 	option_error("error in active-filter expression: %s\n",
 		     pcap_geterr(pc));
-	ret = 1;
+	ret = 0;
     }
     pcap_close(pc);
 
@@ -1510,15 +1542,19 @@
     char **argv;
 {
     int fd, err;
+    uid_t euid;
 
-    if (!privileged_option)
-	seteuid(getuid());
+    euid = geteuid();
+    if (!privileged_option && seteuid(getuid()) == -1) {
+	option_error("unable to drop permissions to open %s: %m", *argv);
+	return 0;
+    }
     fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
     if (fd < 0 && errno == EEXIST)
 	fd = open(*argv, O_WRONLY | O_APPEND);
     err = errno;
-    if (!privileged_option)
-	seteuid(0);
+    if (!privileged_option && seteuid(euid) == -1)
+	fatal("unable to regain privileges: %m");
     if (fd < 0) {
 	errno = err;
 	option_error("Can't open log file %s: %m", *argv);
@@ -1608,3 +1644,154 @@
     return 0;
 }
 #endif /* PLUGIN */
+
+/*
+ * Set an environment variable specified by the user.
+ */
+static int
+user_setenv(argv)
+    char **argv;
+{
+    char *arg = argv[0];
+    char *eqp;
+    struct userenv *uep, **insp;
+
+    if ((eqp = strchr(arg, '=')) == NULL) {
+	option_error("missing = in name=value: %s", arg);
+	return 0;
+    }
+    if (eqp == arg) {
+	option_error("missing variable name: %s", arg);
+	return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+	int nlen = strlen(uep->ue_name);
+	if (nlen == (eqp - arg) &&
+	    strncmp(arg, uep->ue_name, nlen) == 0)
+	    break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+	return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+	uep = malloc(sizeof (*uep) + (eqp-arg));
+	strncpy(uep->ue_name, arg, eqp-arg);
+	uep->ue_name[eqp-arg] = '\0';
+	uep->ue_next = NULL;
+	insp = &userenv_list;
+	while (*insp != NULL)
+	    insp = &(*insp)->ue_next;
+	*insp = uep;
+    } else {
+	struct userenv *uep2;
+	for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+	    if (uep2 != uep && !uep2->ue_isset)
+		break;
+	}
+	if (uep2 == NULL && !uep->ue_isset)
+	    find_option("unset")->flags |= OPT_NOPRINT;
+	free(uep->ue_value);
+    }
+    uep->ue_isset = 1;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = strdup(eqp + 1);
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_setprint(opt, printer, arg)
+    option_t *opt;
+    printer_func printer;
+    void *arg;
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && !uepnext->ue_isset)
+	uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+	uepnext = uep->ue_next;
+	while (uepnext != NULL && !uepnext->ue_isset)
+	    uepnext = uepnext->ue_next;
+	(*printer)(arg, "%s=%s", uep->ue_name, uep->ue_value);
+	if (uepnext != NULL)
+	    (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+	else
+	    opt->source = uep->ue_source;
+    }
+}
+
+static int
+user_unsetenv(argv)
+    char **argv;
+{
+    struct userenv *uep, **insp;
+    char *arg = argv[0];
+
+    if (strchr(arg, '=') != NULL) {
+	option_error("unexpected = in name: %s", arg);
+	return 0;
+    }
+    if (arg == '\0') {
+	option_error("missing variable name for unset");
+	return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+	if (strcmp(arg, uep->ue_name) == 0)
+	    break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+	return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+	uep = malloc(sizeof (*uep) + strlen(arg));
+	strcpy(uep->ue_name, arg);
+	uep->ue_next = NULL;
+	insp = &userenv_list;
+	while (*insp != NULL)
+	    insp = &(*insp)->ue_next;
+	*insp = uep;
+    } else {
+	struct userenv *uep2;
+	for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+	    if (uep2 != uep && uep2->ue_isset)
+		break;
+	}
+	if (uep2 == NULL && uep->ue_isset)
+	    find_option("set")->flags |= OPT_NOPRINT;
+	free(uep->ue_value);
+    }
+    uep->ue_isset = 0;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = NULL;
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_unsetprint(opt, printer, arg)
+    option_t *opt;
+    printer_func printer;
+    void *arg;
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && uepnext->ue_isset)
+	uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+	uepnext = uep->ue_next;
+	while (uepnext != NULL && uepnext->ue_isset)
+	    uepnext = uepnext->ue_next;
+	(*printer)(arg, "%s", uep->ue_name);
+	if (uepnext != NULL)
+	    (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+	else
+	    opt->source = uep->ue_source;
+    }
+}
diff --git a/pppd/patchlevel.h b/pppd/patchlevel.h
index 61c2d33..da5e052 100644
--- a/pppd/patchlevel.h
+++ b/pppd/patchlevel.h
@@ -1,4 +1,2 @@
-/* $Id: patchlevel.h,v 1.62 2004/11/13 12:08:01 paulus Exp $ */
-
-#define VERSION		"2.4.3"
-#define DATE		"13 November 2004"
+#define VERSION		"2.4.7"
+#define DATE		"9 August 2014"
diff --git a/pppd/pathnames.h b/pppd/pathnames.h
index fac026d..a33f046 100644
--- a/pppd/pathnames.h
+++ b/pppd/pathnames.h
@@ -1,7 +1,7 @@
 /*
  * define path names
  *
- * $Id: pathnames.h,v 1.16 2004/11/13 12:02:22 paulus Exp $
+ * $Id: pathnames.h,v 1.18 2005/08/25 23:59:34 paulus Exp $
  */
 
 #ifdef HAVE_PATHS_H
@@ -14,9 +14,9 @@
 #define _PATH_DEVNULL	"/dev/null"
 #endif /* HAVE_PATHS_H */
 
-//#ifndef _ROOT_PATH
-#define _ROOT_PATH 
-//#endif
+#ifndef _ROOT_PATH
+#define _ROOT_PATH
+#endif
 
 #define _PATH_UPAPFILE 	 _ROOT_PATH "/etc/ppp/pap-secrets"
 #define _PATH_CHAPFILE 	 _ROOT_PATH "/etc/ppp/chap-secrets"
@@ -24,6 +24,7 @@
 #define _PATH_SYSOPTIONS _ROOT_PATH "/etc/ppp/options"
 #define _PATH_IPUP	 _ROOT_PATH "/etc/ppp/ip-up"
 #define _PATH_IPDOWN	 _ROOT_PATH "/etc/ppp/ip-down"
+#define _PATH_IPPREUP	 _ROOT_PATH "/etc/ppp/ip-pre-up"
 #define _PATH_AUTHUP	 _ROOT_PATH "/etc/ppp/auth-up"
 #define _PATH_AUTHDOWN	 _ROOT_PATH "/etc/ppp/auth-down"
 #define _PATH_TTYOPT	 _ROOT_PATH "/etc/ppp/options."
@@ -55,5 +56,10 @@
 #endif /* __STDC__ */
 
 #ifdef PLUGIN
-#define _PATH_PLUGIN	"/usr/lib/pppd/" VERSION
+#ifdef __STDC__
+#define _PATH_PLUGIN	DESTDIR "/lib/pppd/" VERSION
+#else /* __STDC__ */
+#define _PATH_PLUGIN	"/usr/lib/pppd"
+#endif /* __STDC__ */
+
 #endif /* PLUGIN */
diff --git a/pppd/plugins/Makefile.linux b/pppd/plugins/Makefile.linux
index b52f284..ab8cf50 100644
--- a/pppd/plugins/Makefile.linux
+++ b/pppd/plugins/Makefile.linux
@@ -1,19 +1,26 @@
-CC	= gcc
+#CC	= gcc
 COPTS	= -O2 -g
 CFLAGS	= $(COPTS) -I.. -I../../include -fPIC
 LDFLAGS	= -shared
 INSTALL	= install
 
-DESTDIR = @DESTDIR@
+DESTDIR = $(INSTROOT)@DESTDIR@
 BINDIR = $(DESTDIR)/sbin
 MANDIR = $(DESTDIR)/share/man/man8
 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
 
-SUBDIRS := rp-pppoe pppoatm radius
+SUBDIRS := rp-pppoe pppoatm pppol2tp
 # Uncomment the next line to include the radius authentication plugin
-# SUBDIRS += radius
+SUBDIRS += radius
 PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so
 
+# This setting should match the one in ../Makefile.linux
+MPPE=y
+
+ifdef MPPE
+CFLAGS   += -DMPPE=1
+endif
+
 # include dependencies if present
 ifeq (.depend,$(wildcard .depend))
 include .depend
diff --git a/pppd/plugins/passprompt.c b/pppd/plugins/passprompt.c
index 7689017..babb6dc 100644
--- a/pppd/plugins/passprompt.c
+++ b/pppd/plugins/passprompt.c
@@ -50,7 +50,7 @@
     }
     if (!kid) {
 	/* we are the child, exec the program */
-	char *argv[4], fdstr[32];
+	char *argv[5], fdstr[32];
 	sys_close();
 	closelog();
 	close(p[0]);
@@ -82,7 +82,6 @@
 	}
 	readgood += red;
     } while (readgood < MAXSECRETLEN - 1);
-    passwd[readgood] = 0;
     close(p[0]);
 
     /* now wait for child to exit */
@@ -95,6 +94,7 @@
 
     if (readgood < 0)
 	return 0;
+    passwd[readgood] = 0;
     if (!WIFEXITED(wstat))
 	warn("%s terminated abnormally", promptprog);
     if (WEXITSTATUS(wstat))
diff --git a/pppd/plugins/pppoatm/Makefile.linux b/pppd/plugins/pppoatm/Makefile.linux
index 72acd0b..20f62e6 100644
--- a/pppd/plugins/pppoatm/Makefile.linux
+++ b/pppd/plugins/pppoatm/Makefile.linux
@@ -1,4 +1,4 @@
-CC	= gcc
+#CC	= gcc
 COPTS	= -O2 -g
 CFLAGS	= $(COPTS) -I../.. -I../../../include -fPIC
 LDFLAGS	= -shared
@@ -6,7 +6,7 @@
 
 #***********************************************************************
 
-DESTDIR = @DESTDIR@
+DESTDIR = $(INSTROOT)@DESTDIR@
 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
 
 VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
diff --git a/pppd/plugins/pppoatm/pppoatm.c b/pppd/plugins/pppoatm/pppoatm.c
index ad04b5c..d693350 100644
--- a/pppd/plugins/pppoatm/pppoatm.c
+++ b/pppd/plugins/pppoatm/pppoatm.c
@@ -133,9 +133,6 @@
 	int fd;
 	struct atm_qos qos;
 
-	/* XXX: This won't work on Android */
-	system ("/sbin/modprobe pppoatm");
-
 	if (!device_got_set)
 		no_device_given_pppoatm();
 	fd = socket(AF_ATMPVC, SOCK_DGRAM, 0);
@@ -169,34 +166,6 @@
 	close(pppoa_fd);
 }
 
-static void send_config_pppoa(int mtu,
-			      u_int32_t asyncmap,
-			      int pcomp,
-			      int accomp)
-{
-	int sock;
-	struct ifreq ifr;
-	if (mtu > pppoatm_max_mtu)
-		error("Couldn't increase MTU to %d", 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_pppoa(int mru,
-			      u_int32_t asyncmap,
-			      int pcomp,
-			      int accomp)
-{
-	if (mru > pppoatm_max_mru)
-		error("Couldn't increase MRU to %d", mru);
-}
-
 void plugin_init(void)
 {
 #if defined(__linux__)
@@ -218,8 +187,8 @@
     disconnect: &disconnect_pppoatm,
     establish_ppp: &generic_establish_ppp,
     disestablish_ppp: &generic_disestablish_ppp,
-    send_config: &send_config_pppoa,
-    recv_config: &recv_config_pppoa,
+    send_config: NULL,
+    recv_config: NULL,
     close: NULL,
     cleanup: NULL
 };
diff --git a/pppd/plugins/radius/Makefile.linux b/pppd/plugins/radius/Makefile.linux
index 2b071aa..24ed3e5 100644
--- a/pppd/plugins/radius/Makefile.linux
+++ b/pppd/plugins/radius/Makefile.linux
@@ -3,7 +3,7 @@
 # Copyright 2002 Roaring Penguin Software Inc.
 #
 
-DESTDIR = @DESTDIR@
+DESTDIR = $(INSTROOT)@DESTDIR@
 MANDIR = $(DESTDIR)/share/man/man8
 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
 
diff --git a/pppd/plugins/radius/config.c b/pppd/plugins/radius/config.c
index 3bd67fc..a29e5e8 100644
--- a/pppd/plugins/radius/config.c
+++ b/pppd/plugins/radius/config.c
@@ -216,8 +216,8 @@
 		p[pos] = '\0';
 
 		if ((option = find_option(p, OT_ANY)) == NULL) {
-			error("%s: line %d: unrecognized keyword: %s", filename, line, p);
-			return (-1);
+			warn("%s: line %d: unrecognized keyword: %s", filename, line, p);
+			continue;
 		}
 
 		if (option->status != ST_UNDEF) {
diff --git a/pppd/plugins/radius/ip_util.c b/pppd/plugins/radius/ip_util.c
index cd59e7b..1f6a76e 100644
--- a/pppd/plugins/radius/ip_util.c
+++ b/pppd/plugins/radius/ip_util.c
@@ -135,3 +135,31 @@
 
 	return this_host_ipaddr;
 }
+
+/*
+ * Function: rc_own_bind_ipaddress
+ *
+ * Purpose: get the IP address to be used as a source address
+ *          for sending requests in host order
+ *
+ * Returns: IP address
+ *
+ */
+
+UINT4 rc_own_bind_ipaddress(void)
+{
+	char *bindaddr;
+	UINT4 rval = 0;
+
+	if ((bindaddr = rc_conf_str("bindaddr")) == NULL ||
+	    strcmp(rc_conf_str("bindaddr"), "*") == 0) {
+		rval = INADDR_ANY;
+	} else {
+		if ((rval = rc_get_ipaddr(bindaddr)) == 0) {
+			error("rc_own_bind_ipaddress: couldn't get IP address from bindaddr");
+			rval = INADDR_ANY;
+		}
+	}
+
+	return rval;
+}
diff --git a/pppd/plugins/radius/options.h b/pppd/plugins/radius/options.h
index aa55305..f4ad986 100644
--- a/pppd/plugins/radius/options.h
+++ b/pppd/plugins/radius/options.h
@@ -55,6 +55,7 @@
 {"radius_timeout",	OT_INT, ST_UNDEF, NULL},
 {"radius_retries",	OT_INT,	ST_UNDEF, NULL},
 {"nas_identifier",      OT_STR, ST_UNDEF, ""},
+{"bindaddr",            OT_STR, ST_UNDEF, NULL},
 /* local options */
 {"login_local",		OT_STR, ST_UNDEF, NULL},
 };
diff --git a/pppd/plugins/radius/radius.c b/pppd/plugins/radius/radius.c
index 932c89a..4ba5f52 100644
--- a/pppd/plugins/radius/radius.c
+++ b/pppd/plugins/radius/radius.c
@@ -24,7 +24,7 @@
 *
 ***********************************************************************/
 static char const RCSID[] =
-"$Id: radius.c,v 1.28 2004/11/14 10:27:57 paulus Exp $";
+"$Id: radius.c,v 1.32 2008/05/26 09:18:08 paulus Exp $";
 
 #include "pppd.h"
 #include "chap-new.h"
@@ -48,6 +48,8 @@
 
 #define MD5_HASH_SIZE	16
 
+#define MSDNS 1
+
 static char *config_file = NULL;
 static int add_avp(char **);
 static struct avpopt {
@@ -410,18 +412,14 @@
     case CHAP_MICROSOFT:
     {
 	/* MS-CHAP-Challenge and MS-CHAP-Response */
-	MS_ChapResponse *rmd = (MS_ChapResponse *) response;
 	u_char *p = cpassword;
 
 	if (response_len != MS_CHAP_RESPONSE_LEN)
 	    return 0;
 	*p++ = id;
 	/* The idiots use a different field order in RADIUS than PPP */
-	memcpy(p, rmd->UseNT, sizeof(rmd->UseNT));
-	p += sizeof(rmd->UseNT);
-	memcpy(p, rmd->LANManResp, sizeof(rmd->LANManResp));
-	p += sizeof(rmd->LANManResp);
-	memcpy(p, rmd->NTResp, sizeof(rmd->NTResp));
+	*p++ = response[MS_CHAP_USENT];
+	memcpy(p, response, MS_CHAP_LANMANRESP_LEN + MS_CHAP_NTRESP_LEN);
 
 	rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE,
 		      challenge, challenge_len, VENDOR_MICROSOFT);
@@ -433,20 +431,15 @@
     case CHAP_MICROSOFT_V2:
     {
 	/* MS-CHAP-Challenge and MS-CHAP2-Response */
-	MS_Chap2Response *rmd = (MS_Chap2Response *) response;
 	u_char *p = cpassword;
 
 	if (response_len != MS_CHAP2_RESPONSE_LEN)
 	    return 0;
 	*p++ = id;
 	/* The idiots use a different field order in RADIUS than PPP */
-	memcpy(p, rmd->Flags, sizeof(rmd->Flags));
-	p += sizeof(rmd->Flags);
-	memcpy(p, rmd->PeerChallenge, sizeof(rmd->PeerChallenge));
-	p += sizeof(rmd->PeerChallenge);
-	memcpy(p, rmd->Reserved, sizeof(rmd->Reserved));
-	p += sizeof(rmd->Reserved);
-	memcpy(p, rmd->NTResp, sizeof(rmd->NTResp));
+	*p++ = response[MS_CHAP2_FLAGS];
+	memcpy(p, response, (MS_CHAP2_PEER_CHAL_LEN + MS_CHAP2_RESERVED_LEN
+			     + MS_CHAP2_NTRESP_LEN));
 
 	rc_avpair_add(&send, PW_MS_CHAP_CHALLENGE,
 		      challenge, challenge_len, VENDOR_MICROSOFT);
@@ -480,6 +473,8 @@
 			 req_info);
     }
 
+    strlcpy(message, radius_msg, message_space);
+
     if (result == OK_RC) {
 	if (!rstate.done_chap_once) {
 	    if (radius_setparams(received, radius_msg, req_info, digest,
@@ -550,6 +545,14 @@
     int mppe_enc_policy = 0;
     int mppe_enc_types = 0;
 #endif
+#ifdef MSDNS
+    ipcp_options *wo = &ipcp_wantoptions[0];
+    ipcp_options *ao = &ipcp_allowoptions[0];
+    int got_msdns_1 = 0;
+    int got_msdns_2 = 0;
+    int got_wins_1 = 0;
+    int got_wins_2 = 0;
+#endif
 
     /* Send RADIUS attributes to anyone else who might be interested */
     if (radius_attributes_hook) {
@@ -588,6 +591,18 @@
 		/* Session timeout */
 		maxconnect = vp->lvalue;
 		break;
+           case PW_FILTER_ID:
+               /* packet filter, will be handled via ip-(up|down) script */
+               script_setenv("RADIUS_FILTER_ID", vp->strvalue, 1);
+               break;
+           case PW_FRAMED_ROUTE:
+               /* route, will be handled via ip-(up|down) script */
+               script_setenv("RADIUS_FRAMED_ROUTE", vp->strvalue, 1);
+               break;
+           case PW_IDLE_TIMEOUT:
+               /* idle parameter */
+               idle_time_limit = vp->lvalue;
+               break;
 #ifdef MAXOCTETS
 	    case PW_SESSION_OCTETS_LIMIT:
 		/* Session traffic limit */
@@ -626,6 +641,9 @@
 		    rstate.ip_addr = remote;
 		}
 		break;
+            case PW_NAS_IP_ADDRESS:
+                wo->ouraddr = htonl(vp->lvalue);
+                break;
 	    case PW_CLASS:
 		/* Save Class attribute to pass it in accounting request */
 		if (vp->lvalue <= MAXCLASSLEN) {
@@ -636,8 +654,8 @@
 	    }
 
 
-#ifdef CHAPMS
 	} else if (vp->vendorcode == VENDOR_MICROSOFT) {
+#ifdef CHAPMS
 	    switch (vp->attribute) {
 	    case PW_MS_CHAP2_SUCCESS:
 		if ((vp->lvalue != 43) || strncmp(vp->strvalue + 1, "S=", 2)) {
@@ -680,13 +698,32 @@
 		break;
 
 #endif /* MPPE */
-#if 0
+#ifdef MSDNS
 	    case PW_MS_PRIMARY_DNS_SERVER:
-	    case PW_MS_SECONDARY_DNS_SERVER:
-	    case PW_MS_PRIMARY_NBNS_SERVER:
-	    case PW_MS_SECONDARY_NBNS_SERVER:
+		ao->dnsaddr[0] = htonl(vp->lvalue);
+		got_msdns_1 = 1;
+		if (!got_msdns_2)
+		    ao->dnsaddr[1] = ao->dnsaddr[0];
 		break;
-#endif
+	    case PW_MS_SECONDARY_DNS_SERVER:
+		ao->dnsaddr[1] = htonl(vp->lvalue);
+		got_msdns_2 = 1;
+		if (!got_msdns_1)
+		    ao->dnsaddr[0] = ao->dnsaddr[1];
+		break;
+	    case PW_MS_PRIMARY_NBNS_SERVER:
+		ao->winsaddr[0] = htonl(vp->lvalue);
+		got_wins_1 = 1;
+		if (!got_wins_2)
+		    ao->winsaddr[1] = ao->winsaddr[0];
+		break;
+	    case PW_MS_SECONDARY_NBNS_SERVER:
+		ao->winsaddr[1] = htonl(vp->lvalue);
+		got_wins_2 = 1;
+		if (!got_wins_1)
+		    ao->winsaddr[0] = ao->winsaddr[1];
+		break;
+#endif /* MSDNS */
 	    }
 #endif /* CHAPMS */
 	}
@@ -950,6 +987,9 @@
 	return;
     }
 
+    if (rstate.acct_interim_interval)
+	UNTIMEOUT(radius_acct_interim, NULL);
+
     rstate.accounting_started = 0;
     rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id,
 		   0, VENDOR_NONE);
@@ -1026,6 +1066,10 @@
 	    av_type = PW_ACCT_IDLE_TIMEOUT;
 	    break;
 
+	case EXIT_CALLBACK:
+	    av_type = PW_CALLBACK;
+	    break;
+	    
 	case EXIT_CONNECT_TIME:
 	    av_type = PW_ACCT_SESSION_TIMEOUT;
 	    break;
diff --git a/pppd/plugins/radius/radiusclient.h b/pppd/plugins/radius/radiusclient.h
index 7b7933e..51b959a 100644
--- a/pppd/plugins/radius/radiusclient.h
+++ b/pppd/plugins/radius/radiusclient.h
@@ -152,6 +152,10 @@
 #define PW_MS_CHAP_MPPE_KEYS		12	/* string */
 #define PW_MS_MPPE_SEND_KEY		16	/* string */
 #define PW_MS_MPPE_RECV_KEY		17	/* string */
+#define PW_MS_PRIMARY_DNS_SERVER	28	/* ipaddr */
+#define PW_MS_SECONDARY_DNS_SERVER	29	/* ipaddr */
+#define PW_MS_PRIMARY_NBNS_SERVER	30	/* ipaddr */
+#define PW_MS_SECONDARY_NBNS_SERVER	31	/* ipaddr */
 
 /*	Accounting */
 
diff --git a/pppd/plugins/radius/radrealms.c b/pppd/plugins/radius/radrealms.c
index 1d8da62..7a30370 100644
--- a/pppd/plugins/radius/radrealms.c
+++ b/pppd/plugins/radius/radrealms.c
@@ -28,7 +28,8 @@
 char radrealms_config[MAXPATHLEN] = "/etc/radiusclient/realms";
 
 static option_t Options[] = {
-    { "realms-config-file", o_string, &radrealms_config },
+    { "realms-config-file", o_string, &radrealms_config,
+      "Configuration file for RADIUS realms", OPT_STATIC, NULL, MAXPATHLEN },
     { NULL }
 };
 
diff --git a/pppd/plugins/radius/sendserver.c b/pppd/plugins/radius/sendserver.c
index 3612b8d..f68aa67 100644
--- a/pppd/plugins/radius/sendserver.c
+++ b/pppd/plugins/radius/sendserver.c
@@ -244,7 +244,7 @@
 	sin = (struct sockaddr_in *) & salocal;
 	memset ((char *) sin, '\0', (size_t) length);
 	sin->sin_family = AF_INET;
-	sin->sin_addr.s_addr = htonl(INADDR_ANY);
+	sin->sin_addr.s_addr = htonl(rc_own_bind_ipaddress());
 	sin->sin_port = htons ((unsigned short) 0);
 	if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
 		   getsockname (sockfd, (struct sockaddr *) sin, &length) < 0)
diff --git a/pppd/plugins/rp-pppoe/Makefile.linux b/pppd/plugins/rp-pppoe/Makefile.linux
index 2ffa1c0..5d7a271 100644
--- a/pppd/plugins/rp-pppoe/Makefile.linux
+++ b/pppd/plugins/rp-pppoe/Makefile.linux
@@ -4,16 +4,17 @@
 # Makefile
 #
 # Makefile for Roaring Penguin's Linux PPPoE plugin.
+# Modified for integration with pppd sources by Paul Mackerras.
 #
 # Copyright (C) 2001 Roaring Penguin Software Inc.
 #
 # This program may be distributed according to the terms of the GNU
 # General Public License, version 2 or (at your option) any later version.
 #
-# $Id: Makefile.linux,v 1.6 2004/11/14 07:58:37 paulus Exp $
+# $Id: Makefile.linux,v 1.8 2008/06/09 08:34:23 paulus Exp $
 #***********************************************************************
 
-DESTDIR = @DESTDIR@
+DESTDIR = $(INSTROOT)@DESTDIR@
 BINDIR = $(DESTDIR)/sbin
 LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION)
 
@@ -22,20 +23,23 @@
 INSTALL	= install
 
 # Version is set ONLY IN THE MAKEFILE!  Don't delete this!
-VERSION=3.3
+RP_VERSION=3.8p
 
 COPTS=-O2 -g
-CFLAGS=$(COPTS) -I../../../include/linux
+CFLAGS=$(COPTS) -I../../../include '-DRP_VERSION="$(RP_VERSION)"'
 all: rp-pppoe.so pppoe-discovery
 
-pppoe-discovery: libplugin.a pppoe-discovery.o
-	$(CC) -o pppoe-discovery pppoe-discovery.o libplugin.a
+pppoe-discovery: pppoe-discovery.o debug.o
+	$(CC) -o pppoe-discovery pppoe-discovery.o debug.o
 
 pppoe-discovery.o: pppoe-discovery.c
-	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o pppoe-discovery.o pppoe-discovery.c
+	$(CC) $(CFLAGS) -c -o pppoe-discovery.o pppoe-discovery.c
 
-rp-pppoe.so: libplugin.a plugin.o
-	$(CC) -o rp-pppoe.so -shared plugin.o libplugin.a
+debug.o: debug.c
+	$(CC) $(CFLAGS) -c -o debug.o debug.c
+
+rp-pppoe.so: plugin.o discovery.o if.o common.o
+	$(CC) -o rp-pppoe.so -shared plugin.o discovery.o if.o common.o
 
 install: all
 	$(INSTALL) -d -m 755 $(LIBDIR)
@@ -44,23 +48,17 @@
 	$(INSTALL) -s -c -m 555 pppoe-discovery $(BINDIR)
 
 clean:
-	rm -f *.o *.so
+	rm -f *.o *.so pppoe-discovery
 
 plugin.o: plugin.c
-	$(CC) '-DRP_VERSION="$(VERSION)"' $(CFLAGS) -I../../..  -c -o plugin.o -fPIC plugin.c
-
-libplugin.a: discovery.o if.o common.o debug.o
-	$(AR) -rc $@ $^
+	$(CC) $(CFLAGS) -I../../.. -c -o plugin.o -fPIC plugin.c
 
 discovery.o: discovery.c
-	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o discovery.o -fPIC discovery.c
+	$(CC) $(CFLAGS) -I../../.. -c -o discovery.o -fPIC discovery.c
 
 if.o: if.c
-	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o if.o -fPIC if.c
-
-debug.o: debug.c
-	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o debug.o -fPIC debug.c
+	$(CC) $(CFLAGS) -I../../.. -c -o if.o -fPIC if.c
 
 common.o: common.c
-	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o common.o -fPIC common.c
+	$(CC) $(CFLAGS) -I../../.. -c -o common.o -fPIC common.c
 
diff --git a/pppd/plugins/rp-pppoe/common.c b/pppd/plugins/rp-pppoe/common.c
index a39e97a..89c633c 100644
--- a/pppd/plugins/rp-pppoe/common.c
+++ b/pppd/plugins/rp-pppoe/common.c
@@ -14,17 +14,16 @@
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: common.c,v 1.2 2004/01/13 04:03:58 paulus Exp $";
+"$Id: common.c,v 1.3 2008/06/09 08:34:23 paulus Exp $";
 
+#define _GNU_SOURCE 1
 #include "pppoe.h"
-
-#ifdef HAVE_SYSLOG_H
-#include <syslog.h>
-#endif
+#include "pppd/pppd.h"
 
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <syslog.h>	/* for LOG_DEBUG */
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -49,18 +48,18 @@
     unsigned char *curTag;
     UINT16_t tagType, tagLen;
 
-    if (packet->ver != 1) {
-	syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver);
+    if (PPPOE_VER(packet->vertype) != 1) {
+	error("Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype));
 	return -1;
     }
-    if (packet->type != 1) {
-	syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type);
+    if (PPPOE_TYPE(packet->vertype) != 1) {
+	error("Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype));
 	return -1;
     }
 
     /* Do some sanity checks on packet */
-    if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
-	syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len);
+    if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */
+	error("Invalid PPPoE packet length (%u)", len);
 	return -1;
     }
 
@@ -68,15 +67,13 @@
     curTag = packet->payload;
     while(curTag - packet->payload < len) {
 	/* Alignment is not guaranteed, so do this by hand... */
-	tagType = (((UINT16_t) curTag[0]) << 8) +
-	    (UINT16_t) curTag[1];
-	tagLen = (((UINT16_t) curTag[2]) << 8) +
-	    (UINT16_t) curTag[3];
+	tagType = (curTag[0] << 8) + curTag[1];
+	tagLen = (curTag[2] << 8) + curTag[3];
 	if (tagType == TAG_END_OF_LIST) {
 	    return 0;
 	}
 	if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
-	    syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen);
+	    error("Invalid PPPoE tag length (%u)", tagLen);
 	    return -1;
 	}
 	func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra);
@@ -85,303 +82,6 @@
     return 0;
 }
 
-/**********************************************************************
-*%FUNCTION: findTag
-*%ARGUMENTS:
-* packet -- the PPPoE discovery packet to parse
-* type -- the type of the tag to look for
-* tag -- will be filled in with tag contents
-*%RETURNS:
-* A pointer to the tag if one of the specified type is found; NULL
-* otherwise. 
-*%DESCRIPTION:
-* Looks for a specific tag type.
-***********************************************************************/
-unsigned char *
-findTag(PPPoEPacket *packet, UINT16_t type, PPPoETag *tag)
-{
-    UINT16_t len = ntohs(packet->length);
-    unsigned char *curTag;
-    UINT16_t tagType, tagLen;
-
-    if (packet->ver != 1) {
-	syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver);
-	return NULL;
-    }
-    if (packet->type != 1) {
-	syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type);
-	return NULL;
-    }
-
-    /* Do some sanity checks on packet */
-    if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
-	syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len);
-	return NULL;
-    }
-
-    /* Step through the tags */
-    curTag = packet->payload;
-    while(curTag - packet->payload < len) {
-	/* Alignment is not guaranteed, so do this by hand... */
-	tagType = (((UINT16_t) curTag[0]) << 8) +
-	    (UINT16_t) curTag[1];
-	tagLen = (((UINT16_t) curTag[2]) << 8) +
-	    (UINT16_t) curTag[3];
-	if (tagType == TAG_END_OF_LIST) {
-	    return NULL;
-	}
-	if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
-	    syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen);
-	    return NULL;
-	}
-	if (tagType == type) {
-	    memcpy(tag, curTag, tagLen + TAG_HDR_SIZE);
-	    return curTag;
-	}
-	curTag = curTag + TAG_HDR_SIZE + tagLen;
-    }
-    return NULL;
-}
-
-/**********************************************************************
-*%FUNCTION: printErr
-*%ARGUMENTS:
-* str -- error message
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Prints a message to stderr and syslog.
-***********************************************************************/
-void
-printErr(char const *str)
-{
-    fprintf(stderr, "pppoe: %s\n", str);
-    syslog(LOG_ERR, "%s", str);
-}
-
-
-/**********************************************************************
-*%FUNCTION: strDup
-*%ARGUMENTS:
-* str -- string to copy
-*%RETURNS:
-* A malloc'd copy of str.  Exits if malloc fails.
-***********************************************************************/
-char *
-strDup(char const *str)
-{
-    char *copy = malloc(strlen(str)+1);
-    if (!copy) {
-	rp_fatal("strdup failed");
-    }
-    strcpy(copy, str);
-    return copy;
-}
-
-#ifdef PPPOE_STANDALONE
-/**********************************************************************
-*%FUNCTION: computeTCPChecksum
-*%ARGUMENTS:
-* ipHdr -- pointer to IP header
-* tcpHdr -- pointer to TCP header
-*%RETURNS:
-* The computed TCP checksum
-***********************************************************************/
-UINT16_t
-computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr)
-{
-    UINT32_t sum = 0;
-    UINT16_t count = ipHdr[2] * 256 + ipHdr[3];
-    unsigned char *addr = tcpHdr;
-    unsigned char pseudoHeader[12];
-
-    /* Count number of bytes in TCP header and data */
-    count -= (ipHdr[0] & 0x0F) * 4;
-
-    memcpy(pseudoHeader, ipHdr+12, 8);
-    pseudoHeader[8] = 0;
-    pseudoHeader[9] = ipHdr[9];
-    pseudoHeader[10] = (count >> 8) & 0xFF;
-    pseudoHeader[11] = (count & 0xFF);
-
-    /* Checksum the pseudo-header */
-    sum += * (UINT16_t *) pseudoHeader;
-    sum += * ((UINT16_t *) (pseudoHeader+2));
-    sum += * ((UINT16_t *) (pseudoHeader+4));
-    sum += * ((UINT16_t *) (pseudoHeader+6));
-    sum += * ((UINT16_t *) (pseudoHeader+8));
-    sum += * ((UINT16_t *) (pseudoHeader+10));
-
-    /* Checksum the TCP header and data */
-    while (count > 1) {
-	sum += * (UINT16_t *) addr;
-	addr += 2;
-	count -= 2;
-    }
-    if (count > 0) {
-	sum += *addr;
-    }
-
-    while(sum >> 16) {
-	sum = (sum & 0xffff) + (sum >> 16);
-    }
-    return (UINT16_t) (~sum & 0xFFFF);
-}
-
-/**********************************************************************
-*%FUNCTION: clampMSS
-*%ARGUMENTS:
-* packet -- PPPoE session packet
-* dir -- either "incoming" or "outgoing"
-* clampMss -- clamp value
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Clamps MSS option if TCP SYN flag is set.
-***********************************************************************/
-void
-clampMSS(PPPoEPacket *packet, char const *dir, int clampMss)
-{
-    unsigned char *tcpHdr;
-    unsigned char *ipHdr;
-    unsigned char *opt;
-    unsigned char *endHdr;
-    unsigned char *mssopt = NULL;
-    UINT16_t csum;
-
-    int len, minlen;
-
-    /* check PPP protocol type */
-    if (packet->payload[0] & 0x01) {
-        /* 8 bit protocol type */
-
-        /* Is it IPv4? */
-        if (packet->payload[0] != 0x21) {
-            /* Nope, ignore it */
-            return;
-        }
-
-        ipHdr = packet->payload + 1;
-	minlen = 41;
-    } else {
-        /* 16 bit protocol type */
-
-        /* Is it IPv4? */
-        if (packet->payload[0] != 0x00 ||
-            packet->payload[1] != 0x21) {
-            /* Nope, ignore it */
-            return;
-        }
-
-        ipHdr = packet->payload + 2;
-	minlen = 42;
-    }
-
-    /* Is it too short? */
-    len = (int) ntohs(packet->length);
-    if (len < minlen) {
-	/* 20 byte IP header; 20 byte TCP header; at least 1 or 2 byte PPP protocol */
-	return;
-    }
-
-    /* Verify once more that it's IPv4 */
-    if ((ipHdr[0] & 0xF0) != 0x40) {
-	return;
-    }
-
-    /* Is it a fragment that's not at the beginning of the packet? */
-    if ((ipHdr[6] & 0x1F) || ipHdr[7]) {
-	/* Yup, don't touch! */
-	return;
-    }
-    /* Is it TCP? */
-    if (ipHdr[9] != 0x06) {
-	return;
-    }
-
-    /* Get start of TCP header */
-    tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4;
-
-    /* Is SYN set? */
-    if (!(tcpHdr[13] & 0x02)) {
-	return;
-    }
-
-    /* Compute and verify TCP checksum -- do not touch a packet with a bad
-       checksum */
-    csum = computeTCPChecksum(ipHdr, tcpHdr);
-    if (csum) {
-	syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum);
-
-	/* Upper layers will drop it */
-	return;
-    }
-
-    /* Look for existing MSS option */
-    endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2);
-    opt = tcpHdr + 20;
-    while (opt < endHdr) {
-	if (!*opt) break;	/* End of options */
-	switch(*opt) {
-	case 1:
-	    opt++;
-	    break;
-
-	case 2:
-	    if (opt[1] != 4) {
-		/* Something fishy about MSS option length. */
-		syslog(LOG_ERR,
-		       "Bogus length for MSS option (%u) from %u.%u.%u.%u",
-		       (unsigned int) opt[1],
-		       (unsigned int) ipHdr[12],
-		       (unsigned int) ipHdr[13],
-		       (unsigned int) ipHdr[14],
-		       (unsigned int) ipHdr[15]);
-		return;
-	    }
-	    mssopt = opt;
-	    break;
-	default:
-	    if (opt[1] < 2) {
-		/* Someone's trying to attack us? */
-		syslog(LOG_ERR,
-		       "Bogus TCP option length (%u) from %u.%u.%u.%u",
-		       (unsigned int) opt[1],
-		       (unsigned int) ipHdr[12],
-		       (unsigned int) ipHdr[13],
-		       (unsigned int) ipHdr[14],
-		       (unsigned int) ipHdr[15]);
-		return;
-	    }
-	    opt += (opt[1]);
-	    break;
-	}
-	/* Found existing MSS option? */
-	if (mssopt) break;
-    }
-
-    /* If MSS exists and it's low enough, do nothing */
-    if (mssopt) {
-	unsigned mss = mssopt[2] * 256 + mssopt[3];
-	if (mss <= clampMss) {
-	    return;
-	}
-
-	mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF;
-	mssopt[3] = ((unsigned) clampMss) & 0xFF;
-    } else {
-	/* No MSS option.  Don't add one; we'll have to use 536. */
-	return;
-    }
-
-    /* Recompute TCP checksum */
-    tcpHdr[16] = 0;
-    tcpHdr[17] = 0;
-    csum = computeTCPChecksum(ipHdr, tcpHdr);
-    (* (UINT16_t *) (tcpHdr+16)) = csum;
-}
-#endif /* PPPOE_STANDALONE */
-
 /***********************************************************************
 *%FUNCTION: sendPADT
 *%ARGUMENTS:
@@ -410,8 +110,7 @@
     memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
 
     packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
-    packet.ver = 1;
-    packet.type = 1;
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
     packet.code = CODE_PADT;
     packet.session = conn->session;
 
@@ -442,7 +141,7 @@
 	cursor += elen + TAG_HDR_SIZE;
 	plen += elen + TAG_HDR_SIZE;
     }
-	    
+
     /* Copy cookie and relay-ID if needed */
     if (conn->cookie.type) {
 	CHECK_ROOM(cursor, packet.payload,
@@ -462,43 +161,126 @@
 
     packet.length = htons(plen);
     sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
-    if (conn->debugFile) {
-	dumpPacket(conn->debugFile, &packet, "SENT");
-	fprintf(conn->debugFile, "\n");
-	fflush(conn->debugFile);
-    }
-    syslog(LOG_INFO,"Sent PADT");
+    info("Sent PADT");
 }
 
-/**********************************************************************
-*%FUNCTION: parseLogErrs
-*%ARGUMENTS:
-* type -- tag type
-* len -- tag length
-* data -- tag data
-* extra -- extra user data
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Picks error tags out of a packet and logs them.
-***********************************************************************/
-void
-parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data,
-	     void *extra)
+#define EH(x)	(x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5]
+
+/* Print out a PPPOE packet for debugging */
+void pppoe_printpkt(PPPoEPacket *packet,
+		    void (*printer)(void *, char *, ...), void *arg)
 {
-    switch(type) {
-    case TAG_SERVICE_NAME_ERROR:
-	syslog(LOG_ERR, "PADT: Service-Name-Error: %.*s", (int) len, data);
-	fprintf(stderr, "PADT: Service-Name-Error: %.*s\n", (int) len, data);
+    int len = ntohs(packet->length);
+    int i, tag, tlen, text;
+
+    switch (ntohs(packet->ethHdr.h_proto)) {
+    case ETH_PPPOE_DISCOVERY:
+	printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype),
+		PPPOE_TYPE(packet->vertype));
+	switch (packet->code) {
+	case CODE_PADI:
+	    printer(arg, "PADI");
+	    break;
+	case CODE_PADO:
+	    printer(arg, "PADO");
+	    break;
+	case CODE_PADR:
+	    printer(arg, "PADR");
+	    break;
+	case CODE_PADS:
+	    printer(arg, "PADS");
+	    break;
+	case CODE_PADT:
+	    printer(arg, "PADT");
+	    break;
+	default:
+	    printer(arg, "unknown code %x", packet->code);
+	}
+	printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len);
 	break;
-    case TAG_AC_SYSTEM_ERROR:
-	syslog(LOG_ERR, "PADT: System-Error: %.*s", (int) len, data);
-	fprintf(stderr, "PADT: System-Error: %.*s\n", (int) len, data);
+    case ETH_PPPOE_SESSION:
+	printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype),
+		PPPOE_TYPE(packet->vertype));
+	printer(arg, " code 0x%x session 0x%x length %d\n", packet->code,
+		ntohs(packet->session), len);
 	break;
-    case TAG_GENERIC_ERROR:
-	syslog(LOG_ERR, "PADT: Generic-Error: %.*s", (int) len, data);
-	fprintf(stderr, "PADT: Generic-Error: %.*s\n", (int) len, data);
-	break;
+    default:
+	printer(arg, "Unknown ethernet frame with proto = 0x%x\n",
+		ntohs(packet->ethHdr.h_proto));
     }
+
+    printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest));
+    printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source));
+    if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY)
+	return;
+
+    for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) {
+	tag = (packet->payload[i] << 8) + packet->payload[i+1];
+	tlen = (packet->payload[i+2] << 8) + packet->payload[i+3];
+	if (i + tlen + TAG_HDR_SIZE > len)
+	    break;
+	text = 0;
+	i += TAG_HDR_SIZE;
+	printer(arg, " [");
+	switch (tag) {
+	case TAG_END_OF_LIST:
+	    printer(arg, "end-of-list");
+	    break;
+	case TAG_SERVICE_NAME:
+	    printer(arg, "service-name");
+	    text = 1;
+	    break;
+	case TAG_AC_NAME:
+	    printer(arg, "AC-name");
+	    text = 1;
+	    break;
+	case TAG_HOST_UNIQ:
+	    printer(arg, "host-uniq");
+	    break;
+	case TAG_AC_COOKIE:
+	    printer(arg, "AC-cookie");
+	    break;
+	case TAG_VENDOR_SPECIFIC:
+	    printer(arg, "vendor-specific");
+	    break;
+	case TAG_RELAY_SESSION_ID:
+	    printer(arg, "relay-session-id");
+	    break;
+	case TAG_PPP_MAX_PAYLOAD:
+	    printer(arg, "PPP-max-payload");
+	    break;
+	case TAG_SERVICE_NAME_ERROR:
+	    printer(arg, "service-name-error");
+	    text = 1;
+	    break;
+	case TAG_AC_SYSTEM_ERROR:
+	    printer(arg, "AC-system-error");
+	    text = 1;
+	    break;
+	case TAG_GENERIC_ERROR:
+	    printer(arg, "generic-error");
+	    text = 1;
+	    break;
+	default:
+	    printer(arg, "unknown tag 0x%x", tag);
+	}
+	if (tlen) {
+	    if (text)
+		printer(arg, " %.*v", tlen, &packet->payload[i]);
+	    else if (tlen <= 32)
+		printer(arg, " %.*B", tlen, &packet->payload[i]);
+	    else
+		printer(arg, " %.32B... (length %d)",
+			&packet->payload[i], tlen);
+	}
+	printer(arg, "]");
+    }
+    printer(arg, "\n");
 }
 
+void pppoe_log_packet(const char *prefix, PPPoEPacket *packet)
+{
+    init_pr_log(prefix, LOG_DEBUG);
+    pppoe_printpkt(packet, pr_log, NULL);
+    end_pr_log();
+}
diff --git a/pppd/plugins/rp-pppoe/config.h b/pppd/plugins/rp-pppoe/config.h
index 644d78e..08bed3d 100644
--- a/pppd/plugins/rp-pppoe/config.h
+++ b/pppd/plugins/rp-pppoe/config.h
@@ -90,10 +90,10 @@
 /* #undef HAVE_NET_IF_DL_H */
 
 /* Define if you have the <net/if_ether.h> header file.  */
-/* #undef HAVE_NET_IF_ETHER_H */
+#define HAVE_NET_IF_ETHER_H 1
 
 /* Define if you have the <net/if_types.h> header file.  */
-/* #undef HAVE_NET_IF_TYPES_H */
+#define HAVE_NET_IF_TYPES_H 1
 
 /* Define if you have the <netinet/if_ether.h> header file.  */
 #define HAVE_NETINET_IF_ETHER_H 1
@@ -130,6 +130,3 @@
 
 /* Define if you have the N_HDLC line discipline in linux/termios.h */
 #define HAVE_N_HDLC 1
-
-/* Define if bitfields are packed in reverse order */
-#define PACK_BITFIELDS_REVERSED 1
diff --git a/pppd/plugins/rp-pppoe/debug.c b/pppd/plugins/rp-pppoe/debug.c
index a443eac..a6e6981 100644
--- a/pppd/plugins/rp-pppoe/debug.c
+++ b/pppd/plugins/rp-pppoe/debug.c
@@ -14,7 +14,7 @@
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: debug.c,v 1.1 2001/12/14 02:55:20 mostrows Exp $";
+"$Id: debug.c,v 1.2 2008/06/09 08:34:23 paulus Exp $";
 
 #include "pppoe.h"
 #include <sys/time.h>
@@ -116,6 +116,8 @@
     case CODE_PADR: fprintf(fp, "PADR "); break;
     case CODE_PADS: fprintf(fp, "PADS "); break;
     case CODE_PADT: fprintf(fp, "PADT "); break;
+    case CODE_PADM: fprintf(fp, "PADM "); break;
+    case CODE_PADN: fprintf(fp, "PADN "); break;
     case CODE_SESS: fprintf(fp, "SESS "); break;
     }
 
diff --git a/pppd/plugins/rp-pppoe/discovery.c b/pppd/plugins/rp-pppoe/discovery.c
index 937ea52..04877cb 100644
--- a/pppd/plugins/rp-pppoe/discovery.c
+++ b/pppd/plugins/rp-pppoe/discovery.c
@@ -9,13 +9,13 @@
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: discovery.c,v 1.3 2004/11/04 10:07:37 paulus Exp $";
+"$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $";
 
+#define _GNU_SOURCE 1
 #include "pppoe.h"
-
-#ifdef HAVE_SYSLOG_H
-#include <syslog.h>
-#endif
+#include "pppd/pppd.h"
+#include "pppd/fsm.h"
+#include "pppd/lcp.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -40,6 +40,30 @@
 
 #include <signal.h>
 
+/* Calculate time remaining until *exp, return 0 if now >= *exp */
+static int time_left(struct timeval *diff, struct timeval *exp)
+{
+    struct timeval now;
+
+    if (gettimeofday(&now, NULL) < 0) {
+	error("gettimeofday: %m");
+	return 0;
+    }
+
+    if (now.tv_sec > exp->tv_sec
+	|| (now.tv_sec == exp->tv_sec && now.tv_usec >= exp->tv_usec))
+	return 0;
+
+    diff->tv_sec = exp->tv_sec - now.tv_sec;
+    diff->tv_usec = exp->tv_usec - now.tv_usec;
+    if (diff->tv_usec < 0) {
+	diff->tv_usec += 1000000;
+	--diff->tv_sec;
+    }
+
+    return 1;
+}
+
 /**********************************************************************
 *%FUNCTION: parseForHostUniq
 *%ARGUMENTS:
@@ -52,7 +76,7 @@
 *%DESCRIPTION:
 * If a HostUnique tag is found which matches our PID, sets *extra to 1.
 ***********************************************************************/
-void
+static void
 parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
 		 void *extra)
 {
@@ -77,7 +101,7 @@
 * If we are using the Host-Unique tag, verifies that packet contains
 * our unique identifier.
 ***********************************************************************/
-int
+static int
 packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
 {
     int forMe = 0;
@@ -106,19 +130,20 @@
 *%DESCRIPTION:
 * Picks interesting tags out of a PADO packet
 ***********************************************************************/
-void
+static void
 parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
 	      void *extra)
 {
     struct PacketCriteria *pc = (struct PacketCriteria *) extra;
     PPPoEConnection *conn = pc->conn;
+    UINT16_t mru;
     int i;
 
     switch(type) {
     case TAG_AC_NAME:
 	pc->seenACName = 1;
 	if (conn->printACNames) {
-	    printf("Access-Concentrator: %.*s\n", (int) len, data);
+	    info("Access-Concentrator: %.*s", (int) len, data);
 	}
 	if (conn->acName && len == strlen(conn->acName) &&
 	    !strncmp((char *) data, conn->acName, len)) {
@@ -127,65 +152,45 @@
 	break;
     case TAG_SERVICE_NAME:
 	pc->seenServiceName = 1;
-	if (conn->printACNames && len > 0) {
-	    printf("       Service-Name: %.*s\n", (int) len, data);
-	}
 	if (conn->serviceName && len == strlen(conn->serviceName) &&
 	    !strncmp((char *) data, conn->serviceName, len)) {
 	    pc->serviceNameOK = 1;
 	}
 	break;
     case TAG_AC_COOKIE:
-	if (conn->printACNames) {
-	    printf("Got a cookie:");
-	    /* Print first 20 bytes of cookie */
-	    for (i=0; i<len && i < 20; i++) {
-		printf(" %02x", (unsigned) data[i]);
-	    }
-	    if (i < len) printf("...");
-	    printf("\n");
-	}
 	conn->cookie.type = htons(type);
 	conn->cookie.length = htons(len);
 	memcpy(conn->cookie.payload, data, len);
 	break;
     case TAG_RELAY_SESSION_ID:
-	if (conn->printACNames) {
-	    printf("Got a Relay-ID:");
-	    /* Print first 20 bytes of relay ID */
-	    for (i=0; i<len && i < 20; i++) {
-		printf(" %02x", (unsigned) data[i]);
-	    }
-	    if (i < len) printf("...");
-	    printf("\n");
-	}
 	conn->relayId.type = htons(type);
 	conn->relayId.length = htons(len);
 	memcpy(conn->relayId.payload, data, len);
 	break;
-    case TAG_SERVICE_NAME_ERROR:
-	if (conn->printACNames) {
-	    printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data);
-	} else {
-	    syslog(LOG_ERR, "PADO: Service-Name-Error: %.*s", (int) len, data);
-	    exit(1);
+    case TAG_PPP_MAX_PAYLOAD:
+	if (len == sizeof(mru)) {
+	    memcpy(&mru, data, sizeof(mru));
+	    mru = ntohs(mru);
+	    if (mru >= ETH_PPPOE_MTU) {
+		if (lcp_allowoptions[0].mru > mru)
+		    lcp_allowoptions[0].mru = mru;
+		if (lcp_wantoptions[0].mru > mru)
+		    lcp_wantoptions[0].mru = mru;
+		conn->seenMaxPayload = 1;
+	    }
 	}
 	break;
+    case TAG_SERVICE_NAME_ERROR:
+	error("PADO: Service-Name-Error: %.*s", (int) len, data);
+	conn->error = 1;
+	break;
     case TAG_AC_SYSTEM_ERROR:
-	if (conn->printACNames) {
-	    printf("Got a System-Error tag: %.*s\n", (int) len, data);
-	} else {
-	    syslog(LOG_ERR, "PADO: System-Error: %.*s", (int) len, data);
-	    exit(1);
-	}
+	error("PADO: System-Error: %.*s", (int) len, data);
+	conn->error = 1;
 	break;
     case TAG_GENERIC_ERROR:
-	if (conn->printACNames) {
-	    printf("Got a Generic-Error tag: %.*s\n", (int) len, data);
-	} else {
-	    syslog(LOG_ERR, "PADO: Generic-Error: %.*s", (int) len, data);
-	    exit(1);
-	}
+	error("PADO: Generic-Error: %.*s", (int) len, data);
+	conn->error = 1;
 	break;
     }
 }
@@ -202,27 +207,41 @@
 *%DESCRIPTION:
 * Picks interesting tags out of a PADS packet
 ***********************************************************************/
-void
+static void
 parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
 	      void *extra)
 {
     PPPoEConnection *conn = (PPPoEConnection *) extra;
+    UINT16_t mru;
     switch(type) {
     case TAG_SERVICE_NAME:
-	syslog(LOG_DEBUG, "PADS: Service-Name: '%.*s'", (int) len, data);
+	dbglog("PADS: Service-Name: '%.*s'", (int) len, data);
+	break;
+    case TAG_PPP_MAX_PAYLOAD:
+	if (len == sizeof(mru)) {
+	    memcpy(&mru, data, sizeof(mru));
+	    mru = ntohs(mru);
+	    if (mru >= ETH_PPPOE_MTU) {
+		if (lcp_allowoptions[0].mru > mru)
+		    lcp_allowoptions[0].mru = mru;
+		if (lcp_wantoptions[0].mru > mru)
+		    lcp_wantoptions[0].mru = mru;
+		conn->seenMaxPayload = 1;
+	    }
+	}
 	break;
     case TAG_SERVICE_NAME_ERROR:
-	syslog(LOG_ERR, "PADS: Service-Name-Error: %.*s", (int) len, data);
-	fprintf(stderr, "PADS: Service-Name-Error: %.*s\n", (int) len, data);
-	exit(1);
+	error("PADS: Service-Name-Error: %.*s", (int) len, data);
+	conn->error = 1;
+	break;
     case TAG_AC_SYSTEM_ERROR:
-	syslog(LOG_ERR, "PADS: System-Error: %.*s", (int) len, data);
-	fprintf(stderr, "PADS: System-Error: %.*s\n", (int) len, data);
-	exit(1);
+	error("PADS: System-Error: %.*s", (int) len, data);
+	conn->error = 1;
+	break;
     case TAG_GENERIC_ERROR:
-	syslog(LOG_ERR, "PADS: Generic-Error: %.*s", (int) len, data);
-	fprintf(stderr, "PADS: Generic-Error: %.*s\n", (int) len, data);
-	exit(1);
+	error("PADS: Generic-Error: %.*s", (int) len, data);
+	conn->error = 1;
+	break;
     case TAG_RELAY_SESSION_ID:
 	conn->relayId.type = htons(type);
 	conn->relayId.length = htons(len);
@@ -240,7 +259,7 @@
 *%DESCRIPTION:
 * Sends a PADI packet
 ***********************************************************************/
-void
+static void
 sendPADI(PPPoEConnection *conn)
 {
     PPPoEPacket packet;
@@ -248,31 +267,38 @@
     PPPoETag *svc = (PPPoETag *) (&packet.payload);
     UINT16_t namelen = 0;
     UINT16_t plen;
+    int omit_service_name = 0;
 
     if (conn->serviceName) {
 	namelen = (UINT16_t) strlen(conn->serviceName);
+	if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) {
+	    omit_service_name = 1;
+	}
     }
-    plen = TAG_HDR_SIZE + namelen;
-    CHECK_ROOM(cursor, packet.payload, plen);
 
     /* Set destination to Ethernet broadcast address */
     memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
     memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
 
     packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
-    packet.ver = 1;
-    packet.type = 1;
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
     packet.code = CODE_PADI;
     packet.session = 0;
 
-    svc->type = TAG_SERVICE_NAME;
-    svc->length = htons(namelen);
-    CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE);
+    if (!omit_service_name) {
+	plen = TAG_HDR_SIZE + namelen;
+	CHECK_ROOM(cursor, packet.payload, plen);
 
-    if (conn->serviceName) {
-	memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
+	svc->type = TAG_SERVICE_NAME;
+	svc->length = htons(namelen);
+
+	if (conn->serviceName) {
+	    memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
+	}
+	cursor += namelen + TAG_HDR_SIZE;
+    } else {
+	plen = 0;
     }
-    cursor += namelen + TAG_HDR_SIZE;
 
     /* If we're using Host-Uniq, copy it over */
     if (conn->useHostUniq) {
@@ -287,14 +313,22 @@
 	plen += sizeof(pid) + TAG_HDR_SIZE;
     }
 
+    /* Add our maximum MTU/MRU */
+    if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
+	PPPoETag maxPayload;
+	UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
+	maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+	maxPayload.length = htons(sizeof(mru));
+	memcpy(maxPayload.payload, &mru, sizeof(mru));
+	CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
+	memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+	cursor += sizeof(mru) + TAG_HDR_SIZE;
+	plen += sizeof(mru) + TAG_HDR_SIZE;
+    }
+
     packet.length = htons(plen);
 
     sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
-    if (conn->debugFile) {
-	dumpPacket(conn->debugFile, &packet, "SENT");
-	fprintf(conn->debugFile, "\n");
-	fflush(conn->debugFile);
-    }
 }
 
 /**********************************************************************
@@ -313,6 +347,8 @@
     fd_set readable;
     int r;
     struct timeval tv;
+    struct timeval expire_at;
+
     PPPoEPacket packet;
     int len;
 
@@ -322,12 +358,20 @@
     pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
     pc.seenACName    = 0;
     pc.seenServiceName = 0;
-	
+    conn->seenMaxPayload = 0;
+    conn->error = 0;
+
+    if (gettimeofday(&expire_at, NULL) < 0) {
+	error("gettimeofday (waitForPADO): %m");
+	return;
+    }
+    expire_at.tv_sec += timeout;
+
     do {
 	if (BPF_BUFFER_IS_EMPTY) {
-	    tv.tv_sec = timeout;
-	    tv.tv_usec = 0;
-	
+	    if (!time_left(&tv, &expire_at))
+		return;		/* Timed out */
+
 	    FD_ZERO(&readable);
 	    FD_SET(conn->discoverySocket, &readable);
 
@@ -336,17 +380,19 @@
 		if (r >= 0 || errno != EINTR) break;
 	    }
 	    if (r < 0) {
-		fatalSys("select (waitForPADO)");
+		error("select (waitForPADO): %m");
+		return;
 	    }
-	    if (r == 0) return;        /* Timed out */
+	    if (r == 0)
+		return;		/* Timed out */
 	}
-	
+
 	/* Get the packet */
 	receivePacket(conn->discoverySocket, &packet, &len);
 
 	/* Check length */
 	if (ntohs(packet.length) + HDR_SIZE > len) {
-	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	    error("Bogus PPPoE length field (%u)",
 		   (unsigned int) ntohs(packet.length));
 	    continue;
 	}
@@ -356,44 +402,34 @@
 	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
 #endif
 
-	if (conn->debugFile) {
-	    dumpPacket(conn->debugFile, &packet, "RCVD");
-	    fprintf(conn->debugFile, "\n");
-	    fflush(conn->debugFile);
-	}
 	/* If it's not for us, loop again */
 	if (!packetIsForMe(conn, &packet)) continue;
 
 	if (packet.code == CODE_PADO) {
 	    if (NOT_UNICAST(packet.ethHdr.h_source)) {
-		printErr("Ignoring PADO packet from non-unicast MAC address");
+		error("Ignoring PADO packet from non-unicast MAC address");
 		continue;
 	    }
-	    parsePacket(&packet, parsePADOTags, &pc);
+	    if (conn->req_peer
+		&& memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) {
+		warn("Ignoring PADO packet from wrong MAC address");
+		continue;
+	    }
+	    if (parsePacket(&packet, parsePADOTags, &pc) < 0)
+		return;
+	    if (conn->error)
+		return;
 	    if (!pc.seenACName) {
-		printErr("Ignoring PADO packet with no AC-Name tag");
+		error("Ignoring PADO packet with no AC-Name tag");
 		continue;
 	    }
 	    if (!pc.seenServiceName) {
-		printErr("Ignoring PADO packet with no Service-Name tag");
+		error("Ignoring PADO packet with no Service-Name tag");
 		continue;
 	    }
 	    conn->numPADOs++;
-	    if (conn->printACNames) {
-		printf("--------------------------------------------------\n");
-	    }
 	    if (pc.acNameOK && pc.serviceNameOK) {
 		memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
-		if (conn->printACNames) {
-		    printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
-			   (unsigned) conn->peerEth[0], 
-			   (unsigned) conn->peerEth[1],
-			   (unsigned) conn->peerEth[2],
-			   (unsigned) conn->peerEth[3],
-			   (unsigned) conn->peerEth[4],
-			   (unsigned) conn->peerEth[5]);
-		    continue;
-		}
 		conn->discoveryState = STATE_RECEIVED_PADO;
 		break;
 	    }
@@ -410,7 +446,7 @@
 *%DESCRIPTION:
 * Sends a PADR packet
 ***********************************************************************/
-void
+static void
 sendPADR(PPPoEConnection *conn)
 {
     PPPoEPacket packet;
@@ -430,8 +466,7 @@
     memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
 
     packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
-    packet.ver = 1;
-    packet.type = 1;
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
     packet.code = CODE_PADR;
     packet.session = 0;
 
@@ -455,6 +490,19 @@
 	plen += sizeof(pid) + TAG_HDR_SIZE;
     }
 
+    /* Add our maximum MTU/MRU */
+    if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
+	PPPoETag maxPayload;
+	UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
+	maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+	maxPayload.length = htons(sizeof(mru));
+	memcpy(maxPayload.payload, &mru, sizeof(mru));
+	CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
+	memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+	cursor += sizeof(mru) + TAG_HDR_SIZE;
+	plen += sizeof(mru) + TAG_HDR_SIZE;
+    }
+
     /* Copy cookie and relay-ID if needed */
     if (conn->cookie.type) {
 	CHECK_ROOM(cursor, packet.payload,
@@ -474,11 +522,6 @@
 
     packet.length = htons(plen);
     sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
-    if (conn->debugFile) {
-	dumpPacket(conn->debugFile, &packet, "SENT");
-	fprintf(conn->debugFile, "\n");
-	fflush(conn->debugFile);
-    }
 }
 
 /**********************************************************************
@@ -491,31 +534,42 @@
 *%DESCRIPTION:
 * Waits for a PADS packet and copies useful information
 ***********************************************************************/
-void
+static void
 waitForPADS(PPPoEConnection *conn, int timeout)
 {
     fd_set readable;
     int r;
     struct timeval tv;
+    struct timeval expire_at;
+
     PPPoEPacket packet;
     int len;
 
+    if (gettimeofday(&expire_at, NULL) < 0) {
+	error("gettimeofday (waitForPADS): %m");
+	return;
+    }
+    expire_at.tv_sec += timeout;
+
+    conn->error = 0;
     do {
 	if (BPF_BUFFER_IS_EMPTY) {
-	    tv.tv_sec = timeout;
-	    tv.tv_usec = 0;
-	    
+	    if (!time_left(&tv, &expire_at))
+		return;		/* Timed out */
+
 	    FD_ZERO(&readable);
 	    FD_SET(conn->discoverySocket, &readable);
-	    
+
 	    while(1) {
 		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
 		if (r >= 0 || errno != EINTR) break;
 	    }
 	    if (r < 0) {
-		fatalSys("select (waitForPADS)");
+		error("select (waitForPADS): %m");
+		return;
 	    }
-	    if (r == 0) return;
+	    if (r == 0)
+		return;		/* Timed out */
 	}
 
 	/* Get the packet */
@@ -523,7 +577,7 @@
 
 	/* Check length */
 	if (ntohs(packet.length) + HDR_SIZE > len) {
-	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
+	    error("Bogus PPPoE length field (%u)",
 		   (unsigned int) ntohs(packet.length));
 	    continue;
 	}
@@ -532,11 +586,6 @@
 	/* If it's not a Discovery packet, loop again */
 	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
 #endif
-	if (conn->debugFile) {
-	    dumpPacket(conn->debugFile, &packet, "RCVD");
-	    fprintf(conn->debugFile, "\n");
-	    fflush(conn->debugFile);
-	}
 
 	/* If it's not from the AC, it's not for me */
 	if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
@@ -547,7 +596,10 @@
 	/* Is it PADS?  */
 	if (packet.code == CODE_PADS) {
 	    /* Parse for goodies */
-	    parsePacket(&packet, parsePADSTags, conn);
+	    if (parsePacket(&packet, parsePADSTags, conn) < 0)
+		return;
+	    if (conn->error)
+		return;
 	    conn->discoveryState = STATE_SESSION;
 	    break;
 	}
@@ -556,11 +608,11 @@
     /* Don't bother with ntohs; we'll just end up converting it back... */
     conn->session = packet.session;
 
-    syslog(LOG_INFO, "PPP session is %d", (int) ntohs(conn->session));
+    info("PPP session is %d", (int) ntohs(conn->session));
 
     /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
     if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
-	syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
+	error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
     }
 }
 
@@ -578,22 +630,7 @@
 {
     int padiAttempts = 0;
     int padrAttempts = 0;
-    int timeout = PADI_TIMEOUT;
-
-    /* Skip discovery and don't open discovery socket? */
-    if (conn->skipDiscovery && conn->noDiscoverySocket) {
-	conn->discoveryState = STATE_SESSION;
-	return;
-    }
-
-    conn->discoverySocket =
-	openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
-
-    /* Skip discovery? */
-    if (conn->skipDiscovery) {
-	conn->discoveryState = STATE_SESSION;
-	return;
-    }
+    int timeout = conn->discoveryTimeout;
 
     do {
 	padiAttempts++;
@@ -607,23 +644,10 @@
 	conn->discoveryState = STATE_SENT_PADI;
 	waitForPADO(conn, timeout);
 
-	/* If we're just probing for access concentrators, don't do
-	   exponential backoff.  This reduces the time for an unsuccessful
-	   probe to 15 seconds. */
-	if (!conn->printACNames) {
-	    timeout *= 2;
-	}
-	if (conn->printACNames && conn->numPADOs) {
-	    break;
-	}
+	timeout *= 2;
     } while (conn->discoveryState == STATE_SENT_PADI);
 
-    /* If we're only printing access concentrator names, we're done */
-    if (conn->printACNames) {
-	die(0);
-    }
-
-    timeout = PADI_TIMEOUT;
+    timeout = conn->discoveryTimeout;
     do {
 	padrAttempts++;
 	if (padrAttempts > MAX_PADI_ATTEMPTS) {
@@ -638,8 +662,15 @@
 	timeout *= 2;
     } while (conn->discoveryState == STATE_SENT_PADR);
 
+    if (!conn->seenMaxPayload) {
+	/* RFC 4638: MUST limit MTU/MRU to 1492 */
+	if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU)
+	    lcp_allowoptions[0].mru = ETH_PPPOE_MTU;
+	if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU)
+	    lcp_wantoptions[0].mru = ETH_PPPOE_MTU;
+    }
+
     /* We're done. */
     conn->discoveryState = STATE_SESSION;
     return;
 }
-
diff --git a/pppd/plugins/rp-pppoe/if.c b/pppd/plugins/rp-pppoe/if.c
index 4e21762..91e9a57 100644
--- a/pppd/plugins/rp-pppoe/if.c
+++ b/pppd/plugins/rp-pppoe/if.c
@@ -14,9 +14,11 @@
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: if.c,v 1.1 2001/12/14 02:55:20 mostrows Exp $";
+"$Id: if.c,v 1.2 2008/06/09 08:34:23 paulus Exp $";
 
+#define _GNU_SOURCE 1
 #include "pppoe.h"
+#include "pppd/pppd.h"
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -40,10 +42,6 @@
 #include <sys/ioctl.h>
 #endif
 
-#ifdef HAVE_SYSLOG_H
-#include <syslog.h>
-#endif
-
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -52,55 +50,6 @@
 #include <net/if_arp.h>
 #endif
 
-#ifdef USE_DLPI
-
-#include <limits.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stream.h>
-#include <sys/stropts.h>
-#include <sys/dlpi.h>
-#include <sys/bufmod.h>
-#include <stdio.h>
-#include <signal.h>
-#include <stropts.h>
-
-/* function declarations */
-
-void dlpromisconreq( int fd, u_long  level);
-void dlinforeq(int fd);
-void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen);
-void dlinfoack(int fd, char *bufp);
-void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest);
-void dlattachreq(int fd, u_long ppa);
-void dlokack(int fd, char *bufp);
-void dlbindack(int fd, char *bufp);
-int strioctl(int fd, int cmd, int timout, int len, char *dp);
-void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller);
-void sigalrm(int sig);
-void expecting(int prim, union DL_primitives *dlp);
-char *dlprim(u_long prim);
-
-/* #define DL_DEBUG */
-
-static	int     dl_abssaplen;
-static	int     dl_saplen;
-static	int 	dl_addrlen;
-
-#endif
-
-#ifdef USE_BPF
-#include <net/bpf.h>
-#include <fcntl.h>
-
-unsigned char *bpfBuffer;	/* Packet filter buffer */
-int bpfLength = 0;		/* Packet filter buffer length */
-int bpfSize = 0;		/* Number of unread bytes in buffer */
-int bpfOffset = 0;		/* Current offset in bpfBuffer */
-#endif
-
 /* Initialize frame types to RFC 2516 values.  Some broken peers apparently
    use different frame types... sigh... */
 
@@ -127,283 +76,11 @@
 {
     UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
     if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
-	syslog(LOG_ERR, "Invalid ether type 0x%x", type);
+	error("Invalid ether type 0x%x", type);
     }
     return type;
 }
 
-#ifdef USE_BPF
-/**********************************************************************
-*%FUNCTION: getHWaddr
-*%ARGUMENTS:
-* ifname -- name of interface
-* hwaddr -- buffer for ehthernet address
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Locates the Ethernet hardware address for an interface.
-***********************************************************************/
-void
-getHWaddr(int sock, char const *ifname, unsigned char *hwaddr)
-{
-    char inbuf[8192];
-    const struct sockaddr_dl *sdl;
-    struct ifconf ifc;
-    struct ifreq ifreq, *ifr;
-    int i;
-    int found = 0;
-
-    ifc.ifc_len = sizeof(inbuf);
-    ifc.ifc_buf = inbuf;
-    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
-	fatalSys("SIOCGIFCONF");
-    }
-    ifr = ifc.ifc_req;
-    ifreq.ifr_name[0] = '\0';
-    for (i = 0; i < ifc.ifc_len; ) {
-	ifr = (struct ifreq *)((caddr_t)ifc.ifc_req + i);
-	i += sizeof(ifr->ifr_name) +
-		    (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)
-		    ? ifr->ifr_addr.sa_len
-		    : sizeof(struct sockaddr));
-	if (ifr->ifr_addr.sa_family == AF_LINK) {
-	    sdl = (const struct sockaddr_dl *) &ifr->ifr_addr;
-	    if ((sdl->sdl_type == IFT_ETHER) &&
-	        (sdl->sdl_alen == ETH_ALEN) &&
-		!strncmp(ifname, ifr->ifr_name, sizeof(ifr->ifr_name))) {
-		if (found) {
-		    char buffer[256];
-		    sprintf(buffer, "interface %.16s has more than one ethernet address", ifname);
-		    rp_fatal(buffer);
-		} else {
-		    found = 1;
-	            memcpy(hwaddr, LLADDR(sdl), ETH_ALEN);
-		}
-	    }
-	}
-    }
-    if (!found) {
-	char buffer[256];
-        sprintf(buffer, "interface %.16s has no ethernet address", ifname);
-	rp_fatal(buffer);
-    }
-}
-
-/**********************************************************************
-*%FUNCTION: initFilter
-*%ARGUMENTS:
-* fd -- file descriptor of BSD device
-* type -- Ethernet frame type (0 for watch mode)
-* hwaddr -- buffer with ehthernet address
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Initializes the packet filter rules.
-***********************************************************************/
-void
-initFilter(int fd, UINT16_t type, unsigned char *hwaddr)
-{
-    /* Packet Filter Instructions:
-     * Note that the ethernet type names come from "pppoe.h" and are
-     * used here to maintain consistency with the rest of this file. */
-    static struct bpf_insn bpfRun[] = {         /* run PPPoE */
-        BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),     /* ethernet type */
-        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_SESSION, 5, 0),
-        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_PPPOE_DISCOVERY, 0, 9),
-        BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0),      /* first word of dest. addr */
-#define PPPOE_BCAST_CMPW 4                     /* offset of word compare */
-        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 2),
-        BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4),      /* next 1/2 word of dest. */
-#define PPPOE_BCAST_CMPH 6                     /* offset of 1/2 word compare */
-        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 4, 0),
-        BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 0),      /* first word of dest. addr */
-#define PPPOE_FILTER_CMPW 8                     /* offset of word compare */
-        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 3),
-        BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 4),      /* next 1/2 word of dest. */
-#define PPPOE_FILTER_CMPH 10                    /* offset of 1/rd compare */
-        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 1),
-        BPF_STMT(BPF_RET+BPF_K, (u_int) -1),    /* keep packet */
-        BPF_STMT(BPF_RET+BPF_K, 0),             /* drop packet */
-    };
-
-    /* Fix the potentially varying parts */
-    bpfRun[1].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K;
-    bpfRun[1].jt   = 5;
-    bpfRun[1].jf   = 0;
-    bpfRun[1].k    = Eth_PPPOE_Session;
-
-    bpfRun[2].code = (u_short) BPF_JMP+BPF_JEQ+BPF_K;
-    bpfRun[2].jt   = 0;
-    bpfRun[2].jf   = 9;
-    bpfRun[2].k    = Eth_PPPOE_Discovery;
-
-    {
-      struct bpf_insn bpfInsn[sizeof(bpfRun) / sizeof(bpfRun[0])];
-      struct bpf_program bpfProgram;
-      memcpy(bpfInsn, bpfRun, sizeof(bpfRun));
-      bpfInsn[PPPOE_BCAST_CMPW].k = ((0xff << 24) | (0xff << 16) |
-                                     (0xff << 8) | 0xff);
-      bpfInsn[PPPOE_BCAST_CMPH].k = ((0xff << 8) | 0xff);
-      bpfInsn[PPPOE_FILTER_CMPW].k = ((hwaddr[0] << 24) | (hwaddr[1] << 16) |
-				      (hwaddr[2] << 8) | hwaddr[3]);
-      bpfInsn[PPPOE_FILTER_CMPH].k = ((hwaddr[4] << 8) | hwaddr[5]);
-      bpfProgram.bf_len = (sizeof(bpfInsn) / sizeof(bpfInsn[0]));
-      bpfProgram.bf_insns = &bpfInsn[0];
-      
-      /* Apply the filter */
-      if (ioctl(fd, BIOCSETF, &bpfProgram) < 0) {
-	fatalSys("ioctl(BIOCSETF)");
-      }
-    }
-}
-
-/**********************************************************************
-*%FUNCTION: openInterface
-*%ARGUMENTS:
-* ifname -- name of interface
-* type -- Ethernet frame type (0 for any frame type)
-* hwaddr -- if non-NULL, set to the hardware address
-*%RETURNS:
-* A file descriptor for talking with the Ethernet card.  Exits on error.
-* Note that the Linux version of this routine returns a socket instead.
-*%DESCRIPTION:
-* Opens a BPF on an interface for all PPPoE traffic (discovery and
-* session).  If 'type' is 0, uses promiscuous mode to watch any PPPoE
-* traffic on this network.
-***********************************************************************/
-int
-openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
-{
-    static int fd = -1;
-    char bpfName[32];
-    u_int optval;
-    struct bpf_version bpf_ver;
-    struct ifreq ifr;
-    int sock;
-    int i;
-
-    /* BSD only opens one socket for both Discovery and Session packets */
-    if (fd >= 0) {
-	return fd;
-    }
-
-    /* Find a free BPF device */
-    for (i = 0; i < 256; i++) {
-	sprintf(bpfName, "/dev/bpf%d", i);
-	if (((fd = open(bpfName, O_RDWR, 0)) >= 0) ||
-	    (errno != EBUSY)) {
-	    break;
-	}
-    }
-    if (fd < 0) {
-	switch (errno) {
-	case EACCES:		/* permission denied */
-	    {
-		char buffer[256];
-		sprintf(buffer, "Cannot open %.32s -- pppoe must be run as root.", bpfName);
-		rp_fatal(buffer);
-	    }
-	    break;
-	case EBUSY:
-	case ENOENT:		/* no such file */
-	    if (i == 0) {
-		rp_fatal("No /dev/bpf* devices (check your kernel configuration for BPF support)");
-	    } else {
-		rp_fatal("All /dev/bpf* devices are in use");
-	    }
-	    break;
-	}
-	fatalSys(bpfName);
-    }
-
-    if ((sock = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
-	fatalSys("socket");
-    }
-
-    /* Check that the interface is up */
-    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
-	fatalSys("ioctl(SIOCGIFFLAGS)");
-    }
-    if ((ifr.ifr_flags & IFF_UP) == 0) {
-	char buffer[256];
-	sprintf(buffer, "Interface %.16s is not up\n", ifname);
-	rp_fatal(buffer);
-    }
-
-    /* Fill in hardware address and initialize the packet filter rules */
-    if (hwaddr == NULL) {
-	rp_fatal("openInterface: no hwaddr arg.");
-    }
-    getHWaddr(sock, ifname, hwaddr);
-    initFilter(fd, type, hwaddr);
-
-    /* Sanity check on MTU -- apparently does not work on OpenBSD */
-#if !defined(__OpenBSD__)
-    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
-	fatalSys("ioctl(SIOCGIFMTU)");
-    }
-    if (ifr.ifr_mtu < ETH_DATA_LEN) {
-	char buffer[256];
-	sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d.  You may have serious connection problems.",
-		ifname, ifr.ifr_mtu, ETH_DATA_LEN);
-	printErr(buffer);
-    }
-#endif
-
-    /* done with the socket */
-    if (close(sock) < 0) {
-	fatalSys("close");
-    }
-
-    /* Check the BPF version number */
-    if (ioctl(fd, BIOCVERSION, &bpf_ver) < 0) {
-	fatalSys("ioctl(BIOCVERSION)");
-    }
-    if ((bpf_ver.bv_major != BPF_MAJOR_VERSION) ||
-        (bpf_ver.bv_minor < BPF_MINOR_VERSION)) {
-	char buffer[256];
-	sprintf(buffer, "Unsupported BPF version: %d.%d (kernel: %d.%d)", 
-			BPF_MAJOR_VERSION, BPF_MINOR_VERSION,
-			bpf_ver.bv_major, bpf_ver.bv_minor);
-	rp_fatal(buffer);
-    }
-
-    /* allocate a receive packet buffer */
-    if (ioctl(fd, BIOCGBLEN, &bpfLength) < 0) {
-	fatalSys("ioctl(BIOCGBLEN)");
-    }
-    if (!(bpfBuffer = (unsigned char *) malloc(bpfLength))) {
-	rp_fatal("malloc");
-    }
-
-    /* reads should return as soon as there is a packet available */
-    optval = 1;
-    if (ioctl(fd, BIOCIMMEDIATE, &optval) < 0) {
-	fatalSys("ioctl(BIOCIMMEDIATE)");
-    }
-
-    /* Bind the interface to the filter */
-    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    if (ioctl(fd, BIOCSETIF, &ifr) < 0) {
-	char buffer[256];
-	sprintf(buffer, "ioctl(BIOCSETIF) can't select interface %.16s",
-		ifname);
-	rp_fatal(buffer);
-    }
-
-    syslog(LOG_INFO, "Interface=%.16s HWaddr=%02X:%02X:%02X:%02X:%02X:%02X Device=%.32s Buffer size=%d",
-	   ifname, 
-	   hwaddr[0], hwaddr[1], hwaddr[2],
-	   hwaddr[3], hwaddr[4], hwaddr[5],
-	   bpfName, bpfLength);
-    return fd;
-}
-
-#endif /* USE_BPF */
-
-#ifdef USE_LINUX_PACKET
 /**********************************************************************
 *%FUNCTION: openInterface
 *%ARGUMENTS:
@@ -442,48 +119,46 @@
     if ((fd = socket(domain, stype, htons(type))) < 0) {
 	/* Give a more helpful message for the common error case */
 	if (errno == EPERM) {
-	    rp_fatal("Cannot create raw socket -- pppoe must be run as root.");
+	    fatal("Cannot create raw socket -- pppoe must be run as root.");
 	}
-	fatalSys("socket");
+	error("Can't open socket for pppoe: %m");
+	return -1;
     }
 
     if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
-	fatalSys("setsockopt");
+	error("Can't set socket options for pppoe: %m");
+	close(fd);
+	return -1;
     }
 
     /* Fill in hardware address */
     if (hwaddr) {
 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
 	if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
-	    fatalSys("ioctl(SIOCGIFHWADDR)");
+	    error("Can't get hardware address for %s: %m", ifname);
+	    close(fd);
+	    return -1;
 	}
 	memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
 #ifdef ARPHRD_ETHER
 	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-	    char buffer[256];
-	    sprintf(buffer, "Interface %.16s is not Ethernet", ifname);
-	    rp_fatal(buffer);
+	    warn("Interface %.16s is not Ethernet", ifname);
 	}
 #endif
 	if (NOT_UNICAST(hwaddr)) {
-	    char buffer[256];
-	    sprintf(buffer,
-		    "Interface %.16s has broadcast/multicast MAC address??",
-		    ifname);
-	    rp_fatal(buffer);
+	    fatal("Can't use interface %.16s: it has broadcast/multicast MAC address",
+		  ifname);
 	}
     }
 
     /* Sanity check on MTU */
     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
-	fatalSys("ioctl(SIOCGIFMTU)");
-    }
-    if (ifr.ifr_mtu < ETH_DATA_LEN) {
-	char buffer[256];
-	sprintf(buffer, "Interface %.16s has MTU of %d -- should be %d.  You may have serious connection problems.",
-		ifname, ifr.ifr_mtu, ETH_DATA_LEN);
-	printErr(buffer);
+	error("Can't get MTU for %s: %m", ifname);
+    } else if (ifr.ifr_mtu < ETH_DATA_LEN) {
+	error("Interface %.16s has MTU of %d -- should be at least %d.",
+	      ifname, ifr.ifr_mtu, ETH_DATA_LEN);
+	error("This may cause serious connection problems.");
     }
 
 #ifdef HAVE_STRUCT_SOCKADDR_LL
@@ -493,7 +168,9 @@
 
     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
-	fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index");
+	error("Could not get interface index for %s: %m", ifname);
+	close(fd);
+	return -1;
     }
     sa.sll_ifindex = ifr.ifr_ifindex;
 
@@ -503,13 +180,14 @@
 
     /* We're only interested in packets on specified interface */
     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
-	fatalSys("bind");
+	error("Failed to bind to interface %s: %m", ifname);
+	close(fd);
+	return -1;
     }
 
     return fd;
 }
 
-#endif /* USE_LINUX */
 
 /***********************************************************************
 *%FUNCTION: sendPacket
@@ -525,92 +203,25 @@
 int
 sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
 {
-#if defined(USE_BPF)
-    if (write(sock, pkt, size) < 0) {
-	sysErr("write (sendPacket)");
-	return -1;
-    }
-#elif defined(HAVE_STRUCT_SOCKADDR_LL)
-    if (send(sock, pkt, size, 0) < 0) {
-	sysErr("send (sendPacket)");
-	return -1;
-    }
-#else
-#ifdef USE_DLPI
+    int err;
 
-#define ABS(x)          ((x) < 0 ? -(x) : (x))
-
-	u_char  addr[MAXDLADDR];
-	u_char  phys[MAXDLADDR];
-	u_char  sap[MAXDLADDR];
-	u_char    xmitbuf[MAXDLBUF];
-	int	data_size;
-
-	short	tmp_sap;
-
-	tmp_sap = htons(pkt->ethHdr.h_proto); 
-	data_size = size - sizeof(struct ethhdr); 
-
-	memcpy((char *)phys, (char *)pkt->ethHdr.h_dest, ETHERADDRL);
-	memcpy((char *)sap,  (char *)&tmp_sap, sizeof(ushort_t));
-	memcpy((char *)xmitbuf, (char *)pkt + sizeof(struct ethhdr), data_size); 
-
-	if (dl_saplen > 0) {  /* order is sap+phys */
-		(void) memcpy((char*)addr, (char*)&sap, dl_abssaplen);
-		(void) memcpy((char*)addr+dl_abssaplen, (char*)phys, ETHERADDRL);
-	} else {        /* order is phys+sap */
-		(void) memcpy((char*)addr, (char*)phys, ETHERADDRL);
-		(void) memcpy((char*)addr+ETHERADDRL, (char*)&sap, dl_abssaplen);
-	}
-
-#ifdef DL_DEBUG
-	printf("%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x\n", 
-		addr[0],addr[1],addr[2],addr[3],addr[4],addr[5],
-		addr[6],addr[7]);
-#endif
-
-	dlunitdatareq(sock, addr, dl_addrlen, 0, 0, xmitbuf, data_size);
-
-
-
+    if (debug)
+	pppoe_log_packet("Send ", pkt);
+#if defined(HAVE_STRUCT_SOCKADDR_LL)
+    err = send(sock, pkt, size, 0);
 #else
     struct sockaddr sa;
 
-    if (!conn) {
-	rp_fatal("relay and server not supported on Linux 2.0 kernels");
-    }
     strcpy(sa.sa_data, conn->ifName);
-    if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) {
-	sysErr("sendto (sendPacket)");
+    err = sendto(sock, pkt, size, 0, &sa, sizeof(sa));
+#endif
+    if (err < 0) {
+	error("error sending pppoe packet: %m");
 	return -1;
     }
-#endif
-#endif
     return 0;
 }
 
-#ifdef USE_BPF
-/***********************************************************************
-*%FUNCTION: clearPacketHeader
-*%ARGUMENTS:
-* pkt -- packet that needs its head clearing
-*%RETURNS:
-* nothing
-*%DESCRIPTION:
-* Clears a PPPoE packet header after a truncated packet has been
-* received.  Insures that the packet will fail any integrity tests
-* and will be discarded by upper level routines.  Also resets the
-* bpfSize and bpfOffset variables to force a new read on the next
-* call to receivePacket().
-***********************************************************************/
-void
-clearPacketHeader(PPPoEPacket *pkt)
-{
-    bpfSize = bpfOffset = 0;
-    memset(pkt, 0, HDR_SIZE);
-}
-#endif
-
 /***********************************************************************
 *%FUNCTION: receivePacket
 *%ARGUMENTS:
@@ -625,473 +236,11 @@
 int
 receivePacket(int sock, PPPoEPacket *pkt, int *size)
 {
-#ifdef USE_BPF
-    struct bpf_hdr hdr;
-    int seglen, copylen;
-
-    if (bpfSize <= 0) {
-	bpfOffset = 0;
-	if ((bpfSize = read(sock, bpfBuffer, bpfLength)) < 0) {
-	    sysErr("read (receivePacket)");
-	    return -1;
-	}
-    }
-    if (bpfSize < sizeof(hdr)) {
-	syslog(LOG_ERR, "Truncated bpf packet header: len=%d", bpfSize);
-	clearPacketHeader(pkt);		/* resets bpfSize and bpfOffset */
-	return 0;
-    }
-    memcpy(&hdr, bpfBuffer + bpfOffset, sizeof(hdr));
-    if (hdr.bh_caplen != hdr.bh_datalen) {
-	syslog(LOG_ERR, "Truncated bpf packet: caplen=%d, datalen=%d",
-	       hdr.bh_caplen, hdr.bh_datalen);
-	clearPacketHeader(pkt);		/* resets bpfSize and bpfOffset */
-	return 0;
-    }
-    seglen = hdr.bh_hdrlen + hdr.bh_caplen;
-    if (seglen > bpfSize) {
-	syslog(LOG_ERR, "Truncated bpf packet: seglen=%d, bpfSize=%d",
-	       seglen, bpfSize);
-	clearPacketHeader(pkt);		/* resets bpfSize and bpfOffset */
-	return 0;
-    }
-    seglen = BPF_WORDALIGN(seglen);
-    *size = copylen = ((hdr.bh_caplen < sizeof(PPPoEPacket)) ?
-			hdr.bh_caplen : sizeof(PPPoEPacket));
-    memcpy(pkt, bpfBuffer + bpfOffset + hdr.bh_hdrlen, copylen);
-    if (seglen >= bpfSize) {
-	bpfSize = bpfOffset = 0;
-    } else {
-	bpfSize -= seglen;
-	bpfOffset += seglen;
-    }
-#else
-#ifdef USE_DLPI
-	struct strbuf data; 
-	int flags = 0; 	
-	int retval; 
-
-	data.buf = (char *) pkt; 
-	data.maxlen = MAXDLBUF; 
-	data.len = 0; 
-	
-	if ((retval = getmsg(sock, NULL, &data, &flags)) < 0) {
-	    sysErr("read (receivePacket)");
-	    return -1;
-	}
-
-	*size = data.len; 
-
-#else
     if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
-	sysErr("recv (receivePacket)");
+	error("error receiving pppoe packet: %m");
 	return -1;
     }
-#endif
-#endif
+    if (debug)
+	pppoe_log_packet("Recv ", pkt);
     return 0;
 }
-
-#ifdef USE_DLPI
-/**********************************************************************
-*%FUNCTION: openInterface
-*%ARGUMENTS:
-* ifname -- name of interface
-* type -- Ethernet frame type
-* hwaddr -- if non-NULL, set to the hardware address
-*%RETURNS:
-* A raw socket for talking to the Ethernet card.  Exits on error.
-*%DESCRIPTION:
-* Opens a raw Ethernet socket
-***********************************************************************/
-int
-openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
-{
-    int fd;
-    long buf[MAXDLBUF]; 
-
-	union   DL_primitives   *dlp;
-
-    char base_dev[PATH_MAX]; 
-    int ppa; 
-
-    if(strlen(ifname) > PATH_MAX) {
-	rp_fatal("socket: string to long"); 
-    }
-
-    ppa = atoi(&ifname[strlen(ifname)-1]);
-    strncpy(base_dev, ifname, PATH_MAX); 
-    base_dev[strlen(base_dev)-1] = '\0'; 
-
-/* rearranged order of DLPI code - delphys 20010803 */
-    dlp = (union DL_primitives*) buf;
-
-    if (( fd = open(base_dev, O_RDWR)) < 0) {
-	/* Give a more helpful message for the common error case */
-	if (errno == EPERM) {
-	    rp_fatal("Cannot create raw socket -- pppoe must be run as root.");
-	}
-	fatalSys("socket");
-    }
-
-/* rearranged order of DLPI code - delphys 20010803 */
-    dlattachreq(fd, ppa); 
-    dlokack(fd, (char *)buf);
-
-    dlbindreq(fd, type, 0, DL_CLDLS, 0, 0);
-    dlbindack(fd, (char *)buf);
-
-    dlinforeq(fd);
-    dlinfoack(fd, (char *)buf);
-
-    dl_abssaplen = ABS(dlp->info_ack.dl_sap_length);
-    dl_saplen = dlp->info_ack.dl_sap_length;
-    if (ETHERADDRL != (dlp->info_ack.dl_addr_length - dl_abssaplen))
-	fatalSys("invalid destination physical address length");
-    dl_addrlen = dl_abssaplen + ETHERADDRL;
-
-/* ethernet address retrieved as part of DL_INFO_ACK - delphys 20010803 */
-    memcpy(hwaddr, (u_char*)((char*)(dlp) + (int)(dlp->info_ack.dl_addr_offset)), ETHERADDRL);
-
-    if ( strioctl(fd, DLIOCRAW, -1, 0, NULL) < 0 ) { 
-	fatalSys("DLIOCRAW"); 
-    }
-
-    if (ioctl(fd, I_FLUSH, FLUSHR) < 0) fatalSys("I_FLUSH");
-
-    return fd;
-}
-
-/* cloned from dlcommon.c */
-
-void dlpromisconreq(int fd, u_long level)
-{
-        dl_promiscon_req_t      promiscon_req;
-        struct  strbuf  ctl;
-        int     flags;
-
-        promiscon_req.dl_primitive = DL_PROMISCON_REQ;
-        promiscon_req.dl_level = level;
-
-        ctl.maxlen = 0;
-        ctl.len = sizeof (promiscon_req);
-        ctl.buf = (char *) &promiscon_req;
-
-        flags = 0;
-
-        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
-                fatalSys("dlpromiscon:  putmsg");
-
-}
-
-void dlinforeq(int fd)
-{
-        dl_info_req_t   info_req;
-        struct  strbuf  ctl;
-        int     flags;
-
-        info_req.dl_primitive = DL_INFO_REQ;
-
-        ctl.maxlen = 0;
-        ctl.len = sizeof (info_req);
-        ctl.buf = (char *) &info_req;
-
-        flags = RS_HIPRI;
-
-        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
-                fatalSys("dlinforeq:  putmsg");
-}
-
-void dlunitdatareq(int fd, u_char *addrp, int addrlen, u_long minpri, u_long maxpri, u_char *datap, int datalen)
-{
-        long    buf[MAXDLBUF];
-        union   DL_primitives   *dlp;
-        struct  strbuf  data, ctl;
-
-        dlp = (union DL_primitives*) buf;
-
-        dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
-        dlp->unitdata_req.dl_dest_addr_length = addrlen;
-        dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
-        dlp->unitdata_req.dl_priority.dl_min = minpri;
-        dlp->unitdata_req.dl_priority.dl_max = maxpri;
-
-        (void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp, addrlen);
-
-        ctl.maxlen = 0;
-        ctl.len = sizeof (dl_unitdata_req_t) + addrlen;
-        ctl.buf = (char *) buf;
-
-        data.maxlen = 0;
-        data.len = datalen;
-        data.buf = (char *) datap;
-
-        if (putmsg(fd, &ctl, &data, 0) < 0)
-                fatalSys("dlunitdatareq:  putmsg");
-}
-
-void dlinfoack(int fd, char *bufp)
-{
-        union   DL_primitives   *dlp;
-        struct  strbuf  ctl;
-        int     flags;
-
-        ctl.maxlen = MAXDLBUF;
-        ctl.len = 0;
-        ctl.buf = bufp;
-
-        strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlinfoack");
-
-        dlp = (union DL_primitives *) ctl.buf;
-
-        expecting(DL_INFO_ACK, dlp);
-
-        if (ctl.len < sizeof (dl_info_ack_t)) {
-		char buffer[256];
-		sprintf(buffer, "dlinfoack:  response ctl.len too short:  %d", ctl.len); 
-                rp_fatal(buffer); 
-	}
-
-        if (flags != RS_HIPRI)
-                rp_fatal("dlinfoack:  DL_INFO_ACK was not M_PCPROTO");
-
-        if (ctl.len < sizeof (dl_info_ack_t)) {
-		char buffer[256];
-		sprintf(buffer, "dlinfoack:  short response ctl.len:  %d", ctl.len); 
-		rp_fatal(buffer); 
-	}
-}
-
-void dlbindreq(int fd, u_long sap, u_long max_conind, u_long service_mode, u_long conn_mgmt, u_long xidtest)
-{
-        dl_bind_req_t   bind_req;
-        struct  strbuf  ctl;
-        int     flags;
-
-        bind_req.dl_primitive = DL_BIND_REQ;
-        bind_req.dl_sap = sap;
-        bind_req.dl_max_conind = max_conind;
-        bind_req.dl_service_mode = service_mode;
-        bind_req.dl_conn_mgmt = conn_mgmt;
-        bind_req.dl_xidtest_flg = xidtest;
-
-        ctl.maxlen = 0;
-        ctl.len = sizeof (bind_req);
-        ctl.buf = (char *) &bind_req;
-
-        flags = 0;
-
-        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
-                fatalSys("dlbindreq:  putmsg");
-}
-
-void dlattachreq(int fd, u_long ppa)
-{
-        dl_attach_req_t attach_req;
-        struct  strbuf  ctl;
-        int     flags;
-
-        attach_req.dl_primitive = DL_ATTACH_REQ;
-        attach_req.dl_ppa = ppa;
-
-        ctl.maxlen = 0;
-        ctl.len = sizeof (attach_req);
-        ctl.buf = (char *) &attach_req;
-
-        flags = 0;
-
-        if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0)
-                fatalSys("dlattachreq:  putmsg");
-}
-
-void dlokack(int fd, char *bufp)
-{
-        union   DL_primitives   *dlp;
-        struct  strbuf  ctl;
-        int     flags;
-
-        ctl.maxlen = MAXDLBUF;
-        ctl.len = 0;
-        ctl.buf = bufp;
-
-        strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack");
-
-        dlp = (union DL_primitives *) ctl.buf;
-
-        expecting(DL_OK_ACK, dlp);
-
-        if (ctl.len < sizeof (dl_ok_ack_t)) { 
-		char buffer[256];
-		sprintf(buffer, "dlokack:  response ctl.len too short:  %d", ctl.len);
-		rp_fatal(buffer); 
-	}
-
-        if (flags != RS_HIPRI)
-                rp_fatal("dlokack:  DL_OK_ACK was not M_PCPROTO");
-
-        if (ctl.len < sizeof (dl_ok_ack_t)) {
-		char buffer[256]; 
-		sprintf(buffer, "dlokack:  short response ctl.len:  %d", ctl.len);
-		rp_fatal(buffer); 
-	}
-}
-
-void dlbindack(int fd, char *bufp)
-{
-        union   DL_primitives   *dlp;
-        struct  strbuf  ctl;
-        int     flags;
-
-        ctl.maxlen = MAXDLBUF;
-        ctl.len = 0;
-        ctl.buf = bufp;
-
-        strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack");
-
-        dlp = (union DL_primitives *) ctl.buf;
-
-        expecting(DL_BIND_ACK, dlp);
-
-        if (flags != RS_HIPRI)
-                rp_fatal("dlbindack:  DL_OK_ACK was not M_PCPROTO");
-
-        if (ctl.len < sizeof (dl_bind_ack_t)) {
-		char buffer[256];
-		sprintf(buffer, "dlbindack:  short response ctl.len:  %d", ctl.len);
-		rp_fatal(buffer); 
-	}
-}
-
-int strioctl(int fd, int cmd, int timout, int len, char *dp)
-{
-        struct  strioctl        sioc;
-        int     rc;
-
-        sioc.ic_cmd = cmd;
-        sioc.ic_timout = timout;
-        sioc.ic_len = len;
-        sioc.ic_dp = dp;
-        rc = ioctl(fd, I_STR, &sioc);
-
-        if (rc < 0)
-                return (rc);
-        else
-                return (sioc.ic_len);
-}
-
-void strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller)
-{
-        int     rc;
-        static  char    errmsg[80];
-
-        /*
-         * Start timer.
-         */
-        (void) signal(SIGALRM, sigalrm);
-        if (alarm(MAXWAIT) < 0) {
-                (void) sprintf(errmsg, "%s:  alarm", caller);
-                fatalSys(errmsg);
-        }
-
-        /*
-         * Set flags argument and issue getmsg().
-         */
-        *flagsp = 0;
-        if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
-                (void) sprintf(errmsg, "%s:  getmsg", caller);
-                fatalSys(errmsg);
-        }
-
-        /*
-         * Stop timer.
-         */
-        if (alarm(0) < 0) {
-                (void) sprintf(errmsg, "%s:  alarm", caller);
-                fatalSys(errmsg);
-        }
-
-        /*
-         * Check for MOREDATA and/or MORECTL.
-         */
-        if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) {
-		char buffer[256]; 
-		sprintf(buffer, "%s:  MORECTL|MOREDATA", caller);
-		rp_fatal(buffer);
-	}
-                
-        if (rc & MORECTL) {
-		char buffer[256];
-		sprintf(buffer, "%s:  MORECTL", caller);
-		rp_fatal(buffer); 
-	}
-        
-        if (rc & MOREDATA) {
-		char buffer[256]; 
-		sprintf(buffer, "%s:  MOREDATA", caller);
-		rp_fatal(buffer);
-	}
-
-        /*
-         * Check for at least sizeof (long) control data portion.
-         */
-        if (ctlp->len < sizeof (long)) {
-		char buffer[256]; 
-		sprintf(buffer, "getmsg:  control portion length < sizeof (long):  %d", ctlp->len);
-		rp_fatal(buffer); 
-	}
-}
-
-void sigalrm(int sig)
-{
-        (void) rp_fatal("sigalrm:  TIMEOUT");
-}
-
-void expecting(int prim, union DL_primitives *dlp)
-{
-        if (dlp->dl_primitive != (u_long)prim) {
-		char buffer[256]; 
-		sprintf(buffer, "expected %s got %s", dlprim(prim), dlprim(dlp->dl_primitive));
-		rp_fatal(buffer); 
-		exit(1); 
-	}
-}
-
-char *dlprim(u_long prim)
-{
-        static  char    primbuf[80];
-
-        switch ((int)prim) {
-                CASERET(DL_INFO_REQ);
-                CASERET(DL_INFO_ACK);
-                CASERET(DL_ATTACH_REQ);
-                CASERET(DL_DETACH_REQ);
-                CASERET(DL_BIND_REQ);
-                CASERET(DL_BIND_ACK);
-                CASERET(DL_UNBIND_REQ);
-                CASERET(DL_OK_ACK);
-                CASERET(DL_ERROR_ACK);
-                CASERET(DL_SUBS_BIND_REQ);
-                CASERET(DL_SUBS_BIND_ACK);
-                CASERET(DL_UNITDATA_REQ);
-                CASERET(DL_UNITDATA_IND);
-                CASERET(DL_UDERROR_IND);
-                CASERET(DL_UDQOS_REQ);
-                CASERET(DL_CONNECT_REQ);
-                CASERET(DL_CONNECT_IND);
-                CASERET(DL_CONNECT_RES);
-                CASERET(DL_CONNECT_CON);
-                CASERET(DL_TOKEN_REQ);
-                CASERET(DL_TOKEN_ACK);
-                CASERET(DL_DISCONNECT_REQ);
-                CASERET(DL_DISCONNECT_IND);
-                CASERET(DL_RESET_REQ);
-                CASERET(DL_RESET_IND);
-                CASERET(DL_RESET_RES);
-                CASERET(DL_RESET_CON);
-                default:
-                        (void) sprintf(primbuf, "unknown primitive 0x%lx", prim);
-                        return (primbuf);
-        }
-}
-
-#endif /* USE_DLPI */
diff --git a/pppd/plugins/rp-pppoe/plugin.c b/pppd/plugins/rp-pppoe/plugin.c
index cd93bfa..a8c2bb4 100644
--- a/pppd/plugins/rp-pppoe/plugin.c
+++ b/pppd/plugins/rp-pppoe/plugin.c
@@ -19,10 +19,11 @@
 * 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.
+*
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: plugin.c,v 1.12 2004/11/04 10:07:37 paulus Exp $";
+"$Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp $";
 
 #define _GNU_SOURCE 1
 #include "pppoe.h"
@@ -32,10 +33,9 @@
 #include "pppd/lcp.h"
 #include "pppd/ipcp.h"
 #include "pppd/ccp.h"
-#include "pppd/pathnames.h"
+/* #include "pppd/pathnames.h" */
 
 #include <linux/types.h>
-#include <syslog.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -48,9 +48,12 @@
 #include <signal.h>
 #include <net/ethernet.h>
 #include <net/if_arp.h>
-#include "ppp_defs.h"
-#include "if_ppp.h"
-#include "if_pppox.h"
+#include <linux/ppp_defs.h>
+#include <linux/if_pppox.h>
+
+#ifndef _ROOT_PATH
+#define _ROOT_PATH ""
+#endif
 
 #define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
 
@@ -63,6 +66,8 @@
 static char *acName = NULL;
 static char *existingSession = NULL;
 static int printACNames = 0;
+static char *pppoe_reqd_mac = NULL;
+unsigned char pppoe_reqd_mac_addr[6];
 
 static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
 static option_t Options[] = {
@@ -78,9 +83,11 @@
       "Attach to existing session (sessid:macaddr)" },
     { "rp_pppoe_verbose", o_int, &printACNames,
       "Be verbose about discovered access concentrators"},
+    { "pppoe-mac", o_string, &pppoe_reqd_mac,
+      "Only connect to specified MAC address" },
     { NULL }
 };
-
+int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL;
 static PPPoEConnection *conn = NULL;
 
 /**********************************************************************
@@ -97,20 +104,15 @@
 {
     conn = malloc(sizeof(PPPoEConnection));
     if (!conn) {
-	fatal("Could not allocate memory for PPPoE session");
+	novm("PPPoE session data");
     }
     memset(conn, 0, sizeof(PPPoEConnection));
-    if (acName) {
-	SET_STRING(conn->acName, acName);
-    }
-    if (pppd_pppoe_service) {
-	SET_STRING(conn->serviceName, pppd_pppoe_service);
-    }
-    SET_STRING(conn->ifName, devnam);
+    conn->ifName = devnam;
     conn->discoverySocket = -1;
     conn->sessionSocket = -1;
     conn->useHostUniq = 1;
     conn->printACNames = printACNames;
+    conn->discoveryTimeout = PADI_TIMEOUT;
     return 1;
 }
 
@@ -127,7 +129,45 @@
 PPPOEConnectDevice(void)
 {
     struct sockaddr_pppox sp;
+    struct ifreq ifr;
+    int s;
 
+    /* Open session socket before discovery phase, to avoid losing session */
+    /* packets sent by peer just after PADS packet (noted on some Cisco    */
+    /* server equipment).                                                  */
+    /* Opening this socket just before waitForPADS in the discovery()      */
+    /* function would be more appropriate, but it would mess-up the code   */
+    conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
+    if (conn->sessionSocket < 0) {
+	error("Failed to create PPPoE socket: %m");
+	return -1;
+    }
+
+    /* Restore configuration */
+    lcp_allowoptions[0].mru = conn->mtu;
+    lcp_wantoptions[0].mru = conn->mru;
+
+    /* Update maximum MRU */
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0) {
+	error("Can't get MTU for %s: %m", conn->ifName);
+	goto errout;
+    }
+    strncpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name));
+    if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {
+	error("Can't get MTU for %s: %m", conn->ifName);
+	close(s);
+	goto errout;
+    }
+    close(s);
+
+    if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD)
+	lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
+    if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD)
+	lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
+
+    conn->acName = acName;
+    conn->serviceName = pppd_pppoe_service;
     strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
     if (existingSession) {
 	unsigned int mac[ETH_ALEN];
@@ -142,21 +182,18 @@
 	    conn->peerEth[i] = (unsigned char) mac[i];
 	}
     } else {
+	conn->discoverySocket =
+            openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
 	discovery(conn);
 	if (conn->discoveryState != STATE_SESSION) {
 	    error("Unable to complete PPPoE Discovery");
-	    return -1;
+	    goto errout;
 	}
     }
 
     /* Set PPPoE session-number for further consumption */
     ppp_session_number = ntohs(conn->session);
 
-    /* Make the session socket */
-    conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
-    if (conn->sessionSocket < 0) {
-	fatal("Failed to create PPPoE socket: %m");
-    }
     sp.sa_family = AF_PPPOX;
     sp.sa_protocol = PX_PROTO_OE;
     sp.sa_addr.pppoe.sid = conn->session;
@@ -172,51 +209,45 @@
 	    (unsigned) conn->peerEth[4],
 	    (unsigned) conn->peerEth[5]);
 
+    warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s",
+	 (unsigned) conn->peerEth[0],
+	 (unsigned) conn->peerEth[1],
+	 (unsigned) conn->peerEth[2],
+	 (unsigned) conn->peerEth[3],
+	 (unsigned) conn->peerEth[4],
+	 (unsigned) conn->peerEth[5],
+	 conn->ifName);
+
+    script_setenv("MACREMOTE", remote_number, 0);
+
     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
 		sizeof(struct sockaddr_pppox)) < 0) {
-	fatal("Failed to connect PPPoE socket: %d %m", errno);
-	return -1;
+	error("Failed to connect PPPoE socket: %d %m", errno);
+	goto errout;
     }
 
     return conn->sessionSocket;
+
+ errout:
+    if (conn->discoverySocket >= 0) {
+	sendPADT(conn, NULL);
+	close(conn->discoverySocket);
+	conn->discoverySocket = -1;
+    }
+    close(conn->sessionSocket);
+    return -1;
 }
 
 static void
-PPPOESendConfig(int mtu,
-		u_int32_t asyncmap,
-		int pcomp,
-		int accomp)
-{
-    int sock;
-    struct ifreq ifr;
-
-    if (mtu > MAX_PPPOE_MTU) {
-	warn("Couldn't increase MTU to %d", mtu);
-	mtu = MAX_PPPOE_MTU;
-    }
-    sock = socket(AF_INET, SOCK_DGRAM, 0);
-    if (sock < 0) {
-	error("Couldn't create IP socket: %m");
-	return;
-    }
-    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    ifr.ifr_mtu = mtu;
-    if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
-	error("Couldn't set interface MTU to %d: %m", mtu);
-	return;
-    }
-    (void) close (sock);
-}
-
-
-static void
 PPPOERecvConfig(int mru,
 		u_int32_t asyncmap,
 		int pcomp,
 		int accomp)
 {
+#if 0 /* broken protocol, but no point harrassing the users I guess... */
     if (mru > MAX_PPPOE_MTU)
 	warn("Couldn't increase MRU to %d", mru);
+#endif
 }
 
 /**********************************************************************
@@ -239,21 +270,20 @@
     memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
     memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
-		sizeof(struct sockaddr_pppox)) < 0) {
-	fatal("Failed to disconnect PPPoE socket: %d %m", errno);
-	return;
-    }
+		sizeof(struct sockaddr_pppox)) < 0)
+	error("Failed to disconnect PPPoE socket: %d %m", errno);
     close(conn->sessionSocket);
     /* don't send PADT?? */
-    close(conn->discoverySocket);
+    if (conn->discoverySocket >= 0)
+	close(conn->discoverySocket);
 }
 
 static void
 PPPOEDeviceOptions(void)
 {
     char buf[256];
-    snprintf(buf, 256, _PATH_ETHOPT "%s",devnam);
-    if(!options_from_file(buf, 0, 0, 1))
+    snprintf(buf, 256, _PATH_ETHOPT "%s", devnam);
+    if (!options_from_file(buf, 0, 0, 1))
 	exit(EXIT_OPTION_ERROR);
 
 }
@@ -279,16 +309,14 @@
     int fd;
     struct ifreq ifr;
 
-    /* Only do it if name is "ethXXX", "nasXXX", "tapXXX" or "nic-XXXX.
-       In latter case strip off the "nic-" */
-    /* Thanks to Russ Couturier for this fix */
+    /*
+     * Take any otherwise-unrecognized option as a possible device name,
+     * and test if it is the name of a network interface with a
+     * hardware address whose sa_family is ARPHRD_ETHER.
+     */
     if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) {
 	/* Strip off "nic-" */
 	cmd += 4;
-    } else if (strlen(cmd) < 4
-	       || (strncmp(cmd, "eth", 3) && strncmp(cmd, "nas", 3)
-		   && strncmp(cmd, "tap", 3) && strncmp(cmd, "br", 2))) {
-	return 0;
     }
 
     /* Open a socket */
@@ -306,8 +334,9 @@
 		r = 0;
 	    } else {
 		if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-		    error("Interface %s not Ethernet", cmd);
-		    r=0;
+		    if (doit)
+			error("Interface %s not Ethernet", cmd);
+		    r = 0;
 		}
 	    }
 	}
@@ -315,31 +344,13 @@
 
     /* Close socket */
     close(fd);
-    if (r) {
+    if (r && doit) {
 	strncpy(devnam, cmd, sizeof(devnam));
 	if (the_channel != &pppoe_channel) {
 
 	    the_channel = &pppoe_channel;
 	    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;
-
 	    PPPOEInitDevice();
 	}
 	return 1;
@@ -370,71 +381,61 @@
 	 RP_VERSION, VERSION);
 }
 
-/**********************************************************************
-*%FUNCTION: fatalSys
-*%ARGUMENTS:
-* str -- error message
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Prints a message plus the errno value to stderr and syslog and exits.
-***********************************************************************/
-void
-fatalSys(char const *str)
+void pppoe_check_options(void)
 {
-    char buf[1024];
-    int i = errno;
-    sprintf(buf, "%.256s: %.256s", str, strerror(i));
-    printErr(buf);
-    sprintf(buf, "RP-PPPoE: %.256s: %.256s", str, strerror(i));
-    sendPADT(conn, buf);
-    exit(1);
-}
+    unsigned int mac[6];
+    int i;
 
-/**********************************************************************
-*%FUNCTION: rp_fatal
-*%ARGUMENTS:
-* str -- error message
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Prints a message to stderr and syslog and exits.
-***********************************************************************/
-void
-rp_fatal(char const *str)
-{
-    char buf[1024];
-    printErr(str);
-    sprintf(buf, "RP-PPPoE: %.256s", str);
-    sendPADT(conn, buf);
-    exit(1);
-}
-/**********************************************************************
-*%FUNCTION: sysErr
-*%ARGUMENTS:
-* str -- error message
-*%RETURNS:
-* Nothing
-*%DESCRIPTION:
-* Prints a message plus the errno value to syslog.
-***********************************************************************/
-void
-sysErr(char const *str)
-{
-    rp_fatal(str);
-}
+    if (pppoe_reqd_mac != NULL) {
+	if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x",
+		   &mac[0], &mac[1], &mac[2], &mac[3],
+		   &mac[4], &mac[5]) != 6) {
+	    option_error("cannot parse pppoe-mac option value");
+	    exit(EXIT_OPTION_ERROR);
+	}
+	for (i = 0; i < 6; ++i)
+	    conn->req_peer_mac[i] = mac[i];
+	conn->req_peer = 1;
+    }
 
+    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;
+
+    if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU)
+	lcp_allowoptions[0].mru = MAX_PPPOE_MTU;
+    if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU)
+	lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
+
+    /* Save configuration */
+    conn->mtu = lcp_allowoptions[0].mru;
+    conn->mru = lcp_wantoptions[0].mru;
+
+    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;
+}
 
 struct channel pppoe_channel = {
-    options: Options,
-    process_extra_options: &PPPOEDeviceOptions,
-    check_options: NULL,
-    connect: &PPPOEConnectDevice,
-    disconnect: &PPPOEDisconnectDevice,
-    establish_ppp: &generic_establish_ppp,
-    disestablish_ppp: &generic_disestablish_ppp,
-    send_config: &PPPOESendConfig,
-    recv_config: &PPPOERecvConfig,
-    close: NULL,
-    cleanup: NULL
+    .options = Options,
+    .process_extra_options = &PPPOEDeviceOptions,
+    .check_options = pppoe_check_options,
+    .connect = &PPPOEConnectDevice,
+    .disconnect = &PPPOEDisconnectDevice,
+    .establish_ppp = &generic_establish_ppp,
+    .disestablish_ppp = &generic_disestablish_ppp,
+    .send_config = NULL,
+    .recv_config = &PPPOERecvConfig,
+    .close = NULL,
+    .cleanup = NULL
 };
diff --git a/pppd/plugins/rp-pppoe/pppoe-discovery.c b/pppd/plugins/rp-pppoe/pppoe-discovery.c
index bfb8706..3d3bf4e 100644
--- a/pppd/plugins/rp-pppoe/pppoe-discovery.c
+++ b/pppd/plugins/rp-pppoe/pppoe-discovery.c
@@ -17,6 +17,36 @@
 
 #include "pppoe.h"
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_NETPACKET_PACKET_H
+#include <netpacket/packet.h>
+#elif defined(HAVE_LINUX_IF_PACKET_H)
+#include <linux/if_packet.h>
+#endif
+
+#ifdef HAVE_NET_ETHERNET_H
+#include <net/ethernet.h>
+#endif
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_NET_IF_ARP_H
+#include <net/if_arp.h>
+#endif
+
 char *xstrdup(const char *s);
 void usage(void);
 
@@ -25,6 +55,584 @@
 	exit(status);
 }
 
+/* Initialize frame types to RFC 2516 values.  Some broken peers apparently
+   use different frame types... sigh... */
+
+UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY;
+UINT16_t Eth_PPPOE_Session   = ETH_PPPOE_SESSION;
+
+/**********************************************************************
+*%FUNCTION: etherType
+*%ARGUMENTS:
+* packet -- a received PPPoE packet
+*%RETURNS:
+* ethernet packet type (see /usr/include/net/ethertypes.h)
+*%DESCRIPTION:
+* Checks the ethernet packet header to determine its type.
+* We should only be receveing DISCOVERY and SESSION types if the BPF
+* is set up correctly.  Logs an error if an unexpected type is received.
+* Note that the ethernet type names come from "pppoe.h" and the packet
+* packet structure names use the LINUX dialect to maintain consistency
+* with the rest of this file.  See the BSD section of "pppoe.h" for
+* translations of the data structure names.
+***********************************************************************/
+UINT16_t
+etherType(PPPoEPacket *packet)
+{
+    UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto);
+    if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) {
+	fprintf(stderr, "Invalid ether type 0x%x\n", type);
+    }
+    return type;
+}
+
+/**********************************************************************
+*%FUNCTION: openInterface
+*%ARGUMENTS:
+* ifname -- name of interface
+* type -- Ethernet frame type
+* hwaddr -- if non-NULL, set to the hardware address
+*%RETURNS:
+* A raw socket for talking to the Ethernet card.  Exits on error.
+*%DESCRIPTION:
+* Opens a raw Ethernet socket
+***********************************************************************/
+int
+openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr)
+{
+    int optval=1;
+    int fd;
+    struct ifreq ifr;
+    int domain, stype;
+
+#ifdef HAVE_STRUCT_SOCKADDR_LL
+    struct sockaddr_ll sa;
+#else
+    struct sockaddr sa;
+#endif
+
+    memset(&sa, 0, sizeof(sa));
+
+#ifdef HAVE_STRUCT_SOCKADDR_LL
+    domain = PF_PACKET;
+    stype = SOCK_RAW;
+#else
+    domain = PF_INET;
+    stype = SOCK_PACKET;
+#endif
+
+    if ((fd = socket(domain, stype, htons(type))) < 0) {
+	/* Give a more helpful message for the common error case */
+	if (errno == EPERM) {
+	    rp_fatal("Cannot create raw socket -- pppoe must be run as root.");
+	}
+	fatalSys("socket");
+    }
+
+    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) {
+	fatalSys("setsockopt");
+    }
+
+    /* Fill in hardware address */
+    if (hwaddr) {
+	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+	    fatalSys("ioctl(SIOCGIFHWADDR)");
+	}
+	memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+#ifdef ARPHRD_ETHER
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+	    char buffer[256];
+	    sprintf(buffer, "Interface %.16s is not Ethernet", ifname);
+	    rp_fatal(buffer);
+	}
+#endif
+	if (NOT_UNICAST(hwaddr)) {
+	    char buffer[256];
+	    sprintf(buffer,
+		    "Interface %.16s has broadcast/multicast MAC address??",
+		    ifname);
+	    rp_fatal(buffer);
+	}
+    }
+
+    /* Sanity check on MTU */
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
+	fatalSys("ioctl(SIOCGIFMTU)");
+    }
+    if (ifr.ifr_mtu < ETH_DATA_LEN) {
+	fprintf(stderr, "Interface %.16s has MTU of %d -- should be %d.\n",
+	      ifname, ifr.ifr_mtu, ETH_DATA_LEN);
+	fprintf(stderr, "You may have serious connection problems.\n");
+    }
+
+#ifdef HAVE_STRUCT_SOCKADDR_LL
+    /* Get interface index */
+    sa.sll_family = AF_PACKET;
+    sa.sll_protocol = htons(type);
+
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
+	fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index");
+    }
+    sa.sll_ifindex = ifr.ifr_ifindex;
+
+#else
+    strcpy(sa.sa_data, ifname);
+#endif
+
+    /* We're only interested in packets on specified interface */
+    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+	fatalSys("bind");
+    }
+
+    return fd;
+}
+
+
+/***********************************************************************
+*%FUNCTION: sendPacket
+*%ARGUMENTS:
+* sock -- socket to send to
+* pkt -- the packet to transmit
+* size -- size of packet (in bytes)
+*%RETURNS:
+* 0 on success; -1 on failure
+*%DESCRIPTION:
+* Transmits a packet
+***********************************************************************/
+int
+sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size)
+{
+#if defined(HAVE_STRUCT_SOCKADDR_LL)
+    if (send(sock, pkt, size, 0) < 0) {
+	sysErr("send (sendPacket)");
+	return -1;
+    }
+#else
+    struct sockaddr sa;
+
+    if (!conn) {
+	rp_fatal("relay and server not supported on Linux 2.0 kernels");
+    }
+    strcpy(sa.sa_data, conn->ifName);
+    if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) {
+	sysErr("sendto (sendPacket)");
+	return -1;
+    }
+#endif
+    return 0;
+}
+
+/***********************************************************************
+*%FUNCTION: receivePacket
+*%ARGUMENTS:
+* sock -- socket to read from
+* pkt -- place to store the received packet
+* size -- set to size of packet in bytes
+*%RETURNS:
+* >= 0 if all OK; < 0 if error
+*%DESCRIPTION:
+* Receives a packet
+***********************************************************************/
+int
+receivePacket(int sock, PPPoEPacket *pkt, int *size)
+{
+    if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) {
+	sysErr("recv (receivePacket)");
+	return -1;
+    }
+    return 0;
+}
+
+/**********************************************************************
+*%FUNCTION: parsePacket
+*%ARGUMENTS:
+* packet -- the PPPoE discovery packet to parse
+* func -- function called for each tag in the packet
+* extra -- an opaque data pointer supplied to parsing function
+*%RETURNS:
+* 0 if everything went well; -1 if there was an error
+*%DESCRIPTION:
+* Parses a PPPoE discovery packet, calling "func" for each tag in the packet.
+* "func" is passed the additional argument "extra".
+***********************************************************************/
+int
+parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra)
+{
+    UINT16_t len = ntohs(packet->length);
+    unsigned char *curTag;
+    UINT16_t tagType, tagLen;
+
+    if (PPPOE_VER(packet->vertype) != 1) {
+	fprintf(stderr, "Invalid PPPoE version (%d)\n",
+		PPPOE_VER(packet->vertype));
+	return -1;
+    }
+    if (PPPOE_TYPE(packet->vertype) != 1) {
+	fprintf(stderr, "Invalid PPPoE type (%d)\n",
+		PPPOE_TYPE(packet->vertype));
+	return -1;
+    }
+
+    /* Do some sanity checks on packet */
+    if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */
+	fprintf(stderr, "Invalid PPPoE packet length (%u)\n", len);
+	return -1;
+    }
+
+    /* Step through the tags */
+    curTag = packet->payload;
+    while(curTag - packet->payload < len) {
+	/* Alignment is not guaranteed, so do this by hand... */
+	tagType = (curTag[0] << 8) + curTag[1];
+	tagLen = (curTag[2] << 8) + curTag[3];
+	if (tagType == TAG_END_OF_LIST) {
+	    return 0;
+	}
+	if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
+	    fprintf(stderr, "Invalid PPPoE tag length (%u)\n", tagLen);
+	    return -1;
+	}
+	func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra);
+	curTag = curTag + TAG_HDR_SIZE + tagLen;
+    }
+    return 0;
+}
+
+/**********************************************************************
+*%FUNCTION: parseForHostUniq
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data.
+* extra -- user-supplied pointer.  This is assumed to be a pointer to int.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* If a HostUnique tag is found which matches our PID, sets *extra to 1.
+***********************************************************************/
+void
+parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
+		 void *extra)
+{
+    int *val = (int *) extra;
+    if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) {
+	pid_t tmp;
+	memcpy(&tmp, data, len);
+	if (tmp == getpid()) {
+	    *val = 1;
+	}
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: packetIsForMe
+*%ARGUMENTS:
+* conn -- PPPoE connection info
+* packet -- a received PPPoE packet
+*%RETURNS:
+* 1 if packet is for this PPPoE daemon; 0 otherwise.
+*%DESCRIPTION:
+* If we are using the Host-Unique tag, verifies that packet contains
+* our unique identifier.
+***********************************************************************/
+int
+packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
+{
+    int forMe = 0;
+
+    /* If packet is not directed to our MAC address, forget it */
+    if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0;
+
+    /* If we're not using the Host-Unique tag, then accept the packet */
+    if (!conn->useHostUniq) return 1;
+
+    parsePacket(packet, parseForHostUniq, &forMe);
+    return forMe;
+}
+
+/**********************************************************************
+*%FUNCTION: parsePADOTags
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data
+* extra -- extra user data.  Should point to a PacketCriteria structure
+*          which gets filled in according to selected AC name and service
+*          name.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Picks interesting tags out of a PADO packet
+***********************************************************************/
+void
+parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
+	      void *extra)
+{
+    struct PacketCriteria *pc = (struct PacketCriteria *) extra;
+    PPPoEConnection *conn = pc->conn;
+    int i;
+
+    switch(type) {
+    case TAG_AC_NAME:
+	pc->seenACName = 1;
+	printf("Access-Concentrator: %.*s\n", (int) len, data);
+	if (conn->acName && len == strlen(conn->acName) &&
+	    !strncmp((char *) data, conn->acName, len)) {
+	    pc->acNameOK = 1;
+	}
+	break;
+    case TAG_SERVICE_NAME:
+	pc->seenServiceName = 1;
+	if (len > 0) {
+	    printf("       Service-Name: %.*s\n", (int) len, data);
+	}
+	if (conn->serviceName && len == strlen(conn->serviceName) &&
+	    !strncmp((char *) data, conn->serviceName, len)) {
+	    pc->serviceNameOK = 1;
+	}
+	break;
+    case TAG_AC_COOKIE:
+	printf("Got a cookie:");
+	/* Print first 20 bytes of cookie */
+	for (i=0; i<len && i < 20; i++) {
+	    printf(" %02x", (unsigned) data[i]);
+	}
+	if (i < len) printf("...");
+	printf("\n");
+	conn->cookie.type = htons(type);
+	conn->cookie.length = htons(len);
+	memcpy(conn->cookie.payload, data, len);
+	break;
+    case TAG_RELAY_SESSION_ID:
+	printf("Got a Relay-ID:");
+	/* Print first 20 bytes of relay ID */
+	for (i=0; i<len && i < 20; i++) {
+	    printf(" %02x", (unsigned) data[i]);
+	}
+	if (i < len) printf("...");
+	printf("\n");
+	conn->relayId.type = htons(type);
+	conn->relayId.length = htons(len);
+	memcpy(conn->relayId.payload, data, len);
+	break;
+    case TAG_SERVICE_NAME_ERROR:
+	printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data);
+	break;
+    case TAG_AC_SYSTEM_ERROR:
+	printf("Got a System-Error tag: %.*s\n", (int) len, data);
+	break;
+    case TAG_GENERIC_ERROR:
+	printf("Got a Generic-Error tag: %.*s\n", (int) len, data);
+	break;
+    }
+}
+
+/***********************************************************************
+*%FUNCTION: sendPADI
+*%ARGUMENTS:
+* conn -- PPPoEConnection structure
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADI packet
+***********************************************************************/
+void
+sendPADI(PPPoEConnection *conn)
+{
+    PPPoEPacket packet;
+    unsigned char *cursor = packet.payload;
+    PPPoETag *svc = (PPPoETag *) (&packet.payload);
+    UINT16_t namelen = 0;
+    UINT16_t plen;
+
+    if (conn->serviceName) {
+	namelen = (UINT16_t) strlen(conn->serviceName);
+    }
+    plen = TAG_HDR_SIZE + namelen;
+    CHECK_ROOM(cursor, packet.payload, plen);
+
+    /* Set destination to Ethernet broadcast address */
+    memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
+    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
+
+    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    packet.vertype = PPPOE_VER_TYPE(1, 1);
+    packet.code = CODE_PADI;
+    packet.session = 0;
+
+    svc->type = TAG_SERVICE_NAME;
+    svc->length = htons(namelen);
+    CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE);
+
+    if (conn->serviceName) {
+	memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName));
+    }
+    cursor += namelen + TAG_HDR_SIZE;
+
+    /* If we're using Host-Uniq, copy it over */
+    if (conn->useHostUniq) {
+	PPPoETag hostUniq;
+	pid_t pid = getpid();
+	hostUniq.type = htons(TAG_HOST_UNIQ);
+	hostUniq.length = htons(sizeof(pid));
+	memcpy(hostUniq.payload, &pid, sizeof(pid));
+	CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE);
+	memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
+	cursor += sizeof(pid) + TAG_HDR_SIZE;
+	plen += sizeof(pid) + TAG_HDR_SIZE;
+    }
+
+    packet.length = htons(plen);
+
+    sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
+    if (conn->debugFile) {
+	dumpPacket(conn->debugFile, &packet, "SENT");
+	fprintf(conn->debugFile, "\n");
+	fflush(conn->debugFile);
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: waitForPADO
+*%ARGUMENTS:
+* conn -- PPPoEConnection structure
+* timeout -- how long to wait (in seconds)
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Waits for a PADO packet and copies useful information
+***********************************************************************/
+void
+waitForPADO(PPPoEConnection *conn, int timeout)
+{
+    fd_set readable;
+    int r;
+    struct timeval tv;
+    PPPoEPacket packet;
+    int len;
+
+    struct PacketCriteria pc;
+    pc.conn          = conn;
+    pc.acNameOK      = (conn->acName)      ? 0 : 1;
+    pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
+    pc.seenACName    = 0;
+    pc.seenServiceName = 0;
+    conn->error = 0;
+	
+    do {
+	if (BPF_BUFFER_IS_EMPTY) {
+	    tv.tv_sec = timeout;
+	    tv.tv_usec = 0;
+	
+	    FD_ZERO(&readable);
+	    FD_SET(conn->discoverySocket, &readable);
+
+	    while(1) {
+		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
+		if (r >= 0 || errno != EINTR) break;
+	    }
+	    if (r < 0) {
+		perror("select (waitForPADO)");
+		return;
+	    }
+	    if (r == 0) return;        /* Timed out */
+	}
+	
+	/* Get the packet */
+	receivePacket(conn->discoverySocket, &packet, &len);
+
+	/* Check length */
+	if (ntohs(packet.length) + HDR_SIZE > len) {
+	    fprintf(stderr, "Bogus PPPoE length field (%u)\n",
+		   (unsigned int) ntohs(packet.length));
+	    continue;
+	}
+
+#ifdef USE_BPF
+	/* If it's not a Discovery packet, loop again */
+	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
+#endif
+
+	if (conn->debugFile) {
+	    dumpPacket(conn->debugFile, &packet, "RCVD");
+	    fprintf(conn->debugFile, "\n");
+	    fflush(conn->debugFile);
+	}
+	/* If it's not for us, loop again */
+	if (!packetIsForMe(conn, &packet)) continue;
+
+	if (packet.code == CODE_PADO) {
+	    if (BROADCAST(packet.ethHdr.h_source)) {
+		fprintf(stderr, "Ignoring PADO packet from broadcast MAC address\n");
+		continue;
+	    }
+	    parsePacket(&packet, parsePADOTags, &pc);
+	    if (conn->error)
+		return;
+	    if (!pc.seenACName) {
+		fprintf(stderr, "Ignoring PADO packet with no AC-Name tag\n");
+		continue;
+	    }
+	    if (!pc.seenServiceName) {
+		fprintf(stderr, "Ignoring PADO packet with no Service-Name tag\n");
+		continue;
+	    }
+	    conn->numPADOs++;
+	    printf("--------------------------------------------------\n");
+	    if (pc.acNameOK && pc.serviceNameOK) {
+		memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
+		if (conn->printACNames) {
+		    printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+			   (unsigned) conn->peerEth[0], 
+			   (unsigned) conn->peerEth[1],
+			   (unsigned) conn->peerEth[2],
+			   (unsigned) conn->peerEth[3],
+			   (unsigned) conn->peerEth[4],
+			   (unsigned) conn->peerEth[5]);
+		    continue;
+		}
+		conn->discoveryState = STATE_RECEIVED_PADO;
+		break;
+	    }
+	}
+    } while (conn->discoveryState != STATE_RECEIVED_PADO);
+}
+
+/**********************************************************************
+*%FUNCTION: discovery
+*%ARGUMENTS:
+* conn -- PPPoE connection info structure
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Performs the PPPoE discovery phase
+***********************************************************************/
+void
+discovery(PPPoEConnection *conn)
+{
+    int padiAttempts = 0;
+    int timeout = PADI_TIMEOUT;
+
+    conn->discoverySocket =
+	openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
+
+    do {
+	padiAttempts++;
+	if (padiAttempts > MAX_PADI_ATTEMPTS) {
+	    fprintf(stderr, "Timeout waiting for PADO packets\n");
+	    close(conn->discoverySocket);
+	    conn->discoverySocket = -1;
+	    return;
+	}
+	sendPADI(conn);
+	conn->discoveryState = STATE_SENT_PADI;
+	waitForPADO(conn, timeout);
+    } while (!conn->numPADOs);
+}
+
 int main(int argc, char *argv[])
 {
     int opt;
@@ -54,7 +662,7 @@
 			optarg, strerror(errno));
 		exit(1);
 	    }
-	    fprintf(conn->debugFile, "pppoe-discovery %s\n", VERSION);
+	    fprintf(conn->debugFile, "pppoe-discovery %s\n", RP_VERSION);
 	    break;
 	case 'I':
 	    conn->ifName = xstrdup(optarg);
@@ -86,21 +694,13 @@
 
 void rp_fatal(char const *str)
 {
-    char buf[1024];
-
-    printErr(str);
-    sprintf(buf, "pppoe-discovery: %.256s", str);
+    fprintf(stderr, "%s\n", str);
     exit(1);
 }
 
 void fatalSys(char const *str)
 {
-    char buf[1024];
-    int i = errno;
-
-    sprintf(buf, "%.256s: %.256s", str, strerror(i));
-    printErr(buf);
-    sprintf(buf, "pppoe-discovery: %.256s: %.256s", str, strerror(i));
+    perror(str);
     exit(1);
 }
 
@@ -120,5 +720,5 @@
 void usage(void)
 {
     fprintf(stderr, "Usage: pppoe-discovery [options]\n");
-    fprintf(stderr, "\nVersion " VERSION "\n");
+    fprintf(stderr, "\nVersion " RP_VERSION "\n");
 }
diff --git a/pppd/plugins/rp-pppoe/pppoe.h b/pppd/plugins/rp-pppoe/pppoe.h
index 2309ad3..9ab2eee 100644
--- a/pppd/plugins/rp-pppoe/pppoe.h
+++ b/pppd/plugins/rp-pppoe/pppoe.h
@@ -9,14 +9,10 @@
 * This program may be distributed according to the terms of the GNU
 * General Public License, version 2 or (at your option) any later version.
 *
-* $Id: pppoe.h,v 1.2 2004/11/04 10:07:37 paulus Exp $
+* $Id: pppoe.h,v 1.4 2008/06/15 04:35:50 paulus Exp $
 *
 ***********************************************************************/
 
-#ifdef __sun__
-#define __EXTENSIONS__
-#endif
-
 #include "config.h"
 
 #if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H)
@@ -62,51 +58,8 @@
 #include <net/if_types.h>
 #endif
 
-#ifdef HAVE_NET_IF_DL_H
-#include <net/if_dl.h>
-#endif
-
-/* I'm not sure why this is needed... I do not have OpenBSD */
-#if defined(__OpenBSD__)
-#include <net/ppp_defs.h>
-#include <net/if_ppp.h>
-#endif
-
-#ifdef USE_BPF
-extern int bpfSize;
-struct PPPoEPacketStruct;
-void sessionDiscoveryPacket(struct PPPoEPacketStruct *packet);
-#define BPF_BUFFER_IS_EMPTY (bpfSize <= 0)
-#define BPF_BUFFER_HAS_DATA (bpfSize > 0)
-#define ethhdr ether_header
-#define h_dest ether_dhost
-#define h_source ether_shost
-#define h_proto ether_type
-#define	ETH_DATA_LEN ETHERMTU
-#define	ETH_ALEN ETHER_ADDR_LEN
-#else
-#undef USE_BPF
 #define BPF_BUFFER_IS_EMPTY 1
 #define BPF_BUFFER_HAS_DATA 0
-#endif
-
-#ifdef USE_DLPI
-#include <sys/ethernet.h>
-#define ethhdr ether_header
-#define	ETH_DATA_LEN ETHERMTU
-#define	ETH_ALEN ETHERADDRL
-#define h_dest ether_dhost.ether_addr_octet
-#define h_source ether_shost.ether_addr_octet
-#define h_proto ether_type
-
-/* cloned from dltest.h */
-#define         MAXDLBUF        8192
-#define         MAXDLADDR       1024
-#define         MAXWAIT         15
-#define         OFFADDR(s, n)   (u_char*)((char*)(s) + (int)(n))
-#define         CASERET(s)      case s:  return ("s")
-
-#endif
 
 /* Define various integer types -- assumes a char is 8 bits */
 #if SIZEOF_UNSIGNED_SHORT == 2
@@ -124,7 +77,7 @@
 #elif SIZEOF_UNSIGNED_LONG == 4
 typedef unsigned long UINT32_t;
 #else
-#error Could not find a 16-bit integer type
+#error Could not find a 32-bit integer type
 #endif
 
 #ifdef HAVE_LINUX_IF_ETHER_H
@@ -160,6 +113,12 @@
 #define CODE_PADR           0x19
 #define CODE_PADS           0x65
 #define CODE_PADT           0xA7
+
+/* Extensions from draft-carrel-info-pppoe-ext-00 */
+/* I do NOT like PADM or PADN, but they are here for completeness */
+#define CODE_PADM           0xD3
+#define CODE_PADN           0xD4
+
 #define CODE_SESS           0x00
 
 /* PPPoE Tags */
@@ -170,10 +129,17 @@
 #define TAG_AC_COOKIE          0x0104
 #define TAG_VENDOR_SPECIFIC    0x0105
 #define TAG_RELAY_SESSION_ID   0x0110
+#define TAG_PPP_MAX_PAYLOAD    0x0120
 #define TAG_SERVICE_NAME_ERROR 0x0201
 #define TAG_AC_SYSTEM_ERROR    0x0202
 #define TAG_GENERIC_ERROR      0x0203
 
+/* Extensions from draft-carrel-info-pppoe-ext-00 */
+/* I do NOT like these tags one little bit */
+#define TAG_HURL               0x111
+#define TAG_MOTM               0x112
+#define TAG_IP_ROUTE_ADD       0x121
+
 /* Discovery phase states */
 #define STATE_SENT_PADI     0
 #define STATE_RECEIVED_PADO 1
@@ -202,34 +168,42 @@
 #define IPV4ALEN     4
 #define SMALLBUF   256
 
+/* There are other fixed-size buffers preventing
+   this from being increased to 16110. The buffer
+   sizes would need to be properly de-coupled from
+   the default MRU. For now, getting up to 1500 is
+   enough. */
+#define ETH_JUMBO_LEN 1508
+
 /* A PPPoE Packet, including Ethernet headers */
 typedef struct PPPoEPacketStruct {
     struct ethhdr ethHdr;	/* Ethernet header */
-#ifdef PACK_BITFIELDS_REVERSED
-    unsigned int type:4;	/* PPPoE Type (must be 1) */
-    unsigned int ver:4;		/* PPPoE Version (must be 1) */
-#else
-    unsigned int ver:4;		/* PPPoE Version (must be 1) */
-    unsigned int type:4;	/* PPPoE Type (must be 1) */
-#endif
+    unsigned int vertype:8;	/* PPPoE Version and Type (must both be 1) */
     unsigned int code:8;	/* PPPoE code */
     unsigned int session:16;	/* PPPoE session */
     unsigned int length:16;	/* Payload length */
-    unsigned char payload[ETH_DATA_LEN]; /* A bit of room to spare */
+    unsigned char payload[ETH_JUMBO_LEN]; /* A bit of room to spare */
 } PPPoEPacket;
 
+#define PPPOE_VER(vt)		((vt) >> 4)
+#define PPPOE_TYPE(vt)		((vt) & 0xf)
+#define PPPOE_VER_TYPE(v, t)	(((v) << 4) | (t))
+
 /* Header size of a PPPoE packet */
 #define PPPOE_OVERHEAD 6  /* type, code, session, length */
 #define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD)
-#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD)
-#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - 2)
+#define MAX_PPPOE_PAYLOAD (ETH_JUMBO_LEN - PPPOE_OVERHEAD)
+#define PPP_OVERHEAD 2  /* protocol */
+#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - PPP_OVERHEAD)
+#define TOTAL_OVERHEAD (PPPOE_OVERHEAD + PPP_OVERHEAD)
+#define ETH_PPPOE_MTU (ETH_DATA_LEN - TOTAL_OVERHEAD)
 
 /* PPPoE Tag */
 
 typedef struct PPPoETagStruct {
     unsigned int type:16;	/* tag type */
     unsigned int length:16;	/* Length of payload */
-    unsigned char payload[ETH_DATA_LEN]; /* A LOT of room to spare */
+    unsigned char payload[ETH_JUMBO_LEN]; /* A LOT of room to spare */
 } PPPoETag;
 /* Header size of a PPPoE tag */
 #define TAG_HDR_SIZE 4
@@ -254,6 +228,8 @@
     int sessionSocket;		/* Raw socket for session frames */
     unsigned char myEth[ETH_ALEN]; /* My MAC address */
     unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */
+    unsigned char req_peer_mac[ETH_ALEN]; /* required peer MAC address */
+    unsigned char req_peer;	/* require mac addr to match req_peer_mac */
     UINT16_t session;		/* Session ID */
     char *ifName;		/* Interface name */
     char *serviceName;		/* Desired service name, if any */
@@ -261,12 +237,16 @@
     int synchronous;		/* Use synchronous PPP */
     int useHostUniq;		/* Use Host-Uniq tag */
     int printACNames;		/* Just print AC names */
-    int skipDiscovery;		/* Skip discovery */
-    int noDiscoverySocket;	/* Don't even open discovery socket */
     FILE *debugFile;		/* Debug file for dumping packets */
     int numPADOs;		/* Number of PADO packets received */
     PPPoETag cookie;		/* We have to send this if we get it */
     PPPoETag relayId;		/* Ditto */
+    int error;			/* Error packet received */
+    int debug;			/* Set to log packets sent and received */
+    int discoveryTimeout;       /* Timeout for discovery packets */
+    int seenMaxPayload;
+    int mtu;			/* Stored MTU */
+    int mru;			/* Stored MRU */
 } PPPoEConnection;
 
 /* Structure used to determine acceptable PADO or PADS packet */
@@ -307,12 +287,16 @@
 unsigned char *findTag(PPPoEPacket *packet, UINT16_t tagType,
 		       PPPoETag *tag);
 
+void pppoe_printpkt(PPPoEPacket *packet,
+		    void (*printer)(void *, char *, ...), void *arg);
+void pppoe_log_packet(const char *prefix, PPPoEPacket *packet);
+
 #define SET_STRING(var, val) do { if (var) free(var); var = strDup(val); } while(0);
 
 #define CHECK_ROOM(cursor, start, len) \
 do {\
     if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \
-        syslog(LOG_ERR, "Would create too-long packet"); \
+	error("Would create too-long packet");	\
         return; \
     } \
 } while(0)
diff --git a/pppd/plugins/winbind.c b/pppd/plugins/winbind.c
index 3041f17..bb05acd 100644
--- a/pppd/plugins/winbind.c
+++ b/pppd/plugins/winbind.c
@@ -209,42 +209,33 @@
  **/
 char * base64_encode(const char *data)
 {
-	int bits = 0;
-	int char_count = 0;
 	size_t out_cnt = 0;
 	size_t len = strlen(data);
-	size_t output_len = strlen(data) * 2;
+	size_t output_len = 4 * ((len + 2) / 3) + 2;
+	const unsigned char *ptr = (const unsigned char *) data;
 	char *result = malloc(output_len); /* get us plenty of space */
+	unsigned int bits;
 
-	while (len-- && out_cnt < (output_len) - 5) {
-		int c = (unsigned char) *(data++);
-		bits += c;
-		char_count++;
-		if (char_count == 3) {
-			result[out_cnt++] = b64[bits >> 18];
-			result[out_cnt++] = b64[(bits >> 12) & 0x3f];
-			result[out_cnt++] = b64[(bits >> 6) & 0x3f];
-	    result[out_cnt++] = b64[bits & 0x3f];
-	    bits = 0;
-	    char_count = 0;
-	} else {
-	    bits <<= 8;
+	for (; len >= 3; len -= 3) {
+		bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2];
+		ptr += 3;
+		result[out_cnt++] = b64[bits >> 18];
+		result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+		result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+		result[out_cnt++] = b64[bits & 0x3f];
 	}
-    }
-    if (char_count != 0) {
-	bits <<= 16 - (8 * char_count);
-	result[out_cnt++] = b64[bits >> 18];
-	result[out_cnt++] = b64[(bits >> 12) & 0x3f];
-	if (char_count == 1) {
-	    result[out_cnt++] = '=';
-	    result[out_cnt++] = '=';
-	} else {
-	    result[out_cnt++] = b64[(bits >> 6) & 0x3f];
-	    result[out_cnt++] = '=';
+	if (len != 0) {
+		bits = ptr[0] << 16;
+		if (len > 1)
+			bits |= ptr[1] << 8;
+		result[out_cnt++] = b64[bits >> 18];
+		result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+		result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '=';
+		result[out_cnt++] = '=';
 	}
-    }
-    result[out_cnt] = '\0';	/* terminate */
-    return result;
+
+	result[out_cnt] = '\0';	/* terminate */
+	return result;
 }
 
 unsigned int run_ntlm_auth(const char *username, 
@@ -305,15 +296,18 @@
 
 	if (forkret == 0) {
 		/* child process */
+		uid_t uid;
+
 		close(child_out[0]);
 		close(child_in[1]);
 
 		/* run winbind as the user that invoked pppd */
 		setgid(getgid());
-		setuid(getuid());
+		uid = getuid();
+		if (setuid(uid) == -1 || getuid() != uid)
+			fatal("pppd/winbind: could not setuid to %d: %m", uid);
 		execl("/bin/sh", "sh", "-c", ntlm_auth, NULL);  
-		perror("pppd/winbind: could not exec /bin/sh");
-		exit(1);
+		fatal("pppd/winbind: could not exec /bin/sh: %m");
 	}
 
         /* parent */
@@ -557,20 +551,19 @@
 		u_char *lm_response = NULL;
 		int nt_response_size = 0;
 		int lm_response_size = 0;
-		MS_ChapResponse *rmd = (MS_ChapResponse *) response;
 		u_char session_key[16];
 		
 		if (response_len != MS_CHAP_RESPONSE_LEN)
 			break;			/* not even the right length */
 		
 		/* Determine which part of response to verify against */
-		if (rmd->UseNT[0]) {
-			nt_response = rmd->NTResp;
-			nt_response_size = sizeof(rmd->NTResp);
+		if (response[MS_CHAP_USENT]) {
+			nt_response = &response[MS_CHAP_NTRESP];
+			nt_response_size = MS_CHAP_NTRESP_LEN;
 		} else {
 #ifdef MSLANMAN
-			lm_response = rmd->LANManResp;
-			lm_response_size = sizeof(rmd->LANManResp);
+			lm_response = &response[MS_CHAP_LANMANRESP];
+			lm_response_size = MS_CHAP_LANMANRESP_LEN;
 #else
 			/* Should really propagate this into the error packet. */
 			notice("Peer request for LANMAN auth not supported");
@@ -584,12 +577,9 @@
 				  domain,
 				  NULL,
 				  NULL,
-				  challenge,
-				  challenge_len,
-				  lm_response,
-				  lm_response ? lm_response_size: 0,
-				  nt_response,
-				  nt_response ? nt_response_size: 0,
+				  challenge, challenge_len,
+				  lm_response, lm_response_size,
+				  nt_response, nt_response_size,
 				  session_key,
 				  &error_string) == AUTHENTICATED) {
 			mppe_set_keys(challenge, session_key);
@@ -610,7 +600,6 @@
 	
 	case CHAP_MICROSOFT_V2:
 	{
-		MS_Chap2Response *rmd = (MS_Chap2Response *) response;
 		u_char Challenge[8];
 		u_char session_key[MD4_SIGNATURE_SIZE];
 		char *error_string = NULL;
@@ -618,7 +607,8 @@
 		if (response_len != MS_CHAP2_RESPONSE_LEN)
 			break;			/* not even the right length */
 		
-		ChallengeHash(rmd->PeerChallenge, challenge, user, Challenge);
+		ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge,
+			      user, Challenge);
 		
 		/* ship off to winbind, and check */
 		
@@ -626,22 +616,20 @@
 				  domain, 
 				  NULL,
 				  NULL,
-				  Challenge,
-				  8,
-				  NULL, 
-				  0,
-				  rmd->NTResp,
-				  sizeof(rmd->NTResp),
-				  
+				  Challenge, 8,
+				  NULL, 0,
+				  &response[MS_CHAP2_NTRESP],
+				  MS_CHAP2_NTRESP_LEN,
 				  session_key,
 				  &error_string) == AUTHENTICATED) {
 			
 			GenerateAuthenticatorResponse(session_key,
-						      rmd->NTResp, rmd->PeerChallenge,
-						      challenge, user,
-						      saresponse);
-			mppe_set_keys2(session_key, rmd->NTResp, MS_CHAP2_AUTHENTICATOR);
-			if (rmd->Flags[0]) {
+				&response[MS_CHAP2_NTRESP],
+				&response[MS_CHAP2_PEER_CHALLENGE],
+				challenge, user, saresponse);
+			mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP],
+				       MS_CHAP2_AUTHENTICATOR);
+			if (response[MS_CHAP2_FLAGS]) {
 				slprintf(message, message_space, "S=%s", saresponse);
 			} else {
 				slprintf(message, message_space, "S=%s M=%s",
diff --git a/pppd/pppcrypt.c b/pppd/pppcrypt.c
index 1302c83..65e7282 100644
--- a/pppd/pppcrypt.c
+++ b/pppd/pppcrypt.c
@@ -171,7 +171,11 @@
 }
 
 bool
+#if defined(__ANDROID__)
 DesEncrypt(clear, cipher)
+#else
+DesEncrypt(clear, key, cipher)
+#endif
 u_char *clear;	/* IN  8 octets */
 u_char *cipher;	/* OUT 8 octets */
 {
diff --git a/pppd/pppcrypt.h b/pppd/pppcrypt.h
index 33b956d..37968c4 100644
--- a/pppd/pppcrypt.h
+++ b/pppd/pppcrypt.h
@@ -38,7 +38,7 @@
 #endif
 
 #ifndef USE_CRYPT
-#ifdef ANDROID_CHANGES
+#if defined(__ANDROID__)
 #include <openssl/des.h>
 #else
 #include <des.h>
diff --git a/pppd/pppd.8 b/pppd/pppd.8
index 22b3b26..e4aad95 100644
--- a/pppd/pppd.8
+++ b/pppd/pppd.8
@@ -1,5 +1,5 @@
 .\" manual page [] for pppd 2.4
-.\" $Id: pppd.8,v 1.83 2004/11/13 12:22:49 paulus Exp $
+.\" $Id: pppd.8,v 1.90 2008/03/26 12:09:40 paulus Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -43,7 +43,8 @@
 .TP
 .I ttyname
 Use the serial port called \fIttyname\fR to communicate with the
-peer.  The string "/dev/" is prepended to \fIttyname\fR to form the
+peer.  If \fIttyname\fR does not begin with a slash (/),
+the string "/dev/" is prepended to \fIttyname\fR to form the
 name of the device to open.  If no device name is given, or if the
 name of the terminal
 connected to the standard input is given, pppd will use that terminal,
@@ -81,8 +82,8 @@
 IP addresses to which the system does not already have a route.
 .TP
 .B call \fIname
-Read options from the file /etc/ppp/peers/\fIname\fR.  This file may
-contain privileged options, such as \fInoauth\fR, even if pppd
+Read additional options from the file /etc/ppp/peers/\fIname\fR.  This
+file may contain privileged options, such as \fInoauth\fR, even if pppd
 is not being run by root.  The \fIname\fR string may not begin with /
 or include .. as a pathname component.  The format of the options file
 is described below.
@@ -152,7 +153,8 @@
 .TP
 .B lock
 Specifies that pppd should create a UUCP-style lock file for the
-serial device to ensure exclusive access to the device.
+serial device to ensure exclusive access to the device.  By default,
+pppd will not create a lock file.
 .TP
 .B mru \fIn
 Set the MRU [Maximum Receive Unit] value to \fIn\fR. Pppd
@@ -191,9 +193,12 @@
 negotiation, unless the \fIipcp\-accept\-local\fR and/or
 \fIipcp\-accept\-remote\fR options are given, respectively.
 .TP
+.B +ipv6
+Enable the IPv6CP and IPv6 protocols.
+.TP
 .B ipv6 \fI<local_interface_identifier>\fR,\fI<remote_interface_identifier>
 Set the local and/or remote 64-bit interface identifier. Either one may be
-omitted. The identifier must be specified in standard ascii notation of
+omitted. The identifier must be specified in standard ASCII notation of
 IPv6 addresses (e.g. ::dead:beef). If the
 \fIipv6cp\-use\-ipaddr\fR
 option is given, the local identifier is the local IPv4 address (see above).
@@ -315,8 +320,9 @@
 .TP
 .B demand
 Initiate the link only on demand, i.e. when data traffic is present.
-With this option, the remote IP address must be specified by the user
-on the command line or in an options file.  Pppd will initially
+With this option, the remote IP address may be specified by the user
+on the command line or in an options file, or if not, pppd will use
+an arbitrary address in the 10.x.x.x range.  Pppd will initially
 configure the interface and enable it for IP traffic without
 connecting to the peer.  When traffic is available, pppd will
 connect to the peer and perform negotiation, authentication, etc.
@@ -326,7 +332,7 @@
 The \fIdemand\fR option implies the \fIpersist\fR option.  If this
 behaviour is not desired, use the \fInopersist\fR option after the
 \fIdemand\fR option.  The \fIidle\fR and \fIholdoff\fR
-options are also useful in conjuction with the \fIdemand\fR option.
+options are also useful in conjunction with the \fIdemand\fR option.
 .TP
 .B domain \fId
 Append the domain name \fId\fR to the local host name for authentication
@@ -350,6 +356,16 @@
 which have been set.  This option is like the \fBdryrun\fR option
 except that pppd proceeds as normal rather than exiting.
 .TP
+.B enable-session
+Enables session accounting via PAM or wtwp/wtmpx, as appropriate.
+When PAM is enabled, the PAM "account" and "session" module stacks
+determine behavior, and are enabled for all PPP authentication
+protocols.  When PAM is disabled, wtmp/wtmpx entries are recorded
+regardless of whether the peer name identifies a valid user on the
+local system, making peers visible in the last(1) log.  This feature
+is automatically enabled when the pppd \fBlogin\fR option is used.
+Session accounting is disabled by default.
+.TP
 .B endpoint \fI<epdisc>
 Sets the endpoint discriminator sent by the local machine to the peer
 during multilink negotiation to \fI<epdisc>\fR.  The default is to use
@@ -432,10 +448,16 @@
 seconds (default 3).
 .TP
 .B ipparam \fIstring
-Provides an extra parameter to the ip\-up and ip\-down scripts.  If this
+Provides an extra parameter to the ip\-up, ip\-pre\-up and ip\-down
+scripts.  If this
 option is given, the \fIstring\fR supplied is given as the 6th
 parameter to those scripts.
 .TP
+.B ipv6cp\-accept\-local
+With this option, pppd will accept the peer's idea of our local IPv6
+interface identifier, even if the local IPv6 interface identifier
+was specified in an option.
+.TP
 .B ipv6cp\-max\-configure \fIn
 Set the maximum number of IPv6CP configure-request transmissions to
 \fIn\fR (default 10).
@@ -512,7 +534,7 @@
 send before it rejects the options. The default value is 3.
 .TP
 .B ipxcp\-max\-terminate \fIn
-Set the maximum nuber of IPXCP terminate request frames before the
+Set the maximum number of IPXCP terminate request frames before the
 local system considers that the peer is not listening to them. The
 default value is 3.
 .TP
@@ -579,7 +601,8 @@
 .B local
 Don't use the modem control lines.  With this option, pppd will ignore
 the state of the CD (Carrier Detect) signal from the modem and will
-not change the state of the DTR (Data Terminal Ready) signal.
+not change the state of the DTR (Data Terminal Ready) signal.  This is
+the opposite of the \fBmodem\fR option.
 .TP
 .B logfd \fIn
 Send log messages to file descriptor \fIn\fR.  Pppd will send log
@@ -598,7 +621,16 @@
 Use the system password database for authenticating the peer using
 PAP, and record the user in the system wtmp file.  Note that the peer
 must have an entry in the /etc/ppp/pap\-secrets file as well as the
-system password database to be allowed access.
+system password database to be allowed access.  See also the
+\fBenable\-session\fR option.
+.TP
+.B master_detach
+If multilink is enabled and this pppd process is the multilink bundle
+master, and the link controlled by this pppd process terminates, this
+pppd process continues to run in order to maintain the bundle.  If the
+\fBmaster_detach\fR option has been given, pppd will detach from its
+controlling terminal in this situation, even if the \fBnodetach\fR
+option has been given.
 .TP
 .B maxconnect \fIn
 Terminate the connection when it has been available for network
@@ -616,7 +648,8 @@
 script is specified), and it will drop the DTR (Data Terminal Ready)
 signal briefly when the connection is terminated and before executing
 the connect script.  On Ultrix, this option implies hardware flow
-control, as for the \fIcrtscts\fR option.
+control, as for the \fIcrtscts\fR option.  This is the opposite of the
+\fBlocal\fR option.
 .TP
 .B mp
 Enables the use of PPP multilink; this is an alias for the `multilink'
@@ -744,6 +777,11 @@
 Opposite of the \fIktune\fR option; disables pppd from changing system
 settings.
 .TP
+.B nolock
+Opposite of the \fIlock\fR option; specifies that pppd should not
+create a UUCP-style lock file for the serial device.  This option is
+privileged.
+.TP
 .B nolog
 Do not send log messages to a file or file descriptor.  This option
 cancels the \fBlogfd\fR and \fBlogfile\fR options.
@@ -796,6 +834,18 @@
 wishes to prevent users from creating proxy ARP entries with pppd can
 do so by placing this option in the /etc/ppp/options file.
 .TP
+.B noremoteip
+Allow pppd to operate without having an IP address for the peer.  This
+option is only available under Linux.  Normally, pppd will request the
+peer's IP address, and if the peer does not supply it, pppd will use
+an arbitrary address in the 10.x.x.x subnet.
+With this option, if the peer does
+not supply its IP address, pppd will not ask the peer for it, and will
+not set the destination address of the ppp interface.  In this
+situation, the ppp interface can be used for routing by creating
+device routes, but the peer itself cannot be addressed directly for IP
+traffic.
+.TP
 .B notty
 Normally, pppd requires a terminal device.  With this option, pppd
 will allocate itself a pseudo-tty master/slave pair and use the slave
@@ -897,7 +947,7 @@
 device.  The \fIscript\fR will be run in a child process with the
 pseudo-tty master as its standard input and output.  An explicit
 device name may not be given if this option is used.  (Note: if the
-\fIrecord\fR option is used in conjuction with the \fIpty\fR option,
+\fIrecord\fR option is used in conjunction with the \fIpty\fR option,
 the child process will have pipes on its standard input and output.)
 .TP
 .B receive\-all
@@ -978,6 +1028,13 @@
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
 .TP
+.B set \fIname\fR=\fIvalue
+Set an environment variable for scripts that are invoked by pppd.
+When set by a privileged source, the variable specified by \fIname\fR
+cannot be changed by options contained in an unprivileged source.  See
+also the \fIunset\fR option and the environment described in
+\fISCRIPTS\fR.
+.TP
 .B show\-password
 When logging the contents of PAP packets, this option causes pppd to
 show the password string in the log message.
@@ -1005,7 +1062,7 @@
 .TP
 .B srp\-use\-pseudonym
 When operating as an EAP SRP\-SHA1 client, attempt to use the pseudonym
-stored in ~/.ppp_psuedonym first as the identity, and save in this
+stored in ~/.ppp_pseudonym first as the identity, and save in this
 file any pseudonym offered by the peer during authentication.
 .TP
 .B sync
@@ -1018,6 +1075,13 @@
 Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
 connections.
 .TP
+.B unset \fIname
+Remove a variable from the environment variable for scripts that are
+invoked by pppd.  When specified by a privileged source, the variable
+\fIname\fR cannot be set by options contained in an unprivileged
+source.  See also the \fIset\fR option and the environment described
+in \fISCRIPTS\fR.
+.TP
 .B updetach
 With this option, pppd will detach from its controlling terminal once
 it has successfully established the ppp connection (to the point where
@@ -1068,7 +1132,7 @@
 .PP
 An options file is parsed into a series of words, delimited by
 whitespace.  Whitespace can be included in a word by enclosing the
-word in double-quotes (").  A backslash (\\) quotes the following character.
+word in double-quotes (").  A backslash (\e) quotes the following character.
 A hash (#) starts a comment, which continues until the end of the
 line.  There is no restriction on using the \fIfile\fR or \fIcall\fR
 options within an options file.
@@ -1380,9 +1444,9 @@
 .br
 "name:" "^Umyuserid"
 .br
-"word:" "\\qmypassword"
+"word:" "\eqmypassword"
 .br
-"ispts" "\\q^Uppp"
+"ispts" "\eq^Uppp"
 .br
 "~\-^Uppp\-~"
 .LP
@@ -1521,7 +1585,8 @@
 Pppd invokes scripts at various stages in its processing which can be
 used to perform site-specific ancillary processing.  These scripts are
 usually shell scripts, but could be executable code files instead.
-Pppd does not wait for the scripts to finish.  The scripts are
+Pppd does not wait for the scripts to finish (except for the ip-pre-up
+script).  The scripts are
 executed as root (with the real and effective user-id set to 0), so
 that they can do things such as update routing tables or run
 privileged daemons.  Be careful that the contents of these scripts do
@@ -1578,11 +1643,13 @@
 .TP
 .B DNS1
 If the peer supplies DNS server addresses, this variable is set to the
-first DNS server address supplied.
+first DNS server address supplied (whether or not the usepeerdns
+option was given).
 .TP
 .B DNS2
 If the peer supplies DNS server addresses, this variable is set to the
-second DNS server address supplied.
+second DNS server address supplied (whether or not the usepeerdns
+option was given).
 .P
 Pppd invokes the following scripts, if they exist.  It is not an error
 if they don't exist.
@@ -1601,6 +1668,15 @@
 /etc/ppp/auth\-up was previously executed.  It is executed in the same
 manner with the same parameters as /etc/ppp/auth\-up.
 .TP
+.B /etc/ppp/ip\-pre\-up
+A program or script which is executed just before the ppp network
+interface is brought up.  It is executed with the same parameters as
+the ip\-up script (below).  At this point the interface exists and has
+IP addresses assigned but is still down.  This can be used to
+add firewall rules before any IP traffic can pass through the
+interface.  Pppd will wait for this script to finish before bringing
+the interface up, so this script should run quickly.
+.TP
 .B /etc/ppp/ip\-up
 A program or script which is executed when the link is available for
 sending and receiving IP packets (that is, IPCP has come up).  It is
@@ -1612,7 +1688,8 @@
 .B /etc/ppp/ip\-down
 A program or script which is executed when the link is no longer
 available for sending and receiving IP packets.  This script can be
-used for undoing the effects of the /etc/ppp/ip\-up script.  It is
+used for undoing the effects of the /etc/ppp/ip\-up and
+/etc/ppp/ip\-pre\-up scripts.  It is
 invoked in the same manner and with the same parameters as the ip\-up
 script.
 .TP
@@ -1712,6 +1789,8 @@
 permit non-privileged users to dial out without requiring the peer to
 authenticate, but only to certain trusted peers.
 .SH SEE ALSO
+.BR chat (8),
+.BR pppstats (8)
 .TP
 .B RFC1144
 Jacobson, V.
@@ -1768,7 +1847,10 @@
 .TP
 .B SIGINT, SIGTERM
 These signals cause pppd to terminate the link (by closing LCP),
-restore the serial device settings, and exit.
+restore the serial device settings, and exit.  If a connector or
+disconnector process is currently running, pppd will send the same
+signal to its process group, so as to terminate the connector or
+disconnector process.
 .TP
 .B SIGHUP
 This signal causes pppd to terminate the link, restore the serial
@@ -1777,6 +1859,8 @@
 serial device and start another connection (after the holdoff period).
 Otherwise pppd will exit.  If this signal is received during the
 holdoff period, it causes pppd to end the holdoff period immediately.
+If a connector or disconnector process is running, pppd will send the
+same signal to its process group.
 .TP
 .B SIGUSR1
 This signal toggles the state of the \fIdebug\fR option.
@@ -1857,7 +1941,7 @@
    prior written permission.
 .LP
 4. Redistributions of any form whatsoever must retain the following
-   acknowledgments:
+   acknowledgements:
 .br
    "This product includes software developed by Computing Services
     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
diff --git a/pppd/pppd.h b/pppd/pppd.h
index 9a86d99..f18cd30 100644
--- a/pppd/pppd.h
+++ b/pppd/pppd.h
@@ -39,7 +39,7 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Id: pppd.h,v 1.88 2004/11/13 12:02:22 paulus Exp $
+ * $Id: pppd.h,v 1.96 2008/06/23 11:47:18 paulus Exp $
  */
 
 /*
@@ -115,19 +115,19 @@
 #define OPT_VALUE	0xff	/* mask for presupplied value */
 #define OPT_HEX		0x100	/* int option is in hex */
 #define OPT_NOARG	0x200	/* option doesn't take argument */
-#define OPT_OR		0x400	/* OR in argument to value */
-#define OPT_INC		0x800	/* increment value */
+#define OPT_OR		0x400	/* for u32, OR in argument to value */
+#define OPT_INC		0x400	/* for o_int, increment value */
 #define OPT_A2OR	0x800	/* for o_bool, OR arg to *(u_char *)addr2 */
 #define OPT_PRIV	0x1000	/* privileged option */
 #define OPT_STATIC	0x2000	/* string option goes into static array */
+#define OPT_NOINCR	0x2000	/* for o_int, value mustn't be increased */
 #define OPT_LLIMIT	0x4000	/* check value against lower limit */
 #define OPT_ULIMIT	0x8000	/* check value against upper limit */
 #define OPT_LIMITS	(OPT_LLIMIT|OPT_ULIMIT)
 #define OPT_ZEROOK	0x10000	/* 0 value is OK even if not within limits */
 #define OPT_HIDE	0x10000	/* for o_string, print value as ?????? */
-#define OPT_A2LIST	0x10000 /* for o_special, keep list of values */
-#define OPT_A2CLRB	0x10000 /* o_bool, clr val bits in *(u_char *)addr2 */
-#define OPT_NOINCR	0x20000	/* value mustn't be increased */
+#define OPT_A2LIST	0x20000 /* for o_special, keep list of values */
+#define OPT_A2CLRB	0x20000 /* o_bool, clr val bits in *(u_char *)addr2 */
 #define OPT_ZEROINF	0x40000	/* with OPT_NOINCR, 0 == infinity */
 #define OPT_PRIO	0x80000	/* process option priorities for this option */
 #define OPT_PRIOSUB	0x100000 /* subsidiary member of priority group */
@@ -139,7 +139,7 @@
 #define OPT_INITONLY	0x4000000 /* option can only be set in init phase */
 #define OPT_DEVEQUIV	0x8000000 /* equiv to device name */
 #define OPT_DEVNAM	(OPT_INITONLY | OPT_DEVEQUIV)
-#define OPT_A2PRINTER	0x10000000 /* *addr2 is a fn for printing option */
+#define OPT_A2PRINTER	0x10000000 /* *addr2 printer_func to print option */
 #define OPT_A2STRVAL	0x20000000 /* *addr2 points to current string value */
 #define OPT_NOPRINT	0x40000000 /* don't print this option at all */
 
@@ -199,6 +199,7 @@
 #define EPD_PHONENUM	5
 
 typedef void (*notify_func) __P((void *, int));
+typedef void (*printer_func) __P((void *, char *, ...));
 
 struct notifier {
     struct notifier *next;
@@ -257,8 +258,10 @@
 extern struct notifier *phasechange; /* for notifications of phase changes */
 extern struct notifier *exitnotify;  /* for notification that we're exiting */
 extern struct notifier *sigreceived; /* notification of received signal */
-extern struct notifier *ip_up_notifier; /* IPCP has come up */
-extern struct notifier *ip_down_notifier; /* IPCP has gone down */
+extern struct notifier *ip_up_notifier;     /* IPCP has come up */
+extern struct notifier *ip_down_notifier;   /* IPCP has gone down */
+extern struct notifier *ipv6_up_notifier;   /* IPV6CP has come up */
+extern struct notifier *ipv6_down_notifier; /* IPV6CP has gone down */
 extern struct notifier *auth_up_notifier; /* peer has authenticated */
 extern struct notifier *link_down_notifier; /* link has gone down */
 extern struct notifier *fork_notifier;	/* we are a new child process */
@@ -276,12 +279,14 @@
 extern int	default_device;	/* Using /dev/tty or equivalent */
 extern char	devnam[MAXPATHLEN];	/* Device name */
 extern int	crtscts;	/* Use hardware flow control */
+extern int	stop_bits;	/* Number of serial port stop bits */
 extern bool	modem;		/* Use modem control lines */
 extern int	inspeed;	/* Input/Output speed requested */
 extern u_int32_t netmask;	/* IP netmask to set on interface */
 extern bool	lockflag;	/* Create lock file to lock the serial dev */
 extern bool	nodetach;	/* Don't detach from controlling tty */
 extern bool	updetach;	/* Detach from controlling tty when link up */
+extern bool	master_detach;	/* Detach when multilink master without link */
 extern char	*initializer;	/* Script to initialize physical link */
 extern char	*connect_script; /* Script to establish physical link */
 extern char	*disconnect_script; /* Script to disestablish physical link */
@@ -293,6 +298,7 @@
 extern bool	auth_required;	/* Peer is required to authenticate */
 extern bool	persist;	/* Reopen link after it goes down */
 extern bool	uselogin;	/* Use /etc/passwd for checking PAP */
+extern bool	session_mgmt;	/* Do session management (login records) */
 extern char	our_name[MAXNAMELEN];/* Our name for authentication purposes */
 extern char	remote_name[MAXNAMELEN]; /* Peer's name for authentication */
 extern bool	explicit_remote;/* remote_name specified with remotename opt */
@@ -405,8 +411,7 @@
     /* Close the protocol */
     void (*close) __P((int unit, char *reason));
     /* Print a packet in readable form */
-    int  (*printpkt) __P((u_char *pkt, int len,
-			  void (*printer) __P((void *, char *, ...)),
+    int  (*printpkt) __P((u_char *pkt, int len, printer_func printer,
 			  void *arg));
     /* Process a received data packet */
     void (*datainput) __P((int unit, u_char *pkt, int len));
@@ -460,6 +465,21 @@
 extern struct channel *the_channel;
 
 /*
+ * This structure contains environment variables that are set or unset
+ * by the user.
+ */
+struct userenv {
+	struct userenv *ue_next;
+	char *ue_value;		/* value (set only) */
+	bool ue_isset;		/* 1 for set, 0 for unset */
+	bool ue_priv;		/* from privileged source */
+	const char *ue_source;	/* source name */
+	char ue_name[1];	/* variable name */
+};
+
+extern struct userenv *userenv_list;
+
+/*
  * Prototypes.
  */
 
@@ -473,12 +493,12 @@
 				/* Call func(arg) after s.us seconds */
 void untimeout __P((void (*func)(void *), void *arg));
 				/* Cancel call to func(arg) */
-void record_child __P((int, char *, void (*) (void *), void *));
+void record_child __P((int, char *, void (*) (void *), void *, int));
 pid_t safe_fork __P((int, int, int));	/* Fork & close stuff in child */
 int  device_script __P((char *cmd, int in, int out, int dont_wait));
 				/* Run `cmd' with given stdin and stdout */
 pid_t run_program __P((char *prog, char **args, int must_exist,
-		       void (*done)(void *), void *arg));
+		       void (*done)(void *), void *arg, int wait));
 				/* Run program prog with args in child */
 void reopen_log __P((void));	/* (re)open the connection to syslog */
 void print_link_stats __P((void)); /* Print stats, if available */
@@ -492,6 +512,7 @@
 void notify __P((struct notifier *, int));
 int  ppp_send_config __P((int, int, u_int32_t, int, int));
 int  ppp_recv_config __P((int, int, u_int32_t, int, int));
+const char *protocol_name __P((int));
 void remove_pidfiles __P((void));
 void lock_db __P((void));
 void unlock_db __P((void));
@@ -502,11 +523,11 @@
 /* Procedures exported from utils.c. */
 void log_packet __P((u_char *, int, char *, int));
 				/* Format a packet and log it with syslog */
-void print_string __P((char *, int,  void (*) (void *, char *, ...),
-		void *));	/* Format a string for output */
+void print_string __P((char *, int,  printer_func, void *));
+				/* Format a string for output */
 int slprintf __P((char *, int, char *, ...));		/* sprintf++ */
 int vslprintf __P((char *, int, char *, va_list));	/* vsprintf++ */
-#if !defined(ANDROID_CHANGES)
+#if !defined(__ANDROID__)
 size_t strlcpy __P((char *, const char *, size_t));	/* safe strcpy */
 size_t strlcat __P((char *, const char *, size_t));	/* safe strncpy */
 #endif
@@ -516,7 +537,7 @@
 void warn __P((char *, ...));	/* log a warning message */
 void error __P((char *, ...));	/* log an error message */
 void fatal __P((char *, ...));	/* log an error message and die(1) */
-void init_pr_log __P((char *, int));	/* initialize for using pr_log */
+void init_pr_log __P((const char *, int)); /* initialize for using pr_log */
 void pr_log __P((void *, char *, ...));	/* printer fn, output to syslog */
 void end_pr_log __P((void));	/* finish up after using pr_log */
 void dump_packet __P((const char *, u_char *, int));
@@ -526,6 +547,7 @@
 
 /* Procedures exported from auth.c */
 void link_required __P((int));	  /* we are starting to use the link */
+void start_link __P((int));	  /* bring the link up now */
 void link_terminated __P((int));  /* we are finished with the link */
 void link_down __P((int));	  /* the LCP layer has left the Opened state */
 void upper_layers_down __P((int));/* take all NCPs down */
@@ -637,6 +659,9 @@
 int  cifaddr __P((int, u_int32_t, u_int32_t));
 				/* Reset i/f IP addresses */
 #ifdef INET6
+int  ether_to_eui64(eui64_t *p_eui64);	/* convert eth0 hw address to EUI64 */
+int  sif6up __P((int));		/* Configure i/f up for IPv6 */
+int  sif6down __P((int));	/* Configure i/f down for IPv6 */
 int  sif6addr __P((int, eui64_t, eui64_t));
 				/* Configure IPv6 addresses for i/f */
 int  cif6addr __P((int, eui64_t, eui64_t));
@@ -690,7 +715,7 @@
 void check_options __P((void));	/* check values after all options parsed */
 int  override_value __P((const char *, int, const char *));
 				/* override value if permitted by priority */
-void print_options __P((void (*) __P((void *, char *, ...)), void *));
+void print_options __P((printer_func, void *));
 				/* print out values of all options */
 
 int parse_dotted_ip __P((char *, u_int32_t *));
@@ -711,9 +736,12 @@
 extern void (*ip_up_hook) __P((void));
 extern void (*ip_down_hook) __P((void));
 extern void (*ip_choose_hook) __P((u_int32_t *));
+extern void (*ipv6_up_hook) __P((void));
+extern void (*ipv6_down_hook) __P((void));
 
 extern int (*chap_check_hook) __P((void));
 extern int (*chap_passwd_hook) __P((char *user, char *passwd));
+extern void (*multilink_join_hook) __P((void));
 
 /* Let a plugin snoop sent and received packets.  Useful for L2TP */
 extern void (*snoop_recv_hook) __P((unsigned char *p, int len));
diff --git a/pppd/session.c b/pppd/session.c
new file mode 100644
index 0000000..cc1e9b0
--- /dev/null
+++ b/pppd/session.c
@@ -0,0 +1,430 @@
+/*
+ * session.c - PPP session control.
+ *
+ * Copyright (c) 2007 Diego Rivera. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from auth.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#if !defined(__ANDROID__)
+#include <crypt.h>
+#endif
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#endif
+#include <time.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "pppd.h"
+#include "session.h"
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif /* #ifdef USE_PAM */
+
+#define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
+#define COPY_STRING(s) ((s) ? strdup(s) : NULL)
+
+#define SUCCESS_MSG "Session started successfully"
+#define ABORT_MSG "Session can't be started without a username"
+#define SERVICE_NAME "ppp"
+
+#define SESSION_FAILED  0
+#define SESSION_OK      1
+
+/* We have successfully started a session */
+static bool logged_in = 0;
+
+#ifdef USE_PAM
+/*
+ * Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static const char *PAM_username;
+static const char *PAM_password;
+static int   PAM_session = 0;
+static pam_handle_t *pamh = NULL;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int conversation (int num_msg,
+#ifndef SOL2
+    const
+#endif
+    struct pam_message **msg,
+    struct pam_response **resp, void *appdata_ptr)
+{
+    int replies = 0;
+    struct pam_response *reply = NULL;
+
+    reply = malloc(sizeof(struct pam_response) * num_msg);
+    if (!reply) return PAM_CONV_ERR;
+
+    for (replies = 0; replies < num_msg; replies++) {
+        switch (msg[replies]->msg_style) {
+            case PAM_PROMPT_ECHO_ON:
+                reply[replies].resp_retcode = PAM_SUCCESS;
+                reply[replies].resp = COPY_STRING(PAM_username);
+                /* PAM frees resp */
+                break;
+            case PAM_PROMPT_ECHO_OFF:
+                reply[replies].resp_retcode = PAM_SUCCESS;
+                reply[replies].resp = COPY_STRING(PAM_password);
+                /* PAM frees resp */
+                break;
+            case PAM_TEXT_INFO:
+                /* fall through */
+            case PAM_ERROR_MSG:
+                /* ignore it, but pam still wants a NULL response... */
+                reply[replies].resp_retcode = PAM_SUCCESS;
+                reply[replies].resp = NULL;
+                break;
+            default:
+                /* Must be an error of some sort... */
+                free (reply);
+                return PAM_CONV_ERR;
+        }
+    }
+    *resp = reply;
+    return PAM_SUCCESS;
+}
+
+static struct pam_conv pam_conv_data = {
+    &conversation,
+    NULL
+};
+#endif /* #ifdef USE_PAM */
+
+int
+session_start(flags, user, passwd, ttyName, msg)
+    const int flags;
+    const char *user;
+    const char *passwd;
+    const char *ttyName;
+    char **msg;
+{
+#ifdef USE_PAM
+    bool ok = 1;
+    const char *usr;
+    int pam_error;
+    bool try_session = 0;
+#else /* #ifdef USE_PAM */
+    struct passwd *pw;
+    char *cbuf;
+#ifdef HAS_SHADOW
+    struct spwd *spwd;
+    struct spwd *getspnam();
+    long now = 0;
+#endif /* #ifdef HAS_SHADOW */
+#endif /* #ifdef USE_PAM */
+
+    SET_MSG(msg, SUCCESS_MSG);
+
+    /* If no verification is requested, then simply return an OK */
+    if (!(SESS_ALL & flags)) {
+        return SESSION_OK;
+    }
+
+#if defined(__ANDROID__)
+    return SESSION_FAILED;
+#endif
+
+    if (user == NULL) {
+       SET_MSG(msg, ABORT_MSG);
+       return SESSION_FAILED;
+    }
+
+#ifdef USE_PAM
+    /* Find the '\\' in the username */
+    /* This needs to be fixed to support different username schemes */
+    if ((usr = strchr(user, '\\')) == NULL)
+	usr = user;
+    else
+	usr++;
+
+    PAM_session = 0;
+    PAM_username = usr;
+    PAM_password = passwd;
+
+    dbglog("Initializing PAM (%d) for user %s", flags, usr);
+    pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh);
+    dbglog("---> PAM INIT Result = %d", pam_error);
+    ok = (pam_error == PAM_SUCCESS);
+
+    if (ok) {
+        ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
+	    (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
+    }
+
+    if (ok && (SESS_AUTH & flags)) {
+        dbglog("Attempting PAM authentication");
+        pam_error = pam_authenticate (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            /* PAM auth was OK */
+            dbglog("PAM Authentication OK for %s", user);
+        } else {
+            /* No matter the reason, we fail because we're authenticating */
+            ok = 0;
+            if (pam_error == PAM_USER_UNKNOWN) {
+                dbglog("User unknown, failing PAM authentication");
+                SET_MSG(msg, "User unknown - cannot authenticate via PAM");
+            } else {
+                /* Any other error means authentication was bad */
+                dbglog("PAM Authentication failed: %d: %s", pam_error,
+		       pam_strerror(pamh, pam_error));
+                SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            }
+        }
+    }
+
+    if (ok && (SESS_ACCT & flags)) {
+        dbglog("Attempting PAM account checks");
+        pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            /*
+	     * PAM account was OK, set the flag which indicates that we should
+	     * try to perform the session checks.
+	     */
+            try_session = 1;
+            dbglog("PAM Account OK for %s", user);
+        } else {
+            /*
+	     * If the account checks fail, then we should not try to perform
+	     * the session check, because they don't make sense.
+	     */
+            try_session = 0;
+            if (pam_error == PAM_USER_UNKNOWN) {
+                /*
+		 * We're checking the account, so it's ok to not have one
+		 * because the user might come from the secrets files, or some
+		 * other plugin.
+		 */
+                dbglog("User unknown, ignoring PAM restrictions");
+                SET_MSG(msg, "User unknown - ignoring PAM restrictions");
+            } else {
+                /* Any other error means session is rejected */
+                ok = 0;
+                dbglog("PAM Account checks failed: %d: %s", pam_error,
+		       pam_strerror(pamh, pam_error));
+                SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            }
+        }
+    }
+
+    if (ok && try_session && (SESS_ACCT & flags)) {
+        /* Only open a session if the user's account was found */
+        pam_error = pam_open_session (pamh, PAM_SILENT);
+        if (pam_error == PAM_SUCCESS) {
+            dbglog("PAM Session opened for user %s", user);
+            PAM_session = 1;
+        } else {
+            dbglog("PAM Session denied for user %s", user);
+            SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
+            ok = 0;
+        }
+    }
+
+    /* This is needed because apparently the PAM stuff closes the log */
+    reopen_log();
+
+    /* If our PAM checks have already failed, then we must return a failure */
+    if (!ok) return SESSION_FAILED;
+
+#elif !defined(__ANDROID__) /* #ifdef USE_PAM */
+
+/*
+ * Use the non-PAM methods directly.  'pw' will remain NULL if the user
+ * has not been authenticated using local UNIX system services.
+ */
+
+    pw = NULL;
+    if ((SESS_AUTH & flags)) {
+	pw = getpwnam(user);
+
+	endpwent();
+	/*
+	 * Here, we bail if we have no user account, because there is nothing
+	 * to verify against.
+	 */
+	if (pw == NULL)
+	    return SESSION_FAILED;
+
+#ifdef HAS_SHADOW
+
+	spwd = getspnam(user);
+	endspent();
+
+	/*
+	 * If there is no shadow entry for the user, then we can't verify the
+	 * account.
+	 */
+	if (spwd == NULL)
+	    return SESSION_FAILED;
+
+	/*
+	 * We check validity all the time, because if the password has expired,
+	 * then clearly we should not authenticate against it (if we're being
+	 * called for authentication only).  Thus, in this particular instance,
+	 * there is no real difference between using the AUTH, SESS or ACCT
+	 * flags, or combinations thereof.
+	 */
+	now = time(NULL) / 86400L;
+	if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
+	    || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
+	    && spwd->sp_lstchg >= 0
+	    && now >= spwd->sp_lstchg + spwd->sp_max)) {
+	    warn("Password for %s has expired", user);
+	    return SESSION_FAILED;
+	}
+
+	/* We have a valid shadow entry, keep the password */
+	pw->pw_passwd = spwd->sp_pwdp;
+
+#endif /* #ifdef HAS_SHADOW */
+
+	/*
+	 * If no passwd, don't let them login if we're authenticating.
+	 */
+        if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2)
+            return SESSION_FAILED;
+	cbuf = crypt(passwd, pw->pw_passwd);
+	if (!cbuf || strcmp(cbuf, pw->pw_passwd) != 0)
+            return SESSION_FAILED;
+    }
+
+#endif /* #ifdef USE_PAM */
+
+    /*
+     * Write a wtmp entry for this user.
+     */
+
+    if (SESS_ACCT & flags) {
+	if (strncmp(ttyName, "/dev/", 5) == 0)
+	    ttyName += 5;
+	logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
+	logged_in = 1;
+
+#if defined(_PATH_LASTLOG) && !defined(USE_PAM)
+	/*
+	 * Enter the user in lastlog only if he has been authenticated using
+	 * local system services.  If he has not, then we don't know what his
+	 * UID might be, and lastlog is indexed by UID.
+	 */
+	if (pw != NULL) {
+            struct lastlog ll;
+            int fd;
+	    time_t tnow;
+
+            if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+                (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
+                memset((void *)&ll, 0, sizeof(ll));
+		(void)time(&tnow);
+                ll.ll_time = tnow;
+                (void)strncpy(ll.ll_line, ttyName, sizeof(ll.ll_line));
+                (void)strncpy(ll.ll_host, ifname, sizeof(ll.ll_host));
+                (void)write(fd, (char *)&ll, sizeof(ll));
+                (void)close(fd);
+            }
+	}
+#endif /* _PATH_LASTLOG and not USE_PAM */
+	info("user %s logged in on tty %s intf %s", user, ttyName, ifname);
+    }
+
+    return SESSION_OK;
+}
+
+/*
+ * session_end - Logout the user.
+ */
+void
+session_end(const char* ttyName)
+{
+#ifdef USE_PAM
+    int pam_error = PAM_SUCCESS;
+
+    if (pamh != NULL) {
+        if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
+        PAM_session = 0;
+        pam_end (pamh, pam_error);
+        pamh = NULL;
+	/* Apparently the pam stuff does closelog(). */
+	reopen_log();
+    }
+#endif
+    if (logged_in) {
+	if (strncmp(ttyName, "/dev/", 5) == 0)
+	    ttyName += 5;
+	logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */
+	logged_in = 0;
+    }
+}
diff --git a/pppd/session.h b/pppd/session.h
new file mode 100644
index 0000000..bee8c41
--- /dev/null
+++ b/pppd/session.h
@@ -0,0 +1,91 @@
+/*
+ * session.c - PPP session control.
+ *
+ * Copyright (c) 2007 Diego Rivera. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SESSION_H
+#define __SESSION_H
+
+#define SESS_AUTH  1	/* Check User Authentication */
+#define SESS_ACCT  2	/* Check Account Validity */
+
+/* Convenience parameter to do the whole enchilada */
+#define SESS_ALL   (SESS_AUTH | SESS_ACCT)
+
+/*
+ * int session_start(...)
+ *
+ * Start a session, performing any necessary validations.
+ *
+ * Parameters:
+ * 	const int flags :
+ * 		Any combination of the SESS_XXX flags, to indicate what the function
+ *		should do as part of its checks
+ *
+ *	const char* user :
+ *		The username to validate.  May safely be null.
+ *
+ *	const char* passwd :
+ *		The password to validate the user with. May safely be null.
+ *
+ *	const char* tty :
+ *		The TTY the user is connected on. May safely be null.
+ *
+ *	char** msg :
+ *		A char* to return an error or success message.  This message will be returned
+ *		regardless of the result.  May safely be null.
+ *
+ * Return Value:
+ * 	Zero value for failure, non-zero value for successful session verification.
+ */
+int
+session_start(const int flags, const char* user, const char* passwd, const char* tty, char** msg);
+
+/* Added these macros for convenience... */
+#define session_auth(user, pass, tty, msg) \
+	session_start(SESS_AUTH, user, pass, tty, msg)
+
+#define session_check(user, pass, tty, msg) \
+	session_start(SESS_ACCT, user, pass, tty, msg)
+
+#define session_full(user, pass, tty, msg) \
+	session_start(SESS_ALL, user, pass, tty, msg)
+
+/*
+ * void session_end(...)
+ *
+ * End a previously-started session.
+ *
+ * Parameters:
+ *	const char* tty :
+ *		The TTY the user is connected on. May safely be null.
+ */
+void
+session_end(const char* tty);
+
+#endif
diff --git a/pppd/sha1.c b/pppd/sha1.c
index da142f1..f4f975c 100644
--- a/pppd/sha1.c
+++ b/pppd/sha1.c
@@ -18,6 +18,7 @@
 
 #include <string.h>
 #include <netinet/in.h>	/* htonl() */
+#include <net/ppp_defs.h>
 #include "sha1.h"
 
 static void
@@ -121,18 +122,17 @@
     j = (context->count[0] >> 3) & 63;
     if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
     context->count[1] += (len >> 29);
-    if ((j + len) > 63) {
-	memcpy(&context->buffer[j], data, (i = 64-j));
+    i = 64 - j;
+    while (len >= i) {
+	memcpy(&context->buffer[j], data, i);
 	SHA1_Transform(context->state, context->buffer);
-	for ( ; i + 63 < len; i += 64) {
-	    SHA1_Transform(context->state, &data[i]);
-	}
+	data += i;
+	len -= i;
+	i = 64;
 	j = 0;
     }
-    else
-	i = 0;
 
-    memcpy(&context->buffer[j], &data[i], len - i);
+    memcpy(&context->buffer[j], data, len);
 }
 
 
diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c
index 8736534..d5dbc7c 100644
--- a/pppd/sys-linux.c
+++ b/pppd/sys-linux.c
@@ -92,7 +92,6 @@
 #include <ctype.h>
 #include <termios.h>
 #include <unistd.h>
-#include <paths.h>
 
 /* This is in netdevice.h. However, this compile will fail miserably if
    you attempt to include netdevice.h because it has so many references
@@ -100,7 +99,7 @@
    really don't use it, but it must be defined, define it now. */
 
 #ifndef MAX_ADDR_LEN
-#define xxMAX_ADDR_LEN 7
+#define MAX_ADDR_LEN 7
 #endif
 
 #if __GLIBC__ >= 2
@@ -111,13 +110,11 @@
 #include <netinet/if_ether.h>
 #else
 #include <linux/types.h>
-#include <linux/tty.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
 #include <linux/route.h>
 #include <linux/if_ether.h>
 #endif
-#include <linux/sockios.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
@@ -148,7 +145,7 @@
 #endif
 
 #ifdef INET6
-#ifndef _LINUX_IN6_H
+#if !defined(_LINUX_IN6_H) && !defined(_UAPI_LINUX_IN6_H)
 /*
  *    This is in linux/include/net/ipv6.h.
  */
@@ -166,6 +163,10 @@
 	eui64_copy(eui64, sin6.s6_addr32[2]);			\
 	} while (0)
 
+#if defined(__ANDROID__)
+#include <net/route.h>
+#endif
+
 #endif /* INET6 */
 
 /* We can get an EIO error on an ioctl if the modem has hung up */
@@ -208,7 +209,8 @@
 static unsigned char inbuf[512]; /* buffer for chars read from loopback */
 
 static int	if_is_up;	/* Interface has been marked up */
-static u_int32_t default_route_gateway;	/* Gateway for default route added */
+static int	if6_is_up;	/* Interface has been marked up for IPv6, to help differentiate */
+static int	have_default_route;	/* Gateway for default route added */
 static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
 static char proxy_arp_dev[16];		/* Device for proxy arp entry */
 static u_int32_t our_old_addr;		/* for detecting address changes */
@@ -242,6 +244,7 @@
 static int set_kdebugflag(int level);
 static int ppp_registered(void);
 static int make_ppp_unit(void);
+static int setifstate (int u, int state);
 
 extern u_char	inpacket_buf[];	/* borrowed from main.c */
 
@@ -341,11 +344,14 @@
 	if_is_up = 0;
 	sifdown(0);
     }
+    if (if6_is_up)
+	sif6down(0);
+
 /*
  * Delete any routes through the device.
  */
-    if (default_route_gateway != 0)
-	cifdefaultroute(0, 0, default_route_gateway);
+    if (have_default_route)
+	cifdefaultroute(0, 0, 0);
 
     if (has_proxy_arp)
 	cifproxyarp(0, proxy_arp_addr);
@@ -852,6 +858,30 @@
 #ifdef B921600
     { 921600, B921600 },
 #endif
+#ifdef B1000000
+    { 1000000, B1000000 },
+#endif
+#ifdef B1152000
+    { 1152000, B1152000 },
+#endif
+#ifdef B1500000
+    { 1500000, B1500000 },
+#endif
+#ifdef B2000000
+    { 2000000, B2000000 },
+#endif
+#ifdef B2500000
+    { 2500000, B2500000 },
+#endif
+#ifdef B3000000
+    { 3000000, B3000000 },
+#endif
+#ifdef B3500000
+    { 3500000, B3500000 },
+#endif
+#ifdef B4000000
+    { 4000000, B4000000 },
+#endif
     { 0, 0 }
 };
 
@@ -945,6 +975,9 @@
 	break;
     }
 
+    if (stop_bits >= 2)
+	tios.c_cflag |= CSTOPB;
+
     speed = translate_speed(inspeed);
     if (speed) {
 	cfsetospeed (&tios, speed);
@@ -1392,7 +1425,7 @@
 	/* Default the mount location of /proc */
 	strlcpy (proc_path, "/proc", sizeof(proc_path));
 	proc_path_len = 5;
-	fp = fopen(_PATH_MOUNTED, "r");
+	fp = fopen(MOUNTED, "r");
 	if (fp != NULL) {
 	    while ((mntent = getmntent(fp)) != NULL) {
 		if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
@@ -1593,17 +1626,17 @@
     struct rtentry rt;
 
     if (defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) {
-	u_int32_t old_gateway = SIN_ADDR(rt.rt_gateway);
-
-	if (old_gateway != gateway)
-	    error("not replacing existing default route to %s [%I]",
-		  rt.rt_dev, old_gateway);
+	if (rt.rt_flags & RTF_GATEWAY)
+	    error("not replacing existing default route via %I",
+		  SIN_ADDR(rt.rt_gateway));
+	else
+	    error("not replacing existing default route through %s",
+		  rt.rt_dev);
 	return 0;
     }
 
-    memset (&rt, '\0', sizeof (rt));
-    SET_SA_FAMILY (rt.rt_dst,     AF_INET);
-    SET_SA_FAMILY (rt.rt_gateway, AF_INET);
+    memset (&rt, 0, sizeof (rt));
+    SET_SA_FAMILY (rt.rt_dst, AF_INET);
 
     rt.rt_dev = ifname;
 
@@ -1612,16 +1645,14 @@
 	SIN_ADDR(rt.rt_genmask) = 0L;
     }
 
-    SIN_ADDR(rt.rt_gateway) = gateway;
-
-    rt.rt_flags = RTF_UP | RTF_GATEWAY;
+    rt.rt_flags = RTF_UP;
     if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
 	if ( ! ok_error ( errno ))
 	    error("default route ioctl(SIOCADDRT): %m");
 	return 0;
     }
 
-    default_route_gateway = gateway;
+    have_default_route = 1;
     return 1;
 }
 
@@ -1634,20 +1665,20 @@
 {
     struct rtentry rt;
 
-    default_route_gateway = 0;
+    have_default_route = 0;
 
     memset (&rt, '\0', sizeof (rt));
     SET_SA_FAMILY (rt.rt_dst,     AF_INET);
     SET_SA_FAMILY (rt.rt_gateway, AF_INET);
 
+    rt.rt_dev = ifname;
+
     if (kernel_version > KVERSION(2,1,0)) {
 	SET_SA_FAMILY (rt.rt_genmask, AF_INET);
 	SIN_ADDR(rt.rt_genmask) = 0L;
     }
 
-    SIN_ADDR(rt.rt_gateway) = gateway;
-
-    rt.rt_flags = RTF_UP | RTF_GATEWAY;
+    rt.rt_flags = RTF_UP;
     if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
 	if (still_ppp()) {
 	    if ( ! ok_error ( errno ))
@@ -2006,14 +2037,6 @@
     int    my_version, my_modification, my_patch;
     int osmaj, osmin, ospatch;
 
-    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"
-	"module, try `/sbin/modprobe -v ppp'.  If that fails, check that\n"
-	"ppp.o exists in /lib/modules/`uname -r`/net.\n"
-	"See README.linux file in the ppp distribution for more details.\n";
-
     /* get the kernel version now, since we are called before sys_init */
     uname(&utsname);
     osmaj = osmin = ospatch = 0;
@@ -2021,21 +2044,6 @@
     kernel_version = KVERSION(osmaj, osmin, ospatch);
 
     fd = open("/dev/ppp", O_RDWR);
-#if 0
-    if (fd < 0 && errno == ENOENT) {
-	/* try making it and see if that helps. */
-	if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR,
-		  makedev(108, 0)) >= 0) {
-	    fd = open("/dev/ppp", O_RDWR);
-	    if (fd >= 0)
-		info("Created /dev/ppp device node");
-	    else
-		unlink("/dev/ppp");	/* didn't work, undo the mknod */
-	} else if (errno == EEXIST) {
-	    fd = open("/dev/ppp", O_RDWR);
-	}
-    }
-#endif /* 0 */
     if (fd >= 0) {
 	new_style_driver = 1;
 
@@ -2046,16 +2054,29 @@
 	close(fd);
 	return 1;
     }
+
     if (kernel_version >= KVERSION(2,3,13)) {
+	error("Couldn't open the /dev/ppp device: %m");
 	if (errno == ENOENT)
 	    no_ppp_msg =
-		"pppd is unable to open the /dev/ppp device.\n"
 		"You need to create the /dev/ppp device node by\n"
 		"executing the following command as root:\n"
 		"	mknod /dev/ppp c 108 0\n";
+	else if (errno == ENODEV || errno == ENXIO)
+	    no_ppp_msg =
+		"Please load the ppp_generic kernel module.\n";
 	return 0;
     }
 
+    /* we are running on a really really old kernel */
+    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"
+	"module, try `/sbin/modprobe -v ppp'.  If that fails, check that\n"
+	"ppp.o exists in /lib/modules/`uname -r`/net.\n"
+	"See README.linux file in the ppp distribution for more details.\n";
+
 /*
  * Open a socket for doing the ioctl operations.
  */
@@ -2139,6 +2160,9 @@
     return ok;
 }
 
+#if defined(__ANDROID__)
+void logwtmp (const char *line, const char *name, const char *host) {}
+#elif !defined(HAVE_LOGWTMP)
 /********************************************************************
  *
  * Update the wtmp file with the appropriate user name and tty device.
@@ -2212,7 +2236,7 @@
     }
 #endif
 }
-
+#endif /* HAVE_LOGWTMP */
 
 /********************************************************************
  *
@@ -2224,9 +2248,10 @@
 	u_int x;
 
 	if (vjcomp) {
-		if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0)
+		if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
 			error("Couldn't set up TCP header compression: %m");
-		vjcomp = 0;
+			vjcomp = 0;
+		}
 	}
 
 	x = (vjcomp? SC_COMP_TCP: 0) | (cidcomp? 0: SC_NO_TCP_CCID);
@@ -2242,25 +2267,12 @@
 
 int sifup(int u)
 {
-    struct ifreq ifr;
+    int ret;
 
-    memset (&ifr, '\0', sizeof (ifr));
-    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
-    if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-	if (! ok_error (errno))
-	    error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
-	return 0;
-    }
+    if ((ret = setifstate(u, 1)))
+	if_is_up++;
 
-    ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
-    if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-	if (! ok_error (errno))
-	    error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
-	return 0;
-    }
-    if_is_up++;
-
-    return 1;
+    return ret;
 }
 
 /********************************************************************
@@ -2271,11 +2283,59 @@
 
 int sifdown (int u)
 {
-    struct ifreq ifr;
-
     if (if_is_up && --if_is_up > 0)
 	return 1;
 
+#ifdef INET6
+    if (if6_is_up)
+	return 1;
+#endif /* INET6 */
+
+    return setifstate(u, 0);
+}
+
+#ifdef INET6
+/********************************************************************
+ *
+ * sif6up - Config the interface up for IPv6
+ */
+
+int sif6up(int u)
+{
+    int ret;
+
+    if ((ret = setifstate(u, 1)))
+	if6_is_up = 1;
+
+    return ret;
+}
+
+/********************************************************************
+ *
+ * sif6down - Disable the IPv6CP protocol and config the interface
+ *	      down if there are no remaining protocols.
+ */
+
+int sif6down (int u)
+{
+    if6_is_up = 0;
+
+    if (if_is_up)
+	return 1;
+
+    return setifstate(u, 0);
+}
+#endif /* INET6 */
+
+/********************************************************************
+ *
+ * setifstate - Config the interface up or down
+ */
+
+static int setifstate (int u, int state)
+{
+    struct ifreq ifr;
+
     memset (&ifr, '\0', sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
     if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
@@ -2284,7 +2344,10 @@
 	return 0;
     }
 
-    ifr.ifr_flags &= ~IFF_UP;
+    if (state)
+	ifr.ifr_flags |= IFF_UP;
+    else
+	ifr.ifr_flags &= ~IFF_UP;
     ifr.ifr_flags |= IFF_POINTOPOINT;
     if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno))
@@ -2330,11 +2393,13 @@
 /*
  *  Set the gateway address
  */
-    SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
-    if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
-	if (! ok_error (errno))
-	    error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__);
-	return (0);
+    if (his_adr != 0) {
+	SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
+	if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+	    if (! ok_error (errno))
+		error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__);
+	    return (0);
+	}
     }
 /*
  *  Set the netmask.
@@ -2812,8 +2877,8 @@
 
     if (ipxcp_protent.enabled_flag) {
 	struct stat stat_buf;
-	if ((path = path_to_procfs("/net/ipx/interface")) == 0
-	    || (path = path_to_procfs("/net/ipx_interface")) == 0
+	if (  ((path = path_to_procfs("/net/ipx/interface")) == NULL
+	    && (path = path_to_procfs("/net/ipx_interface")) == NULL)
 	    || lstat(path, &stat_buf) < 0) {
 	    error("IPX support is not present in the kernel\n");
 	    ipxcp_protent.enabled_flag = 0;
@@ -2867,7 +2932,7 @@
     /*
      * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
      */
-    ptr = ifr.ifr_hwaddr.sa_data;
+    ptr = (unsigned char *) ifr.ifr_hwaddr.sa_data;
     p_eui64->e8[0] = ptr[0] | 0x02;
     p_eui64->e8[1] = ptr[1];
     p_eui64->e8[2] = ptr[2];
diff --git a/pppd/sys-solaris.c b/pppd/sys-solaris.c
index fb8f0fd..93d9033 100644
--- a/pppd/sys-solaris.c
+++ b/pppd/sys-solaris.c
@@ -85,7 +85,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: sys-solaris.c,v 1.13 2004/11/04 10:02:26 paulus Exp $"
+#define RCSID	"$Id: sys-solaris.c,v 1.16 2008/01/30 14:26:53 carlsonj Exp $"
 
 #include <limits.h>
 #include <stdio.h>
@@ -194,7 +194,7 @@
 	eui64_copy(eui64, s->sin6_addr.s6_addr32[2]);	\
 	s->sin6_family = AF_INET6;		\
 	l.lifr_addr.ss_family = AF_INET6;	\
-	l.lifr_addrlen = 10;			\
+	l.lifr_addrlen = 64;			\
 	l.lifr_addr = laddr;			\
 	} while (0)
 
@@ -1225,6 +1225,9 @@
     }
 #endif
 
+    if (stop_bits >= 2)
+	tios.c_cflag |= CSTOPB;
+
     tios.c_cflag |= CS8 | CREAD | HUPCL;
     if (local || !modem)
 	tios.c_cflag |= CLOCAL;
@@ -2220,7 +2223,7 @@
     char *name;
     struct sockaddr *hwaddr;
 {
-    char *p, *q;
+    char *q;
     int unit, iffd, adrlen;
     unsigned char *adrp;
     char ifdev[24];
@@ -2465,8 +2468,13 @@
     if (name[0] != 0) {
 	/* logging in */
 	strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user));
-	strncpy(utmpx.ut_id, ifname, sizeof(utmpx.ut_id));
 	strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
+	strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
+	if (*host != '\0') {
+	    utmpx.ut_syslen = strlen(host) + 1;
+	    if (utmpx.ut_syslen > sizeof(utmpx.ut_host))
+		utmpx.ut_syslen = sizeof(utmpx.ut_host);
+	}
 	utmpx.ut_pid = getpid();
 	utmpx.ut_type = USER_PROCESS;
     } else {
@@ -2739,7 +2747,6 @@
 {
     int mfd, sfd;
     char *pty_name;
-    struct termios tios;
 
     mfd = open("/dev/ptmx", O_RDWR);
     if (mfd < 0) {
diff --git a/pppd/tty.c b/pppd/tty.c
index a911fde..c9a0b33 100644
--- a/pppd/tty.c
+++ b/pppd/tty.c
@@ -68,12 +68,13 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: tty.c,v 1.22 2004/11/13 12:07:29 paulus Exp $"
+#define RCSID	"$Id: tty.c,v 1.27 2008/07/01 12:27:56 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <unistd.h>
 #include <signal.h>
 #include <errno.h>
@@ -135,6 +136,7 @@
 
 /* option variables */
 int	crtscts = 0;		/* Use hardware flow control */
+int	stop_bits = 1;		/* Number of serial port stop bits */
 bool	modem = 1;		/* Use modem control lines */
 int	inspeed = 0;		/* Input/Output speed requested */
 bool	lockflag = 0;		/* Create lock file to lock the serial dev */
@@ -220,6 +222,9 @@
       OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
     { "xonxoff", o_special_noarg, (void *)setxonxoff,
       "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
+    { "stop-bits", o_int, &stop_bits,
+      "Number of stop bits in serial port",
+      OPT_PRIO | OPT_PRIVFIX | OPT_LIMITS, NULL, 2, 1 },
 
     { "modem", o_bool, &modem,
       "Use modem control lines", OPT_PRIO | 1 },
@@ -553,7 +558,6 @@
 	 * out and we want to use the modem lines, we reopen it later
 	 * in order to wait for the carrier detect signal from the modem.
 	 */
-	hungup = 0;
 	got_sigterm = 0;
 	connector = doing_callback? callback_script: connect_script;
 	if (devnam[0] != 0) {
@@ -563,12 +567,16 @@
 			int err, prio;
 
 			prio = privopen? OPRIO_ROOT: tty_options[0].priority;
-			if (prio < OPRIO_ROOT)
-				seteuid(uid);
+			if (prio < OPRIO_ROOT && seteuid(uid) == -1) {
+				error("Unable to drop privileges before opening %s: %m\n",
+				      devnam);
+				status = EXIT_OPEN_FAILED;
+				goto errret;
+			}
 			real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
 			err = errno;
-			if (prio < OPRIO_ROOT)
-				seteuid(0);
+			if (prio < OPRIO_ROOT && seteuid(0) == -1)
+				fatal("Unable to regain privileges");
 			if (real_ttyfd >= 0)
 				break;
 			errno = err;
@@ -684,11 +692,11 @@
 			if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
 				error("Initializer script failed");
 				status = EXIT_INIT_FAILED;
-				goto errret;
+				goto errretf;
 			}
 			if (got_sigterm) {
 				disconnect_tty();
-				goto errret;
+				goto errretf;
 			}
 			info("Serial port initialized.");
 		}
@@ -697,11 +705,11 @@
 			if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
 				error("Connect script failed");
 				status = EXIT_CONNECT_FAILED;
-				goto errret;
+				goto errretf;
 			}
 			if (got_sigterm) {
 				disconnect_tty();
-				goto errret;
+				goto errretf;
 			}
 			info("Serial connection established.");
 		}
@@ -750,19 +758,14 @@
 
 	return ttyfd;
 
+ errretf:
+	if (real_ttyfd >= 0)
+		tcflush(real_ttyfd, TCIOFLUSH);
  errret:
 	if (pty_master >= 0) {
 		close(pty_master);
 		pty_master = -1;
 	}
-	if (pty_slave >= 0) {
-		close(pty_slave);
-		pty_slave = -1;
-	}
-	if (real_ttyfd >= 0) {
-		close(real_ttyfd);
-		real_ttyfd = -1;
-	}
 	ttyfd = -1;
 	if (got_sigterm)
 		asked_to_quit = 1;
@@ -781,6 +784,7 @@
 	} else {
 		info("Serial link disconnected.");
 	}
+	stop_charshunt(NULL, 0);
 }
 
 void tty_close_fds()
@@ -944,8 +948,7 @@
 	exit(0);
     }
     charshunt_pid = cpid;
-    add_notifier(&sigreceived, stop_charshunt, 0);
-    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
+    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL, 1);
     return 1;
 }
 
diff --git a/pppd/upap.c b/pppd/upap.c
index 02fd8f8..8a73621 100644
--- a/pppd/upap.c
+++ b/pppd/upap.c
@@ -40,7 +40,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: upap.c,v 1.29 2002/12/04 23:03:33 paulus Exp $"
+#define RCSID	"$Id: upap.c,v 1.30 2005/07/13 10:41:58 paulus Exp $"
 
 /*
  * TODO:
@@ -498,7 +498,6 @@
 
     u->us_clientstate = UPAPCS_OPEN;
 
-    notice("PAP authentication succeeded");
     auth_withpeer_success(u->us_unit, PPP_PAP, 0);
 }
 
diff --git a/pppd/utils.c b/pppd/utils.c
index 726657e..87a118d 100644
--- a/pppd/utils.c
+++ b/pppd/utils.c
@@ -28,8 +28,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID	"$Id: utils.c,v 1.24 2004/11/04 10:02:26 paulus Exp $"
-#define LOG_TAG "pppd"
+#define RCSID	"$Id: utils.c,v 1.25 2008/06/03 12:06:37 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -60,10 +59,6 @@
 #include "fsm.h"
 #include "lcp.h"
 
-#ifdef ANDROID_CHANGES
-#include <android/log.h>
-#endif
-
 static const char rcsid[] = RCSID;
 
 #if defined(SUNOS4)
@@ -73,15 +68,14 @@
 static void logit __P((int, char *, va_list));
 static void log_write __P((int, char *));
 static void vslp_printer __P((void *, char *, ...));
-static void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
-			       void *));
+static void format_packet __P((u_char *, int, printer_func, void *));
 
 struct buffer_info {
     char *ptr;
     int len;
 };
 
-#if !defined(ANDROID_CHANGES)
+#if !defined(__ANDROID__)
 
 /*
  * strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
@@ -233,10 +227,17 @@
 	    switch (c) {
 	    case 'd':
 		val = va_arg(args, long);
-                if ((long)val < 0) {
+#if defined(__ANDROID__)
+		if ((long)val < 0) {
 		    neg = 1;
-                    val = (unsigned long)(-(long)val);
+		    val = (unsigned long)(-(long)val);
 		}
+#else
+		if (val < 0) {
+		    neg = 1;
+		    val = -val;
+		}
+#endif
 		base = 10;
 		break;
 	    case 'u':
@@ -244,8 +245,8 @@
 		base = 10;
 		break;
 	    default:
-		*buf++ = '%'; --buflen;
-		*buf++ = 'l'; --buflen;
+		OUTCHAR('%');
+		OUTCHAR('l');
 		--fmt;		/* so %lz outputs %lz etc. */
 		continue;
 	    }
@@ -295,19 +296,6 @@
 		     (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
 	    str = num;
 	    break;
-#if 0	/* not used, and breaks on S/390, apparently */
-	case 'r':
-	    f = va_arg(args, char *);
-#ifndef __powerpc__
-	    n = vslprintf(buf, buflen + 1, f, va_arg(args, va_list));
-#else
-	    /* On the powerpc, a va_list is an array of 1 structure */
-	    n = vslprintf(buf, buflen + 1, f, va_arg(args, void *));
-#endif
-	    buf += n;
-	    buflen -= n;
-	    continue;
-#endif
 	case 't':
 	    time(&t);
 	    str = ctime(&t);
@@ -318,6 +306,8 @@
 	case 'q':		/* quoted string */
 	    quoted = c == 'q';
 	    p = va_arg(args, unsigned char *);
+	    if (p == NULL)
+		    p = (unsigned char *)"<NULL>";
 	    if (fillch == '0' && prec >= 0) {
 		n = prec;
 	    } else {
@@ -483,7 +473,7 @@
 format_packet(p, len, printer, arg)
     u_char *p;
     int len;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
     int i, n;
@@ -535,7 +525,7 @@
 
 void
 init_pr_log(prefix, level)
-     char *prefix;
+     const char *prefix;
      int level;
 {
 	linep = line;
@@ -621,7 +611,7 @@
 print_string(p, len, printer, arg)
     char *p;
     int len;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
     int c;
@@ -661,42 +651,18 @@
     char *fmt;
     va_list args;
 {
-    int n;
     char buf[1024];
 
-    n = vslprintf(buf, sizeof(buf), fmt, args);
+    vslprintf(buf, sizeof(buf), fmt, args);
     log_write(level, buf);
 }
 
-#ifdef ANDROID_CHANGES
-
-#if LOG_PRIMASK != 7
-#error Syslog.h has been changed! Please fix this table!
-#endif
-
-static int syslog_to_android[] = {
-    [LOG_EMERG] = ANDROID_LOG_FATAL,
-    [LOG_ALERT] = ANDROID_LOG_FATAL,
-    [LOG_CRIT] = ANDROID_LOG_FATAL,
-    [LOG_ERR] = ANDROID_LOG_ERROR,
-    [LOG_WARNING] = ANDROID_LOG_WARN,
-    [LOG_NOTICE] = ANDROID_LOG_INFO,
-    [LOG_INFO] = ANDROID_LOG_INFO,
-    [LOG_DEBUG] = ANDROID_LOG_DEBUG,
-};
-
-#endif
-
 static void
 log_write(level, buf)
     int level;
     char *buf;
 {
-#ifndef ANDROID_CHANGES
     syslog(level, "%s", buf);
-
-    fprintf(stderr, buf);
-    
     if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
 	int n = strlen(buf);
 
@@ -706,9 +672,6 @@
 	    || write(log_to_fd, "\n", 1) != 1)
 	    log_to_fd = -1;
     }
-#else
-    __android_log_write(syslog_to_android[level], LOG_TAG, buf);
-#endif
 }
 
 /*