diff -Naur tomato/release/src/router/pppd/chat/chat.8 tomato121/release/src/router/pppd/chat/chat.8
--- tomato/release/src/router/pppd/chat/chat.8	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/chat.8	2008-04-22 21:44:05.000000000 +1000
@@ -1,6 +1,6 @@
 .\" -*- nroff -*-
 .\" manual page [] for chat 1.8
-.\" $Id: chat.8,v 1.1 2003/07/10 07:43:04 honor Exp $
+.\" $Id: chat.8,v 1.11 2004/11/13 12:22:49 paulus Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -23,81 +23,81 @@
 the remote's \fIpppd\fR process.
 .SH OPTIONS
 .TP
-.B -f \fI<chat file>
+.B \-f \fI<chat file>
 Read the chat script from the chat \fIfile\fR. The use of this option
 is mutually exclusive with the chat script parameters. The user must
 have read access to the file. Multiple lines are permitted in the
 file. Space or horizontal tab characters should be used to separate
 the strings.
 .TP
-.B -t \fI<timeout>
+.B \-t \fI<timeout>
 Set the timeout for the expected string to be received. If the string
 is not received within the time limit then the reply string is not
 sent. An alternate reply may be sent or the script will fail if there
 is no alternate reply string. A failed script will cause the
 \fIchat\fR program to terminate with a non-zero error code.
 .TP
-.B -r \fI<report file>
+.B \-r \fI<report file>
 Set the file for output of the report strings. If you use the keyword
 \fIREPORT\fR, the resulting strings are written to this file. If this
 option is not used and you still use \fIREPORT\fR keywords, the
 \fIstderr\fR file is used for the report strings.
 .TP
-.B -e
+.B \-e
 Start with the echo option turned on. Echoing may also be turned on
 or off at specific points in the chat script by using the \fIECHO\fR
 keyword. When echoing is enabled, all output from the modem is echoed
 to \fIstderr\fR.
 .TP
-.B -E
+.B \-E
 Enables environment variable substituion within chat scripts using the
 standard \fI$xxx\fR syntax.
 .TP
-.B -v
+.B \-v
 Request that the \fIchat\fR script be executed in a verbose mode. The
 \fIchat\fR program will then log the execution state of the chat
 script as well as all text received from the modem and the output
 strings sent to the modem.  The default is to log through the SYSLOG;
-the logging method may be altered with the -S and -s flags.
+the logging method may be altered with the \-S and \-s flags.
 .TP
-.B -V
+.B \-V
 Request that the \fIchat\fR script be executed in a stderr verbose
 mode. The \fIchat\fR program will then log all text received from the
 modem and the output strings sent to the modem to the stderr device. This
 device is usually the local console at the station running the chat or
 pppd program.
 .TP
-.B -s
-Use stderr.  All log messages from '-v' and all error messages will be
+.B \-s
+Use stderr.  All log messages from '\-v' and all error messages will be
 sent to stderr.
 .TP
-.B -S
+.B \-S
 Do not use the SYSLOG.  By default, error messages are sent to the
-SYSLOG.  The use of -S will prevent both log messages from '-v' and
+SYSLOG.  The use of \-S will prevent both log messages from '\-v' and
 error messages from being sent to the SYSLOG.
 .TP
-.B -T \fI<phone number>
+.B \-T \fI<phone number>
 Pass in an arbitary string, usually a phone number, that will be
 substituted for the \\T substitution metacharacter in a send string.
 .TP
-.B -U \fI<phone number 2>
+.B \-U \fI<phone number 2>
 Pass in a second string, usually a phone number, that will be
 substituted for the \\U substitution metacharacter in a send string.
 This is useful when dialing an ISDN terminal adapter that requires two 
 numbers.
 .TP
 .B script
-If the script is not specified in a file with the \fI-f\fR option then
+If the script is not specified in a file with the \fI\-f\fR option then
 the script is included as parameters to the \fIchat\fR program.
 .SH CHAT SCRIPT
 .LP
 The \fIchat\fR script defines the communications.
 .LP
-A script consists of one or more "expect-send" pairs of strings,
-separated by spaces, with an optional "subexpect-subsend" string pair,
+A script consists of one or more "expect\-send" pairs of strings,
+separated by spaces, with an optional "subexpect\-subsend" string pair,
 separated by a dash as in the following example:
 .IP
-ogin:-BREAK-ogin: ppp ssword: hello2u2
+ogin:\-BREAK\-ogin: ppp ssword: hello2u2
 .LP
 This line indicates that the \fIchat\fR program should expect the string
 "ogin:". If it fails to receive a login prompt within the time interval
@@ -136,7 +136,7 @@
 should include sub-expect sequences should the original string not be
 received. For example, consider the following script:
 .IP
-ogin:--ogin: ppp ssword: hello2u2
+ogin:\-\-ogin: ppp ssword: hello2u2
 .LP
 This would be a better script than the simple one used earlier. This would look
 for the same login: prompt, however, if one was not received, a single
@@ -154,7 +154,7 @@
 .IP
 # Now wait for the prompt and send logout string
 .br
-\'# ' logout
+\&'# ' logout
 .LP
 
 .SH SENDING DATA FROM A FILE
@@ -200,7 +200,7 @@
 at the terminal via standard error.  If \fBchat\fR is being run by
 pppd, and pppd is running as a daemon (detached from its controlling
 terminal), standard error will normally be redirected to the file
-/etc/ppp/connect-errors.
+/etc/ppp/connect\-errors.
 .LP
 \fBSAY\fR strings must be enclosed in single or double quotes. If
 carriage return and line feed are needed in the string to be output,
@@ -216,7 +216,7 @@
 .br
 SAY "Dialling your ISP...\\n" 
 .br
-\'' ATDT5551212 
+\&'' ATDT5551212 
 .br
 TIMEOUT 120
 .br
@@ -268,7 +268,7 @@
 ATDT5551212 to dial the telephone. The expected string is
 \fICONNECT\fR. If the string \fICONNECT\fR is received the remainder
 of the script is executed. In addition the program will write to the
-expect-file the string "CONNECT" plus any characters which follow it
+expect\-file the string "CONNECT" plus any characters which follow it
 such as the connection rate.
 .SH CLR_REPORT STRINGS
 This sequence allows for clearing previously set \fBREPORT\fR strings.
@@ -277,8 +277,8 @@
 entries so that new strings can use that space.
 .SH ECHO
 The echo options controls whether the output from the modem is echoed
-to \fIstderr\fR. This option may be set with the \fI-e\fR option, but
-it can also be controlled by the \fIECHO\fR keyword. The "expect-send"
+to \fIstderr\fR. This option may be set with the \fI\-e\fR option, but
+it can also be controlled by the \fIECHO\fR keyword. The "expect\-send"
 pair \fIECHO\fR \fION\fR enables echoing, and \fIECHO\fR \fIOFF\fR
 disables it. With this keyword you can select which parts of the
 conversation should be visible. For instance, with the following
@@ -326,13 +326,13 @@
 .br
 CONNECT \\c
 .br
-\'Callback login:' call_back_ID
+\&'Callback login:' call_back_ID
 .br
 HANGUP OFF
 .br
 ABORT "Bad Login"
 .br
-\'Callback Password:' Call_back_password
+\&'Callback Password:' Call_back_password
 .br
 TIMEOUT 120
 .br
@@ -342,18 +342,18 @@
 .br
 ABORT "NO CARRIER"
 .br
-ogin:--BREAK--ogin: real_account
+ogin:\-\-BREAK\-\-ogin: real_account
 .br
 \fIetc ...\fR
 .LP
 .SH TIMEOUT
-The initial timeout value is 45 seconds. This may be changed using the \fB-t\fR
+The initial timeout value is 45 seconds. This may be changed using the \fB\-t\fR
 parameter.
 .LP
 To change the timeout value for the next expect string, the following
 example may be used:
 .IP
-ATZ OK ATDT5551212 CONNECT TIMEOUT 10 ogin:--ogin: TIMEOUT 5 assword: hello2u2
+ATZ OK ATDT5551212 CONNECT TIMEOUT 10 ogin:\-\-ogin: TIMEOUT 5 assword: hello2u2
 .LP
 This will change the timeout to 10 seconds when it expects the login:
 prompt. The timeout is then changed to 5 seconds when it looks for the
@@ -434,11 +434,11 @@
 Send or expect a tab character.
 .TP
 .B \\\\T
-Send the phone number string as specified with the \fI-T\fR option
+Send the phone number string as specified with the \fI\-T\fR option
 .I (not valid in expect.)
 .TP
 .B \\\\U
-Send the phone number 2 string as specified with the \fI-U\fR option
+Send the phone number 2 string as specified with the \fI\-U\fR option
 .I (not valid in expect.)
 .TP
 .B \\\\\\\\
@@ -454,7 +454,7 @@
 For example, the character DC1 (17) is shown as \^^Q.
 .I (some characters are not valid in expect.)
 .SH ENVIRONMENT VARIABLES
-Environment variables are available within chat scripts, if  the \fI-E\fR
+Environment variables are available within chat scripts, if  the \fI\-E\fR
 option was specified in the command line. The metacharacter \fI$\fR is used
 to introduce the name of the environment variable to substitute. If the
 substition fails, because the requested environment variable is not set,
@@ -479,7 +479,7 @@
 .TP
 .B 3
 A timeout event occurred when there was an \fIexpect\fR string without
-having a "-subsend" string. This may mean that you did not program the
+having a "\-subsend" string. This may mean that you did not program the
 script correctly for the condition or that some unexpected event has
 occurred and the expected string could not be found.
 .TP
diff -Naur tomato/release/src/router/pppd/chat/chat.c tomato121/release/src/router/pppd/chat/chat.c
--- tomato/release/src/router/pppd/chat/chat.c	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/chat.c	2008-04-22 21:44:05.000000000 +1000
@@ -87,7 +87,7 @@
 #endif
 
 #ifndef lint
-static const char rcsid[] = "$Id: chat.c,v 1.1 2003/07/10 07:43:04 honor Exp $";
+static const char rcsid[] = "$Id: chat.c,v 1.30 2004/01/17 05:47:55 carlsonj Exp $";
 #endif
 
 #include <stdio.h>
@@ -203,7 +203,7 @@
 int clear_abort_next = 0;
 
 char *report_string[MAX_REPORTS] ;
-char  report_buffer[50] ;
+char  report_buffer[256] ;
 int n_reports = 0, report_next = 0, report_gathering = 0 ; 
 int clear_report_next = 0;
 
@@ -211,8 +211,9 @@
 
 void *dup_mem __P((void *b, size_t c));
 void *copy_of __P((char *s));
+char *grow __P((char *s, char **p, size_t len));
 void usage __P((void));
-void logf __P((const char *fmt, ...));
+void msgf __P((const char *fmt, ...));
 void fatal __P((int code, const char *fmt, ...));
 SIGTYPE sigalrm __P((int signo));
 SIGTYPE sigint __P((int signo));
@@ -260,6 +261,21 @@
     return dup_mem (s, strlen (s) + 1);
 }
 
+/* grow a char buffer and keep a pointer offset */
+char *grow(s, p, len)
+char *s;
+char **p;
+size_t len;
+{
+    size_t l = *p - s;		/* save p as distance into s */
+
+    s = realloc(s, len);
+    if (!s)
+	fatal(2, "memory error!");
+    *p = s + l;			/* restore p */
+    return s;
+}
+
 /*
  * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
  * [ -r report-file ] \
@@ -479,7 +495,7 @@
 /*
  * Send a message to syslog and/or stderr.
  */
-void logf __V((const char *fmt, ...))
+void msgf __V((const char *fmt, ...))
 {
     va_list args;
 
@@ -542,7 +558,7 @@
 	fatal(2, "Can't set file mode flags on stdin: %m");
 
     if (verbose)
-	logf("alarm");
+	msgf("alarm");
 }
 
 void unalarm()
@@ -669,67 +685,78 @@
 register char *s;
 int sending;  /* set to 1 when sending (putting) this string. */
 {
-    char temp[STR_LEN], env_str[STR_LEN], cur_chr;
-    register char *s1, *phchar;
+    char cur_chr;
+    char *s1, *p, *phchar;
     int add_return = sending;
+    size_t len = strlen(s) + 3;		/* see len comments below */
+
 #define isoctal(chr)	(((chr) >= '0') && ((chr) <= '7'))
 #define isalnumx(chr)	((((chr) >= '0') && ((chr) <= '9')) \
 			 || (((chr) >= 'a') && ((chr) <= 'z')) \
 			 || (((chr) >= 'A') && ((chr) <= 'Z')) \
 			 || (chr) == '_')
 
-    s1 = temp;
+    p = s1 = malloc(len);
+    if (!p)
+	fatal(2, "memory error!");
     while (*s) {
 	cur_chr = *s++;
 	if (cur_chr == '^') {
 	    cur_chr = *s++;
 	    if (cur_chr == '\0') {
-		*s1++ = '^';
+		*p++ = '^';
 		break;
 	    }
 	    cur_chr &= 0x1F;
 	    if (cur_chr != 0) {
-		*s1++ = cur_chr;
+		*p++ = cur_chr;
 	    }
 	    continue;
 	}
-	
+
 	if (use_env && cur_chr == '$') {		/* ARI */
-	    phchar = env_str;
+	    char c;
+
+	    phchar = s;
 	    while (isalnumx(*s))
-		*phchar++ = *s++;
-	    *phchar = '\0';
-	    phchar = getenv(env_str);
-	    if (phchar)
+		s++;
+	    c = *s;		/* save */
+	    *s = '\0';
+	    phchar = getenv(phchar);
+	    *s = c;		/* restore */
+	    if (phchar) {
+		len += strlen(phchar);
+		s1 = grow(s1, &p, len);
 		while (*phchar)
-		    *s1++ = *phchar++;
+		    *p++ = *phchar++;
+	    }
 	    continue;
 	}
 
 	if (cur_chr != '\\') {
-	    *s1++ = cur_chr;
+	    *p++ = cur_chr;
 	    continue;
 	}
 
 	cur_chr = *s++;
 	if (cur_chr == '\0') {
 	    if (sending) {
-		*s1++ = '\\';
-		*s1++ = '\\';
+		*p++ = '\\';
+		*p++ = '\\';	/* +1 for len */
 	    }
 	    break;
 	}
 
 	switch (cur_chr) {
 	case 'b':
-	    *s1++ = '\b';
+	    *p++ = '\b';
 	    break;
 
 	case 'c':
 	    if (sending && *s == '\0')
 		add_return = 0;
 	    else
-		*s1++ = cur_chr;
+		*p++ = cur_chr;
 	    break;
 
 	case '\\':
@@ -737,29 +764,33 @@
 	case 'p':
 	case 'd':
 	    if (sending)
-		*s1++ = '\\';
-	    *s1++ = cur_chr;
+		*p++ = '\\';
+	    *p++ = cur_chr;
 	    break;
 
 	case 'T':
 	    if (sending && phone_num) {
+		len += strlen(phone_num);
+		s1 = grow(s1, &p, len);
 		for (phchar = phone_num; *phchar != '\0'; phchar++) 
-		    *s1++ = *phchar;
+		    *p++ = *phchar;
 	    }
 	    else {
-		*s1++ = '\\';
-		*s1++ = 'T';
+		*p++ = '\\';
+		*p++ = 'T';
 	    }
 	    break;
 
 	case 'U':
 	    if (sending && phone_num2) {
+		len += strlen(phone_num2);
+		s1 = grow(s1, &p, len);
 		for (phchar = phone_num2; *phchar != '\0'; phchar++) 
-		    *s1++ = *phchar;
+		    *p++ = *phchar;
 	    }
 	    else {
-		*s1++ = '\\';
-		*s1++ = 'U';
+		*p++ = '\\';
+		*p++ = 'U';
 	    }
 	    break;
 
@@ -768,33 +799,33 @@
 	    break;
 
 	case 'r':
-	    *s1++ = '\r';
+	    *p++ = '\r';
 	    break;
 
 	case 'n':
-	    *s1++ = '\n';
+	    *p++ = '\n';
 	    break;
 
 	case 's':
-	    *s1++ = ' ';
+	    *p++ = ' ';
 	    break;
 
 	case 't':
-	    *s1++ = '\t';
+	    *p++ = '\t';
 	    break;
 
 	case 'N':
 	    if (sending) {
-		*s1++ = '\\';
-		*s1++ = '\0';
+		*p++ = '\\';
+		*p++ = '\0';
 	    }
 	    else
-		*s1++ = 'N';
+		*p++ = 'N';
 	    break;
-	    
+
 	case '$':			/* ARI */
 	    if (use_env) {
-		*s1++ = cur_chr;
+		*p++ = cur_chr;
 		break;
 	    }
 	    /* FALL THROUGH */
@@ -813,25 +844,24 @@
 
 		if (cur_chr != 0 || sending) {
 		    if (sending && (cur_chr == '\\' || cur_chr == 0))
-			*s1++ = '\\';
-		    *s1++ = cur_chr;
+			*p++ = '\\';
+		    *p++ = cur_chr;
 		}
 		break;
 	    }
 
 	    if (sending)
-		*s1++ = '\\';
-	    *s1++ = cur_chr;
+		*p++ = '\\';
+	    *p++ = cur_chr;
 	    break;
 	}
     }
 
     if (add_return)
-	*s1++ = '\r';
+	*p++ = '\r';	/* +2 for len */
 
-    *s1++ = '\0'; /* guarantee closure */
-    *s1++ = '\0'; /* terminate the string */
-    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
+    *p = '\0';		/* +3 for len */
+    return s1;
 }
 
 /*
@@ -971,9 +1001,9 @@
  * The expectation did not occur. This is terminal.
  */
     if (fail_reason)
-	logf("Failed (%s)", fail_reason);
+	msgf("Failed (%s)", fail_reason);
     else
-	logf("Failed");
+	msgf("Failed");
     terminate(exit_code);
 }
 
@@ -1049,7 +1079,7 @@
 	abort_string[n_aborts++] = s1;
 
 	if (verbose)
-	    logf("abort on (%v)", s);
+	    msgf("abort on (%v)", s);
 	return;
     }
 
@@ -1075,7 +1105,7 @@
 		pack++;
 		n_aborts--;
 		if (verbose)
-		    logf("clear abort on (%v)", s);
+		    msgf("clear abort on (%v)", s);
 	    }
 	}
         free(s1);
@@ -1092,14 +1122,14 @@
 	    fatal(2, "Too many REPORT strings");
 	
 	s1 = clean(s, 0);
-	
-	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+	if (strlen(s1) > strlen(s)
+	    || strlen(s1) + 1 > sizeof(fail_buffer))
 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
 	
 	report_string[n_reports++] = s1;
 	
 	if (verbose)
-	    logf("report (%v)", s);
+	    msgf("report (%v)", s);
 	return;
     }
 
@@ -1113,7 +1143,8 @@
 	
 	s1 = clean(s, 0);
 	
-	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+	if (strlen(s1) > strlen(s)
+	    || strlen(s1) + 1 > sizeof(fail_buffer))
 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
 
 	old_max = n_reports;
@@ -1124,7 +1155,7 @@
 		pack++;
 		n_reports--;
 		if (verbose)
-		    logf("clear report (%v)", s);
+		    msgf("clear report (%v)", s);
 	    }
 	}
         free(s1);
@@ -1142,7 +1173,7 @@
 	    timeout = DEFAULT_CHAT_TIMEOUT;
 
 	if (verbose)
-	    logf("timeout set to %d seconds", timeout);
+	    msgf("timeout set to %d seconds", timeout);
 
 	return;
     }
@@ -1205,7 +1236,7 @@
 	return ((int)c & 0x7F);
 
     default:
-	logf("warning: read() on stdin returned %d", status);
+	msgf("warning: read() on stdin returned %d", status);
 
     case -1:
 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
@@ -1233,7 +1264,7 @@
 	return (0);
 	
     default:
-	logf("warning: write() on stdout returned %d", status);
+	msgf("warning: write() on stdout returned %d", status);
 	
     case -1:
 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
@@ -1255,9 +1286,9 @@
 
 	if (verbose) {
 	    if (errno == EINTR || errno == EWOULDBLOCK)
-		logf(" -- write timed out");
+		msgf(" -- write timed out");
 	    else
-		logf(" -- write failed: %m");
+		msgf(" -- write failed: %m");
 	}
 	return (0);
     }
@@ -1272,9 +1303,9 @@
 
     if (verbose) {
 	if (quiet)
-	    logf("send (??????)");
+	    msgf("send (??????)");
 	else
-	    logf("send (%v)", s);
+	    msgf("send (%v)", s);
     }
 
     alarm(timeout); alarmed = 0;
@@ -1361,17 +1392,17 @@
     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
 
     if (verbose)
-	logf("expect (%v)", string);
+	msgf("expect (%v)", string);
 
     if (len > STR_LEN) {
-	logf("expect string is too long");
+	msgf("expect string is too long");
 	exit_code = 1;
 	return 0;
     }
 
     if (len == 0) {
 	if (verbose)
-	    logf("got it");
+	    msgf("got it");
 	return (1);
     }
 
@@ -1385,16 +1416,16 @@
 	    echo_stderr(c);
 	if (verbose && c == '\n') {
 	    if (s == logged)
-		logf("");	/* blank line */
+		msgf("");	/* blank line */
 	    else
-		logf("%0.*v", s - logged, logged);
+		msgf("%0.*v", s - logged, logged);
 	    logged = s + 1;
 	}
 
 	*s++ = c;
 
 	if (verbose && s >= logged + 80) {
-	    logf("%0.*v", s - logged, logged);
+	    msgf("%0.*v", s - logged, logged);
 	    logged = s;
 	}
 
@@ -1439,8 +1470,8 @@
 	    strncmp(s - len, string, len) == 0) {
 	    if (verbose) {
 		if (s > logged)
-		    logf("%0.*v", s - logged, logged);
-		logf(" -- got it\n");
+		    msgf("%0.*v", s - logged, logged);
+		msgf(" -- got it\n");
 	    }
 
 	    alarm(0);
@@ -1453,8 +1484,8 @@
 		strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
 		if (verbose) {
 		    if (s > logged)
-			logf("%0.*v", s - logged, logged);
-		    logf(" -- failed");
+			msgf("%0.*v", s - logged, logged);
+		    msgf(" -- failed");
 		}
 
 		alarm(0);
@@ -1468,7 +1499,7 @@
 	if (s >= end) {
 	    if (logged < s - minlen) {
 		if (verbose)
-		    logf("%0.*v", s - logged, logged);
+		    msgf("%0.*v", s - logged, logged);
 		logged = s;
 	    }
 	    s -= minlen;
@@ -1478,16 +1509,16 @@
 	}
 
 	if (alarmed && verbose)
-	    logf("warning: alarm synchronization problem");
+	    msgf("warning: alarm synchronization problem");
     }
 
     alarm(0);
     
     if (verbose && printed) {
 	if (alarmed)
-	    logf(" -- read timed out");
+	    msgf(" -- read timed out");
 	else
-	    logf(" -- read failed: %m");
+	    msgf(" -- read failed: %m");
     }
 
     exit_code = 3;
diff -Naur tomato/release/src/router/pppd/chat/Makefile tomato121/release/src/router/pppd/chat/Makefile
--- tomato/release/src/router/pppd/chat/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,32 @@
+#	$Id: Makefile.linux,v 1.15 2006/06/04 05:07:46 paulus Exp $
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+CDEF1=	-DTERMIOS			# Use the termios structure
+CDEF2=	-DSIGTYPE=void			# Standard definition
+CDEF3=	-UNO_SLEEP			# Use the usleep function
+CDEF4=	-DFNDELAY=O_NDELAY		# Old name value
+CDEFS=	$(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4)
+
+COPTS=	-O2 -g -pipe
+CFLAGS=	$(COPTS) $(CDEFS)
+
+INSTALL= install
+
+all:	chat
+
+chat:	chat.o
+	$(CC) -o chat chat.o
+
+chat.o:	chat.c
+	$(CC) -c $(CFLAGS) -o chat.o chat.c
+
+install: chat
+	mkdir -p $(BINDIR) $(MANDIR)
+	$(INSTALL) -s -c chat $(BINDIR)
+	$(INSTALL) -c -m 644 chat.8 $(MANDIR)
+
+clean:
+	rm -f chat.o chat *~
diff -Naur tomato/release/src/router/pppd/chat/Makefile.linux tomato121/release/src/router/pppd/chat/Makefile.linux
--- tomato/release/src/router/pppd/chat/Makefile.linux	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -1,4 +1,8 @@
-#	$Id: Makefile.linux,v 1.1 2003/07/10 07:43:04 honor Exp $
+#	$Id: Makefile.linux,v 1.15 2006/06/04 05:07:46 paulus Exp $
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
 
 CDEF1=	-DTERMIOS			# Use the termios structure
 CDEF2=	-DSIGTYPE=void			# Standard definition
@@ -6,7 +10,8 @@
 CDEF4=	-DFNDELAY=O_NDELAY		# Old name value
 CDEFS=	$(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4)
 
-CFLAGS=	-O2 -g -pipe $(CDEFS)
+COPTS=	-O2 -g -pipe
+CFLAGS=	$(COPTS) $(CDEFS)
 
 INSTALL= install
 
@@ -19,9 +24,9 @@
 	$(CC) -c $(CFLAGS) -o chat.o chat.c
 
 install: chat
-	mkdir -p $(BINDIR)
+	mkdir -p $(BINDIR) $(MANDIR)
 	$(INSTALL) -s -c chat $(BINDIR)
-	$(INSTALL) -c -m 644 chat.8 $(MANDIR)/man8
+	$(INSTALL) -c -m 644 chat.8 $(MANDIR)
 
 clean:
 	rm -f chat.o chat *~
diff -Naur tomato/release/src/router/pppd/chat/Makefile.linux.orig tomato121/release/src/router/pppd/chat/Makefile.linux.orig
--- tomato/release/src/router/pppd/chat/Makefile.linux.orig	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/Makefile.linux.orig	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,32 @@
+#	$Id: Makefile.linux,v 1.13 2004/11/03 11:51:47 paulus Exp $
+
+DESTDIR = @DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+CDEF1=	-DTERMIOS			# Use the termios structure
+CDEF2=	-DSIGTYPE=void			# Standard definition
+CDEF3=	-UNO_SLEEP			# Use the usleep function
+CDEF4=	-DFNDELAY=O_NDELAY		# Old name value
+CDEFS=	$(CDEF1) $(CDEF2) $(CDEF3) $(CDEF4)
+
+COPTS=	-O2 -g -pipe
+CFLAGS=	$(COPTS) $(CDEFS)
+
+INSTALL= install
+
+all:	chat
+
+chat:	chat.o
+	$(CC) -o chat chat.o
+
+chat.o:	chat.c
+	$(CC) -c $(CFLAGS) -o chat.o chat.c
+
+install: chat
+	mkdir -p $(BINDIR)
+	$(INSTALL) -s -c chat $(BINDIR)
+	$(INSTALL) -c -m 644 chat.8 $(MANDIR)
+
+clean:
+	rm -f chat.o chat *~
diff -Naur tomato/release/src/router/pppd/chat/Makefile.sol2 tomato121/release/src/router/pppd/chat/Makefile.sol2
--- tomato/release/src/router/pppd/chat/Makefile.sol2	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/Makefile.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -2,7 +2,7 @@
 # Makefile for chat on Solaris 2
 #
 
-include ../solaris/Makedefs
+include ../Makedefs.com
 
 CFLAGS = $(COPTS) -DNO_USLEEP -DSOL2
 
diff -Naur tomato/release/src/router/pppd/chat/Makefile.sunos4 tomato121/release/src/router/pppd/chat/Makefile.sunos4
--- tomato/release/src/router/pppd/chat/Makefile.sunos4	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/chat/Makefile.sunos4	1970-01-01 10:00:00.000000000 +1000
@@ -1,19 +0,0 @@
-#
-# Makefile for chat on suns
-#
-
-include ../sunos4/Makedefs
-
-CFLAGS = -DSUNOS $(COPTS)
-
-all:	chat
-
-chat: chat.o
-	$(CC) -o chat chat.o
-
-install: chat
-	$(INSTALL) -c chat $(BINDIR)/chat
-	$(INSTALL) -c -m 444 chat.8 $(MANDIR)/man8/chat.8
-
-clean:
-	rm -f *~ *.o chat
diff -Naur tomato/release/src/router/pppd/common/zlib.c tomato121/release/src/router/pppd/common/zlib.c
--- tomato/release/src/router/pppd/common/zlib.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/common/zlib.c	2008-04-22 21:44:05.000000000 +1000
@@ -10,7 +10,7 @@
  * - added inflateIncomp and deflateOutputPending
  * - allow strm->next_out to be NULL, meaning discard the output
  *
- * $Id: zlib.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * $Id: zlib.c,v 1.12 2002/04/02 13:34:03 dfs Exp $
  */
 
 /* 
@@ -3861,10 +3861,11 @@
                              &s->sub.trees.tb, z);
       if (t != Z_OK)
       {
-        ZFREE(z, s->sub.trees.blens);
         r = t;
-        if (r == Z_DATA_ERROR)
+        if (r == Z_DATA_ERROR) {
           s->mode = BADB;
+          ZFREE(z, s->sub.trees.blens);
+        }
         LEAVE
       }
       s->sub.trees.index = 0;
@@ -3929,14 +3930,16 @@
 #endif
         t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
                                   s->sub.trees.blens, &bl, &bd, &tl, &td, z);
-        ZFREE(z, s->sub.trees.blens);
         if (t != Z_OK)
         {
-          if (t == (uInt)Z_DATA_ERROR)
+          if (t == (uInt)Z_DATA_ERROR) {
             s->mode = BADB;
+            ZFREE(z, s->sub.trees.blens);
+          }
           r = t;
           LEAVE
         }
+        ZFREE(z, s->sub.trees.blens);
         Tracev((stderr, "inflate:       trees ok, %d * %d bytes used\n",
               inflate_hufts, sizeof(inflate_huft)));
         if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
diff -Naur tomato/release/src/router/pppd/common/zlib.h tomato121/release/src/router/pppd/common/zlib.h
--- tomato/release/src/router/pppd/common/zlib.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/common/zlib.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,4 +1,4 @@
-/*	$Id: zlib.h,v 1.1 2003/07/10 07:43:04 honor Exp $	*/
+/*	$Id: zlib.h,v 1.7 1997/11/27 06:03:33 paulus Exp $	*/
 
 /*
  * This file is derived from zlib.h and zconf.h from the zlib-1.0.4
diff -Naur tomato/release/src/router/pppd/configure tomato121/release/src/router/pppd/configure
--- tomato/release/src/router/pppd/configure	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/configure	2008-04-22 21:44:05.000000000 +1000
@@ -1,12 +1,15 @@
 #!/bin/sh
-# $Id: configure,v 1.1 2003/07/10 07:43:04 honor Exp $
+# $Id: configure,v 1.37 2005/06/26 23:53:17 carlsonj Exp $
+
+# Where to install stuff by default
+DESTDIR=/usr/local
+SYSCONF=/etc
 
 #  if [ -d /NextApps ]; then
 #    system="NeXTStep"
 #  else
   system=`uname -s`
   release=`uname -r`
-  machine=`uname -p`
   arch=`uname -m`
 #  fi
 state="unknown"
@@ -17,18 +20,58 @@
     ksrc="linux";
     state="known";;
   SunOS)
+    karch=`/usr/bin/isainfo -k`
     case $release in
 #      [0-3]*)	state="ancient";;
 #      4*)	state="known"; ksrc="sunos4"; makext="sunos4";;
-      5.[1-6]*)	state="known"; ksrc="solaris"; makext="sol2";;
-      5.[7-9]*)	state="known"; ksrc="solaris"; makext="sol2";
-              case $arch in
-		sun4u)	lp64='y';;
+      5.[7-9]*|5.[1-9][0-9]*)	state="known"; ksrc="solaris"; makext="sol2";
+              case "$karch" in
+		amd64)		archvariant='-64x';;
+		sparcv9)	archvariant='-64';;
 		*)	;;
 	      esac;;
-    esac;;
-    NetBSD|FreeBSD|ULTRIX|OSF1|NeXTStep|SINIX-?|UNIX_SV|UNIX_System_V)
-      state="notincluded";;
+      5.[1-6]*)	state="known"; ksrc="solaris"; makext="sol2";;
+    esac
+    if [ "$1" = gcc ]; then
+	shift
+	usegcc=gcc
+    fi
+    if [ "$1" = 32 ]; then
+	shift
+	archvariant=
+    fi
+    if [ -x /opt/SUNWspro/bin/cc -a "$usegcc" != gcc ] &&
+       /opt/SUNWspro/bin/cc -flags >/dev/null 2>&1; then
+      if [ "$archvariant" = "-64x" ]; then
+        ( cd /tmp; echo "int x;" > ppp$$.c
+	  /opt/SUNWspro/bin/cc -c -errwarn -xchip=opteron -xarch=amd64 ppp$$.c >/dev/null 2>&1 || (
+	    echo "WorkShop C is unable to make 64 bit modules, and your $karch system needs"
+	    echo "them.  Consider upgrading cc on this machine."
+	    rm -f ppp$$.c
+	    exit 1
+	  ) || exit 1
+	  rm -f ppp$$.c ppp$$.o
+        ) || exit 1
+      fi
+    elif gcc --version >/dev/null 2>&1; then
+      archvariant=gcc$archvariant
+      compiletype=.gcc
+      if [ "$archvariant" = "gcc-64" -o"$archvariant" = "gcc-64x" ]; then
+        ( cd /tmp; touch ppp$$.c
+	  gcc -c -m64 ppp$$.c >/dev/null 2>&1 || (
+	    echo "gcc is unable to make 64 bit modules, and your $karch system needs them."
+	    echo "Consider upgrading gcc on this machine, or switching to Sun WorkShop."
+	    rm -f ppp$$.c
+	    exit 1
+	  ) || exit 1
+	  rm -f ppp$$.c ppp$$.o
+        ) || exit 1
+      fi
+    else
+      echo "C compiler not found; hoping for the best."
+    fi;;
+  NetBSD|FreeBSD|ULTRIX|OSF1|NeXTStep|SINIX-?|UNIX_SV|UNIX_System_V)
+    state="notincluded";;
 #    NetBSD)
 #      makext="bsd";
 #      case $release in
@@ -105,37 +148,59 @@
     echo "This is a newer release of $system than is supported by"
     echo "this software.  It may or may not work.";;
   unknown)
-    echo "This software has not been ported to this system.  Sorry.";;
+    echo "This software has not been ported to $system.  Sorry.";;
   notincluded)
-    echo "Support for this system has not been included"
+    echo "Support for $system has not been included"
     echo "in this distribution.  Sorry.";;
+  known)
+    echo "Configuring for $system";;
 esac
 
-orig_makext=$makext
+# Parse arguments
+while [ $# -gt 0 ]; do
+    arg=$1
+    val=
+    shift
+    case $arg in
+	*=*)
+	    val=`expr "x$arg" : 'x[^=]*=\(.*\)'`
+	    arg=`expr "x$arg" : 'x\([^=]*\)=.*'`
+	    ;;
+	--prefix|--sysconf)
+	    if [ $# -eq 0 ]; then
+		echo "error: the $arg argument requires a value" 1>&2
+		exit 1
+	    fi
+	    val=$1
+	    shift
+	    ;;
+    esac
+    case $arg in
+	--prefix)	DESTDIR=$val ;;
+	--sysconfdir)	SYSCONF=$val ;;
+    esac
+done
+
+mkmkf() {
+    rm -f $2
+    if [ -f $1 ]; then
+	echo "  $2 <= $1"
+	sed -e "s,@DESTDIR@,$DESTDIR,g" -e "s,@SYSCONF@,$SYSCONF,g" $1 >$2
+    fi
+}
 
 if [ -d "$ksrc" ]; then
-  echo "Creating links to Makefiles."
-  rm -f Makefile
-  ln -s $ksrc/Makefile.top Makefile
-  echo "  Makefile -> $ksrc/Makefile.top"
-  if [ "$ksrc" = solaris ]; then
-    # Point to 64-bit Makefile extension
-    if [ "$lp64" = y ]; then 
-      makext=$makext-64 
-    fi
-    rm -f $ksrc/Makefile
-    ln -s Makefile.$makext $ksrc/Makefile
-    echo "  $ksrc/Makefile -> Makefile.$makext"
-    # Restore extension
-    if [ "$lp64" = y ]; then 
-      makext=$orig_makext 
-    fi
-  fi
-  for dir in pppd pppstats chat pppdump; do
-    rm -f $dir/Makefile
-    if [ -f $dir/Makefile.$makext ]; then
-      ln -s Makefile.$makext $dir/Makefile
-      echo "  $dir/Makefile -> Makefile.$makext"
+    echo "Creating Makefiles."
+    mkmkf $ksrc/Makefile.top Makefile
+    mkmkf $ksrc/Makedefs$compiletype Makedefs.com
+    for dir in pppd pppstats chat pppdump pppd/plugins pppd/plugins/rp-pppoe \
+	       pppd/plugins/radius pppd/plugins/pppoatm; do
+	mkmkf $dir/Makefile.$makext $dir/Makefile
+    done
+    if [ -f $ksrc/Makefile.$makext$archvariant ]; then
+	mkmkf $ksrc/Makefile.$makext$archvariant $ksrc/Makefile
     fi
-  done
+else
+  echo "Unable to locate kernel source $ksrc"
+  exit 1
 fi
diff -Naur tomato/release/src/router/pppd/contrib/pppgetpass/Makefile.linux tomato121/release/src/router/pppd/contrib/pppgetpass/Makefile.linux
--- tomato/release/src/router/pppd/contrib/pppgetpass/Makefile.linux	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/contrib/pppgetpass/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,16 @@
+all: pppgetpass.vt pppgetpass.gtk
+
+pppgetpass.vt: pppgetpass.vt.o
+
+pppgetpass.gtk: pppgetpass.gtk.o
+	$(CC) $(LDFLAGS) pppgetpass.gtk.o `gtk-config --libs` -o pppgetpass.gtk
+pppgetpass.gtk.o: pppgetpass.gtk.c
+	$(CC) $(CFLAGS) -c pppgetpass.gtk.c `gtk-config --cflags`
+
+install: all
+	install -m 755 pppgetpass.sh /usr/bin/pppgetpass
+	install -m 4755 -o root -g root pppgetpass.vt /usr/bin/
+	install -m 755 -o root -g root pppgetpass.gtk /usr/X11/bin/
+
+clean:
+	rm -f *.o pppgetpass.gtk pppgetpass.vt core
diff -Naur tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.8 tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.8
--- tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.8	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.8	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,18 @@
+.TH PPPGETPASS 8 "26 Sep 1999"
+.SH NAME
+pppgetpass \- prompt for PAP password
+.SH SYNOPSIS
+.B pppgetpass
+.I client server fd
+.SH DESCRIPTION
+.B pppgetpass
+the outer half of a plugin for PAP password prompting in pppd.
+If the peer requires PAP, and the
+.B passprompt.so
+plugin is loaded into pppd, it will run
+.B /usr/sbin/pppgetpass
+(or another program specified by the
+.B promptprog
+option) to prompt the user for the password.
+.SH SEE ALSO
+pppd(8)
diff -Naur tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.gtk.c tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.gtk.c
--- tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.gtk.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.gtk.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,92 @@
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtksignal.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+int outfd;
+int err;
+
+static void okpressed(void *widget, void *clientdata)
+{
+  GtkWidget *answer=clientdata;
+  gchar *pass;
+  int passlen;
+  ssize_t wrote;
+  (void)widget;
+
+  pass=gtk_entry_get_text(GTK_ENTRY(answer));
+
+  passlen=strlen(pass);
+  if(!passlen)
+    return;
+
+  if((wrote=write(outfd, pass, passlen))!=passlen) {
+    if(wrote<0)
+      syslog(LOG_ERR, "write error on outpipe: %m");
+    else
+      syslog(LOG_ERR, "short write on outpipe");
+    err=1;
+  }
+  gtk_main_quit();
+}
+
+int main(int argc, char **argv)
+{
+  GtkWidget *mainwindow, *vbox, *question, *answer, *ok;
+  char buf[1024];
+  gtk_init(&argc, &argv);
+
+  openlog(argv[0], LOG_PID, LOG_DAEMON);
+  if(argc!=4) {
+    syslog(LOG_WARNING, "Usage error");
+    return 1;
+  }
+  outfd=atoi(argv[3]);
+  mainwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title(GTK_WINDOW(mainwindow), "pppgetpass");
+  gtk_signal_connect(GTK_OBJECT(mainwindow), "destroy",
+                     GTK_SIGNAL_FUNC(gtk_main_quit), 0);
+
+  vbox=gtk_vbox_new(FALSE, 5);
+  gtk_container_add(GTK_CONTAINER(mainwindow), vbox);
+  gtk_widget_show(vbox);
+
+  if(argv[1][0] && argv[2][0])
+    snprintf(buf, sizeof buf, "Password for PPP client %s on server %s: ", argv[1], argv[2]);
+  else if(argv[1][0] && !argv[2][0])
+    snprintf(buf, sizeof buf, "Password for PPP client %s: ", argv[1]);
+  else if(!argv[1][0] && argv[2][0])
+    snprintf(buf, sizeof buf, "Password for PPP on server %s: ", argv[2]);
+  else
+    snprintf(buf, sizeof buf, "Enter PPP password: ");
+  question=gtk_label_new(buf);
+  gtk_box_pack_start(GTK_BOX(vbox), question, FALSE, TRUE, 0);
+  gtk_widget_show(question);
+
+  answer=gtk_entry_new();
+  gtk_entry_set_visibility(GTK_ENTRY(answer), 0);
+  gtk_box_pack_start(GTK_BOX(vbox), answer, FALSE, TRUE, 0);
+  gtk_widget_show(answer);
+
+  ok=gtk_button_new_with_label("OK");
+  gtk_box_pack_start(GTK_BOX(vbox), ok, FALSE, TRUE, 0);
+  gtk_signal_connect(GTK_OBJECT(ok), "clicked",
+                     GTK_SIGNAL_FUNC(okpressed), answer);
+  gtk_widget_show(ok);
+
+  gtk_widget_show(mainwindow);
+  gtk_main();
+
+  return err;
+}
diff -Naur tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.sh tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.sh
--- tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.sh	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.sh	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ -z "$DISPLAY" ]; then
+  exec pppgetpass.vt "$@"
+else
+  exec pppgetpass.gtk "$@"
+fi
diff -Naur tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.vt.c tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.vt.c
--- tomato/release/src/router/pppd/contrib/pppgetpass/pppgetpass.vt.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/contrib/pppgetpass/pppgetpass.vt.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,218 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <termios.h>
+#include <sys/vt.h>
+
+static int console_owner(uid_t, int);
+
+int main(int argc, char **argv)
+{
+  int console;
+  uid_t uid;
+  struct vt_stat origstate;
+  int openvtnum;
+  char openvtname[256];
+  int openvt;
+  gid_t gid;
+  int chowned;
+  FILE *fp;
+  struct termios t;
+  char pass[256], *nl;
+  int outfd, passlen;
+  ssize_t wrote;
+  console=open("/dev/console", O_RDWR);
+
+  uid=getuid();
+  gid=getgid();
+  seteuid(uid);
+
+  openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+  if(argc!=4) {
+    syslog(LOG_WARNING, "Usage error");
+    return 1;
+  }
+
+  if(console<0) {
+    syslog(LOG_ERR, "open(/dev/console): %m");
+    return 1;
+  }
+
+  if(ioctl(console, VT_GETSTATE, &origstate)<0) {
+    syslog(LOG_ERR, "VT_GETSTATE: %m");
+    return 1;
+  }
+
+  if(uid) {
+    if(!console_owner(uid, origstate.v_active)) {
+      int i;
+      for(i=0;i<64;++i) {
+        if(i!=origstate.v_active && console_owner(uid, i))
+          break;
+      }
+      if(i==64) {
+        syslog(LOG_WARNING, "run by uid %lu not at console", (unsigned long)uid);
+        return 1;
+      }
+    }
+  }
+
+  if(ioctl(console, VT_OPENQRY, &openvtnum)<0) {
+    syslog(LOG_ERR, "VT_OPENQRY: %m");
+    return 1;
+  }
+  if(openvtnum==-1) {
+    syslog(LOG_ERR, "No free VTs");
+    return 1;
+  }
+
+  snprintf(openvtname, sizeof openvtname, "/dev/tty%d", openvtnum);
+  seteuid(0);
+  openvt=open(openvtname, O_RDWR);
+  if(openvt<0) {
+    seteuid(uid);
+    syslog(LOG_ERR, "open(%s): %m", openvtname);
+    return 1;
+  }
+
+  chowned=fchown(openvt, uid, gid);
+  if(chowned<0) {
+    seteuid(uid);
+    syslog(LOG_ERR, "fchown(%s): %m", openvtname);
+    return 1;
+  }
+
+  close(console);
+
+  if(ioctl(openvt, VT_ACTIVATE, openvtnum)<0) {
+    seteuid(uid);
+    syslog(LOG_ERR, "VT_ACTIVATE(%d): %m", openvtnum);
+    return 1;
+  }
+
+  while(ioctl(openvt, VT_WAITACTIVE, openvtnum)<0) {
+    if(errno!=EINTR) {
+      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+      seteuid(uid);
+      syslog(LOG_ERR, "VT_WAITACTIVE(%d): %m", openvtnum);
+      return 1;
+    }
+  }
+
+  seteuid(uid);
+  fp=fdopen(openvt, "r+");
+  if(!fp) {
+    seteuid(0);
+    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+    seteuid(uid);
+    syslog(LOG_ERR, "fdopen(%s): %m", openvtname);
+    return 1;
+  }
+
+  if(tcgetattr(openvt, &t)<0) {
+    seteuid(0);
+    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+    seteuid(uid);
+    syslog(LOG_ERR, "tcgetattr(%s): %m", openvtname);
+    return 1;
+  }
+  t.c_lflag &= ~ECHO;
+  if(tcsetattr(openvt, TCSANOW, &t)<0) {
+    seteuid(0);
+    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+    seteuid(uid);
+    syslog(LOG_ERR, "tcsetattr(%s): %m", openvtname);
+    return 1;
+  }
+
+  if(fprintf(fp, "\033[2J\033[H")<0) {
+    seteuid(0);
+    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+    seteuid(uid);
+    syslog(LOG_ERR, "write error on %s: %m", openvtname);
+    return 1;
+  }
+  if(argv[1][0] && argv[2][0]) {
+    if(fprintf(fp, "Password for PPP client %s on server %s: ", argv[1], argv[2])<0) {
+      seteuid(0);
+      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+      seteuid(uid);
+      syslog(LOG_ERR, "write error on %s: %m", openvtname);
+      return 1;
+    }
+  } else if(argv[1][0] && !argv[2][0]) {
+    if(fprintf(fp, "Password for PPP client %s: ", argv[1])<0) {
+      syslog(LOG_ERR, "write error on %s: %m", openvtname);
+      seteuid(0);
+      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+      seteuid(uid);
+      return 1;
+    }
+  } else if(!argv[1][0] && argv[2][0]) {
+    if(fprintf(fp, "Password for PPP on server %s: ", argv[2])<0) {
+      seteuid(0);
+      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+      seteuid(uid);
+      syslog(LOG_ERR, "write error on %s: %m", openvtname);
+      return 1;
+    }
+  } else {
+    if(fprintf(fp, "Enter PPP password: ")<0) {
+      seteuid(0);
+      ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+      seteuid(uid);
+      syslog(LOG_ERR, "write error on %s: %m", openvtname);
+      return 1;
+    }
+  }
+
+  if(!fgets(pass, sizeof pass, fp)) {
+    seteuid(0);
+    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+    seteuid(uid);
+    if(ferror(fp)) {
+      syslog(LOG_ERR, "read error on %s: %m", openvtname);
+    }
+    return 1;
+  }
+  if((nl=strchr(pass, '\n'))) 
+    *nl=0;
+  passlen=strlen(pass);
+  
+  outfd=atoi(argv[3]);
+  if((wrote=write(outfd, pass, passlen))!=passlen) {
+    seteuid(0);
+    ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+    seteuid(uid);
+    if(wrote<0)
+      syslog(LOG_ERR, "write error on outpipe: %m");
+    else
+      syslog(LOG_ERR, "short write on outpipe");
+    return 1;
+  }
+
+  seteuid(0);
+  ioctl(openvt, VT_ACTIVATE, origstate.v_active);
+  seteuid(uid);
+  return 0;
+}
+
+static int console_owner(uid_t uid, int cons)
+{
+  char name[256];
+  struct stat st;
+  snprintf(name, sizeof name, "/dev/tty%d", cons);
+  if(stat(name, &st)<0) {
+    if(errno!=ENOENT)
+      syslog(LOG_ERR, "stat(%s): %m", name);
+    return 0;
+  }
+  return uid==st.st_uid;
+}
diff -Naur tomato/release/src/router/pppd/FAQ tomato121/release/src/router/pppd/FAQ
--- tomato/release/src/router/pppd/FAQ	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/FAQ	2008-04-22 21:44:05.000000000 +1000
@@ -378,8 +378,10 @@
 appropriate secret from /etc/ppp/pap-secrets.
 
 Microsoft also has a variant of CHAP which uses a different hashing
-arrangement from normal CHAP.  There is a client-side implementation
-of Microsoft's CHAP in ppp-2.3; see README.MSCHAP80.
+arrangement from normal CHAP.  There is a client-side (authenticatee)
+implementation of Microsoft's CHAP in ppp-2.3; see README.MSCHAP80.
+In ppp-2.4.2, server-side (authenticator) support was added as well as
+support for Microsoft CHAP v2; see README.MSCHAP81.
 
 
 ------------------------------------------------------------------------
diff -Naur tomato/release/src/router/pppd/include/linux/if_ether.h tomato121/release/src/router/pppd/include/linux/if_ether.h
--- tomato/release/src/router/pppd/include/linux/if_ether.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/linux/if_ether.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,99 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Global definitions for the Ethernet IEEE 802.3 interface.
+ *
+ * Version:	@(#)if_ether.h	1.0.1a	02/08/94
+ *
+ * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@super.org>
+ *		Alan Cox, <alan@redhat.com>
+ *		Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+ 
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
+
+/*
+ *	IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
+ *	and FCS/CRC (frame check sequence). 
+ */
+
+#define ETH_ALEN	6		/* Octets in one ethernet addr	 */
+#define ETH_HLEN	14		/* Total octets in header.	 */
+#define ETH_ZLEN	60		/* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN	1500		/* Max. octets in payload	 */
+#define ETH_FRAME_LEN	1514		/* Max. octets in frame sans FCS */
+
+/*
+ *	These are the defined Ethernet Protocol ID's.
+ */
+
+#define ETH_P_LOOP	0x0060		/* Ethernet Loopback packet	*/
+#define ETH_P_PUP	0x0200		/* Xerox PUP packet		*/
+#define ETH_P_PUPAT	0x0201		/* Xerox PUP Addr Trans packet	*/
+#define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
+#define ETH_P_X25	0x0805		/* CCITT X.25			*/
+#define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
+#define	ETH_P_BPQ	0x08FF		/* G8BPQ AX.25 Ethernet Packet	[ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_IEEEPUP	0x0a00		/* Xerox IEEE802.3 PUP packet */
+#define ETH_P_IEEEPUPAT	0x0a01		/* Xerox IEEE802.3 PUP Addr Trans packet */
+#define ETH_P_DEC       0x6000          /* DEC Assigned proto           */
+#define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */
+#define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */
+#define ETH_P_DNA_RT    0x6003          /* DEC DNA Routing              */
+#define ETH_P_LAT       0x6004          /* DEC LAT                      */
+#define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
+#define ETH_P_CUST      0x6006          /* DEC Customer use             */
+#define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
+#define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
+#define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
+#define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/
+#define ETH_P_IPX	0x8137		/* IPX over DIX			*/
+#define ETH_P_IPV6	0x86DD		/* IPv6 over bluebook		*/
+#define ETH_P_PPP_DISC	0x8863		/* PPPoE discovery messages     */
+#define ETH_P_PPP_SES	0x8864		/* PPPoE session messages	*/
+#define ETH_P_ATMMPOA	0x884c		/* MultiProtocol Over ATM	*/
+#define ETH_P_ATMFATE	0x8884		/* Frame-based ATM Transport
+					 * over Ethernet
+					 */
+
+/*
+ *	Non DIX types. Won't clash for 1500 types.
+ */
+ 
+#define ETH_P_802_3	0x0001		/* Dummy type for 802.3 frames  */
+#define ETH_P_AX25	0x0002		/* Dummy protocol id for AX.25  */
+#define ETH_P_ALL	0x0003		/* Every packet (be careful!!!) */
+#define ETH_P_802_2	0x0004		/* 802.2 frames 		*/
+#define ETH_P_SNAP	0x0005		/* Internal only		*/
+#define ETH_P_DDCMP     0x0006          /* DEC DDCMP: Internal only     */
+#define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
+#define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
+#define ETH_P_LOCALTALK 0x0009		/* Localtalk pseudo type 	*/
+#define ETH_P_PPPTALK	0x0010		/* Dummy type for Atalk over PPP*/
+#define ETH_P_TR_802_2	0x0011		/* 802.2 frames 		*/
+#define ETH_P_MOBITEX	0x0015		/* Mobitex (kaz@cafe.net)	*/
+#define ETH_P_CONTROL	0x0016		/* Card specific control frames */
+#define ETH_P_IRDA	0x0017		/* Linux-IrDA			*/
+#define ETH_P_ECONET	0x0018		/* Acorn Econet			*/
+
+/*
+ *	This is an Ethernet frame header.
+ */
+ 
+struct ethhdr 
+{
+	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/
+	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/
+	unsigned short	h_proto;		/* packet type ID field	*/
+};
+
+#endif	/* _LINUX_IF_ETHER_H */
diff -Naur tomato/release/src/router/pppd/include/linux/if_ppp.h tomato121/release/src/router/pppd/include/linux/if_ppp.h
--- tomato/release/src/router/pppd/include/linux/if_ppp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/include/linux/if_ppp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,45 @@
-/*	$Id: if_ppp.h,v 1.1 2003/07/10 07:43:04 honor Exp $	*/
+/*	$Id: if_ppp.h,v 1.23 2002/12/06 09:49:15 paulus Exp $	*/
 
 /*
  * if_ppp.h - Point-to-Point Protocol definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  *
  */
 
diff -Naur tomato/release/src/router/pppd/include/linux/if_pppox.h tomato121/release/src/router/pppd/include/linux/if_pppox.h
--- tomato/release/src/router/pppd/include/linux/if_pppox.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/linux/if_pppox.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,145 @@
+/***************************************************************************
+ * Linux PPP over X - Generic PPP transport layer sockets
+ * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516) 
+ *
+ * This file supplies definitions required by the PPP over Ethernet driver
+ * (pppox.c).  All version information wrt this file is located in pppox.c
+ *
+ * License:
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef __LINUX_IF_PPPOX_H
+#define __LINUX_IF_PPPOX_H
+
+
+#include <asm/types.h>
+#include <asm/byteorder.h>
+
+#ifdef  __KERNEL__
+#include <linux/if_ether.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <asm/semaphore.h>
+#include <linux/ppp_channel.h>
+#endif /* __KERNEL__ */
+
+/* For user-space programs to pick up these definitions
+ * which they wouldn't get otherwise without defining __KERNEL__
+ */
+#ifndef AF_PPPOX
+#define AF_PPPOX	24
+#define PF_PPPOX	AF_PPPOX
+#endif /* !(AF_PPPOX) */
+
+/************************************************************************ 
+ * PPPoE addressing definition 
+ */ 
+typedef __u16 sid_t; 
+struct pppoe_addr{ 
+       sid_t           sid;                    /* Session identifier */ 
+       unsigned char   remote[ETH_ALEN];       /* Remote address */ 
+       char            dev[IFNAMSIZ];          /* Local device to use */ 
+}; 
+ 
+/************************************************************************ 
+ * Protocols supported by AF_PPPOX 
+ */ 
+#define PX_PROTO_OE    0 /* Currently just PPPoE */
+#define PX_MAX_PROTO   1	
+ 
+struct sockaddr_pppox { 
+       sa_family_t     sa_family;            /* address family, AF_PPPOX */ 
+       unsigned int    sa_protocol;          /* protocol identifier */ 
+       union{ 
+               struct pppoe_addr       pppoe; 
+       }sa_addr; 
+}__attribute__ ((packed)); 
+
+
+/*********************************************************************
+ *
+ * ioctl interface for defining forwarding of connections
+ *
+ ********************************************************************/
+
+#define PPPOEIOCSFWD	_IOW(0xB1 ,0, sizeof(struct sockaddr_pppox))
+#define PPPOEIOCDFWD	_IO(0xB1 ,1)
+/*#define PPPOEIOCGFWD	_IOWR(0xB1,2, sizeof(struct sockaddr_pppox))*/
+
+/* Codes to identify message types */
+#define PADI_CODE	0x09
+#define PADO_CODE	0x07
+#define PADR_CODE	0x19
+#define PADS_CODE	0x65
+#define PADT_CODE	0xa7
+struct pppoe_tag {
+	__u16 tag_type;
+	__u16 tag_len;
+	char tag_data[0];
+} __attribute ((packed));
+
+/* Tag identifiers */
+#define PTT_EOL		__constant_htons(0x0000)
+#define PTT_SRV_NAME	__constant_htons(0x0101)
+#define PTT_AC_NAME	__constant_htons(0x0102)
+#define PTT_HOST_UNIQ	__constant_htons(0x0103)
+#define PTT_AC_COOKIE	__constant_htons(0x0104)
+#define PTT_VENDOR 	__constant_htons(0x0105)
+#define PTT_RELAY_SID	__constant_htons(0x0110)
+#define PTT_SRV_ERR     __constant_htons(0x0201)
+#define PTT_SYS_ERR  	__constant_htons(0x0202)
+#define PTT_GEN_ERR  	__constant_htons(0x0203)
+
+struct pppoe_hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 ver : 4;
+	__u8 type : 4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u8 type : 4;
+	__u8 ver : 4;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+	__u8 code;
+	__u16 sid;
+	__u16 length;
+	struct pppoe_tag tag[0];
+} __attribute__ ((packed));
+
+#ifdef __KERNEL__
+
+struct pppox_proto {
+	int (*create)(struct socket *sock);
+	int (*ioctl)(struct socket *sock, unsigned int cmd,
+		     unsigned long arg);
+};
+
+extern int register_pppox_proto(int proto_num, struct pppox_proto *pp);
+extern void unregister_pppox_proto(int proto_num);
+extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */
+extern int pppox_channel_ioctl(struct ppp_channel *pc, unsigned int cmd,
+			       unsigned long arg);
+
+/* PPPoE socket states */
+enum {
+    PPPOX_NONE		= 0,  /* initial state */
+    PPPOX_CONNECTED	= 1,  /* connection established ==TCP_ESTABLISHED */
+    PPPOX_BOUND		= 2,  /* bound to ppp device */
+    PPPOX_RELAY		= 4,  /* forwarding is enabled */
+    PPPOX_ZOMBIE        = 8,  /* dead, but still connected */
+    PPPOX_DEAD		= 16
+};
+
+extern struct ppp_channel_ops pppoe_chan_ops;
+
+extern int pppox_proto_init(struct net_proto *np);
+
+#endif /* __KERNEL__ */
+
+#endif /* !(__LINUX_IF_PPPOX_H) */
diff -Naur tomato/release/src/router/pppd/include/linux/if_pppvar.h tomato121/release/src/router/pppd/include/linux/if_pppvar.h
--- tomato/release/src/router/pppd/include/linux/if_pppvar.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/include/linux/if_pppvar.h	2008-04-22 21:44:05.000000000 +1000
@@ -2,43 +2,74 @@
 /*
  * if_pppvar.h - private structures and declarations for PPP.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
+ *
+ * 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.
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/include/linux/ppp-comp.h tomato121/release/src/router/pppd/include/linux/ppp-comp.h
--- tomato/release/src/router/pppd/include/linux/ppp-comp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/include/linux/ppp-comp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,34 +1,42 @@
 /*
  * ppp-comp.h - Definitions for doing PPP packet compression.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1984 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ppp-comp.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: ppp-comp.h,v 1.10 2002/12/06 09:49:15 paulus Exp $
  */
 
 /*
- *  ==FILEVERSION 980319==
+ *  ==FILEVERSION 20020715==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -48,10 +56,10 @@
  */
 
 #ifndef DO_BSD_COMPRESS
-#define DO_BSD_COMPRESS	1	/* by default, include BSD-Compress */
+#define DO_BSD_COMPRESS	0	/* by default, don't include BSD-Compress */
 #endif
 #ifndef DO_DEFLATE
-#define DO_DEFLATE	1	/* by default, include Deflate */
+#define DO_DEFLATE	0	/* by default, don't include Deflate */
 #endif
 #define DO_PREDICTOR_1	0
 #define DO_PREDICTOR_2	0
@@ -187,6 +195,127 @@
 #define DEFLATE_CHK_SEQUENCE	0
 
 /*
+ * Definitions for MPPE.
+ */
+
+#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)
+
+/* MPPE/MPPC definitions by J.D.*/
+#define MPPE_STATELESS          MPPE_H_BIT	/* configuration bit H */
+#define MPPE_40BIT              MPPE_L_BIT	/* configuration bit L */
+#define MPPE_56BIT              MPPE_M_BIT	/* configuration bit M */
+#define MPPE_128BIT             MPPE_S_BIT	/* configuration bit S */
+#define MPPE_MPPC               MPPE_C_BIT	/* configuration bit C */
+
+/*
+ * Definitions for Stac LZS.
+ */
+
+#define CI_LZS			17	/* config option for Stac LZS */
+#define CILEN_LZS		5	/* length of config option */
+
+#define LZS_OVHD		4	/* max. LZS overhead */
+#define LZS_HIST_LEN		2048	/* LZS history size */
+#define LZS_MAX_CCOUNT		0x0FFF	/* max. coherency counter value */
+
+#define LZS_MODE_NONE		0
+#define LZS_MODE_LCB		1
+#define LZS_MODE_CRC		2
+#define LZS_MODE_SEQ		3
+#define LZS_MODE_EXT		4
+
+#define LZS_EXT_BIT_FLUSHED	0x80	/* bit A */
+#define LZS_EXT_BIT_COMP	0x20	/* bit C */
+
+/*
  * Definitions for other, as yet unsupported, compression methods.
  */
 
diff -Naur tomato/release/src/router/pppd/include/linux/ppp_defs.h tomato121/release/src/router/pppd/include/linux/ppp_defs.h
--- tomato/release/src/router/pppd/include/linux/ppp_defs.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/include/linux/ppp_defs.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,34 +1,42 @@
-/*	$Id: ppp_defs.h,v 1.1 2003/07/10 07:43:04 honor Exp $	*/
+/*	$Id: ppp_defs.h,v 1.11 2002/12/06 09:49:15 paulus Exp $	*/
 
 /*
  * ppp_defs.h - PPP definitions.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  */
 
 /*
- *  ==FILEVERSION 20000114==
+ *  ==FILEVERSION 20020521==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -80,6 +88,8 @@
 #define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
 #define PPP_CCPFRAG	0x80fb	/* CCP at link level (below MP bundle) */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
+#define PPP_ECPFRAG	0x8055	/* ECP at link level (below MP bundle) */
+#define PPP_ECP		0x8053	/* Encryption Control Protocol */
 #define PPP_LCP		0xc021	/* Link Control Protocol */
 #define PPP_PAP		0xc023	/* Password Authentication Protocol */
 #define PPP_LQR		0xc025	/* Link Quality Report protocol */
diff -Naur tomato/release/src/router/pppd/include/net/if_ppp.h tomato121/release/src/router/pppd/include/net/if_ppp.h
--- tomato/release/src/router/pppd/include/net/if_ppp.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/net/if_ppp.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,156 @@
+/*	$Id: if_ppp.h,v 1.19 2002/12/06 09:49:15 paulus Exp $	*/
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * 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.
+ */
+
+#ifndef _IF_PPP_H_
+#define _IF_PPP_H_
+
+/*
+ * Bit definitions for flags.
+ */
+#define SC_COMP_PROT	0x00000001	/* protocol compression (output) */
+#define SC_COMP_AC	0x00000002	/* header compression (output) */
+#define	SC_COMP_TCP	0x00000004	/* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID	0x00000008	/* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC	0x00000010	/* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP	0x00000020	/* reject TCP (VJ) comp. on input */
+#define SC_CCP_OPEN	0x00000040	/* Look at CCP packets */
+#define SC_CCP_UP	0x00000080	/* May send/recv compressed packets */
+#define SC_DEBUG	0x00010000	/* enable debug messages */
+#define SC_LOG_INPKT	0x00020000	/* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent */
+#define SC_LOG_RAWIN	0x00080000	/* log all chars received */
+#define SC_LOG_FLUSH	0x00100000	/* log all chars flushed */
+#define SC_RCV_B7_0	0x01000000	/* have rcvd char with bit 7 = 0 */
+#define SC_RCV_B7_1	0x02000000	/* have rcvd char with bit 7 = 1 */
+#define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
+#define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
+#define SC_SYNC		0x00200000	/* use synchronous HDLC framing */
+#define	SC_MASK		0x0fff00ff	/* bits that user can change */
+
+/*
+ * State bits in sc_flags, not changeable by user.
+ */
+#define SC_TIMEOUT	0x00000400	/* timeout is currently pending */
+#define SC_VJ_RESET	0x00000800	/* need to reset VJ decomp */
+#define SC_COMP_RUN	0x00001000	/* compressor has been inited */
+#define SC_DECOMP_RUN	0x00002000	/* decompressor has been inited */
+#define SC_DC_ERROR	0x00004000	/* non-fatal decomp error detected */
+#define SC_DC_FERROR	0x00008000	/* fatal decomp error detected */
+#define SC_TBUSY	0x10000000	/* xmitter doesn't need a packet yet */
+#define SC_PKTLOST	0x20000000	/* have lost or dropped a packet */
+#define	SC_FLUSH	0x40000000	/* flush input until next PPP_FLAG */
+#define	SC_ESCAPED	0x80000000	/* saw a PPP_ESCAPE */
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+    int		protocol;	/* PPP procotol, e.g. PPP_IP */
+    enum NPmode	mode;
+};
+
+/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
+struct ppp_option_data {
+	u_char	*ptr;
+	u_int	length;
+	int	transmit;
+};
+
+struct ifpppstatsreq {
+    char ifr_name[IFNAMSIZ];
+    struct ppp_stats stats;
+};
+
+struct ifpppcstatsreq {
+    char ifr_name[IFNAMSIZ];
+    struct ppp_comp_stats stats;
+};
+
+/*
+ * Ioctl definitions.
+ */
+
+#define	PPPIOCGFLAGS	_IOR('t', 90, int)	/* get configuration flags */
+#define	PPPIOCSFLAGS	_IOW('t', 89, int)	/* set configuration flags */
+#define	PPPIOCGASYNCMAP	_IOR('t', 88, int)	/* get async map */
+#define	PPPIOCSASYNCMAP	_IOW('t', 87, int)	/* set async map */
+#define	PPPIOCGUNIT	_IOR('t', 86, int)	/* get ppp unit number */
+#define	PPPIOCGRASYNCMAP _IOR('t', 85, int)	/* get receive async map */
+#define	PPPIOCSRASYNCMAP _IOW('t', 84, int)	/* set receive async map */
+#define	PPPIOCGMRU	_IOR('t', 83, int)	/* get max receive unit */
+#define	PPPIOCSMRU	_IOW('t', 82, int)	/* set max receive unit */
+#define	PPPIOCSMAXCID	_IOW('t', 81, int)	/* set VJ max slot ID */
+#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */
+#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */
+#define PPPIOCXFERUNIT	_IO('t', 78)		/* transfer PPP unit */
+#define PPPIOCSCOMPRESS	_IOW('t', 77, struct ppp_option_data)
+#define PPPIOCGNPMODE	_IOWR('t', 76, struct npioctl) /* get NP mode */
+#define PPPIOCSNPMODE	_IOW('t', 75, struct npioctl)  /* set NP mode */
+#define PPPIOCGIDLE	_IOR('t', 74, struct ppp_idle) /* get idle time */
+#ifdef PPP_FILTER
+#define PPPIOCSPASS	_IOW('t', 71, struct bpf_program) /* set pass filter */
+#define PPPIOCSACTIVE	_IOW('t', 70, struct bpf_program) /* set active filt */
+#endif /* PPP_FILTER */
+
+/* PPPIOC[GS]MTU are alternatives to SIOC[GS]IFMTU, used under Ultrix */
+#define PPPIOCGMTU	_IOR('t', 73, int)	/* get interface MTU */
+#define PPPIOCSMTU	_IOW('t', 72, int)	/* set interface MTU */
+
+/*
+ * These two are interface ioctls so that pppstats can do them on
+ * a socket without having to open the serial device.
+ */
+#define SIOCGPPPSTATS	_IOWR('i', 123, struct ifpppstatsreq)
+#define SIOCGPPPCSTATS	_IOWR('i', 122, struct ifpppcstatsreq)
+
+#if !defined(ifr_mtu)
+#define ifr_mtu	ifr_ifru.ifru_metric
+#endif
+
+#if (defined(_KERNEL) || defined(KERNEL)) && !defined(NeXT)
+void pppattach __P((void));
+void pppintr __P((void));
+#endif
+#endif /* _IF_PPP_H_ */
diff -Naur tomato/release/src/router/pppd/include/net/ppp-comp.h tomato121/release/src/router/pppd/include/net/ppp-comp.h
--- tomato/release/src/router/pppd/include/net/ppp-comp.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/net/ppp-comp.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,293 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1984 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.
+ *
+ * $Id: ppp-comp.h,v 1.13 2002/12/06 09:49:15 paulus Exp $
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS	0	/* by default, don't include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE	0	/* by default, don't include Deflate */
+#endif
+#define DO_PREDICTOR_1	0
+#define DO_PREDICTOR_2	0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+#ifdef PACKETPTR
+struct compressor {
+	int	compress_proto;	/* CCP compression protocol number */
+
+	/* Allocate space for a compressor (transmit side) */
+	void	*(*comp_alloc) __P((u_char *options, int opt_len));
+	/* Free space used by a compressor */
+	void	(*comp_free) __P((void *state));
+	/* Initialize a compressor */
+	int	(*comp_init) __P((void *state, u_char *options, int opt_len,
+				  int unit, int hdrlen, int debug));
+	/* Reset a compressor */
+	void	(*comp_reset) __P((void *state));
+	/* Compress a packet */
+	int	(*compress) __P((void *state, PACKETPTR *mret,
+				 PACKETPTR mp, int orig_len, int max_len));
+	/* Return compression statistics */
+	void	(*comp_stat) __P((void *state, struct compstat *stats));
+
+	/* Allocate space for a decompressor (receive side) */
+	void	*(*decomp_alloc) __P((u_char *options, int opt_len));
+	/* Free space used by a decompressor */
+	void	(*decomp_free) __P((void *state));
+	/* Initialize a decompressor */
+	int	(*decomp_init) __P((void *state, u_char *options, int opt_len,
+				    int unit, int hdrlen, int mru, int debug));
+	/* Reset a decompressor */
+	void	(*decomp_reset) __P((void *state));
+	/* Decompress a packet. */
+	int	(*decompress) __P((void *state, PACKETPTR mp,
+				   PACKETPTR *dmpp));
+	/* Update state for an incompressible packet received */
+	void	(*incomp) __P((void *state, PACKETPTR mp));
+	/* Return decompression statistics */
+	void	(*decomp_stat) __P((void *state, struct compstat *stats));
+};
+#endif /* PACKETPTR */
+
+/*
+ * Return values for decompress routine.
+ * We need to make these distinctions so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression.  This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+#define DECOMP_OK		0	/* everything went OK */
+#define DECOMP_ERROR		1	/* error detected before decomp. */
+#define DECOMP_FATALERROR	2	/* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+#define CCP_CONFREQ	1
+#define CCP_CONFACK	2
+#define CCP_TERMREQ	5
+#define CCP_TERMACK	6
+#define CCP_RESETREQ	14
+#define CCP_RESETACK	15
+
+/*
+ * Max # bytes for a CCP option
+ */
+#define CCP_MAX_OPTION_LENGTH	32
+
+/*
+ * Parts of a CCP packet.
+ */
+#define CCP_CODE(dp)		((dp)[0])
+#define CCP_ID(dp)		((dp)[1])
+#define CCP_LENGTH(dp)		(((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN		4
+
+#define CCP_OPT_CODE(dp)	((dp)[0])
+#define CCP_OPT_LENGTH(dp)	((dp)[1])
+#define CCP_OPT_MINLEN		2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+#define CI_BSD_COMPRESS		21	/* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS	3	/* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x)		((x) & 0x1F)	/* number of bits requested */
+#define BSD_VERSION(x)		((x) >> 5)	/* version of option format */
+#define BSD_CURRENT_VERSION	1		/* current version number */
+#define BSD_MAKE_OPT(v, n)	(((v) << 5) | (n))
+
+#define BSD_MIN_BITS		9	/* smallest code size supported */
+#define BSD_MAX_BITS		15	/* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+#define CI_DEFLATE		26	/* config option for Deflate */
+#define CI_DEFLATE_DRAFT	24	/* value used in original draft RFC */
+#define CILEN_DEFLATE		4	/* length of its config option */
+
+#define DEFLATE_MIN_SIZE	8
+#define DEFLATE_MAX_SIZE	15
+#define DEFLATE_METHOD_VAL	8
+#define DEFLATE_SIZE(x)		(((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x)	((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w)	((((w) - DEFLATE_MIN_SIZE) << 4) \
+				 + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE	0
+
+/*
+ * Definitions for MPPE.
+ */
+#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)
+
+/* MPPE/MPPC definitions by J.D.*/
+#define MPPE_STATELESS          MPPE_H_BIT	/* configuration bit H */
+#define MPPE_40BIT              MPPE_L_BIT	/* configuration bit L */
+#define MPPE_56BIT              MPPE_M_BIT	/* configuration bit M */
+#define MPPE_128BIT             MPPE_S_BIT	/* configuration bit S */
+#define MPPE_MPPC               MPPE_C_BIT	/* configuration bit C */
+
+/*
+ * Definitions for Stac LZS.
+ */
+
+#define CI_LZS			17	/* config option for Stac LZS */
+#define CILEN_LZS		5	/* length of config option */
+
+#define LZS_OVHD		4	/* max. LZS overhead */
+#define LZS_HIST_LEN		2048	/* LZS history size */
+#define LZS_MAX_CCOUNT		0x0FFF	/* max. coherency counter value */
+
+#define LZS_MODE_NONE		0
+#define LZS_MODE_LCB		1
+#define LZS_MODE_CRC		2
+#define LZS_MODE_SEQ		3
+#define LZS_MODE_EXT		4
+
+#define LZS_EXT_BIT_FLUSHED	0x80	/* bit A */
+#define LZS_EXT_BIT_COMP	0x20	/* bit C */
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+#define CI_PREDICTOR_1		1	/* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1	2	/* length of its config option */
+#define CI_PREDICTOR_2		2	/* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2	2	/* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff -Naur tomato/release/src/router/pppd/include/net/ppp_defs.h tomato121/release/src/router/pppd/include/net/ppp_defs.h
--- tomato/release/src/router/pppd/include/net/ppp_defs.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/net/ppp_defs.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,194 @@
+/*	$Id: ppp_defs.h,v 1.17 2002/12/06 09:49:15 paulus Exp $	*/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1984 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.
+ */
+
+#ifndef _PPP_DEFS_H_
+#define _PPP_DEFS_H_
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN	4	/* octets for standard ppp header */
+#define PPP_FCSLEN	2	/* octets for FCS */
+
+/*
+ * Packet sizes
+ *
+ * Note - lcp shouldn't be allowed to negotiate stuff outside these
+ *	  limits.  See lcp.h in the pppd directory.
+ * (XXX - these constants should simply be shared by lcp.c instead
+ *	  of living in lcp.h)
+ */
+#define	PPP_MTU		1500	/* Default MTU (size of Info field) */
+#define PPP_MAXMTU	65535 - (PPP_HDRLEN + PPP_FCSLEN)
+#define PPP_MINMTU	64
+#define PPP_MRU		1500	/* default MRU = max length of info field */
+#define PPP_MAXMRU	65000	/* Largest MRU we allow */
+#define PPP_MINMRU	128
+
+#define PPP_ADDRESS(p)	(((u_char *)(p))[0])
+#define PPP_CONTROL(p)	(((u_char *)(p))[1])
+#define PPP_PROTOCOL(p)	((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define	PPP_ALLSTATIONS	0xff	/* All-Stations broadcast address */
+#define	PPP_UI		0x03	/* Unnumbered Information */
+#define	PPP_FLAG	0x7e	/* Flag Sequence */
+#define	PPP_ESCAPE	0x7d	/* Asynchronous Control Escape */
+#define	PPP_TRANS	0x20	/* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP		0x21	/* Internet Protocol */
+#define PPP_AT		0x29	/* AppleTalk Protocol */
+#define PPP_IPX		0x2b	/* IPX protocol */
+#define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
+#define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
+#define PPP_COMP	0xfd	/* compressed packet */
+#define PPP_IPCP	0x8021	/* IP Control Protocol */
+#define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
+#define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
+#define PPP_CCP		0x80fd	/* Compression Control Protocol */
+#define PPP_ECP		0x8053	/* Encryption Control Protocol */
+#define PPP_LCP		0xc021	/* Link Control Protocol */
+#define PPP_PAP		0xc023	/* Password Authentication Protocol */
+#define PPP_LQR		0xc025	/* Link Quality Report protocol */
+#define PPP_CHAP	0xc223	/* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP	0xc029	/* Callback Control Protocol */
+#define PPP_EAP		0xc227	/* Extensible Authentication Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS	0xffff	/* Initial FCS value */
+#define PPP_GOODFCS	0xf0b8	/* Good final FCS value */
+#define PPP_FCS(fcs, c)	(((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * A 32-bit unsigned integral type.
+ */
+
+#if !defined(__BIT_TYPES_DEFINED__) && !defined(_BITYPES) \
+ && !defined(__FreeBSD__) && (NS_TARGET < 40)
+#ifdef	UINT32_T
+typedef UINT32_T	u_int32_t;
+#else
+typedef unsigned int	u_int32_t;
+typedef unsigned short  u_int16_t;
+#endif
+#endif
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u_int32_t	ext_accm[8];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+    NPMODE_PASS,		/* pass the packet through */
+    NPMODE_DROP,		/* silently drop the packet */
+    NPMODE_ERROR,		/* return an error */
+    NPMODE_QUEUE		/* save it up for later. */
+};
+
+/*
+ * Statistics.
+ */
+struct pppstat	{
+    unsigned int ppp_ibytes;	/* bytes received */
+    unsigned int ppp_ipackets;	/* packets received */
+    unsigned int ppp_ierrors;	/* receive errors */
+    unsigned int ppp_obytes;	/* bytes sent */
+    unsigned int ppp_opackets;	/* packets sent */
+    unsigned int ppp_oerrors;	/* transmit errors */
+};
+
+struct vjstat {
+    unsigned int vjs_packets;	/* outbound packets */
+    unsigned int vjs_compressed; /* outbound compressed packets */
+    unsigned int vjs_searches;	/* searches for connection state */
+    unsigned int vjs_misses;	/* times couldn't find conn. state */
+    unsigned int vjs_uncompressedin; /* inbound uncompressed packets */
+    unsigned int vjs_compressedin; /* inbound compressed packets */
+    unsigned int vjs_errorin;	/* inbound unknown type packets */
+    unsigned int vjs_tossed;	/* inbound packets tossed because of error */
+};
+
+struct ppp_stats {
+    struct pppstat p;		/* basic PPP statistics */
+    struct vjstat vj;		/* VJ header compression statistics */
+};
+
+struct compstat {
+    unsigned int unc_bytes;	/* total uncompressed bytes */
+    unsigned int unc_packets;	/* total uncompressed packets */
+    unsigned int comp_bytes;	/* compressed bytes */
+    unsigned int comp_packets;	/* compressed packets */
+    unsigned int inc_bytes;	/* incompressible bytes */
+    unsigned int inc_packets;	/* incompressible packets */
+    unsigned int ratio;		/* recent compression ratio << 8 */
+};
+
+struct ppp_comp_stats {
+    struct compstat c;		/* packet compression statistics */
+    struct compstat d;		/* packet decompression statistics */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+    time_t xmit_idle;		/* time since last NP packet sent */
+    time_t recv_idle;		/* time since last NP packet received */
+};
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x)	x
+#else
+#define __P(x)	()
+#endif
+#endif
+
+#endif /* _PPP_DEFS_H_ */
diff -Naur tomato/release/src/router/pppd/include/net/pppio.h tomato121/release/src/router/pppd/include/net/pppio.h
--- tomato/release/src/router/pppd/include/net/pppio.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/net/pppio.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,107 @@
+/*
+ * pppio.h - ioctl and other misc. definitions for STREAMS modules.
+ *
+ * Copyright (c) 1994 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.
+ *
+ * $Id: pppio.h,v 1.9 2002/12/06 09:49:15 paulus Exp $
+ */
+
+#define _PPPIO(n)	(('p' << 8) + (n))
+
+#define PPPIO_NEWPPA	_PPPIO(130)	/* allocate a new PPP unit */
+#define PPPIO_GETSTAT	_PPPIO(131)	/* get PPP statistics */
+#define PPPIO_GETCSTAT	_PPPIO(132)	/* get PPP compression stats */
+#define PPPIO_MTU	_PPPIO(133)	/* set max transmission unit */
+#define PPPIO_MRU	_PPPIO(134)	/* set max receive unit */
+#define PPPIO_CFLAGS	_PPPIO(135)	/* set/clear/get compression flags */
+#define PPPIO_XCOMP	_PPPIO(136)	/* alloc transmit compressor */
+#define PPPIO_RCOMP	_PPPIO(137)	/* alloc receive decompressor */
+#define PPPIO_XACCM	_PPPIO(138)	/* set transmit asyncmap */
+#define PPPIO_RACCM	_PPPIO(139)	/* set receive asyncmap */
+#define PPPIO_VJINIT	_PPPIO(140)	/* initialize VJ comp/decomp */
+#define PPPIO_ATTACH	_PPPIO(141)	/* attach to a ppa (without putmsg) */
+#define PPPIO_LASTMOD	_PPPIO(142)	/* mark last ppp module */
+#define PPPIO_GCLEAN	_PPPIO(143)	/* get 8-bit-clean flags */
+#define PPPIO_DEBUG	_PPPIO(144)	/* request debug information */
+#define PPPIO_BIND	_PPPIO(145)	/* bind to SAP */
+#define PPPIO_NPMODE	_PPPIO(146)	/* set mode for handling data pkts */
+#define PPPIO_GIDLE	_PPPIO(147)	/* get time since last data pkt */
+#define PPPIO_PASSFILT	_PPPIO(148)	/* set filter for packets to pass */
+#define PPPIO_ACTIVEFILT _PPPIO(149)	/* set filter for "link active" pkts */
+
+/*
+ * Values for PPPIO_CFLAGS
+ */
+#define COMP_AC		0x1		/* compress address/control */
+#define DECOMP_AC	0x2		/* decompress address/control */
+#define COMP_PROT	0x4		/* compress PPP protocol */
+#define DECOMP_PROT	0x8		/* decompress PPP protocol */
+
+#define COMP_VJC	0x10		/* compress TCP/IP headers */
+#define COMP_VJCCID	0x20		/* compress connection ID as well */
+#define DECOMP_VJC	0x40		/* decompress TCP/IP headers */
+#define DECOMP_VJCCID	0x80		/* accept compressed connection ID */
+
+#define CCP_ISOPEN	0x100		/* look at CCP packets */
+#define CCP_ISUP	0x200		/* do packet comp/decomp */
+#define CCP_ERROR	0x400		/* (status) error in packet decomp */
+#define CCP_FATALERROR	0x800		/* (status) fatal error ditto */
+#define CCP_COMP_RUN	0x1000		/* (status) seen CCP ack sent */
+#define CCP_DECOMP_RUN	0x2000		/* (status) seen CCP ack rcvd */
+
+/*
+ * Values for 8-bit-clean flags.
+ */
+#define RCV_B7_0	1		/* have rcvd char with bit 7 = 0 */
+#define RCV_B7_1	2		/* have rcvd char with bit 7 = 1 */
+#define RCV_EVNP	4		/* have rcvd char with even parity */
+#define RCV_ODDP	8		/* have rcvd char with odd parity */
+
+/*
+ * Values for the first byte of M_CTL messages passed between
+ * PPP modules.
+ */
+#define PPPCTL_OERROR	0xe0		/* output error [up] */
+#define PPPCTL_IERROR	0xe1		/* input error (e.g. FCS) [up] */
+#define PPPCTL_MTU	0xe2		/* set MTU [down] */
+#define PPPCTL_MRU	0xe3		/* set MRU [down] */
+#define PPPCTL_UNIT	0xe4		/* note PPP unit number [down] */
+
+/*
+ * Values for the integer argument to PPPIO_DEBUG.
+ */
+#define PPPDBG_DUMP	0x10000		/* print out debug info now */
+#define PPPDBG_LOG	0x100		/* log various things */
+#define PPPDBG_DRIVER	0		/* identifies ppp driver as target */
+#define PPPDBG_IF	1		/* identifies ppp network i/f target */
+#define PPPDBG_COMP	2		/* identifies ppp compression target */
+#define PPPDBG_AHDLC	3		/* identifies ppp async hdlc target */
diff -Naur tomato/release/src/router/pppd/include/net/slcompress.h tomato121/release/src/router/pppd/include/net/slcompress.h
--- tomato/release/src/router/pppd/include/net/slcompress.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/net/slcompress.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,148 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: slcompress.h,v 1.4 1994/09/21 06:50:08 paulus Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ */
+
+#ifndef _SLCOMPRESS_H_
+#define _SLCOMPRESS_H_
+
+#define MAX_STATES 16		/* must be > 2 and < 256 */
+#define MAX_HDR MLEN		/* XXX 4bsd-ism: should really be 128 */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ * 
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C	0x40	/* flag bits for what changed in a packet */
+#define NEW_I	0x20
+#define NEW_S	0x08
+#define NEW_A	0x04
+#define NEW_W	0x02
+#define NEW_U	0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+	struct cstate *cs_next;	/* next most recently used cstate (xmit only) */
+	u_short cs_hlen;	/* size of hdr (receive only) */
+	u_char cs_id;		/* connection # associated with this state */
+	u_char cs_filler;
+	union {
+		char csu_hdr[MAX_HDR];
+		struct ip csu_ip;	/* ip/tcp hdr from most recent packet */
+	} slcs_u;
+};
+#define cs_ip slcs_u.csu_ip
+#define cs_hdr slcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these
+ * per line).
+ */
+struct slcompress {
+	struct cstate *last_cs;	/* most recently used tstate */
+	u_char last_recv;	/* last rcvd conn. id */
+	u_char last_xmit;	/* last sent conn. id */
+	u_short flags;
+#ifndef SL_NO_STATS
+	int sls_packets;	/* outbound packets */
+	int sls_compressed;	/* outbound compressed packets */
+	int sls_searches;	/* searches for connection state */
+	int sls_misses;		/* times couldn't find conn. state */
+	int sls_uncompressedin;	/* inbound uncompressed packets */
+	int sls_compressedin;	/* inbound compressed packets */
+	int sls_errorin;	/* inbound unknown type packets */
+	int sls_tossed;		/* inbound packets tossed because of error */
+#endif
+	struct cstate tstate[MAX_STATES];	/* xmit connection states */
+	struct cstate rstate[MAX_STATES];	/* receive connection states */
+};
+/* flag values */
+#define SLF_TOSS 1		/* tossing rcvd frames because of input err */
+
+void	sl_compress_init __P((struct slcompress *));
+void	sl_compress_setup __P((struct slcompress *, int));
+u_int	sl_compress_tcp __P((struct mbuf *,
+	    struct ip *, struct slcompress *, int));
+int	sl_uncompress_tcp __P((u_char **, int, u_int, struct slcompress *));
+int	sl_uncompress_tcp_core __P((u_char *, int, int, u_int,
+	    struct slcompress *, u_char **, u_int *));
+
+#endif /* _SLCOMPRESS_H_ */
diff -Naur tomato/release/src/router/pppd/include/net/vjcompress.h tomato121/release/src/router/pppd/include/net/vjcompress.h
--- tomato/release/src/router/pppd/include/net/vjcompress.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/include/net/vjcompress.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,144 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: vjcompress.h,v 1.3 1996/05/28 00:55:33 paulus Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ */
+
+#ifndef _VJCOMPRESS_H_
+#define _VJCOMPRESS_H_
+
+#define MAX_STATES 16		/* must be > 2 and < 256 */
+#define MAX_HDR	   128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ * 
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C	0x40	/* flag bits for what changed in a packet */
+#define NEW_I	0x20
+#define NEW_S	0x08
+#define NEW_A	0x04
+#define NEW_W	0x02
+#define NEW_U	0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+    struct cstate *cs_next;	/* next most recently used state (xmit only) */
+    u_short cs_hlen;		/* size of hdr (receive only) */
+    u_char cs_id;		/* connection # associated with this state */
+    u_char cs_filler;
+    union {
+	char csu_hdr[MAX_HDR];
+	struct ip csu_ip;	/* ip/tcp hdr from most recent packet */
+    } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+    struct cstate *last_cs;	/* most recently used tstate */
+    u_char last_recv;		/* last rcvd conn. id */
+    u_char last_xmit;		/* last sent conn. id */
+    u_short flags;
+#ifndef VJ_NO_STATS
+    struct vjstat stats;
+#endif
+    struct cstate tstate[MAX_STATES];	/* xmit connection states */
+    struct cstate rstate[MAX_STATES];	/* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1		/* tossing rcvd frames because of input err */
+
+extern void  vj_compress_init __P((struct vjcompress *comp, int max_state));
+extern u_int vj_compress_tcp __P((struct ip *ip, u_int mlen,
+				struct vjcompress *comp, int compress_cid_flag,
+				u_char **vjhdrp));
+extern void  vj_uncompress_err __P((struct vjcompress *comp));
+extern int   vj_uncompress_uncomp __P((u_char *buf, int buflen,
+				struct vjcompress *comp));
+extern int   vj_uncompress_tcp __P((u_char *buf, int buflen, int total_len,
+				struct vjcompress *comp, u_char **hdrp,
+				u_int *hlenp));
+
+#endif /* _VJCOMPRESS_H_ */
diff -Naur tomato/release/src/router/pppd/include/pcap-int.h tomato121/release/src/router/pppd/include/pcap-int.h
--- tomato/release/src/router/pppd/include/pcap-int.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/include/pcap-int.h	2008-04-22 21:44:05.000000000 +1000
@@ -30,7 +30,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * @(#) $Header: /home/cvsroot/wrt54g/src/router/pppd/include/pcap-int.h,v 1.1 2003/07/10 07:43:04 honor Exp $ (LBL)
+ * @(#) $Header: /data/cvs/ppp/include/pcap-int.h,v 1.1 2000/08/01 01:37:24 paulus Exp $ (LBL)
  */
 
 #ifndef pcap_int_h
diff -Naur tomato/release/src/router/pppd/linux/Makefile.top tomato121/release/src/router/pppd/linux/Makefile.top
--- tomato/release/src/router/pppd/linux/Makefile.top	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/linux/Makefile.top	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,36 @@
 # PPP top-level Makefile for Linux.
 
-
-BINDIR = $(DESTDIR)/usr/sbin
-MANDIR = $(DESTDIR)/usr/man
-ETCDIR = $(DESTDIR)/etc/ppp
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+INCDIR = $(DESTDIR)/include
+MANDIR = $(DESTDIR)/share/man
+ETCDIR = $(INSTROOT)@SYSCONF@/ppp
 
 # uid 0 = root
 INSTALL= install
 
 all:
 	cd chat; $(MAKE) $(MFLAGS) all
+	cd pppd/plugins; $(MAKE) $(MFLAGS) all
 	cd pppd; $(MAKE) $(MFLAGS) all
 	cd pppstats; $(MAKE) $(MFLAGS) all
 	cd pppdump; $(MAKE) $(MFLAGS) all
 
-install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-devel
 
 install-progs:
-	cd chat; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
-	cd pppd; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
-	cd pppstats; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
-	cd pppdump; $(MAKE) BINDIR=$(BINDIR) MANDIR=$(MANDIR) $(MFLAGS) install
+	cd chat; $(MAKE) $(MFLAGS) install
+	cd pppd/plugins; $(MAKE) $(MFLAGS) install
+	cd pppd; $(MAKE) $(MFLAGS) install
+	cd pppstats; $(MAKE) $(MFLAGS) install
+	cd pppdump; $(MAKE) $(MFLAGS) install
 
 install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
 	$(ETCDIR)/chap-secrets
 
+install-devel:
+	cd pppd; $(MAKE) $(MFLAGS) install-devel
+
 $(ETCDIR)/options:
 	$(INSTALL) -c -m 644 etc.ppp/options $@
 $(ETCDIR)/pap-secrets:
@@ -44,6 +50,7 @@
 	rm -f `find . -name 'core' -print`
 	rm -f `find . -name '*~' -print`
 	cd chat; $(MAKE) clean
+	cd pppd/plugins; $(MAKE) clean
 	cd pppd; $(MAKE) clean
 	cd pppstats; $(MAKE) clean
 	cd pppdump; $(MAKE) clean
@@ -53,3 +60,7 @@
 
 #kernel:
 #	cd linux; ./kinstall.sh
+
+# no tests yet, one day...
+installcheck:
+	true
diff -Naur tomato/release/src/router/pppd/Makefile tomato121/release/src/router/pppd/Makefile
--- tomato/release/src/router/pppd/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,66 @@
+# PPP top-level Makefile for Linux.
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+INCDIR = $(DESTDIR)/include
+MANDIR = $(DESTDIR)/share/man
+ETCDIR = $(INSTROOT)@SYSCONF@/ppp
+
+# uid 0 = root
+INSTALL= install
+
+all:
+	cd chat; $(MAKE) $(MFLAGS) all
+	cd pppd/plugins; $(MAKE) $(MFLAGS) all
+	cd pppd; $(MAKE) $(MFLAGS) all
+	cd pppstats; $(MAKE) $(MFLAGS) all
+	cd pppdump; $(MAKE) $(MFLAGS) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-devel
+
+install-progs:
+	cd chat; $(MAKE) $(MFLAGS) install
+	cd pppd/plugins; $(MAKE) $(MFLAGS) install
+	cd pppd; $(MAKE) $(MFLAGS) install
+	cd pppstats; $(MAKE) $(MFLAGS) install
+	cd pppdump; $(MAKE) $(MFLAGS) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+	$(ETCDIR)/chap-secrets
+
+install-devel:
+	cd pppd; $(MAKE) $(MFLAGS) install-devel
+
+$(ETCDIR)/options:
+	$(INSTALL) -c -m 644 etc.ppp/options $@
+$(ETCDIR)/pap-secrets:
+	$(INSTALL) -c -m 600 etc.ppp/pap-secrets $@
+$(ETCDIR)/chap-secrets:
+	$(INSTALL) -c -m 600 etc.ppp/chap-secrets $@
+
+$(BINDIR):
+	$(INSTALL) -d -m 755 $@
+$(MANDIR)/man8:
+	$(INSTALL) -d -m 755 $@
+$(ETCDIR):
+	$(INSTALL) -d -m 755 $@
+
+clean:
+	rm -f `find . -name '*.[oas]' -print`
+	rm -f `find . -name 'core' -print`
+	rm -f `find . -name '*~' -print`
+	cd chat; $(MAKE) clean
+	cd pppd/plugins; $(MAKE) clean
+	cd pppd; $(MAKE) clean
+	cd pppstats; $(MAKE) clean
+	cd pppdump; $(MAKE) clean
+
+dist-clean:	clean
+	rm -f Makefile `find . -name Makefile -print`
+
+#kernel:
+#	cd linux; ./kinstall.sh
+
+# no tests yet, one day...
+installcheck:
+	true
diff -Naur tomato/release/src/router/pppd/modules/bsd-comp.c tomato121/release/src/router/pppd/modules/bsd-comp.c
--- tomato/release/src/router/pppd/modules/bsd-comp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/bsd-comp.c	2008-04-22 21:44:05.000000000 +1000
@@ -41,7 +41,7 @@
  * This version is for use with STREAMS under SunOS 4.x,
  * Digital UNIX, AIX 4.x, and SVR4 systems including Solaris 2.
  *
- * $Id: bsd-comp.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * $Id: bsd-comp.c,v 1.21 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 #ifdef AIX4
@@ -66,6 +66,10 @@
 #define BSD_LITTLE_ENDIAN
 #endif
 
+#ifdef SOL2
+#include <sys/sunddi.h>
+#endif
+
 #define PACKETPTR	mblk_t *
 #include <net/ppp-comp.h>
 
diff -Naur tomato/release/src/router/pppd/modules/deflate.c tomato121/release/src/router/pppd/modules/deflate.c
--- tomato/release/src/router/pppd/modules/deflate.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/deflate.c	2008-04-22 21:44:05.000000000 +1000
@@ -4,30 +4,38 @@
  * This version is for use with STREAMS under SunOS 4.x, Solaris 2,
  * SVR4, OSF/1 and AIX 4.x.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: deflate.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: deflate.c,v 1.12 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 #ifdef AIX4
@@ -48,6 +56,10 @@
 #include "../common/zlib.h"
 #endif
 
+#ifdef SOL2
+#include <sys/sunddi.h>
+#endif
+
 #if DO_DEFLATE
 
 #define DEFLATE_DEBUG	1
diff -Naur tomato/release/src/router/pppd/modules/if_ppp.c tomato121/release/src/router/pppd/modules/if_ppp.c
--- tomato/release/src/router/pppd/modules/if_ppp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/if_ppp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * if_ppp.c - a network interface connected to a STREAMS module.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: if_ppp.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: if_ppp.c,v 1.18 2002/12/06 09:49:15 paulus Exp $
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/modules/ppp_ahdlc.c tomato121/release/src/router/pppd/modules/ppp_ahdlc.c
--- tomato/release/src/router/pppd/modules/ppp_ahdlc.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/ppp_ahdlc.c	2008-04-22 21:44:05.000000000 +1000
@@ -18,21 +18,36 @@
  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 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.
  *
  * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
@@ -41,7 +56,7 @@
  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
  * OR MODIFICATIONS.
  *
- * $Id: ppp_ahdlc.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * $Id: ppp_ahdlc.c,v 1.18 2002/12/06 09:49:15 paulus Exp $
  */
 
 /*
@@ -527,7 +542,6 @@
     switch (mp->b_datap->db_type) {
     case M_DATA:
 	ahdlc_decode(q, mp);
-	freemsg(mp);
 	break;
 
     case M_HANGUP:
@@ -715,26 +729,6 @@
     ahdlc_state_t   *state;
     mblk_t	    *om;
     uchar_t	    *dp;
-    ushort_t	    fcs;
-#if defined(SOL2)
-    mblk_t	    *zmp;
-#endif /* SOL2 */
-
-#if defined(SOL2)
-    /*
-     * In case the driver (or something below) doesn't send
-     * data upstream in one message block, concatenate everything
-     */
-    if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) && 
-         ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) {
-
-	zmp = msgpullup(mp, -1);
-	freemsg(mp);
-	mp = zmp;
-	if (mp == 0)
-	    return; 
-    }
-#endif /* SOL2 */
 
     state = (ahdlc_state_t *) q->q_ptr;
 
@@ -744,6 +738,7 @@
 
     state->stats.ppp_ibytes += msgdsize(mp);
 
+    for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om)
     for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
 
 	/*
diff -Naur tomato/release/src/router/pppd/modules/ppp.c tomato121/release/src/router/pppd/modules/ppp.c
--- tomato/release/src/router/pppd/modules/ppp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/ppp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: ppp.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: ppp.c,v 1.26 2002/12/06 09:49:15 paulus Exp $
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/modules/ppp_comp.c tomato121/release/src/router/pppd/modules/ppp_comp.c
--- tomato/release/src/router/pppd/modules/ppp_comp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/ppp_comp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: ppp_comp.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: ppp_comp.c,v 1.14 2002/12/06 09:49:15 paulus Exp $
  */
 
 /*
@@ -616,7 +624,7 @@
         if (!bcanputnext(q,mp->b_band))
 #else
         if (!canputnext(q))
-#endif PRIOQ
+#endif /* PRIOQ */
 	{
 	    putbq(q, mp);
 	    break;
@@ -694,7 +702,7 @@
 	    if (cmp != NULL) {
 #ifdef PRIOQ
 		cmp->b_band=mp->b_band;
-#endif PRIOQ
+#endif /* PRIOQ */
 		freemsg(mp);
 		mp = cmp;
 	    }
diff -Naur tomato/release/src/router/pppd/modules/vjcompress.c tomato121/release/src/router/pppd/modules/vjcompress.c
--- tomato/release/src/router/pppd/modules/vjcompress.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/modules/vjcompress.c	2008-04-22 21:44:05.000000000 +1000
@@ -29,7 +29,7 @@
  * This version is used under SunOS 4.x, Digital UNIX, AIX 4.x,
  * and SVR4 systems including Solaris 2.
  *
- * $Id: vjcompress.c,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * $Id: vjcompress.c,v 1.11 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 #include <sys/types.h>
@@ -56,6 +56,10 @@
 #include <netinet/in_systm.h>
 #endif
 
+#ifdef SOL2
+#include <sys/sunddi.h>
+#endif
+
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
 
diff -Naur tomato/release/src/router/pppd/PLUGINS tomato121/release/src/router/pppd/PLUGINS
--- tomato/release/src/router/pppd/PLUGINS	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/PLUGINS	2008-04-22 21:44:05.000000000 +1000
@@ -3,9 +3,7 @@
 affect its behaviour in various ways.  The idea of plugins is to
 provide a way for people to customize the behaviour of pppd without
 having to either apply local patches to each version or get their
-patches accepted into the standard distribution.  My aim is that
-plugins will be able to be used with successive versions of pppd
-without needing to recompile the plugins.
+patches accepted into the standard distribution.
 
 A plugin is a standard shared library object, typically with a name
 ending in .so.  They are loaded using the standard dlopen() library
@@ -15,8 +13,11 @@
 
 Plugins are loaded into pppd using the `plugin' option, which takes
 one argument, the name of a shared object file.  The plugin option is
-a privileged option.  I suggest that you give the full path name of
-the shared object file; if you don't, it may be possible for
+a privileged option.  If the name given does not contain a slash, pppd
+will look in the /usr/lib/pppd/<version> directory for the file, where
+<version> is the version number of pppd, for example, 2.4.2.  I
+suggest that you either give the full path name of the shared object
+file or just the base name; if you don't, it may be possible for
 unscrupulous users to substitute another shared object file for the
 one you mean to load, e.g. by setting the LD_LIBRARY_PATH variable.
 
@@ -38,9 +39,15 @@
 
 Every plugin must contain a global procedure called `plugin_init'.
 This procedure will get called (with no arguments) immediately after
-the plugin is loaded.
+the plugin is loaded.  Every plugin should also contain a variable
+called pppd_version declared as follows:
 
-Plugins can affect the behaviour of pppd in at least three ways:
+char pppd_version[] = VERSION;
+
+If this declaration is included, pppd will not load the module if its
+version number differs from that compiled into the plugin binary.
+
+Plugins can affect the behaviour of pppd in at least four ways:
 
 1. They can add extra options which pppd will then recognize.  This is
    done by calling the add_options() procedure with a pointer to an
@@ -56,6 +63,13 @@
 3. Plugin code can call any global procedures and access any global
    variables in pppd.
 
+4. Plugins can register procedures to be called when particular events
+   occur, using the `notifier' mechanism in pppd.  The differences
+   between hooks and notifiers are that a hook will only call one
+   function, whereas a notifier can call an arbitrary number, and that
+   a hook usually returns some value to pppd, whereas a notifier
+   function returns nothing.
+
 Here is a list of the currently implemented hooks in pppd.
 
 
@@ -84,11 +98,10 @@
 
 int (*pap_check_hook)(void);
 int (*pap_passwd_hook)(char *user, char *passwd);
-int (*pap_auth_hook)(char *user, int userlen,
-		     char *passwd, int passlen,
-		     char **msgp, int *msglenp,
+int (*pap_auth_hook)(char *user, char *passwd, char **msgp,
 		     struct wordlist **paddrs,
 		     struct wordlist **popts);
+void (*pap_logout_hook)(void);
 
 These hooks are designed to allow a plugin to replace the normal PAP
 password processing in pppd with something different (e.g. contacting
@@ -127,5 +140,141 @@
 set *popts to a wordlist containing any extra options for this user
 which pppd should apply at this point.
 
+The pap_logout_hook is called when the link is terminated, instead of
+pppd's internal `plogout' function.  It can be used for accounting
+purposes.  This hook is deprecated and will be replaced by a notifier.
+
+
+int (*chap_check_hook)(void);
+int (*chap_passwd_hook)(char *user, char *passwd);
+int (*chap_verify_hook)(char *name, char *ourname, int id,
+			struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space)
+
+These hooks are designed to allow a plugin to replace the normal CHAP
+password processing in pppd with something different (e.g. contacting
+an external server).
+
+The chap_check_hook is called to check whether there is any possibility
+that the peer could authenticate itself to us.  If it returns 1, pppd
+will ask the peer to authenticate itself.  If it returns 0, pppd will
+not ask the peer to authenticate itself (but if authentication is
+required, pppd may exit, or terminate the link before network protocol
+negotiation).  If it returns -1, pppd will look in the chap-secrets
+file as it would normally.
+
+The chap_passwd_hook is called to determine what password
+pppd should use in authenticating itself to the peer with CHAP.  The
+user string will already be initialized, by the `user' option, the
+`name' option, or from the hostname, but can be changed if necessary.
+This hook is called only if pppd is a client, not if it is a server.
+
+MAXSECRETLEN bytes of space are available at *passwd.  If this hook
+returns 0, pppd will use the value *passwd; if it returns -1, pppd
+will fail to authenticate.
+
+The chap_verify_hook is called to determine whether the peer's
+response to our CHAP challenge is valid -- it should return 1 if valid
+or 0 if not.  The parameters are:
+
+* name points to a null-terminated string containing the username
+  supplied by the peer, or the remote name specified with the
+  "remotename" option.
+* ourname points to a null-terminated string containing the name of
+  the local machine (the hostname, or the name specified with the
+  "name" option).
+* id is the value of the id field from the challenge.
+* digest points to a chap_digest_type struct, which contains an
+  identifier for the type of digest in use plus function pointers for
+  functions for dealing with digests of that type.
+* challenge points to the challenge as a counted string (length byte
+  followed by the actual challenge bytes).
+* response points to the response as a counted string.
+* message points to an area of message_space bytes in which to store
+  any message that should be returned to the peer.
+
+
+int (*null_auth_hook)(struct wordlist **paddrs,
+		      struct wordlist **popts);
+
+This hook allows a plugin to determine what the policy should be if
+the peer refuses to authenticate when it is requested to.  If the
+return value is 0, the link will be terminated; if it is 1, the
+connection is allowed to proceed, and in this case *paddrs and *popts
+can be set as for pap_auth_hook, to specify what IP addresses are
+permitted and any extra options to be applied.  If the return value is
+-1, pppd will look in the pap-secrets file as usual.
+
+
+void (*ip_choose_hook)(u_int32_t *addrp);
+
+This hook is called at the beginning of IPCP negotiation.  It gives a
+plugin the opportunity to set the IP address for the peer; the address
+should be stored in *addrp.  If nothing is stored in *addrp, pppd will
+determine the peer's address in the usual manner.
+
+
+int (*allowed_address_hook)(u_int32_t addr)
+
+This hook is called to see if a peer is allowed to use the specified
+address.  If the hook returns 1, the address is accepted.  If it returns
+0, the address is rejected.  If it returns -1, the address is verified
+in the normal away against the appropriate options and secrets files.
+
+
+void (*snoop_recv_hook)(unsigned char *p, int len)
+void (*snoop_send_hook)(unsigned char *p, int len)
+
+These hooks are called whenever pppd receives or sends a packet.  The
+packet is in p; its length is len.  This allows plugins to "snoop in"
+on the pppd conversation.  The hooks may prove useful in implmenting
+L2TP.
+
+A plugin registers itself with a notifier by declaring a procedure of
+the form:
+
+void my_notify_proc(void *opaque, int arg);
+
+and then registering the procedure with the appropriate notifier with
+a call of the form
+
+	add_notifier(&interesting_notifier, my_notify_proc, opaque);
+
+The `opaque' parameter in the add_notifier call will be passed to
+my_notify_proc every time it is called.  The `arg' parameter to
+my_notify_proc depends on the notifier.
+
+A notify procedure can be removed from the list for a notifier with a
+call of the form
+
+	remove_notifier(&interesting_notifier, my_notify_proc, opaque);
+
+Here is a list of the currently-implemented notifiers in pppd.
+
+* pidchange.  This notifier is called in the parent when pppd has
+  forked and the child is continuing pppd's processing, i.e. when pppd
+  detaches from its controlling terminal.  The argument is the pid of
+  the child.
+
+* phasechange.  This is called when pppd moves from one phase of
+  operation to another.  The argument is the new phase number.
+
+* exitnotify.  This is called just before pppd exits.  The argument is
+  the status with which pppd will exit (i.e. the argument to exit()).
+
+* sigreceived.  This is called when a signal is received, from within
+  the signal handler.  The argument is the signal number.
+
+* ip_up_notifier.  This is called when IPCP has come up.
+
+* ip_down_notifier.  This is called when IPCP goes down.
+
+* auth_up_notifier.  This is called when the peer has successfully
+  authenticated itself.
+
+* link_down_notifier.  This is called when the link goes down.
+
+
 
-## $Id: PLUGINS,v 1.1 2003/07/10 07:43:04 honor Exp $ ##
+## $Id: PLUGINS,v 1.7 2005/07/12 08:56:07 paulus Exp $ ##
diff -Naur tomato/release/src/router/pppd/pppd/auth.c tomato121/release/src/router/pppd/pppd/auth.c
--- tomato/release/src/router/pppd/pppd/auth.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/auth.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,43 +1,80 @@
 /*
  * auth.c - PPP authentication and phase control.
  *
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University.  The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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 main.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.
  */
 
-#define RCSID	"$Id: auth.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: auth.c,v 1.112 2006/06/18 11:26:00 paulus Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <errno.h>
 #include <pwd.h>
 #include <grp.h>
 #include <string.h>
@@ -46,7 +83,7 @@
 #include <sys/socket.h>
 #include <utmp.h>
 #include <fcntl.h>
-#if defined(_PATH_LASTLOG) && defined(_linux_)
+#if defined(_PATH_LASTLOG) && defined(__linux__)
 #include <lastlog.h>
 #endif
 
@@ -64,13 +101,17 @@
 #define PW_PPP PW_LOGIN
 #endif
 #endif
+#include <time.h>
 
 #include "pppd.h"
 #include "fsm.h"
 #include "lcp.h"
+#include "ccp.h"
+#include "ecp.h"
 #include "ipcp.h"
 #include "upap.h"
-#include "chap.h"
+#include "chap-new.h"
+#include "eap.h"
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
 #endif
@@ -90,6 +131,9 @@
 /* Records which authentication operations haven't completed yet. */
 static int auth_pending[NUM_PPP];
 
+/* Records which authentication operations have been completed. */
+int auth_done[NUM_PPP];
+
 /* Set if we have successfully called plogin() */
 static int logged_in;
 
@@ -100,6 +144,12 @@
    without authenticating itself. */
 static struct wordlist *noauth_addrs;
 
+/* Remote telephone number, if available */
+char remote_number[MAXNAMELEN];
+
+/* Wordlist giving remote telephone numbers which may connect. */
+static struct wordlist *permitted_numbers;
+
 /* Extra options to apply, from the secrets file entry for the peer. */
 static struct wordlist *extra_options;
 
@@ -132,6 +182,26 @@
 /* Hook for a plugin to get the PAP password for authenticating us */
 int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
 
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/* Hook for a plugin to say whether it is OK if the peer
+   refuses to authenticate. */
+int (*null_auth_hook) __P((struct wordlist **paddrs,
+			   struct wordlist **popts)) = NULL;
+
+int (*allowed_address_hook) __P((u_int32_t addr)) = NULL;
+
+/* A notifier for when the peer has authenticated itself,
+   and we are proceeding to the network phase. */
+struct notifier *auth_up_notifier = NULL;
+
+/* A notifier for when the link goes down. */
+struct notifier *link_down_notifier = NULL;
+
 /*
  * This is used to ensure that we don't start an auth-up/down
  * script while one is already running.
@@ -154,6 +224,15 @@
 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 */
+bool refuse_eap = 0;		/* Don't wanna auth. ourselves with EAP */
+#ifdef CHAPMS
+bool refuse_mschap = 0;		/* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 0;	/* Don't wanna auth. ourselves with MS-CHAPv2 */
+bool ms_ignore_domain = 0;
+#else
+bool refuse_mschap = 1;		/* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 1;	/* Don't wanna auth. ourselves with MS-CHAPv2 */
+#endif
 bool usehostname = 0;		/* Use hostname for our_name */
 bool auth_required = 0;		/* Always require authentication from peer */
 bool allow_any_ip = 0;		/* Allow peer to use any IP address */
@@ -162,12 +241,6 @@
 
 static char *uafname;		/* name of most recent +ua file */
 
-/* Bits in auth_pending[] */
-#define PAP_WITHPEER	1
-#define PAP_PEER	2
-#define CHAP_WITHPEER	4
-#define CHAP_PEER	8
-
 extern char *crypt __P((const char *, const char *));
 
 /* Prototypes for procedures local to this file. */
@@ -181,10 +254,12 @@
 static int  get_pap_passwd __P((char *));
 static int  have_pap_secret __P((int *));
 static int  have_chap_secret __P((char *, char *, int, int *));
+static int  have_srp_secret __P((char *client, char *server, int need_ip,
+    int *lacks_ipp));
 static int  ip_addr_check __P((u_int32_t, struct permitted_ip *));
 static int  scan_authfile __P((FILE *, char *, char *, char *,
 			       struct wordlist **, struct wordlist **,
-			       char *));
+			       char *, int));
 static void free_wordlist __P((struct wordlist *));
 static void auth_script __P((char *));
 static void auth_script_done __P((void *));
@@ -193,9 +268,14 @@
 static int  setupapfile __P((char **));
 static int  privgroup __P((char **));
 static int  set_noauth_addr __P((char **));
+static int  set_permitted_number __P((char **));
 static void check_access __P((FILE *, char *));
 static int  wordlist_count __P((struct wordlist *));
 
+#ifdef MAXOCTETS
+static void check_maxoctets __P((void *));
+#endif
+
 /*
  * Authentication-related options.
  */
@@ -211,22 +291,79 @@
     { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
       "Require PAP authentication from peer",
       OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
-    { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
+    { "require-chap", o_bool, &auth_required,
       "Require CHAP authentication from peer",
-      OPT_PRIOSUB | 1, &auth_required },
-    { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+chap", o_bool, &auth_required,
       "Require CHAP authentication from peer",
-      OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "chap-secrets", o_string, &chapseccustom,
+      "Specify custom chap-secrets file", OPT_PRIO },
+    { "pap-secrets", o_string, &papseccustom,
+      "Specify custom pap-secrets file", OPT_PRIO },
+    { "srp-secrets", o_string, &srpseccustom,
+      "Specify custom srp-secrets file", OPT_PRIO },
+
+#ifdef CHAPMS
+    { "require-mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "require-mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "ms-ignore-domain", o_bool, &ms_ignore_domain,
+      "Ignore any MS domain prefix in the username", 1 },
+
+#endif
 
     { "refuse-pap", o_bool, &refuse_pap,
       "Don't agree to auth to peer with PAP", 1 },
     { "-pap", o_bool, &refuse_pap,
       "Don't allow PAP authentication with peer", OPT_ALIAS | 1 },
-
     { "refuse-chap", o_bool, &refuse_chap,
-      "Don't agree to auth to peer with CHAP", 1 },
+      "Don't agree to auth to peer with CHAP",
+      OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
     { "-chap", o_bool, &refuse_chap,
-      "Don't allow CHAP authentication with peer", OPT_ALIAS | 1 },
+      "Don't allow CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+#ifdef CHAPMS
+    { "refuse-mschap", o_bool, &refuse_mschap,
+      "Don't agree to auth to peer with MS-CHAP",
+      OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap", o_bool, &refuse_mschap,
+      "Don't allow MS-CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "refuse-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't agree to auth to peer with MS-CHAPv2",
+      OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't allow MS-CHAPv2 authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+#endif
+
+    { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap,
+      "Require EAP authentication from peer", OPT_PRIOSUB | 1,
+      &auth_required },
+    { "refuse-eap", o_bool, &refuse_eap,
+      "Don't agree to authenticate to peer with EAP", 1 },
 
     { "name", o_string, our_name,
       "Set local name for authentication",
@@ -263,6 +400,14 @@
       "Set IP address(es) which can be used without authentication",
       OPT_PRIV | OPT_A2LIST },
 
+    { "remotenumber", o_string, remote_number,
+      "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC,
+      NULL, MAXNAMELEN },
+
+    { "allow-number", o_special, (void *)set_permitted_number,
+      "Set telephone number(s) which are allowed to connect",
+      OPT_PRIV | OPT_A2LIST },
+
     { NULL }
 };
 
@@ -275,6 +420,7 @@
 {
     FILE *ufile;
     int l;
+    uid_t euid;
     char u[MAXNAMELEN], p[MAXSECRETLEN];
     char *fname;
 
@@ -284,9 +430,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;
@@ -296,7 +447,8 @@
 
     /* get username */
     if (fgets(u, MAXNAMELEN - 1, ufile) == NULL
-	|| fgets(p, MAXSECRETLEN - 1, ufile) == NULL){
+	|| fgets(p, MAXSECRETLEN - 1, ufile) == NULL) {
+	fclose(ufile);
 	option_error("unable to read user login data file %s", fname);
 	return 0;
     }
@@ -368,8 +520,29 @@
 
 
 /*
+ * set_permitted_number - set remote telephone number(s) that may connect.
+ */
+static int
+set_permitted_number(argv)
+    char **argv;
+{
+    char *number = *argv;
+    int l = strlen(number) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+	novm("allow-number argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = permitted_numbers;
+    BCOPY(number, wp->word, l);
+    permitted_numbers = wp;
+    return 1;
+}
+
+
+/*
  * 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)
@@ -378,6 +551,70 @@
 }
 
 /*
+ * Bring the link up to the point of being able to do ppp.
+ */
+void start_link(unit)
+    int unit;
+{
+    char *msg;
+
+    new_phase(PHASE_SERIALCONN);
+
+    devfd = the_channel->connect();
+    msg = "Connect script failed";
+    if (devfd < 0)
+	goto fail;
+
+    /* set up the serial device as a ppp interface */
+    /*
+     * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+     * (from establish_ppp to set_ifunit).  However, we won't be
+     * doing the set_ifunit in multilink mode, which is the only time
+     * we need the atomicity that the tdb_writelock/tdb_writeunlock
+     * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.
+     */
+    fd_ppp = the_channel->establish_ppp(devfd);
+    msg = "ppp establishment failed";
+    if (fd_ppp < 0) {
+	status = EXIT_FATAL_ERROR;
+	goto disconnect;
+    }
+
+    if (!demand && ifunit >= 0)
+	set_ifunit(1);
+
+    /*
+     * Start opening the connection and wait for
+     * incoming events (reply, timeout, etc.).
+     */
+    if (ifunit >= 0)
+    {
+	notice("Connect: %s <--> %s", ifname, ppp_devnam);
+    }
+    else
+    {
+	notice("Starting negotiation on %s", ppp_devnam);
+    }
+    add_fd(fd_ppp);
+
+    status = EXIT_NEGOTIATION_FAILED;
+    new_phase(PHASE_ESTABLISH);
+
+    lcp_lowerup(0);
+    return;
+
+ disconnect:
+    new_phase(PHASE_DISCONNECT);
+    if (the_channel->disconnect)
+	the_channel->disconnect();
+
+ fail:
+    new_phase(PHASE_DEAD);
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+}
+
+/*
  * LCP has terminated the link; go to the Dead phase and take the
  * physical layer down.
  */
@@ -385,16 +622,67 @@
 link_terminated(unit)
     int unit;
 {
-    if (phase == PHASE_DEAD)
+    if (phase == PHASE_DEAD || phase == PHASE_MASTER)
 	return;
+    new_phase(PHASE_DISCONNECT);
+
     if (pap_logout_hook) {
 	pap_logout_hook();
     } else {
 	if (logged_in)
 	    plogout();
     }
-    new_phase(PHASE_DEAD);
-    notice("Connection terminated.");
+
+    if (!doing_multilink) {
+	notice("Connection terminated.");
+	print_link_stats();
+    } else
+	notice("Link terminated.");
+
+    /*
+     * Delete pid files before disestablishing ppp.  Otherwise it
+     * can happen that another pppd gets the same unit and then
+     * we delete its pid file.
+     */
+    if (!doing_multilink && !demand)
+	remove_pidfiles();
+
+    /*
+     * If we may want to bring the link up again, transfer
+     * the ppp unit back to the loopback.  Set the
+     * real serial device back to its normal mode of operation.
+     */
+    if (fd_ppp >= 0) {
+	remove_fd(fd_ppp);
+	clean_check();
+	the_channel->disestablish_ppp(devfd);
+	if (doing_multilink)
+	    mp_exit_bundle();
+	fd_ppp = -1;
+    }
+    if (!hungup)
+	lcp_lowerdown(0);
+    if (!doing_multilink && !demand)
+	script_unsetenv("IFNAME");
+
+    /*
+     * Run disconnector script, if requested.
+     * XXX we may not be able to do this if the line has hung up!
+     */
+    if (devfd >= 0 && the_channel->disconnect) {
+	the_channel->disconnect();
+	devfd = -1;
+    }
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+
+    if (doing_multilink && multilink_master) {
+	if (!bundle_terminating)
+	    new_phase(PHASE_MASTER);
+	else
+	    mp_bundle_terminated();
+    } else
+	new_phase(PHASE_DEAD);
 }
 
 /*
@@ -404,15 +692,29 @@
 link_down(unit)
     int unit;
 {
+    if (auth_state != s_down) {
+	notify(link_down_notifier, 0);
+	auth_state = s_down;
+	if (auth_script_state == s_up && auth_script_pid == 0) {
+	    update_link_stats(unit);
+	    auth_script_state = s_down;
+	    auth_script(_PATH_AUTHDOWN);
+	}
+    }
+    if (!doing_multilink) {
+	upper_layers_down(unit);
+	if (phase != PHASE_DEAD && phase != PHASE_MASTER)
+	    new_phase(PHASE_ESTABLISH);
+    }
+    /* XXX if doing_multilink, should do something to stop
+       network-layer traffic on the link */
+}
+
+void upper_layers_down(int unit)
+{
     int i;
     struct protent *protp;
 
-    auth_state = s_down;
-    if (auth_script_state == s_up && auth_script_pid == 0) {
-	update_link_stats(unit);
-	auth_script_state = s_down;
-	auth_script(_PATH_AUTHDOWN);
-    }
     for (i = 0; (protp = protocols[i]) != NULL; ++i) {
 	if (!protp->enabled_flag)
 	    continue;
@@ -423,8 +725,6 @@
     }
     num_np_open = 0;
     num_np_up = 0;
-    if (phase != PHASE_DEAD)
-	new_phase(PHASE_TERMINATE);
 }
 
 /*
@@ -445,25 +745,30 @@
     /*
      * Tell higher-level protocols that LCP is up.
      */
-    for (i = 0; (protp = protocols[i]) != NULL; ++i)
-        if (protp->protocol != PPP_LCP && protp->enabled_flag
-	    && protp->lowerup != NULL)
-	    (*protp->lowerup)(unit);
+    if (!doing_multilink) {
+	for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	    if (protp->protocol != PPP_LCP && protp->enabled_flag
+		&& protp->lowerup != NULL)
+		(*protp->lowerup)(unit);
+    }
+
+    if (!auth_required && noauth_addrs != NULL)
+	set_allowed_addrs(unit, NULL, NULL);
 
-    if (auth_required && !(go->neg_chap || go->neg_upap)) {
+    if (auth_required && !(go->neg_upap || go->neg_chap || go->neg_eap)) {
 	/*
 	 * We wanted the peer to authenticate itself, and it refused:
 	 * if we have some address(es) it can use without auth, fine,
 	 * otherwise treat it as though it authenticated with PAP using
-	 * a username * of "" and a password of "".  If that's not OK,
+	 * a username of "" and a password of "".  If that's not OK,
 	 * boot it out.
 	 */
 	if (noauth_addrs != NULL) {
 	    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;
 	}
     }
@@ -471,15 +776,21 @@
     new_phase(PHASE_AUTHENTICATE);
     used_login = 0;
     auth = 0;
-    if (go->neg_chap) {
-	ChapAuthPeer(unit, our_name, go->chap_mdtype);
+    if (go->neg_eap) {
+	eap_authpeer(unit, our_name);
+	auth |= EAP_PEER;
+    } else if (go->neg_chap) {
+	chap_auth_peer(unit, our_name, CHAP_DIGEST(go->chap_mdtype));
 	auth |= CHAP_PEER;
     } else if (go->neg_upap) {
 	upap_authpeer(unit);
 	auth |= PAP_PEER;
     }
-    if (ho->neg_chap) {
-	ChapAuthWithPeer(unit, user, ho->chap_mdtype);
+    if (ho->neg_eap) {
+	eap_authwithpeer(unit, user);
+	auth |= EAP_WITHPEER;
+    } else if (ho->neg_chap) {
+	chap_auth_with_peer(unit, user, CHAP_DIGEST(ho->chap_mdtype));
 	auth |= CHAP_WITHPEER;
     } else if (ho->neg_upap) {
 	if (passwd[0] == 0) {
@@ -491,6 +802,7 @@
 	auth |= PAP_WITHPEER;
     }
     auth_pending[unit] = auth;
+    auth_done[unit] = 0;
 
     if (!auth)
 	network_phase(unit);
@@ -505,10 +817,15 @@
 {
     lcp_options *go = &lcp_gotoptions[unit];
 
+    /* Log calling number. */
+    if (*remote_number)
+	notice("peer from calling number %q authorized", remote_number);
+
     /*
      * If the peer had to authenticate, run the auth-up script now.
      */
-    if (go->neg_chap || go->neg_upap) {
+    if (go->neg_chap || go->neg_upap || go->neg_eap) {
+	notify(auth_up_notifier, 0);
 	auth_state = s_up;
 	if (auth_script_state == s_down && auth_script_pid == 0) {
 	    auth_script_state = s_up;
@@ -535,14 +852,16 @@
 	free_wordlist(extra_options);
 	extra_options = 0;
     }
-    start_networks();
+    start_networks(unit);
 }
 
 void
-start_networks()
+start_networks(unit)
+    int unit;
 {
     int i;
     struct protent *protp;
+    int ecp_required, mppe_required;
 
     new_phase(PHASE_NETWORK);
 
@@ -560,12 +879,37 @@
     if (!demand)
 	set_filters(&pass_filter, &active_filter);
 #endif
+    /* Start CCP and ECP */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if ((protp->protocol == PPP_ECP || protp->protocol == PPP_CCP)
+	    && protp->enabled_flag && protp->open != NULL)
+	    (*protp->open)(0);
+
+    /*
+     * Bring up other network protocols iff encryption is not required.
+     */
+    ecp_required = ecp_gotoptions[unit].required;
+    mppe_required = ccp_gotoptions[unit].mppe;
+    if (!ecp_required && !mppe_required)
+	continue_networks(unit);
+}
+
+void
+continue_networks(unit)
+    int unit;
+{
+    int i;
+    struct protent *protp;
+
+    /*
+     * Start the "real" network protocols.
+     */
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
-        if (protp->protocol < 0xC000 && protp->enabled_flag
-	    && protp->open != NULL) {
+	if (protp->protocol < 0xC000
+	    && protp->protocol != PPP_CCP && protp->protocol != PPP_ECP
+	    && protp->enabled_flag && protp->open != NULL) {
 	    (*protp->open)(0);
-	    if (protp->protocol != PPP_CCP)
-		++num_np_open;
+	    ++num_np_open;
 	}
 
     if (num_np_open == 0)
@@ -583,16 +927,16 @@
     /*
      * Authentication failure: take the link down
      */
-    lcp_close(unit, "Authentication failed");
     status = EXIT_PEER_AUTH_FAILED;
+    lcp_close(unit, "Authentication failed");
 }
 
 /*
  * The peer has been successfully authenticated using `protocol'.
  */
 void
-auth_peer_success(unit, protocol, name, namelen)
-    int unit, protocol;
+auth_peer_success(unit, protocol, prot_flavor, name, namelen)
+    int unit, protocol, prot_flavor;
     char *name;
     int namelen;
 {
@@ -601,10 +945,26 @@
     switch (protocol) {
     case PPP_CHAP:
 	bit = CHAP_PEER;
+	switch (prot_flavor) {
+	case CHAP_MD5:
+	    bit |= CHAP_MD5_PEER;
+	    break;
+#ifdef CHAPMS
+	case CHAP_MICROSOFT:
+	    bit |= CHAP_MS_PEER;
+	    break;
+	case CHAP_MICROSOFT_V2:
+	    bit |= CHAP_MS2_PEER;
+	    break;
+#endif
+	}
 	break;
     case PPP_PAP:
 	bit = PAP_PEER;
 	break;
+    case PPP_EAP:
+	bit = EAP_PEER;
+	break;
     default:
 	warn("auth_peer_success: unknown protocol %x", protocol);
 	return;
@@ -619,6 +979,9 @@
     peer_authname[namelen] = 0;
     script_setenv("PEERNAME", peer_authname, 0);
 
+    /* Save the authentication method for later. */
+    auth_done[unit] |= bit;
+
     /*
      * If there is no more authentication still to be done,
      * proceed to the network (or callback) phase.
@@ -642,33 +1005,58 @@
      * 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");
 }
 
 /*
  * We have successfully authenticated ourselves with the peer using `protocol'.
  */
 void
-auth_withpeer_success(unit, protocol)
-    int unit, protocol;
+auth_withpeer_success(unit, protocol, prot_flavor)
+    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;
+	    break;
+#ifdef CHAPMS
+	case CHAP_MICROSOFT:
+	    bit |= CHAP_MS_WITHPEER;
+	    break;
+	case CHAP_MICROSOFT_V2:
+	    bit |= CHAP_MS2_WITHPEER;
+	    break;
+#endif
+	}
 	break;
     case PPP_PAP:
 	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;
+
     /*
      * If there is no more authentication still being done,
      * proceed to the network (or callback) phase.
@@ -709,6 +1097,11 @@
 	if (maxconnect > 0)
 	    TIMEOUT(connect_time_expired, 0, maxconnect);
 
+#ifdef MAXOCTETS
+	if (maxoctets > 0)
+	    TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+#endif
+
 	/*
 	 * Detach now, if the updetach option was given.
 	 */
@@ -727,6 +1120,10 @@
 {
     if (--num_np_up == 0) {
 	UNTIMEOUT(check_idle, NULL);
+	UNTIMEOUT(connect_time_expired, NULL);
+#ifdef MAXOCTETS
+	UNTIMEOUT(check_maxoctets, NULL);
+#endif	
 	new_phase(PHASE_NETWORK);
     }
 }
@@ -744,6 +1141,44 @@
     }
 }
 
+#ifdef MAXOCTETS
+static void
+check_maxoctets(arg)
+    void *arg;
+{
+    int diff;
+    unsigned int used;
+
+    update_link_stats(ifunit);
+    link_stats_valid=0;
+    
+    switch(maxoctets_dir) {
+	case PPP_OCTETS_DIRECTION_IN:
+	    used = link_stats.bytes_in;
+	    break;
+	case PPP_OCTETS_DIRECTION_OUT:
+	    used = link_stats.bytes_out;
+	    break;
+	case PPP_OCTETS_DIRECTION_MAXOVERAL:
+	case PPP_OCTETS_DIRECTION_MAXSESSION:
+	    used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out;
+	    break;
+	default:
+	    used = link_stats.bytes_in+link_stats.bytes_out;
+	    break;
+    }
+    diff = maxoctets - used;
+    if(diff < 0) {
+	notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used);
+	status = EXIT_TRAFFIC_LIMIT;
+	lcp_close(0, "Traffic limit");
+	need_holdoff = 0;
+    } else {
+        TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+    }
+}
+#endif
+
 /*
  * check_idle - check whether the link has been idle for long
  * enough that we can shut it down.
@@ -767,9 +1202,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);
     }
@@ -783,8 +1218,8 @@
     void *arg;
 {
     info("Connect time expired");
-    lcp_close(0, "Connect time expired");	/* Close connection */
     status = EXIT_CONNECT_TIME;
+    lcp_close(0, "Connect time expired");	/* Close connection */
 }
 
 /*
@@ -812,28 +1247,41 @@
 	default_auth = 1;
     }
 
-    /* If authentication is required, ask peer for CHAP or PAP. */
+    /* If we selected any CHAP flavors, we should probably negotiate it. :-) */
+    if (wo->chap_mdtype)
+	wo->neg_chap = 1;
+
+    /* If authentication is required, ask peer for CHAP, PAP, or EAP. */
     if (auth_required) {
 	allow_any_ip = 0;
-	if (!wo->neg_chap && !wo->neg_upap) {
-	    wo->neg_chap = 1;
+	if (!wo->neg_chap && !wo->neg_upap && !wo->neg_eap) {
+	    wo->neg_chap = chap_mdtype_all != MDTYPE_NONE;
+	    wo->chap_mdtype = chap_mdtype_all;
 	    wo->neg_upap = 1;
+	    wo->neg_eap = 1;
 	}
     } else {
 	wo->neg_chap = 0;
+	wo->chap_mdtype = MDTYPE_NONE;
 	wo->neg_upap = 0;
+	wo->neg_eap = 0;
     }
 
     /*
      * Check whether we have appropriate secrets to use
-     * to authenticate the peer.
+     * to authenticate the peer.  Note that EAP can authenticate by way
+     * of a CHAP-like exchanges as well as SRP.
      */
     lacks_ip = 0;
     can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
-    if (!can_auth && wo->neg_chap) {
+    if (!can_auth && (wo->neg_chap || wo->neg_eap)) {
 	can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
 				    our_name, 1, &lacks_ip);
     }
+    if (!can_auth && wo->neg_eap) {
+	can_auth = have_srp_secret((explicit_remote? remote_name: NULL),
+				    our_name, 1, &lacks_ip);
+    }
 
     if (auth_required && !can_auth && noauth_addrs == NULL) {
 	if (default_auth) {
@@ -842,20 +1290,33 @@
 	    option_error(
 "(because this system has a default route to the internet)");
 	} else if (explicit_remote)
+	{
 	    option_error(
 "The remote system (%s) is required to authenticate itself",
 			 remote_name);
+	}
 	else
+	{
 	    option_error(
 "The remote system is required to authenticate itself");
+	}
 	option_error(
 "but I couldn't find any suitable secret (password) for it to use to do so.");
 	if (lacks_ip)
+	{
 	    option_error(
 "(None of the available passwords would let it use an IP address.)");
-
+	}
 	exit(1);
     }
+
+    /*
+     * Early check for remote number authorization.
+     */
+    if (!auth_number()) {
+	warn("calling number %q is not authorized", remote_number);
+	exit(EXIT_CNID_AUTH_FAILED);
+    }
 }
 
 /*
@@ -868,21 +1329,36 @@
     int unit;
 {
     lcp_options *go = &lcp_gotoptions[unit];
-    lcp_options *ao = &lcp_allowoptions[0];
+    lcp_options *ao = &lcp_allowoptions[unit];
+    int hadchap;
 
+    hadchap = -1;
     ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
-    ao->neg_chap = !refuse_chap
-	&& (passwd[0] != 0
-	    || have_chap_secret(user, (explicit_remote? remote_name: NULL),
-				0, NULL));
+    ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
+	&& (passwd[0] != 0 ||
+	    (hadchap = have_chap_secret(user, (explicit_remote? remote_name:
+					       NULL), 0, NULL)));
+    ao->neg_eap = !refuse_eap && (
+	passwd[0] != 0 ||
+	(hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
+	    (explicit_remote? remote_name: NULL), 0, NULL))) ||
+	have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
 
+    hadchap = -1;
     if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
 	go->neg_upap = 0;
     if (go->neg_chap) {
-	if (!have_chap_secret((explicit_remote? remote_name: NULL),
-			      our_name, 1, NULL))
+	if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL),
+			      our_name, 1, NULL)))
 	    go->neg_chap = 0;
     }
+    if (go->neg_eap &&
+	(hadchap == 0 || (hadchap == -1 &&
+	    !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
+		1, NULL))) &&
+	!have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
+	    NULL))
+	go->neg_eap = 0;
 }
 
 
@@ -928,11 +1404,15 @@
     if (pap_auth_hook) {
 	ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts);
 	if (ret >= 0) {
+	    /* note: set_allowed_addrs() saves opts (but not addrs):
+	       don't free it! */
 	    if (ret)
 		set_allowed_addrs(unit, addrs, opts);
-	    BZERO(passwd, sizeof(passwd));
+	    else if (opts != 0)
+		free_wordlist(opts);
 	    if (addrs != 0)
 		free_wordlist(addrs);
+	    BZERO(passwd, sizeof(passwd));
 	    return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
 	}
     }
@@ -941,7 +1421,7 @@
      * Open the file of pap secrets and scan for a suitable secret
      * for authenticating this user.
      */
-    filename = _PATH_UPAPFILE;
+    filename = papseccustom ? papseccustom : _PATH_UPAPFILE;
     addrs = opts = NULL;
     ret = UPAP_AUTHNAK;
     f = fopen(filename, "r");
@@ -950,7 +1430,7 @@
 
     } else {
 	check_access(f, filename);
-	if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename) < 0) {
+	if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) {
 	    warn("no PAP secret found for %s", user);
 	} else {
 	    /*
@@ -961,19 +1441,14 @@
 	    ret = UPAP_AUTHACK;
 	    if (uselogin || login_secret) {
 		/* login option or secret is @login */
-		ret = plogin(user, passwd, msg);
-		if (ret == UPAP_AUTHNAK)
-		    warn("PAP login failure for %s", user);
-		else
+		if ((ret = plogin(user, passwd, msg)) == UPAP_AUTHACK)
 		    used_login = 1;
 	    }
 	    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) {
+		    && strcmp(crypt(passwd, secret), secret) != 0)
 		    ret = UPAP_AUTHNAK;
-		    warn("PAP authentication failure for %s", user);
-		}
 	    }
 	}
 	fclose(f);
@@ -1030,8 +1505,12 @@
  * echo off means password.
  */
 
-static int PAM_conv (int num_msg, const struct pam_message **msg,
-                    struct pam_response **resp, void *appdata_ptr)
+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;
@@ -1120,7 +1599,7 @@
     if (pam_error == PAM_SUCCESS && !PAM_error) {    
         pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
         if (pam_error == PAM_SUCCESS)
-	    pam_open_session (pamh, PAM_SILENT);
+	    pam_error = pam_open_session (pamh, PAM_SILENT);
     }
 
     *msg = (char *) pam_strerror (pamh, pam_error);
@@ -1183,7 +1662,7 @@
     tty = devnam;
     if (strncmp(tty, "/dev/", 5) == 0)
 	tty += 5;
-    logwtmp(tty, user, remote_name);		/* Add wtmp login entry */
+    logwtmp(tty, user, ifname);		/* Add wtmp login entry */
 
 #if defined(_PATH_LASTLOG) && !defined(USE_PAM)
     if (pw != (struct passwd *)NULL) {
@@ -1213,6 +1692,7 @@
 static void
 plogout()
 {
+    char *tty;
 #ifdef USE_PAM
     int pam_error;
 
@@ -1223,14 +1703,12 @@
     }
     /* Apparently the pam stuff does closelog(). */
     reopen_log();
-#else /* ! USE_PAM */   
-    char *tty;
+#endif /* USE_PAM */
 
     tty = devnam;
     if (strncmp(tty, "/dev/", 5) == 0)
 	tty += 5;
     logwtmp(tty, "", "");		/* Wipe out utmp logout entry */
-#endif /* ! USE_PAM */
     logged_in = 0;
 }
 
@@ -1251,18 +1729,28 @@
     char secret[MAXWORDLEN];
 
     /*
+     * Check if a plugin wants to handle this.
+     */
+    ret = -1;
+    if (null_auth_hook)
+	ret = (*null_auth_hook)(&addrs, &opts);
+
+    /*
      * Open the file of pap secrets and scan for a suitable secret.
      */
-    filename = _PATH_UPAPFILE;
-    addrs = NULL;
-    f = fopen(filename, "r");
-    if (f == NULL)
-	return 0;
-    check_access(f, filename);
+    if (ret <= 0) {
+	filename = papseccustom ? papseccustom : _PATH_UPAPFILE;
+	addrs = NULL;
+	f = fopen(filename, "r");
+	if (f == NULL)
+	    return 0;
+	check_access(f, filename);
 
-    i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename);
-    ret = i >= 0 && secret[0] == 0;
-    BZERO(secret, sizeof(secret));
+	i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0);
+	ret = i >= 0 && secret[0] == 0;
+	BZERO(secret, sizeof(secret));
+	fclose(f);
+    }
 
     if (ret)
 	set_allowed_addrs(unit, addrs, opts);
@@ -1271,7 +1759,6 @@
     if (addrs != 0)
 	free_wordlist(addrs);
 
-    fclose(f);
     return ret;
 }
 
@@ -1307,7 +1794,7 @@
     check_access(f, filename);
     ret = scan_authfile(f, user,
 			(remote_name[0]? remote_name: NULL),
-			secret, NULL, NULL, filename);
+			secret, NULL, NULL, filename, 0);
     fclose(f);
     if (ret < 0)
 	return 0;
@@ -1338,13 +1825,13 @@
 	    return ret;
     }
 
-    filename = _PATH_UPAPFILE;
+    filename = papseccustom ? papseccustom : _PATH_UPAPFILE;
     f = fopen(filename, "r");
     if (f == NULL)
 	return 0;
 
     ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
-			NULL, &addrs, NULL, filename);
+			NULL, &addrs, NULL, filename, 0);
     fclose(f);
     if (ret >= 0 && !some_ip_ok(addrs)) {
 	if (lacks_ipp != 0)
@@ -1376,7 +1863,14 @@
     char *filename;
     struct wordlist *addrs;
 
-    filename = _PATH_CHAPFILE;
+    if (chap_check_hook) {
+	ret = (*chap_check_hook)();
+	if (ret >= 0) {
+	    return ret;
+	}
+    }
+
+    filename = chapseccustom ? chapseccustom : _PATH_CHAPFILE;
     f = fopen(filename, "r");
     if (f == NULL)
 	return 0;
@@ -1386,7 +1880,49 @@
     else if (server != NULL && server[0] == 0)
 	server = NULL;
 
-    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename);
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * have_srp_secret - check whether we have a SRP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_srp_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    filename = srpseccustom ? srpseccustom : _PATH_SRPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+	return 0;
+
+    if (client != NULL && client[0] == 0)
+	client = NULL;
+    else if (server != NULL && server[0] == 0)
+	server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
     fclose(f);
     if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
 	if (lacks_ipp != 0)
@@ -1422,8 +1958,14 @@
 
     if (!am_server && passwd[0] != 0) {
 	strlcpy(secbuf, passwd, sizeof(secbuf));
+    } else if (!am_server && chap_passwd_hook) {
+	if ( (*chap_passwd_hook)(client, secbuf) < 0) {
+	    error("Unable to obtain CHAP password for %s on %s from plugin",
+		  client, server);
+	    return 0;
+	}
     } else {
-	filename = _PATH_CHAPFILE;
+	filename = chapseccustom ? chapseccustom : _PATH_CHAPFILE;
 	addrs = NULL;
 	secbuf[0] = 0;
 
@@ -1434,7 +1976,7 @@
 	}
 	check_access(f, filename);
 
-	ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename);
+	ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0);
 	fclose(f);
 	if (ret < 0)
 	    return 0;
@@ -1459,6 +2001,56 @@
     return 1;
 }
 
+
+/*
+ * get_srp_secret - open the SRP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_srp_secret(unit, client, server, secret, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int am_server;
+{
+    FILE *fp;
+    int ret;
+    char *filename;
+    struct wordlist *addrs, *opts;
+
+    if (!am_server && passwd[0] != '\0') {
+	strlcpy(secret, passwd, MAXWORDLEN);
+    } else {
+	filename = _PATH_SRPFILE;
+	addrs = NULL;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+	    error("Can't open srp secret file %s: %m", filename);
+	    return 0;
+	}
+	check_access(fp, filename);
+
+	secret[0] = '\0';
+	ret = scan_authfile(fp, client, server, secret, &addrs, &opts,
+	    filename, am_server);
+	fclose(fp);
+	if (ret < 0)
+	    return 0;
+
+	if (am_server)
+	    set_allowed_addrs(unit, addrs, opts);
+	else if (opts != NULL)
+	    free_wordlist(opts);
+	if (addrs != NULL)
+	    free_wordlist(addrs);
+    }
+
+    return 1;
+}
+
 /*
  * set_allowed_addrs() - set the list of allowed addresses.
  * Also looks for `--' indicating options to apply for this peer
@@ -1553,7 +2145,7 @@
 	} else {
 	    np = getnetbyname (ptr_word);
 	    if (np != NULL && np->n_addrtype == AF_INET) {
-		a = htonl (*(u_int32_t *)np->n_net);
+		a = htonl ((u_int32_t)np->n_net);
 		if (ptr_mask == NULL) {
 		    /* calculate appropriate mask for net */
 		    ah = ntohl(a);
@@ -1631,11 +2223,17 @@
     if (bad_ip_adrs(addr))
 	return 0;
 
+    if (allowed_address_hook) {
+	ok = allowed_address_hook(addr);
+	if (ok >= 0) return ok;
+    }
+
     if (addresses[unit] != NULL) {
 	ok = ip_addr_check(addr, addresses[unit]);
 	if (ok >= 0)
 	    return ok;
     }
+
     if (auth_required)
 	return 0;		/* no addresses authorized */
     return allow_any_ip || privileged || !have_route_to(addr);
@@ -1683,6 +2281,34 @@
 }
 
 /*
+ * auth_number - check whether the remote number is allowed to connect.
+ * Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_number()
+{
+    struct wordlist *wp = permitted_numbers;
+    int l;
+
+    /* Allow all if no authorization list. */
+    if (!wp)
+	return 1;
+
+    /* Allow if we have a match in the authorization list. */
+    while (wp) {
+	/* trailing '*' wildcard */
+	l = strlen(wp->word);
+	if ((wp->word)[l - 1] == '*')
+	    l--;
+	if (!strncasecmp(wp->word, remote_number, l))
+	    return 1;
+	wp = wp->next;
+    }
+
+    return 0;
+}
+
+/*
  * check_access - complain if a secret file has too-liberal permissions.
  */
 static void
@@ -1712,9 +2338,11 @@
  * following words (extra options) are placed in a wordlist and
  * returned in *opts.
  * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ * Flags are non-zero if we need two colons in the secret in order to
+ * match.
  */
 static int
-scan_authfile(f, client, server, secret, addrs, opts, filename)
+scan_authfile(f, client, server, secret, addrs, opts, filename, flags)
     FILE *f;
     char *client;
     char *server;
@@ -1722,6 +2350,7 @@
     struct wordlist **addrs;
     struct wordlist **opts;
     char *filename;
+    int flags;
 {
     int newline, xxx;
     int got_flag, best_flag;
@@ -1730,6 +2359,7 @@
     char word[MAXWORDLEN];
     char atfile[MAXWORDLEN];
     char lsecret[MAXWORDLEN];
+    char *cp;
 
     if (addrs != NULL)
 	*addrs = NULL;
@@ -1788,6 +2418,14 @@
 	if (newline)
 	    continue;
 
+	/*
+	 * SRP-SHA1 authenticator should never be reading secrets from
+	 * a file.  (Authenticatee may, though.)
+	 */
+	if (flags && ((cp = strchr(word, ':')) == NULL ||
+	    strchr(cp + 1, ':') == NULL))
+	    continue;
+
 	if (secret != NULL) {
 	    /*
 	     * Special syntax: @/pathname means read secret from file.
@@ -1948,5 +2586,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 -Naur tomato/release/src/router/pppd/pppd/auth.c.orig tomato121/release/src/router/pppd/pppd/auth.c.orig
--- tomato/release/src/router/pppd/pppd/auth.c.orig	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/auth.c.orig	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,2575 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993-2002 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. 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 main.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.
+ */
+
+#define RCSID	"$Id: auth.c,v 1.108 2005/08/28 05:22:48 paulus Exp $"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && defined(__linux__)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+#include <time.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "ecp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap-new.h"
+#include "eap.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+#include "pathnames.h"
+
+static const char rcsid[] = RCSID;
+
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER	1
+#define NONWILD_CLIENT	2
+
+#define ISWILD(word)	(word[0] == '*' && word[1] == 0)
+
+/* The name by which the peer authenticated itself to us. */
+char peer_authname[MAXNAMELEN];
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* 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];
+
+/* Wordlist giving addresses which the peer may use
+   without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Remote telephone number, if available */
+char remote_number[MAXNAMELEN];
+
+/* Wordlist giving remote telephone numbers which may connect. */
+static struct wordlist *permitted_numbers;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+			  struct wordlist **paddrs,
+			  struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/* Hook for a plugin to say whether it is OK if the peer
+   refuses to authenticate. */
+int (*null_auth_hook) __P((struct wordlist **paddrs,
+			   struct wordlist **popts)) = NULL;
+
+int (*allowed_address_hook) __P((u_int32_t addr)) = NULL;
+
+/* A notifier for when the peer has authenticated itself,
+   and we are proceeding to the network phase. */
+struct notifier *auth_up_notifier = NULL;
+
+/* A notifier for when the link goes down. */
+struct notifier *link_down_notifier = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+    s_down,
+    s_up
+};
+
+static enum script_state auth_state = s_down;
+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 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 */
+bool refuse_eap = 0;		/* Don't wanna auth. ourselves with EAP */
+#ifdef CHAPMS
+bool refuse_mschap = 0;		/* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 0;	/* Don't wanna auth. ourselves with MS-CHAPv2 */
+bool ms_ignore_domain = 0;
+#else
+bool refuse_mschap = 1;		/* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 1;	/* Don't wanna auth. ourselves with MS-CHAPv2 */
+#endif
+bool usehostname = 0;		/* Use hostname for our_name */
+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 */
+char remote_name[MAXNAMELEN];	/* Peer's name for authentication */
+
+static char *uafname;		/* name of most recent +ua file */
+
+extern char *crypt __P((const char *, const char *));
+
+/* Prototypes for procedures local to this file. */
+
+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 *));
+static int  have_chap_secret __P((char *, char *, int, int *));
+static int  have_srp_secret __P((char *client, char *server, int need_ip,
+    int *lacks_ipp));
+static int  ip_addr_check __P((u_int32_t, struct permitted_ip *));
+static int  scan_authfile __P((FILE *, char *, char *, char *,
+			       struct wordlist **, struct wordlist **,
+			       char *, int));
+static void free_wordlist __P((struct wordlist *));
+static void auth_script __P((char *));
+static void auth_script_done __P((void *));
+static void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *));
+static int  some_ip_ok __P((struct wordlist *));
+static int  setupapfile __P((char **));
+static int  privgroup __P((char **));
+static int  set_noauth_addr __P((char **));
+static int  set_permitted_number __P((char **));
+static void check_access __P((FILE *, char *));
+static int  wordlist_count __P((struct wordlist *));
+
+#ifdef MAXOCTETS
+static void check_maxoctets __P((void *));
+#endif
+
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+    { "auth", o_bool, &auth_required,
+      "Require authentication from peer", OPT_PRIO | 1 },
+    { "noauth", o_bool, &auth_required,
+      "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV,
+      &allow_any_ip },
+    { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+      "Require PAP authentication from peer",
+      OPT_PRIOSUB | 1, &auth_required },
+    { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+      "Require PAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+    { "require-chap", o_bool, &auth_required,
+      "Require CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+chap", o_bool, &auth_required,
+      "Require CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "chap-secrets", o_string, &chapseccustom,
+      "Specify custom chap-secrets file", OPT_PRIO },
+    { "pap-secrets", o_string, &papseccustom,
+      "Specify custom pap-secrets file", OPT_PRIO },
+    { "srp-secrets", o_string, &srpseccustom,
+      "Specify custom srp-secrets file", OPT_PRIO },
+
+#ifdef CHAPMS
+    { "require-mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap", o_bool, &auth_required,
+      "Require MS-CHAP authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "require-mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "+mschap-v2", o_bool, &auth_required,
+      "Require MS-CHAPv2 authentication from peer",
+      OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+      &lcp_wantoptions[0].chap_mdtype },
+    { "ms-ignore-domain", o_bool, &ms_ignore_domain,
+      "Ignore any MS domain prefix in the username", 1 },
+
+#endif
+
+    { "refuse-pap", o_bool, &refuse_pap,
+      "Don't agree to auth to peer with PAP", 1 },
+    { "-pap", o_bool, &refuse_pap,
+      "Don't allow PAP authentication with peer", OPT_ALIAS | 1 },
+    { "refuse-chap", o_bool, &refuse_chap,
+      "Don't agree to auth to peer with CHAP",
+      OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-chap", o_bool, &refuse_chap,
+      "Don't allow CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5,
+      &lcp_allowoptions[0].chap_mdtype },
+#ifdef CHAPMS
+    { "refuse-mschap", o_bool, &refuse_mschap,
+      "Don't agree to auth to peer with MS-CHAP",
+      OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap", o_bool, &refuse_mschap,
+      "Don't allow MS-CHAP authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "refuse-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't agree to auth to peer with MS-CHAPv2",
+      OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+    { "-mschap-v2", o_bool, &refuse_mschap_v2,
+      "Don't allow MS-CHAPv2 authentication with peer",
+      OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+      &lcp_allowoptions[0].chap_mdtype },
+#endif
+
+    { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap,
+      "Require EAP authentication from peer", OPT_PRIOSUB | 1,
+      &auth_required },
+    { "refuse-eap", o_bool, &refuse_eap,
+      "Don't agree to authenticate to peer with EAP", 1 },
+
+    { "name", o_string, our_name,
+      "Set local name for authentication",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
+
+    { "+ua", o_special, (void *)setupapfile,
+      "Get PAP user and password from file",
+      OPT_PRIO | OPT_A2STRVAL, &uafname },
+
+    { "user", o_string, user,
+      "Set name for auth with peer", OPT_PRIO | OPT_STATIC, NULL, MAXNAMELEN },
+
+    { "password", o_string, passwd,
+      "Password for authenticating us to the peer",
+      OPT_PRIO | OPT_STATIC | OPT_HIDE, NULL, MAXSECRETLEN },
+
+    { "usehostname", o_bool, &usehostname,
+      "Must use hostname for authentication", 1 },
+
+    { "remotename", o_string, remote_name,
+      "Set remote name for authentication", OPT_PRIO | OPT_STATIC,
+      &explicit_remote, MAXNAMELEN },
+
+    { "login", o_bool, &uselogin,
+      "Use system password database for PAP", 1 },
+
+    { "papcrypt", o_bool, &cryptpap,
+      "PAP passwords are encrypted", 1 },
+
+    { "privgroup", o_special, (void *)privgroup,
+      "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST },
+
+    { "allow-ip", o_special, (void *)set_noauth_addr,
+      "Set IP address(es) which can be used without authentication",
+      OPT_PRIV | OPT_A2LIST },
+
+    { "remotenumber", o_string, remote_number,
+      "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC,
+      NULL, MAXNAMELEN },
+
+    { "allow-number", o_special, (void *)set_permitted_number,
+      "Set telephone number(s) which are allowed to connect",
+      OPT_PRIV | OPT_A2LIST },
+
+    { NULL }
+};
+
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+    char **argv;
+{
+    FILE *ufile;
+    int l;
+    char u[MAXNAMELEN], p[MAXSECRETLEN];
+    char *fname;
+
+    lcp_allowoptions[0].neg_upap = 1;
+
+    /* open user info file */
+    fname = strdup(*argv);
+    if (fname == NULL)
+	novm("+ua file name");
+    seteuid(getuid());
+    ufile = fopen(fname, "r");
+    seteuid(0);
+    if (ufile == NULL) {
+	option_error("unable to open user login data file %s", fname);
+	return 0;
+    }
+    check_access(ufile, fname);
+    uafname = fname;
+
+    /* get username */
+    if (fgets(u, MAXNAMELEN - 1, ufile) == NULL
+	|| fgets(p, MAXSECRETLEN - 1, ufile) == NULL) {
+	fclose(ufile);
+	option_error("unable to read user login data file %s", fname);
+	return 0;
+    }
+    fclose(ufile);
+
+    /* get rid of newlines */
+    l = strlen(u);
+    if (l > 0 && u[l-1] == '\n')
+	u[l-1] = 0;
+    l = strlen(p);
+    if (l > 0 && p[l-1] == '\n')
+	p[l-1] = 0;
+
+    if (override_value("user", option_priority, fname))
+	strlcpy(user, u, sizeof(user));
+    if (override_value("passwd", option_priority, fname))
+	strlcpy(passwd, p, sizeof(passwd));
+
+    return (1);
+}
+
+
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(argv)
+    char **argv;
+{
+    struct group *g;
+    int i;
+
+    g = getgrnam(*argv);
+    if (g == 0) {
+	option_error("group %s is unknown", *argv);
+	return 0;
+    }
+    for (i = 0; i < ngroups; ++i) {
+	if (groups[i] == g->gr_gid) {
+	    privileged = 1;
+	    break;
+	}
+    }
+    return 1;
+}
+
+
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(argv)
+    char **argv;
+{
+    char *addr = *argv;
+    int l = strlen(addr) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+	novm("allow-ip argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = noauth_addrs;
+    BCOPY(addr, wp->word, l);
+    noauth_addrs = wp;
+    return 1;
+}
+
+
+/*
+ * set_permitted_number - set remote telephone number(s) that may connect.
+ */
+static int
+set_permitted_number(argv)
+    char **argv;
+{
+    char *number = *argv;
+    int l = strlen(number) + 1;
+    struct wordlist *wp;
+
+    wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+    if (wp == NULL)
+	novm("allow-number argument");
+    wp->word = (char *) (wp + 1);
+    wp->next = permitted_numbers;
+    BCOPY(number, wp->word, l);
+    permitted_numbers = wp;
+    return 1;
+}
+
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ */
+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;
+{
+    char *msg;
+
+    new_phase(PHASE_SERIALCONN);
+
+    devfd = the_channel->connect();
+    msg = "Connect script failed";
+    if (devfd < 0)
+	goto fail;
+
+    /* set up the serial device as a ppp interface */
+    /*
+     * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+     * (from establish_ppp to set_ifunit).  However, we won't be
+     * doing the set_ifunit in multilink mode, which is the only time
+     * we need the atomicity that the tdb_writelock/tdb_writeunlock
+     * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.
+     */
+    fd_ppp = the_channel->establish_ppp(devfd);
+    msg = "ppp establishment failed";
+    if (fd_ppp < 0) {
+	status = EXIT_FATAL_ERROR;
+	goto disconnect;
+    }
+
+    if (!demand && ifunit >= 0)
+	set_ifunit(1);
+
+    /*
+     * Start opening the connection and wait for
+     * incoming events (reply, timeout, etc.).
+     */
+    if (ifunit >= 0)
+	notice("Connect: %s <--> %s", ifname, ppp_devnam);
+    else
+	notice("Starting negotiation on %s", ppp_devnam);
+    add_fd(fd_ppp);
+
+    status = EXIT_NEGOTIATION_FAILED;
+    new_phase(PHASE_ESTABLISH);
+
+    lcp_lowerup(0);
+    return;
+
+ disconnect:
+    new_phase(PHASE_DISCONNECT);
+    if (the_channel->disconnect)
+	the_channel->disconnect();
+
+ fail:
+    new_phase(PHASE_DEAD);
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(unit)
+    int unit;
+{
+    if (phase == PHASE_DEAD || phase == PHASE_MASTER)
+	return;
+    new_phase(PHASE_DISCONNECT);
+
+    if (pap_logout_hook) {
+	pap_logout_hook();
+    } else {
+	if (logged_in)
+	    plogout();
+    }
+
+    if (!doing_multilink) {
+	notice("Connection terminated.");
+	print_link_stats();
+    } else
+	notice("Link terminated.");
+
+    /*
+     * Delete pid files before disestablishing ppp.  Otherwise it
+     * can happen that another pppd gets the same unit and then
+     * we delete its pid file.
+     */
+    if (!doing_multilink && !demand)
+	remove_pidfiles();
+
+    /*
+     * If we may want to bring the link up again, transfer
+     * the ppp unit back to the loopback.  Set the
+     * real serial device back to its normal mode of operation.
+     */
+    if (fd_ppp >= 0) {
+	remove_fd(fd_ppp);
+	clean_check();
+	the_channel->disestablish_ppp(devfd);
+	if (doing_multilink)
+	    mp_exit_bundle();
+	fd_ppp = -1;
+    }
+    if (!hungup)
+	lcp_lowerdown(0);
+    if (!doing_multilink && !demand)
+	script_unsetenv("IFNAME");
+
+    /*
+     * Run disconnector script, if requested.
+     * XXX we may not be able to do this if the line has hung up!
+     */
+    if (devfd >= 0 && the_channel->disconnect) {
+	the_channel->disconnect();
+	devfd = -1;
+    }
+    if (the_channel->cleanup)
+	(*the_channel->cleanup)();
+
+    if (doing_multilink && multilink_master) {
+	if (!bundle_terminating)
+	    new_phase(PHASE_MASTER);
+	else
+	    mp_bundle_terminated();
+    } else
+	new_phase(PHASE_DEAD);
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(unit)
+    int unit;
+{
+    if (auth_state != s_down) {
+	notify(link_down_notifier, 0);
+	auth_state = s_down;
+	if (auth_script_state == s_up && auth_script_pid == 0) {
+	    update_link_stats(unit);
+	    auth_script_state = s_down;
+	    auth_script(_PATH_AUTHDOWN);
+	}
+    }
+    if (!doing_multilink) {
+	upper_layers_down(unit);
+	if (phase != PHASE_DEAD && phase != PHASE_MASTER)
+	    new_phase(PHASE_ESTABLISH);
+    }
+    /* XXX if doing_multilink, should do something to stop
+       network-layer traffic on the link */
+}
+
+void upper_layers_down(int unit)
+{
+    int i;
+    struct protent *protp;
+
+    for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+	if (!protp->enabled_flag)
+	    continue;
+        if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+	    (*protp->lowerdown)(unit);
+        if (protp->protocol < 0xC000 && protp->close != NULL)
+	    (*protp->close)(unit, "LCP down");
+    }
+    num_np_open = 0;
+    num_np_up = 0;
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+    int unit;
+{
+    int auth;
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ho = &lcp_hisoptions[unit];
+    int i;
+    struct protent *protp;
+
+    /*
+     * Tell higher-level protocols that LCP is up.
+     */
+    if (!doing_multilink) {
+	for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	    if (protp->protocol != PPP_LCP && protp->enabled_flag
+		&& protp->lowerup != NULL)
+		(*protp->lowerup)(unit);
+    }
+
+    if (!auth_required && noauth_addrs != NULL)
+	set_allowed_addrs(unit, NULL, NULL);
+
+    if (auth_required && !(go->neg_upap || go->neg_chap || go->neg_eap)) {
+	/*
+	 * We wanted the peer to authenticate itself, and it refused:
+	 * if we have some address(es) it can use without auth, fine,
+	 * otherwise treat it as though it authenticated with PAP using
+	 * a username of "" and a password of "".  If that's not OK,
+	 * boot it out.
+	 */
+	if (noauth_addrs != NULL) {
+	    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;
+	    return;
+	}
+    }
+
+    new_phase(PHASE_AUTHENTICATE);
+    used_login = 0;
+    auth = 0;
+    if (go->neg_eap) {
+	eap_authpeer(unit, our_name);
+	auth |= EAP_PEER;
+    } else if (go->neg_chap) {
+	chap_auth_peer(unit, our_name, CHAP_DIGEST(go->chap_mdtype));
+	auth |= CHAP_PEER;
+    } else if (go->neg_upap) {
+	upap_authpeer(unit);
+	auth |= PAP_PEER;
+    }
+    if (ho->neg_eap) {
+	eap_authwithpeer(unit, user);
+	auth |= EAP_WITHPEER;
+    } else if (ho->neg_chap) {
+	chap_auth_with_peer(unit, user, CHAP_DIGEST(ho->chap_mdtype));
+	auth |= CHAP_WITHPEER;
+    } else if (ho->neg_upap) {
+	if (passwd[0] == 0) {
+	    passwd_from_file = 1;
+	    if (!get_pap_passwd(passwd))
+		error("No secret found for PAP login");
+	}
+	upap_authwithpeer(unit, user, passwd);
+	auth |= PAP_WITHPEER;
+    }
+    auth_pending[unit] = auth;
+    auth_done[unit] = 0;
+
+    if (!auth)
+	network_phase(unit);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(unit)
+    int unit;
+{
+    lcp_options *go = &lcp_gotoptions[unit];
+
+    /* Log calling number. */
+    if (*remote_number)
+	notice("peer from calling number %q authorized", remote_number);
+
+    /*
+     * If the peer had to authenticate, run the auth-up script now.
+     */
+    if (go->neg_chap || go->neg_upap || go->neg_eap) {
+	notify(auth_up_notifier, 0);
+	auth_state = s_up;
+	if (auth_script_state == s_down && auth_script_pid == 0) {
+	    auth_script_state = s_up;
+	    auth_script(_PATH_AUTHUP);
+	}
+    }
+
+#ifdef CBCP_SUPPORT
+    /*
+     * If we negotiated callback, do it now.
+     */
+    if (go->neg_cbcp) {
+	new_phase(PHASE_CALLBACK);
+	(*cbcp_protent.open)(unit);
+	return;
+    }
+#endif
+
+    /*
+     * Process extra options from the secrets file
+     */
+    if (extra_options) {
+	options_from_list(extra_options, 1);
+	free_wordlist(extra_options);
+	extra_options = 0;
+    }
+    start_networks(unit);
+}
+
+void
+start_networks(unit)
+    int unit;
+{
+    int i;
+    struct protent *protp;
+    int ecp_required, mppe_required;
+
+    new_phase(PHASE_NETWORK);
+
+#ifdef HAVE_MULTILINK
+    if (multilink) {
+	if (mp_join_bundle()) {
+	    if (updetach && !nodetach)
+		detach();
+	    return;
+	}
+    }
+#endif /* HAVE_MULTILINK */
+
+#ifdef PPP_FILTER
+    if (!demand)
+	set_filters(&pass_filter, &active_filter);
+#endif
+    /* Start CCP and ECP */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if ((protp->protocol == PPP_ECP || protp->protocol == PPP_CCP)
+	    && protp->enabled_flag && protp->open != NULL)
+	    (*protp->open)(0);
+
+    /*
+     * Bring up other network protocols iff encryption is not required.
+     */
+    ecp_required = ecp_gotoptions[unit].required;
+    mppe_required = ccp_gotoptions[unit].mppe;
+    if (!ecp_required && !mppe_required)
+	continue_networks(unit);
+}
+
+void
+continue_networks(unit)
+    int unit;
+{
+    int i;
+    struct protent *protp;
+
+    /*
+     * Start the "real" network protocols.
+     */
+    for (i = 0; (protp = protocols[i]) != NULL; ++i)
+	if (protp->protocol < 0xC000
+	    && protp->protocol != PPP_CCP && protp->protocol != PPP_ECP
+	    && protp->enabled_flag && protp->open != NULL) {
+	    (*protp->open)(0);
+	    ++num_np_open;
+	}
+
+    if (num_np_open == 0)
+	/* nothing to do */
+	lcp_close(0, "No network protocols running");
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(unit, protocol)
+    int unit, protocol;
+{
+    /*
+     * Authentication failure: take the link down
+     */
+    lcp_close(unit, "Authentication failed");
+    status = EXIT_PEER_AUTH_FAILED;
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol, prot_flavor, name, namelen)
+    int unit, protocol, prot_flavor;
+    char *name;
+    int namelen;
+{
+    int bit;
+
+    switch (protocol) {
+    case PPP_CHAP:
+	bit = CHAP_PEER;
+	switch (prot_flavor) {
+	case CHAP_MD5:
+	    bit |= CHAP_MD5_PEER;
+	    break;
+#ifdef CHAPMS
+	case CHAP_MICROSOFT:
+	    bit |= CHAP_MS_PEER;
+	    break;
+	case CHAP_MICROSOFT_V2:
+	    bit |= CHAP_MS2_PEER;
+	    break;
+#endif
+	}
+	break;
+    case PPP_PAP:
+	bit = PAP_PEER;
+	break;
+    case PPP_EAP:
+	bit = EAP_PEER;
+	break;
+    default:
+	warn("auth_peer_success: unknown protocol %x", protocol);
+	return;
+    }
+
+    /*
+     * Save the authenticated name of the peer for later.
+     */
+    if (namelen > sizeof(peer_authname) - 1)
+	namelen = sizeof(peer_authname) - 1;
+    BCOPY(name, peer_authname, namelen);
+    peer_authname[namelen] = 0;
+    script_setenv("PEERNAME", peer_authname, 0);
+
+    /* Save the authentication method for later. */
+    auth_done[unit] |= bit;
+
+    /*
+     * If there is no more authentication still to be done,
+     * proceed to the network (or callback) phase.
+     */
+    if ((auth_pending[unit] &= ~bit) == 0)
+        network_phase(unit);
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(unit, protocol)
+    int unit, protocol;
+{
+    if (passwd_from_file)
+	BZERO(passwd, MAXSECRETLEN);
+    /*
+     * We've failed to authenticate ourselves to our peer.
+     * Some servers keep sending CHAP challenges, but there
+     * 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;
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol, prot_flavor)
+    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;
+	    break;
+#ifdef CHAPMS
+	case CHAP_MICROSOFT:
+	    bit |= CHAP_MS_WITHPEER;
+	    break;
+	case CHAP_MICROSOFT_V2:
+	    bit |= CHAP_MS2_WITHPEER;
+	    break;
+#endif
+	}
+	break;
+    case PPP_PAP:
+	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;
+
+    /*
+     * If there is no more authentication still being done,
+     * proceed to the network (or callback) phase.
+     */
+    if ((auth_pending[unit] &= ~bit) == 0)
+	network_phase(unit);
+}
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(unit, proto)
+    int unit, proto;
+{
+    int tlim;
+
+    if (num_np_up == 0) {
+	/*
+	 * At this point we consider that the link has come up successfully.
+	 */
+	status = EXIT_OK;
+	unsuccess = 0;
+	new_phase(PHASE_RUNNING);
+
+	if (idle_time_hook != 0)
+	    tlim = (*idle_time_hook)(NULL);
+	else
+	    tlim = idle_time_limit;
+	if (tlim > 0)
+	    TIMEOUT(check_idle, NULL, tlim);
+
+	/*
+	 * Set a timeout to close the connection once the maximum
+	 * connect time has expired.
+	 */
+	if (maxconnect > 0)
+	    TIMEOUT(connect_time_expired, 0, maxconnect);
+
+#ifdef MAXOCTETS
+	if (maxoctets > 0)
+	    TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+#endif
+
+	/*
+	 * Detach now, if the updetach option was given.
+	 */
+	if (updetach && !nodetach)
+	    detach();
+    }
+    ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(unit, proto)
+    int unit, proto;
+{
+    if (--num_np_up == 0) {
+	UNTIMEOUT(check_idle, NULL);
+	UNTIMEOUT(connect_time_expired, NULL);
+#ifdef MAXOCTETS
+	UNTIMEOUT(check_maxoctets, NULL);
+#endif	
+	new_phase(PHASE_NETWORK);
+    }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(unit, proto)
+    int unit, proto;
+{
+    if (--num_np_open <= 0) {
+	/* no further use for the link: shut up shop. */
+	lcp_close(0, "No network protocols running");
+    }
+}
+
+#ifdef MAXOCTETS
+static void
+check_maxoctets(arg)
+    void *arg;
+{
+    int diff;
+    unsigned int used;
+
+    update_link_stats(ifunit);
+    link_stats_valid=0;
+    
+    switch(maxoctets_dir) {
+	case PPP_OCTETS_DIRECTION_IN:
+	    used = link_stats.bytes_in;
+	    break;
+	case PPP_OCTETS_DIRECTION_OUT:
+	    used = link_stats.bytes_out;
+	    break;
+	case PPP_OCTETS_DIRECTION_MAXOVERAL:
+	case PPP_OCTETS_DIRECTION_MAXSESSION:
+	    used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out;
+	    break;
+	default:
+	    used = link_stats.bytes_in+link_stats.bytes_out;
+	    break;
+    }
+    diff = maxoctets - used;
+    if(diff < 0) {
+	notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used);
+	lcp_close(0, "Traffic limit");
+	need_holdoff = 0;
+	status = EXIT_TRAFFIC_LIMIT;
+    } else {
+        TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+    }
+}
+#endif
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(arg)
+    void *arg;
+{
+    struct ppp_idle idle;
+    time_t itime;
+    int tlim;
+
+    if (!get_idle_time(0, &idle))
+	return;
+    if (idle_time_hook != 0) {
+	tlim = idle_time_hook(&idle);
+    } else {
+	itime = MIN(idle.xmit_idle, idle.recv_idle);
+	tlim = idle_time_limit - itime;
+    }
+    if (tlim <= 0) {
+	/* link is idle: shut it down. */
+	notice("Terminating connection due to lack of activity.");
+	lcp_close(0, "Link inactive");
+	need_holdoff = 0;
+	status = EXIT_IDLE_TIMEOUT;
+    } else {
+	TIMEOUT(check_idle, NULL, tlim);
+    }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(arg)
+    void *arg;
+{
+    info("Connect time expired");
+    status = EXIT_CONNECT_TIME;
+    lcp_close(0, "Connect time expired");	/* Close connection */
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+    lcp_options *wo = &lcp_wantoptions[0];
+    int can_auth;
+    int lacks_ip;
+
+    /* 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)
+	strlcpy(user, our_name, sizeof(user));
+
+    /*
+     * If we have a default route, require the peer to authenticate
+     * unless the noauth option was given or the real user is root.
+     */
+    if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
+	auth_required = 1;
+	default_auth = 1;
+    }
+
+    /* If we selected any CHAP flavors, we should probably negotiate it. :-) */
+    if (wo->chap_mdtype)
+	wo->neg_chap = 1;
+
+    /* If authentication is required, ask peer for CHAP, PAP, or EAP. */
+    if (auth_required) {
+	allow_any_ip = 0;
+	if (!wo->neg_chap && !wo->neg_upap && !wo->neg_eap) {
+	    wo->neg_chap = chap_mdtype_all != MDTYPE_NONE;
+	    wo->chap_mdtype = chap_mdtype_all;
+	    wo->neg_upap = 1;
+	    wo->neg_eap = 1;
+	}
+    } else {
+	wo->neg_chap = 0;
+	wo->chap_mdtype = MDTYPE_NONE;
+	wo->neg_upap = 0;
+	wo->neg_eap = 0;
+    }
+
+    /*
+     * Check whether we have appropriate secrets to use
+     * to authenticate the peer.  Note that EAP can authenticate by way
+     * of a CHAP-like exchanges as well as SRP.
+     */
+    lacks_ip = 0;
+    can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
+    if (!can_auth && (wo->neg_chap || wo->neg_eap)) {
+	can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
+				    our_name, 1, &lacks_ip);
+    }
+    if (!can_auth && wo->neg_eap) {
+	can_auth = have_srp_secret((explicit_remote? remote_name: NULL),
+				    our_name, 1, &lacks_ip);
+    }
+
+    if (auth_required && !can_auth && noauth_addrs == NULL) {
+	if (default_auth) {
+	    option_error(
+"By default the remote system is required to authenticate itself");
+	    option_error(
+"(because this system has a default route to the internet)");
+	} else if (explicit_remote)
+	    option_error(
+"The remote system (%s) is required to authenticate itself",
+			 remote_name);
+	else
+	    option_error(
+"The remote system is required to authenticate itself");
+	option_error(
+"but I couldn't find any suitable secret (password) for it to use to do so.");
+	if (lacks_ip)
+	    option_error(
+"(None of the available passwords would let it use an IP address.)");
+
+	exit(1);
+    }
+
+    /*
+     * Early check for remote number authorization.
+     */
+    if (!auth_number()) {
+	warn("calling number %q is not authorized", remote_number);
+	exit(EXIT_CNID_AUTH_FAILED);
+    }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+    int unit;
+{
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+    int hadchap;
+
+    hadchap = -1;
+    ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+    ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
+	&& (passwd[0] != 0 ||
+	    (hadchap = have_chap_secret(user, (explicit_remote? remote_name:
+					       NULL), 0, NULL)));
+    ao->neg_eap = !refuse_eap && (
+	passwd[0] != 0 ||
+	(hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
+	    (explicit_remote? remote_name: NULL), 0, NULL))) ||
+	have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
+
+    hadchap = -1;
+    if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
+	go->neg_upap = 0;
+    if (go->neg_chap) {
+	if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL),
+			      our_name, 1, NULL)))
+	    go->neg_chap = 0;
+    }
+    if (go->neg_eap &&
+	(hadchap == 0 || (hadchap == -1 &&
+	    !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
+		1, NULL))) &&
+	!have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
+	    NULL))
+	go->neg_eap = 0;
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file.  If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ *	UPAP_AUTHNAK: Authentication failed.
+ *	UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
+    int unit;
+    char *auser;
+    int userlen;
+    char *apasswd;
+    int passwdlen;
+    char **msg;
+{
+    int ret;
+    char *filename;
+    FILE *f;
+    struct wordlist *addrs = NULL, *opts = NULL;
+    char passwd[256], user[256];
+    char secret[MAXWORDLEN];
+    static int attempts = 0;
+
+    /*
+     * Make copies of apasswd and auser, then null-terminate them.
+     * If there are unprintable characters in the password, make
+     * them visible.
+     */
+    slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd);
+    slprintf(user, sizeof(user), "%.*v", userlen, auser);
+    *msg = "";
+
+    /*
+     * Check if a plugin wants to handle this.
+     */
+    if (pap_auth_hook) {
+	ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts);
+	if (ret >= 0) {
+	    /* note: set_allowed_addrs() saves opts (but not addrs):
+	       don't free it! */
+	    if (ret)
+		set_allowed_addrs(unit, addrs, opts);
+	    else if (opts != 0)
+		free_wordlist(opts);
+	    if (addrs != 0)
+		free_wordlist(addrs);
+	    BZERO(passwd, sizeof(passwd));
+	    return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
+	}
+    }
+
+    /*
+     * Open the file of pap secrets and scan for a suitable secret
+     * for authenticating this user.
+     */
+    filename = papseccustom ? papseccustom : _PATH_UPAPFILE;
+    addrs = opts = NULL;
+    ret = UPAP_AUTHNAK;
+    f = fopen(filename, "r");
+    if (f == NULL) {
+	error("Can't open PAP password file %s: %m", filename);
+
+    } else {
+	check_access(f, filename);
+	if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename, 0) < 0) {
+	    warn("no PAP secret found for %s", user);
+	} else {
+	    /*
+	     * If the secret is "@login", it means to check
+	     * the password against the login database.
+	     */
+	    int login_secret = strcmp(secret, "@login") == 0;
+	    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 (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;
+	    }
+	}
+	fclose(f);
+    }
+
+    if (ret == UPAP_AUTHNAK) {
+        if (**msg == 0)
+	    *msg = "Login incorrect";
+	/*
+	 * XXX can we ever get here more than once??
+	 * Frustrate passwd stealer programs.
+	 * Allow 10 tries, but start backing off after 3 (stolen from login).
+	 * On 10'th, drop the connection.
+	 */
+	if (attempts++ >= 10) {
+	    warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
+	    lcp_close(unit, "login failed");
+	}
+	if (attempts > 3)
+	    sleep((u_int) (attempts - 3) * 5);
+	if (opts != NULL)
+	    free_wordlist(opts);
+
+    } else {
+	attempts = 0;			/* Reset count */
+	if (**msg == 0)
+	    *msg = "Login ok";
+	set_allowed_addrs(unit, addrs, opts);
+    }
+
+    if (addrs != NULL)
+	free_wordlist(addrs);
+    BZERO(passwd, sizeof(passwd));
+    BZERO(secret, sizeof(secret));
+
+    return ret;
+}
+
+/*
+ * 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;
+
+#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);
+}
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout()
+{
+    char *tty;
+#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();
+#endif /* USE_PAM */
+
+    tty = devnam;
+    if (strncmp(tty, "/dev/", 5) == 0)
+	tty += 5;
+    logwtmp(tty, "", "");		/* Wipe out utmp logout entry */
+    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.
+ */
+static int
+null_login(unit)
+    int unit;
+{
+    char *filename;
+    FILE *f;
+    int i, ret;
+    struct wordlist *addrs, *opts;
+    char secret[MAXWORDLEN];
+
+    /*
+     * Check if a plugin wants to handle this.
+     */
+    ret = -1;
+    if (null_auth_hook)
+	ret = (*null_auth_hook)(&addrs, &opts);
+
+    /*
+     * Open the file of pap secrets and scan for a suitable secret.
+     */
+    if (ret <= 0) {
+	filename = papseccustom ? papseccustom : _PATH_UPAPFILE;
+	addrs = NULL;
+	f = fopen(filename, "r");
+	if (f == NULL)
+	    return 0;
+	check_access(f, filename);
+
+	i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0);
+	ret = i >= 0 && secret[0] == 0;
+	BZERO(secret, sizeof(secret));
+	fclose(f);
+    }
+
+    if (ret)
+	set_allowed_addrs(unit, addrs, opts);
+    else if (opts != 0)
+	free_wordlist(opts);
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP.  Returns 1 on success, 0 if no suitable password
+ * could be found.
+ * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null).
+ */
+static int
+get_pap_passwd(passwd)
+    char *passwd;
+{
+    char *filename;
+    FILE *f;
+    int ret;
+    char secret[MAXWORDLEN];
+
+    /*
+     * Check whether a plugin wants to supply this.
+     */
+    if (pap_passwd_hook) {
+	ret = (*pap_passwd_hook)(user, passwd);
+	if (ret >= 0)
+	    return ret;
+    }
+
+    filename = _PATH_UPAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+	return 0;
+    check_access(f, filename);
+    ret = scan_authfile(f, user,
+			(remote_name[0]? remote_name: NULL),
+			secret, NULL, NULL, filename, 0);
+    fclose(f);
+    if (ret < 0)
+	return 0;
+    if (passwd != NULL)
+	strlcpy(passwd, secret, MAXSECRETLEN);
+    BZERO(secret, sizeof(secret));
+    return 1;
+}
+
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(lacks_ipp)
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    /* let the plugin decide, if there is one */
+    if (pap_check_hook) {
+	ret = (*pap_check_hook)();
+	if (ret >= 0)
+	    return ret;
+    }
+
+    filename = papseccustom ? papseccustom : _PATH_UPAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+	return 0;
+
+    ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
+			NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    if (chap_check_hook) {
+	ret = (*chap_check_hook)();
+	if (ret >= 0) {
+	    return ret;
+	}
+    }
+
+    filename = chapseccustom ? chapseccustom : _PATH_CHAPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+	return 0;
+
+    if (client != NULL && client[0] == 0)
+	client = NULL;
+    else if (server != NULL && server[0] == 0)
+	server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * have_srp_secret - check whether we have a SRP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'.  Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_srp_secret(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+
+    filename = srpseccustom ? srpseccustom : _PATH_SRPFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+	return 0;
+
+    if (client != NULL && client[0] == 0)
+	client = NULL;
+    else if (server != NULL && server[0] == 0)
+	server = NULL;
+
+    ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+    fclose(f);
+    if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+	if (lacks_ipp != 0)
+	    *lacks_ipp = 1;
+	ret = -1;
+    }
+    if (addrs != 0)
+	free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int *secret_len;
+    int am_server;
+{
+    FILE *f;
+    int ret, len;
+    char *filename;
+    struct wordlist *addrs, *opts;
+    char secbuf[MAXWORDLEN];
+
+    if (!am_server && passwd[0] != 0) {
+	strlcpy(secbuf, passwd, sizeof(secbuf));
+    } else if (!am_server && chap_passwd_hook) {
+	if ( (*chap_passwd_hook)(client, secbuf) < 0) {
+	    error("Unable to obtain CHAP password for %s on %s from plugin",
+		  client, server);
+	    return 0;
+	}
+    } else {
+	filename = chapseccustom ? chapseccustom : _PATH_CHAPFILE;
+	addrs = NULL;
+	secbuf[0] = 0;
+
+	f = fopen(filename, "r");
+	if (f == NULL) {
+	    error("Can't open chap secret file %s: %m", filename);
+	    return 0;
+	}
+	check_access(f, filename);
+
+	ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0);
+	fclose(f);
+	if (ret < 0)
+	    return 0;
+
+	if (am_server)
+	    set_allowed_addrs(unit, addrs, opts);
+	else if (opts != 0)
+	    free_wordlist(opts);
+	if (addrs != 0)
+	    free_wordlist(addrs);
+    }
+
+    len = strlen(secbuf);
+    if (len > MAXSECRETLEN) {
+	error("Secret for %s on %s is too long", client, server);
+	len = MAXSECRETLEN;
+    }
+    BCOPY(secbuf, secret, len);
+    BZERO(secbuf, sizeof(secbuf));
+    *secret_len = len;
+
+    return 1;
+}
+
+
+/*
+ * get_srp_secret - open the SRP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_srp_secret(unit, client, server, secret, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *secret;
+    int am_server;
+{
+    FILE *fp;
+    int ret;
+    char *filename;
+    struct wordlist *addrs, *opts;
+
+    if (!am_server && passwd[0] != '\0') {
+	strlcpy(secret, passwd, MAXWORDLEN);
+    } else {
+	filename = _PATH_SRPFILE;
+	addrs = NULL;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+	    error("Can't open srp secret file %s: %m", filename);
+	    return 0;
+	}
+	check_access(fp, filename);
+
+	secret[0] = '\0';
+	ret = scan_authfile(fp, client, server, secret, &addrs, &opts,
+	    filename, am_server);
+	fclose(fp);
+	if (ret < 0)
+	    return 0;
+
+	if (am_server)
+	    set_allowed_addrs(unit, addrs, opts);
+	else if (opts != NULL)
+	    free_wordlist(opts);
+	if (addrs != NULL)
+	    free_wordlist(addrs);
+    }
+
+    return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ * Also looks for `--' indicating options to apply for this peer
+ * and leaves the following words in extra_options.
+ */
+static void
+set_allowed_addrs(unit, addrs, opts)
+    int unit;
+    struct wordlist *addrs;
+    struct wordlist *opts;
+{
+    int n;
+    struct wordlist *ap, **plink;
+    struct permitted_ip *ip;
+    char *ptr_word, *ptr_mask;
+    struct hostent *hp;
+    struct netent *np;
+    u_int32_t a, mask, ah, offset;
+    struct ipcp_options *wo = &ipcp_wantoptions[unit];
+    u_int32_t suggested_ip = 0;
+
+    if (addresses[unit] != NULL)
+	free(addresses[unit]);
+    addresses[unit] = NULL;
+    if (extra_options != NULL)
+	free_wordlist(extra_options);
+    extra_options = opts;
+
+    /*
+     * Count the number of IP addresses given.
+     */
+    n = wordlist_count(addrs) + wordlist_count(noauth_addrs);
+    if (n == 0)
+	return;
+    ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip));
+    if (ip == 0)
+	return;
+
+    /* temporarily append the noauth_addrs list to addrs */
+    for (plink = &addrs; *plink != NULL; plink = &(*plink)->next)
+	;
+    *plink = noauth_addrs;
+
+    n = 0;
+    for (ap = addrs; ap != NULL; ap = ap->next) {
+	/* "-" means no addresses authorized, "*" means any address allowed */
+	ptr_word = ap->word;
+	if (strcmp(ptr_word, "-") == 0)
+	    break;
+	if (strcmp(ptr_word, "*") == 0) {
+	    ip[n].permit = 1;
+	    ip[n].base = ip[n].mask = 0;
+	    ++n;
+	    break;
+	}
+
+	ip[n].permit = 1;
+	if (*ptr_word == '!') {
+	    ip[n].permit = 0;
+	    ++ptr_word;
+	}
+
+	mask = ~ (u_int32_t) 0;
+	offset = 0;
+	ptr_mask = strchr (ptr_word, '/');
+	if (ptr_mask != NULL) {
+	    int bit_count;
+	    char *endp;
+
+	    bit_count = (int) strtol (ptr_mask+1, &endp, 10);
+	    if (bit_count <= 0 || bit_count > 32) {
+		warn("invalid address length %v in auth. address list",
+		     ptr_mask+1);
+		continue;
+	    }
+	    bit_count = 32 - bit_count;	/* # bits in host part */
+	    if (*endp == '+') {
+		offset = ifunit + 1;
+		++endp;
+	    }
+	    if (*endp != 0) {
+		warn("invalid address length syntax: %v", ptr_mask+1);
+		continue;
+	    }
+	    *ptr_mask = '\0';
+	    mask <<= bit_count;
+	}
+
+	hp = gethostbyname(ptr_word);
+	if (hp != NULL && hp->h_addrtype == AF_INET) {
+	    a = *(u_int32_t *)hp->h_addr;
+	} else {
+	    np = getnetbyname (ptr_word);
+	    if (np != NULL && np->n_addrtype == AF_INET) {
+		a = htonl ((u_int32_t)np->n_net);
+		if (ptr_mask == NULL) {
+		    /* calculate appropriate mask for net */
+		    ah = ntohl(a);
+		    if (IN_CLASSA(ah))
+			mask = IN_CLASSA_NET;
+		    else if (IN_CLASSB(ah))
+			mask = IN_CLASSB_NET;
+		    else if (IN_CLASSC(ah))
+			mask = IN_CLASSC_NET;
+		}
+	    } else {
+		a = inet_addr (ptr_word);
+	    }
+	}
+
+	if (ptr_mask != NULL)
+	    *ptr_mask = '/';
+
+	if (a == (u_int32_t)-1L) {
+	    warn("unknown host %s in auth. address list", ap->word);
+	    continue;
+	}
+	if (offset != 0) {
+	    if (offset >= ~mask) {
+		warn("interface unit %d too large for subnet %v",
+		     ifunit, ptr_word);
+		continue;
+	    }
+	    a = htonl((ntohl(a) & mask) + offset);
+	    mask = ~(u_int32_t)0;
+	}
+	ip[n].mask = htonl(mask);
+	ip[n].base = a & ip[n].mask;
+	++n;
+	if (~mask == 0 && suggested_ip == 0)
+	    suggested_ip = a;
+    }
+    *plink = NULL;
+
+    ip[n].permit = 0;		/* make the last entry forbid all addresses */
+    ip[n].base = 0;		/* to terminate the list */
+    ip[n].mask = 0;
+
+    addresses[unit] = ip;
+
+    /*
+     * If the address given for the peer isn't authorized, or if
+     * the user hasn't given one, AND there is an authorized address
+     * which is a single host, then use that if we find one.
+     */
+    if (suggested_ip != 0
+	&& (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) {
+	wo->hisaddr = suggested_ip;
+	/*
+	 * Do we insist on this address?  No, if there are other
+	 * addresses authorized than the suggested one.
+	 */
+	if (n > 1)
+	    wo->accept_remote = 1;
+    }
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address.  Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+    int unit;
+    u_int32_t addr;
+{
+    int ok;
+
+    /* don't allow loopback or multicast address */
+    if (bad_ip_adrs(addr))
+	return 0;
+
+    if (allowed_address_hook) {
+	ok = allowed_address_hook(addr);
+	if (ok >= 0) return ok;
+    }
+
+    if (addresses[unit] != NULL) {
+	ok = ip_addr_check(addr, addresses[unit]);
+	if (ok >= 0)
+	    return ok;
+    }
+
+    if (auth_required)
+	return 0;		/* no addresses authorized */
+    return allow_any_ip || privileged || !have_route_to(addr);
+}
+
+static int
+ip_addr_check(addr, addrs)
+    u_int32_t addr;
+    struct permitted_ip *addrs;
+{
+    for (; ; ++addrs)
+	if ((addr & addrs->mask) == addrs->base)
+	    return addrs->permit;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+    u_int32_t addr;
+{
+    addr = ntohl(addr);
+    return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+	|| IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(addrs)
+    struct wordlist *addrs;
+{
+    for (; addrs != 0; addrs = addrs->next) {
+	if (addrs->word[0] == '-')
+	    break;
+	if (addrs->word[0] != '!')
+	    return 1;		/* some IP address is allowed */
+    }
+    return 0;
+}
+
+/*
+ * auth_number - check whether the remote number is allowed to connect.
+ * Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_number()
+{
+    struct wordlist *wp = permitted_numbers;
+    int l;
+
+    /* Allow all if no authorization list. */
+    if (!wp)
+	return 1;
+
+    /* Allow if we have a match in the authorization list. */
+    while (wp) {
+	/* trailing '*' wildcard */
+	l = strlen(wp->word);
+	if ((wp->word)[l - 1] == '*')
+	    l--;
+	if (!strncasecmp(wp->word, remote_number, l))
+	    return 1;
+	wp = wp->next;
+    }
+
+    return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(f, filename)
+    FILE *f;
+    char *filename;
+{
+    struct stat sbuf;
+
+    if (fstat(fileno(f), &sbuf) < 0) {
+	warn("cannot stat secret file %s: %m", filename);
+    } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+	warn("Warning - secret file %s has world and/or group access",
+	     filename);
+    }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'.  The return value is -1
+ * if no secret is found, otherwise >= 0.  The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs.  Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ * Flags are non-zero if we need two colons in the secret in order to
+ * match.
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, opts, filename, flags)
+    FILE *f;
+    char *client;
+    char *server;
+    char *secret;
+    struct wordlist **addrs;
+    struct wordlist **opts;
+    char *filename;
+    int flags;
+{
+    int newline, xxx;
+    int got_flag, best_flag;
+    FILE *sf;
+    struct wordlist *ap, *addr_list, *alist, **app;
+    char word[MAXWORDLEN];
+    char atfile[MAXWORDLEN];
+    char lsecret[MAXWORDLEN];
+    char *cp;
+
+    if (addrs != NULL)
+	*addrs = NULL;
+    if (opts != NULL)
+	*opts = NULL;
+    addr_list = NULL;
+    if (!getword(f, word, &newline, filename))
+	return -1;		/* file is empty??? */
+    newline = 1;
+    best_flag = -1;
+    for (;;) {
+	/*
+	 * Skip until we find a word at the start of a line.
+	 */
+	while (!newline && getword(f, word, &newline, filename))
+	    ;
+	if (!newline)
+	    break;		/* got to end of file */
+
+	/*
+	 * Got a client - check if it's a match or a wildcard.
+	 */
+	got_flag = 0;
+	if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+	    newline = 0;
+	    continue;
+	}
+	if (!ISWILD(word))
+	    got_flag = NONWILD_CLIENT;
+
+	/*
+	 * Now get a server and check if it matches.
+	 */
+	if (!getword(f, word, &newline, filename))
+	    break;
+	if (newline)
+	    continue;
+	if (!ISWILD(word)) {
+	    if (server != NULL && strcmp(word, server) != 0)
+		continue;
+	    got_flag |= NONWILD_SERVER;
+	}
+
+	/*
+	 * Got some sort of a match - see if it's better than what
+	 * we have already.
+	 */
+	if (got_flag <= best_flag)
+	    continue;
+
+	/*
+	 * Get the secret.
+	 */
+	if (!getword(f, word, &newline, filename))
+	    break;
+	if (newline)
+	    continue;
+
+	/*
+	 * SRP-SHA1 authenticator should never be reading secrets from
+	 * a file.  (Authenticatee may, though.)
+	 */
+	if (flags && ((cp = strchr(word, ':')) == NULL ||
+	    strchr(cp + 1, ':') == NULL))
+	    continue;
+
+	if (secret != NULL) {
+	    /*
+	     * Special syntax: @/pathname means read secret from file.
+	     */
+	    if (word[0] == '@' && word[1] == '/') {
+		strlcpy(atfile, word+1, sizeof(atfile));
+		if ((sf = fopen(atfile, "r")) == NULL) {
+		    warn("can't open indirect secret file %s", atfile);
+		    continue;
+		}
+		check_access(sf, atfile);
+		if (!getword(sf, word, &xxx, atfile)) {
+		    warn("no secret in indirect secret file %s", atfile);
+		    fclose(sf);
+		    continue;
+		}
+		fclose(sf);
+	    }
+	    strlcpy(lsecret, word, sizeof(lsecret));
+	}
+
+	/*
+	 * Now read address authorization info and make a wordlist.
+	 */
+	app = &alist;
+	for (;;) {
+	    if (!getword(f, word, &newline, filename) || newline)
+		break;
+	    ap = (struct wordlist *)
+		    malloc(sizeof(struct wordlist) + strlen(word) + 1);
+	    if (ap == NULL)
+		novm("authorized addresses");
+	    ap->word = (char *) (ap + 1);
+	    strcpy(ap->word, word);
+	    *app = ap;
+	    app = &ap->next;
+	}
+	*app = NULL;
+
+	/*
+	 * This is the best so far; remember it.
+	 */
+	best_flag = got_flag;
+	if (addr_list)
+	    free_wordlist(addr_list);
+	addr_list = alist;
+	if (secret != NULL)
+	    strlcpy(secret, lsecret, MAXWORDLEN);
+
+	if (!newline)
+	    break;
+    }
+
+    /* scan for a -- word indicating the start of options */
+    for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+	if (strcmp(ap->word, "--") == 0)
+	    break;
+    /* ap = start of options */
+    if (ap != NULL) {
+	ap = ap->next;		/* first option */
+	free(*app);			/* free the "--" word */
+	*app = NULL;		/* terminate addr list */
+    }
+    if (opts != NULL)
+	*opts = ap;
+    else if (ap != NULL)
+	free_wordlist(ap);
+    if (addrs != NULL)
+	*addrs = addr_list;
+    else if (addr_list != NULL)
+	free_wordlist(addr_list);
+
+    return best_flag;
+}
+
+/*
+ * wordlist_count - return the number of items in a wordlist
+ */
+static int
+wordlist_count(wp)
+    struct wordlist *wp;
+{
+    int n;
+
+    for (n = 0; wp != NULL; wp = wp->next)
+	++n;
+    return n;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+    struct wordlist *wp;
+{
+    struct wordlist *next;
+
+    while (wp != NULL) {
+	next = wp->next;
+	free(wp);
+	wp = next;
+    }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+static void
+auth_script_done(arg)
+    void *arg;
+{
+    auth_script_pid = 0;
+    switch (auth_script_state) {
+    case s_up:
+	if (auth_state == s_down) {
+	    auth_script_state = s_down;
+	    auth_script(_PATH_AUTHDOWN);
+	}
+	break;
+    case s_down:
+	if (auth_state == s_up) {
+	    auth_script_state = s_up;
+	    auth_script(_PATH_AUTHUP);
+	}
+	break;
+    }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(script)
+    char *script;
+{
+    char strspeed[32];
+    struct passwd *pw;
+    char struid[32];
+    char *user_name;
+    char *argv[8];
+
+    if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+	user_name = pw->pw_name;
+    else {
+	slprintf(struid, sizeof(struid), "%d", getuid());
+	user_name = struid;
+    }
+    slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+    argv[0] = script;
+    argv[1] = ifname;
+    argv[2] = peer_authname;
+    argv[3] = user_name;
+    argv[4] = devnam;
+    argv[5] = strspeed;
+    argv[6] = NULL;
+
+    auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL, 0);
+}
diff -Naur tomato/release/src/router/pppd/pppd/cbcp.c tomato121/release/src/router/pppd/pppd/cbcp.c
--- tomato/release/src/router/pppd/pppd/cbcp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/cbcp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,24 +1,39 @@
 /*
  * cbcp - Call Back Configuration Protocol.
  *
- * Copyright (c) 1995 Pedro Roque Marques
- * All rights reserved.
+ * Copyright (c) 1995 Pedro Roque Marques.  All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Pedro Roque Marques.  The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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 names 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 Pedro Roque Marques
+ *     <pedro_m@yahoo.com>"
+ *
+ * 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 RCSID	"$Id: cbcp.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: cbcp.c,v 1.17 2006/05/22 00:04:07 paulus Exp $"
 
 #include <stdio.h>
 #include <string.h>
@@ -38,7 +53,7 @@
 static int setcbcp __P((char **));
 
 static option_t cbcp_option_list[] = {
-    { "callback", o_special, setcbcp,
+    { "callback", o_special, (void *)setcbcp,
       "Ask for callback", OPT_PRIO | OPT_A2STRVAL, &cbcp[0].us_number },
     { NULL }
 };
@@ -79,11 +94,11 @@
 
 /* internal prototypes */
 
-static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len));
 static void cbcp_resp __P((cbcp_state *us));
 static void cbcp_up __P((cbcp_state *us));
-static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
-static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
+static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len));
 
 /* option processing */
 static int
@@ -150,7 +165,8 @@
     inp = inpacket;
 
     if (pktlen < CBCP_MINLEN) {
-        error("CBCP packet is too small");
+	if (debug)
+	    dbglog("CBCP packet is too small");
 	return;
     }
 
@@ -158,12 +174,11 @@
     GETCHAR(id, inp);
     GETSHORT(len, inp);
 
-#if 0
-    if (len > pktlen) {
-        error("CBCP packet: invalid length");
+    if (len > pktlen || len < CBCP_MINLEN) {
+	if (debug)
+	    dbglog("CBCP packet: invalid length %d", len);
         return;
     }
-#endif
 
     len -= CBCP_MINLEN;
  
@@ -174,11 +189,12 @@
 	break;
 
     case CBCP_RESP:
-	dbglog("CBCP_RESP received");
+	if (debug)
+	    dbglog("CBCP_RESP received");
 	break;
 
     case CBCP_ACK:
-	if (id != us->us_id)
+	if (debug && id != us->us_id)
 	    dbglog("id doesn't match: expected %d recv %d",
 		   us->us_id, id);
 
@@ -269,8 +285,8 @@
 		printer(arg, " number = %s", str);
 	    }
 	    printer(arg, ">");
-	    break;
 	}
+	break;
 
     default:
 	break;
@@ -288,7 +304,7 @@
 static void
 cbcp_recvreq(us, pckt, pcktlen)
     cbcp_state *us;
-    char *pckt;
+    u_char *pckt;
     int pcktlen;
 {
     u_char type, opt_len, delay, addr_type;
@@ -297,11 +313,13 @@
 
     address[0] = 0;
 
-    while (len) {
+    while (len >= 2) {
         dbglog("length: %d", len);
 
 	GETCHAR(type, pckt);
 	GETCHAR(opt_len, pckt);
+	if (opt_len < 2 || opt_len > len)
+	    break;
 
 	if (opt_len > 2)
 	    GETCHAR(delay, pckt);
@@ -333,6 +351,11 @@
 	}
 	len -= opt_len;
     }
+    if (len != 0) {
+	if (debug)
+	    dbglog("cbcp_recvreq: malformed packet (%d bytes left)", len);
+	return;
+    }
 
     cbcp_resp(us);
 }
@@ -345,6 +368,7 @@
     u_char buf[256];
     u_char *bufp = buf;
     int len = 0;
+    int slen;
 
     cb_type = us->us_allowed & us->us_type;
     dbglog("cbcp_resp cb_type=%d", cb_type);
@@ -356,12 +380,17 @@
 
     if (cb_type & ( 1 << CB_CONF_USER ) ) {
 	dbglog("cbcp_resp CONF_USER");
+	slen = strlen(us->us_number);
+	if (slen > 250) {
+	    warn("callback number truncated to 250 characters");
+	    slen = 250;
+	}
 	PUTCHAR(CB_CONF_USER, bufp);
-	len = 3 + 1 + strlen(us->us_number) + 1;
+	len = 3 + 1 + slen + 1;
 	PUTCHAR(len , bufp);
 	PUTCHAR(5, bufp); /* delay */
 	PUTCHAR(1, bufp);
-	BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+	BCOPY(us->us_number, bufp, slen + 1);
 	cbcp_send(us, CBCP_RESP, buf, len);
 	return;
     }
@@ -379,11 +408,10 @@
     if (cb_type & ( 1 << CB_CONF_NO ) ) {
         dbglog("cbcp_resp CONF_NO");
 	PUTCHAR(CB_CONF_NO, bufp);
-	len = 3;
+	len = 2;
 	PUTCHAR(len , bufp);
-	PUTCHAR(0, bufp);
 	cbcp_send(us, CBCP_RESP, buf, len);
-	start_networks();
+	start_networks(us->us_unit);
 	return;
     }
 }
@@ -391,7 +419,7 @@
 static void
 cbcp_send(us, code, buf, len)
     cbcp_state *us;
-    u_char code;
+    int code;
     u_char *buf;
     int len;
 {
@@ -417,32 +445,36 @@
 static void
 cbcp_recvack(us, pckt, len)
     cbcp_state *us;
-    char *pckt;
+    u_char *pckt;
     int len;
 {
     u_char type, delay, addr_type;
     int opt_len;
     char address[256];
 
-    if (len) {
+    if (len >= 2) {
         GETCHAR(type, pckt);
 	GETCHAR(opt_len, pckt);
+	if (opt_len >= 2 && opt_len <= len) {
      
-	if (opt_len > 2)
-	    GETCHAR(delay, pckt);
+	    if (opt_len > 2)
+		GETCHAR(delay, pckt);
 
-	if (opt_len > 4) {
-	    GETCHAR(addr_type, pckt);
-	    memcpy(address, pckt, opt_len - 4);
-	    address[opt_len - 4] = 0;
-	    if (address[0])
-	        dbglog("peer will call: %s", address);
-	}
-	if (type == CB_CONF_NO)
-	    return;
-    }
+	    if (opt_len > 4) {
+		GETCHAR(addr_type, pckt);
+		memcpy(address, pckt, opt_len - 4);
+		address[opt_len - 4] = 0;
+		if (address[0])
+		    dbglog("peer will call: %s", address);
+	    }
+	    if (type == CB_CONF_NO)
+		return;
+
+	    cbcp_up(us);
 
-    cbcp_up(us);
+	} else if (debug)
+	    dbglog("cbcp_recvack: malformed packet");
+    }
 }
 
 /* ok peer will do callback */
@@ -451,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 -Naur tomato/release/src/router/pppd/pppd/ccp.c tomato121/release/src/router/pppd/pppd/ccp.c
--- tomato/release/src/router/pppd/pppd/ccp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ccp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,31 +1,34 @@
 /*
  * ccp.c - PPP Compression Control Protocol.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  */
 
-#define RCSID	"$Id: ccp.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: ccp.c,v 1.50 2005/06/26 19:34:41 carlsonj Exp $"
 
 #include <stdlib.h>
 #include <string.h>
@@ -35,6 +38,11 @@
 #include "ccp.h"
 #include <net/ppp-comp.h>
 
+#ifdef MPPE
+#include "chap_ms.h"	/* mppe_xxxx_key, mppe_keys_set */
+#include "lcp.h"	/* lcp_close(), lcp_fsm */
+#endif
+
 static const char rcsid[] = RCSID;
 
 /*
@@ -54,6 +62,14 @@
 static char bsd_value[8];
 static char deflate_value[8];
 
+/*
+ * Option variables.
+ */
+#ifdef MPPE
+static int setmppe(char **);
+static int setnomppe(void);
+#endif /* MPPE */
+
 static option_t ccp_option_list[] = {
     { "noccp", o_bool, &ccp_protent.enabled_flag,
       "Disable CCP negotiation" },
@@ -85,7 +101,7 @@
       &ccp_allowoptions[0].deflate_draft },
 
     { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
-      "request Predictor-1", 1, &ccp_allowoptions[0].predictor_1, OPT_PRIO },
+      "request Predictor-1", OPT_PRIO | 1 },
     { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1,
       "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR,
       &ccp_allowoptions[0].predictor_1 },
@@ -93,6 +109,38 @@
       "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
       &ccp_allowoptions[0].predictor_1 },
 
+    { "lzs", o_bool, &ccp_wantoptions[0].lzs,
+      "request Stac LZS", 1, &ccp_allowoptions[0].lzs, OPT_PRIO },
+    { "+lzs", o_bool, &ccp_wantoptions[0].lzs,
+      "request Stac LZS", 1, &ccp_allowoptions[0].lzs, OPT_ALIAS | OPT_PRIO },
+    { "nolzs", o_bool, &ccp_wantoptions[0].lzs,
+      "don't allow Stac LZS", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].lzs },
+    { "-lzs", o_bool, &ccp_wantoptions[0].lzs,
+      "don't allow Stac LZS", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].lzs },
+
+#ifdef MPPE
+    { "mppc", o_bool, &ccp_wantoptions[0].mppc,
+      "request MPPC compression", 1, &ccp_allowoptions[0].mppc },
+    { "+mppc", o_bool, &ccp_wantoptions[0].mppc,
+      "request MPPC compression", 1, &ccp_allowoptions[0].mppc, OPT_ALIAS },
+    { "nomppc", o_bool, &ccp_wantoptions[0].mppc,
+      "don't allow MPPC compression", OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].mppc },
+    { "-mppc", o_bool, &ccp_wantoptions[0].mppc,
+      "don't allow MPPC compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+      &ccp_allowoptions[0].mppc },
+    { "mppe", o_special, (void *)setmppe,
+      "request MPPE encryption" },
+    { "+mppe", o_special, (void *)setmppe,
+      "request MPPE encryption" },
+    { "nomppe", o_special_noarg, (void *)setnomppe,
+      "don't allow MPPE encryption" },
+    { "-mppe", o_special_noarg, (void *)setnomppe,
+      "don't allow MPPE encryption" },
+#endif /* MPPE */
+
     { NULL }
 };
 
@@ -144,7 +192,7 @@
 static int  ccp_cilen __P((fsm *));
 static void ccp_addci __P((fsm *, u_char *, int *));
 static int  ccp_ackci __P((fsm *, u_char *, int));
-static int  ccp_nakci __P((fsm *, u_char *, int));
+static int  ccp_nakci __P((fsm *, u_char *, int, int));
 static int  ccp_rejci __P((fsm *, u_char *, int));
 static int  ccp_reqci __P((fsm *, u_char *, int *, int));
 static void ccp_up __P((fsm *));
@@ -175,7 +223,8 @@
  * Do we want / did we get any compression?
  */
 #define ANY_COMPRESS(opt)	((opt).deflate || (opt).bsd_compress \
-				 || (opt).predictor_1 || (opt).predictor_2)
+				 || (opt).predictor_1 || (opt).predictor_2 \
+				 || (opt).lzs || (opt).mppc || (opt).mppe)
 
 /*
  * Local state (mainly for handling reset-reqs and reset-acks).
@@ -278,6 +327,100 @@
     return 1;
 }
 
+#ifdef MPPE
+/*
+ * Functions called from config options
+ */
+/* 
+   MPPE suboptions:
+	required - require MPPE; disconnect if peer doesn't support it
+	stateless - use stateless mode
+	no40 - disable 40 bit keys
+	no56 - disable 56 bit keys
+	no128 - disable 128 bit keys
+*/
+int setmppe(char **argv)
+{
+    int i;
+    char *str, cmdbuf[16];
+
+    ccp_allowoptions[0].mppe = 1;
+    ccp_allowoptions[0].mppe_40 = 1;
+    ccp_allowoptions[0].mppe_56 = 1;
+    ccp_allowoptions[0].mppe_128 = 1;
+    ccp_allowoptions[0].mppe_stateless = 0;
+    ccp_wantoptions[0].mppe = 0;
+
+    str = *argv;
+
+    while (1) {
+	i = 0;
+	memset(cmdbuf, '\0', 16);
+	while ((i < 16) && (*str != ',') && (*str != '\0'))
+	    cmdbuf[i++] = *str++;
+	cmdbuf[i] = '\0';
+	if (!strncasecmp(cmdbuf, "no40", strlen("no40"))) {
+	    ccp_allowoptions[0].mppe_40 = 0;
+	    goto next_param;
+	} else if (!strncasecmp(cmdbuf, "no56", strlen("no56"))) {
+	    ccp_allowoptions[0].mppe_56 = 0;
+	    goto next_param;
+	} else if (!strncasecmp(cmdbuf, "no128", strlen("no128"))) {
+	    ccp_allowoptions[0].mppe_128 = 0;
+	    goto next_param;
+	} else if (!strncasecmp(cmdbuf, "stateless", strlen("stateless"))) {
+	    ccp_allowoptions[0].mppe_stateless = 1;
+	    goto next_param;
+	} else if (!strncasecmp(cmdbuf, "required", strlen("required"))) {
+	    ccp_wantoptions[0].mppe = 1;
+	    goto next_param;
+	} else {
+	    option_error("invalid parameter '%s' for mppe option", cmdbuf);
+	    return 0;
+	}
+
+    next_param:
+	if (*str == ',') {
+	    str++;
+	    continue;
+	}
+	if (*str == '\0') {
+	    if (!(ccp_allowoptions[0].mppe_40 || ccp_allowoptions[0].mppe_56 ||
+		  ccp_allowoptions[0].mppe_128)) {
+		if (ccp_wantoptions[0].mppe == 1) {
+		    option_error("You require MPPE but you have switched off "
+				 "all encryption key lengths.");
+		    return 0;
+		}
+		ccp_wantoptions[0].mppe = ccp_allowoptions[0].mppe = 0;
+		ccp_wantoptions[0].mppe_stateless =
+		    ccp_allowoptions[0].mppe_stateless = 0;
+	    } else {
+		ccp_allowoptions[0].mppe = 1;
+		ccp_wantoptions[0].mppe_stateless =
+		    ccp_allowoptions[0].mppe_stateless;
+		if (ccp_wantoptions[0].mppe == 1) {
+		    ccp_wantoptions[0].mppe_40 = ccp_allowoptions[0].mppe_40;
+		    ccp_wantoptions[0].mppe_56 = ccp_allowoptions[0].mppe_56;
+		    ccp_wantoptions[0].mppe_128 = ccp_allowoptions[0].mppe_128;
+		}
+	    }
+	    return 1;
+	}
+    }
+}
+
+int setnomppe(void)
+{
+    ccp_wantoptions[0].mppe = ccp_allowoptions[0].mppe = 0;
+    ccp_wantoptions[0].mppe_40 = ccp_allowoptions[0].mppe_40 = 0;
+    ccp_wantoptions[0].mppe_56 = ccp_allowoptions[0].mppe_56 = 0;
+    ccp_wantoptions[0].mppe_128 = ccp_allowoptions[0].mppe_128 = 0;
+    ccp_wantoptions[0].mppe_stateless = ccp_allowoptions[0].mppe_stateless = 0;
+    return 1;
+}
+#endif /* MPPE */
+
 /*
  * ccp_init - initialize CCP.
  */
@@ -312,6 +455,30 @@
     ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
 
     ccp_allowoptions[0].predictor_1 = 1;
+
+    ccp_wantoptions[0].lzs = 0; /* Stac LZS  - will be enabled in the future */
+    ccp_wantoptions[0].lzs_mode = LZS_MODE_SEQ;
+    ccp_wantoptions[0].lzs_hists = 1;
+    ccp_allowoptions[0].lzs = 0; /* Stac LZS  - will be enabled in the future */
+    ccp_allowoptions[0].lzs_mode = LZS_MODE_SEQ;
+    ccp_allowoptions[0].lzs_hists = 1;
+
+#ifdef MPPE
+    /* by default allow and request MPPC... */
+    ccp_wantoptions[0].mppc = ccp_allowoptions[0].mppc = 1;
+
+    /* ... and allow but don't request MPPE */
+    ccp_allowoptions[0].mppe = 1;
+    ccp_allowoptions[0].mppe_40 = 1;
+    ccp_allowoptions[0].mppe_56 = 1;
+    ccp_allowoptions[0].mppe_128 = 1;
+    ccp_allowoptions[0].mppe_stateless = 1;
+    ccp_wantoptions[0].mppe = 0;
+    ccp_wantoptions[0].mppe_40 = 0;
+    ccp_wantoptions[0].mppe_56 = 0;
+    ccp_wantoptions[0].mppe_128 = 0;
+    ccp_wantoptions[0].mppe_stateless = 0;
+#endif /* MPPE */
 }
 
 /*
@@ -386,8 +553,15 @@
      */
     oldstate = f->state;
     fsm_input(f, p, len);
-    if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED)
+    if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED) {
 	notice("Compression disabled by peer.");
+#ifdef MPPE
+	if (ccp_wantoptions[unit].mppe) {
+	    error("MPPE disabled, closing LCP");
+	    lcp_close(unit, "MPPE disabled by peer");
+	}
+#endif /* MPPE */
+    }
 
     /*
      * If we get a terminate-ack and we're not asking for compression,
@@ -414,6 +588,15 @@
 	    break;
 	/* send a reset-ack, which the transmitter will see and
 	   reset its compression state. */
+
+	/* In case of MPPE/MPPC or LZS we shouldn't send CCP_RESETACK,
+	   but we do it in order to reset compressor; CCP_RESETACK is
+	   then silently discarded. See functions ppp_send_frame and
+	   ppp_ccp_peek in ppp_generic.c (Linux only !!!). All the
+	   confusion is caused by the fact that CCP code is splited
+	   into two parts - one part is handled by pppd, the other one
+	   is handled by kernel. */
+
 	fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
 	break;
 
@@ -440,6 +623,13 @@
 {
     ccp_flags_set(unit, 0, 0);
     fsm_lowerdown(&ccp_fsm[unit]);
+
+#ifdef MPPE
+    if (ccp_wantoptions[unit].mppe) {
+	error("MPPE required but peer negotiation failed");
+	lcp_close(unit, "MPPE required but peer negotiation failed");
+    }
+#endif /* MPPE */
 }
 
 /*
@@ -450,15 +640,129 @@
     fsm *f;
 {
     ccp_options *go = &ccp_gotoptions[f->unit];
-    u_char opt_buf[16];
+    u_char opt_buf[CCP_MAX_OPTION_LENGTH];
 
     *go = ccp_wantoptions[f->unit];
     all_rejected[f->unit] = 0;
 
-    /*
-     * Check whether the kernel knows about the various
-     * compression methods we might request.
-     */
+#ifdef MPPE
+    if (go->mppe || go->mppc) {
+	ccp_options *ao = &ccp_allowoptions[f->unit];
+	int auth_mschap_bits = auth_done[f->unit];
+	int numbits;
+
+	/*
+	 * Start with a basic sanity check: mschap[v2] auth must be in
+	 * exactly one direction.  RFC 3079 says that the keys are
+	 * 'derived from the credentials of the peer that initiated the call',
+	 * however the PPP protocol doesn't have such a concept, and pppd
+	 * cannot get this info externally.  Instead we do the best we can.
+	 * NB: If MPPE is required, all other compression opts are invalid.
+	 *     So, we return right away if we can't do it.
+	 */
+	if (ccp_wantoptions[f->unit].mppe) {
+	    /* Leave only the mschap auth bits set */
+	    auth_mschap_bits &= (CHAP_MS_WITHPEER  | CHAP_MS_PEER |
+				 CHAP_MS2_WITHPEER | CHAP_MS2_PEER);
+	    /* Count the mschap auths */
+	    auth_mschap_bits >>= CHAP_MS_SHIFT;
+	    numbits = 0;
+	    do {
+		numbits += auth_mschap_bits & 1;
+		auth_mschap_bits >>= 1;
+	    } while (auth_mschap_bits);
+	    if (numbits > 1) {
+		error("MPPE required, but auth done in both directions.");
+		lcp_close(f->unit, "MPPE required but not available");
+		return;
+	    }
+	    if (!numbits) {
+		error("MPPE required, but MS-CHAP[v2] auth not performed.");
+		lcp_close(f->unit, "MPPE required but not available");
+		return;
+	    }
+
+	    /* A plugin (eg radius) may not have obtained key material. */
+	    if (!mppe_keys_set) {
+		error("MPPE required, but keys are not available.  "
+		      "Possible plugin problem?");
+		lcp_close(f->unit, "MPPE required but not available");
+		return;
+	    }
+	}
+
+	/*
+	 * Check whether the kernel knows about the various
+	 * compression methods we might request. Key material
+	 * unimportant here.
+	 */
+	if (go->mppc) {
+	    opt_buf[0] = CI_MPPE;
+	    opt_buf[1] = CILEN_MPPE;
+	    opt_buf[2] = 0;
+	    opt_buf[3] = 0;
+	    opt_buf[4] = 0;
+	    opt_buf[5] = MPPE_MPPC;
+	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE, 0) <= 0)
+		go->mppc = 0;
+	}
+	if (go->mppe_40) {
+	    opt_buf[0] = CI_MPPE;
+	    opt_buf[1] = CILEN_MPPE;
+	    opt_buf[2] = MPPE_STATELESS;
+	    opt_buf[3] = 0;
+	    opt_buf[4] = 0;
+	    opt_buf[5] = MPPE_40BIT;
+	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0)
+		go->mppe_40 = 0;
+	}
+	if (go->mppe_56) {
+	    opt_buf[0] = CI_MPPE;
+	    opt_buf[1] = CILEN_MPPE;
+	    opt_buf[2] = MPPE_STATELESS;
+	    opt_buf[3] = 0;
+	    opt_buf[4] = 0;
+	    opt_buf[5] = MPPE_56BIT;
+	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0)
+		go->mppe_56 = 0;
+	}
+	if (go->mppe_128) {
+	    opt_buf[0] = CI_MPPE;
+	    opt_buf[1] = CILEN_MPPE;
+	    opt_buf[2] = MPPE_STATELESS;
+	    opt_buf[3] = 0;
+	    opt_buf[4] = 0;
+	    opt_buf[5] = MPPE_128BIT;
+	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0)
+		go->mppe_128 = 0;
+	}
+	if (!go->mppe_40 && !go->mppe_56 && !go->mppe_128) {
+	    if (ccp_wantoptions[f->unit].mppe) {
+		error("MPPE required, but kernel has no support.");
+		lcp_close(f->unit, "MPPE required but not available");
+	    }
+	    go->mppe = go->mppe_stateless = 0;
+	} else {
+	    /* MPPE is not compatible with other compression types */
+	    if (ccp_wantoptions[f->unit].mppe) {
+		ao->bsd_compress = go->bsd_compress = 0;
+		ao->predictor_1  = go->predictor_1  = 0;
+		ao->predictor_2  = go->predictor_2  = 0;
+		ao->deflate	 = go->deflate	    = 0;
+		ao->lzs		 = go->lzs	    = 0;
+	    }
+	}
+    }
+#endif /* MPPE */
+    if (go->lzs) {
+	opt_buf[0] = CI_LZS;
+	opt_buf[1] = CILEN_LZS;
+	opt_buf[2] = go->lzs_hists >> 8;
+	opt_buf[3] = go->lzs_hists & 0xff;
+	opt_buf[4] = LZS_MODE_SEQ;
+	if (ccp_test(f->unit, opt_buf, CILEN_LZS, 0) <= 0)
+	    go->lzs = 0;
+    }
     if (go->bsd_compress) {
 	opt_buf[0] = CI_BSD_COMPRESS;
 	opt_buf[1] = CILEN_BSD_COMPRESS;
@@ -512,7 +816,9 @@
     return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
 	+ (go->deflate? CILEN_DEFLATE: 0)
 	+ (go->predictor_1? CILEN_PREDICTOR_1: 0)
-	+ (go->predictor_2? CILEN_PREDICTOR_2: 0);
+	+ (go->predictor_2? CILEN_PREDICTOR_2: 0)
+	+ (go->lzs? CILEN_LZS: 0)
+	+ ((go->mppe || go->mppc)? CILEN_MPPE: 0);
 }
 
 /*
@@ -526,6 +832,8 @@
 {
     int res;
     ccp_options *go = &ccp_gotoptions[f->unit];
+    ccp_options *ao = &ccp_allowoptions[f->unit];
+    ccp_options *wo = &ccp_wantoptions[f->unit];
     u_char *p0 = p;
 
     /*
@@ -533,23 +841,68 @@
      * preference order.  Get the kernel to allocate the first one
      * in case it gets Acked.
      */
+#ifdef MPPE
+    if (go->mppe || go->mppc || (!wo->mppe && ao->mppe)) {
+	u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+
+	p[0] = CI_MPPE;
+	p[1] = CILEN_MPPE;
+	p[2] = (go->mppe_stateless ? MPPE_STATELESS : 0);
+	p[3] = 0;
+	p[4] = 0;
+	p[5] = (go->mppe_40 ? MPPE_40BIT : 0) | (go->mppe_56 ? MPPE_56BIT : 0) |
+	    (go->mppe_128 ? MPPE_128BIT : 0) | (go->mppc ? MPPE_MPPC : 0);
+
+	BCOPY(p, opt_buf, CILEN_MPPE);
+	BCOPY(mppe_recv_key, &opt_buf[CILEN_MPPE], MPPE_MAX_KEY_LEN);
+	res = ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0);
+	if (res > 0) {
+	    p += CILEN_MPPE;
+	} else {
+	    /* This shouldn't happen, we've already tested it! */
+	    go->mppe = go->mppe_40 = go->mppe_56 = go->mppe_128 =
+		go->mppe_stateless = go->mppc = 0;
+	    if (ccp_wantoptions[f->unit].mppe)
+		lcp_close(f->unit, "MPPE required but not available in kernel");
+	}
+    }
+#endif /* MPPE */
+    if (go->lzs) {
+	p[0] = CI_LZS;
+	p[1] = CILEN_LZS;
+	p[2] = go->lzs_hists >> 8;
+	p[3] = go->lzs_hists & 0xff;
+	p[4] = LZS_MODE_SEQ;
+	res = ccp_test(f->unit, p, CILEN_LZS, 0);
+	if (res > 0) {
+	    p += CILEN_LZS;
+	} else
+	    go->lzs = 0;
+    }
     if (go->deflate) {
 	p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
 	p[1] = CILEN_DEFLATE;
 	p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
 	p[3] = DEFLATE_CHK_SEQUENCE;
-	for (;;) {
-	    res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
-	    if (res > 0) {
-		p += CILEN_DEFLATE;
-		break;
-	    }
-	    if (res < 0 || go->deflate_size <= DEFLATE_MIN_WORKS) {
-		go->deflate = 0;
-		break;
+	if (p != p0) {
+	    p += CILEN_DEFLATE;
+	} else {
+	    for (;;) {
+		if (go->deflate_size < DEFLATE_MIN_WORKS) {
+		    go->deflate = 0;
+		    break;
+		}
+		res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+		if (res > 0) {
+		    p += CILEN_DEFLATE;
+		    break;
+		} else if (res < 0) {
+		    go->deflate = 0;
+		    break;
+		}
+		--go->deflate_size;
+		p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
 	    }
-	    --go->deflate_size;
-	    p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
 	}
 	if (p != p0 && go->deflate_correct && go->deflate_draft) {
 	    p[0] = CI_DEFLATE_DRAFT;
@@ -567,12 +920,15 @@
 	    p += CILEN_BSD_COMPRESS;	/* not the first option */
 	} else {
 	    for (;;) {
+		if (go->bsd_bits < BSD_MIN_BITS) {
+		    go->bsd_compress = 0;
+		    break;
+		}
 		res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
 		if (res > 0) {
 		    p += CILEN_BSD_COMPRESS;
 		    break;
-		}
-		if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) {
+		} else if (res < 0) {
 		    go->bsd_compress = 0;
 		    break;
 		}
@@ -608,7 +964,7 @@
 
 /*
  * ccp_ackci - process a received configure-ack, and return
- * 1 iff the packet was OK.
+ * 1 if the packet was OK.
  */
 static int
 ccp_ackci(f, p, len)
@@ -617,8 +973,44 @@
     int len;
 {
     ccp_options *go = &ccp_gotoptions[f->unit];
+    ccp_options *ao = &ccp_allowoptions[f->unit];
+    ccp_options *wo = &ccp_wantoptions[f->unit];
     u_char *p0 = p;
 
+#ifdef MPPE
+    if (go->mppe || go->mppc || (!wo->mppe && ao->mppe)) {
+	if (len < CILEN_MPPE
+	    || p[1] != CILEN_MPPE || p[0] != CI_MPPE
+	    || p[2] != (go->mppe_stateless ? MPPE_STATELESS : 0)
+	    || p[3] != 0
+	    || p[4] != 0
+	    || (p[5] != ((go->mppe_40 ? MPPE_40BIT : 0) |
+			 (go->mppc ? MPPE_MPPC : 0))
+		&& p[5] != ((go->mppe_56 ? MPPE_56BIT : 0) |
+			    (go->mppc ? MPPE_MPPC : 0))
+		&& p[5] != ((go->mppe_128 ? MPPE_128BIT : 0) |
+			    (go->mppc ? MPPE_MPPC : 0))))
+	    return 0;
+	if (go->mppe_40 || go->mppe_56 || go->mppe_128)
+	    go->mppe = 1;
+	p += CILEN_MPPE;
+	len -= CILEN_MPPE;
+	/* Cope with first/fast ack */
+	if (p == p0 && len == 0)
+	    return 1;
+    }
+#endif /* MPPE */
+    if (go->lzs) {
+	if (len < CILEN_LZS || p[0] != CI_LZS || p[1] != CILEN_LZS
+	    || p[2] != go->lzs_hists>>8 || p[3] != (go->lzs_hists&0xff)
+	    || p[4] != LZS_MODE_SEQ)
+	    return 0;
+	p += CILEN_LZS;
+	len -= CILEN_LZS;
+	/* XXX Cope with first/fast ack */
+	if (p == p0 && len == 0)
+	    return 1;
+    }
     if (go->deflate) {
 	if (len < CILEN_DEFLATE
 	    || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
@@ -681,21 +1073,119 @@
 
 /*
  * ccp_nakci - process received configure-nak.
- * Returns 1 iff the nak was OK.
+ * Returns 1 if the nak was OK.
  */
 static int
-ccp_nakci(f, p, len)
+ccp_nakci(f, p, len, treat_as_reject)
     fsm *f;
     u_char *p;
     int len;
+    int treat_as_reject;
 {
     ccp_options *go = &ccp_gotoptions[f->unit];
+    ccp_options *ao = &ccp_allowoptions[f->unit];
+    ccp_options *wo = &ccp_wantoptions[f->unit];
     ccp_options no;		/* options we've seen already */
     ccp_options try;		/* options to ask for next time */
 
     memset(&no, 0, sizeof(no));
     try = *go;
 
+#ifdef MPPE
+    if ((go->mppe || go->mppc || (!wo->mppe && ao->mppe)) &&
+	len >= CILEN_MPPE && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+
+	if (go->mppc) {
+	    no.mppc = 1;
+	    if (!(p[5] & MPPE_MPPC))
+		try.mppc = 0;
+	}
+
+	if (go->mppe)
+	    no.mppe = 1;
+	if (go->mppe_40)
+	    no.mppe_40 = 1;
+	if (go->mppe_56)
+	    no.mppe_56 = 1;
+	if (go->mppe_128)
+	    no.mppe_128 = 1;
+	if (go->mppe_stateless)
+	    no.mppe_stateless = 1;
+
+	if (ao->mppe_40) {
+	    if ((p[5] & MPPE_40BIT))
+		try.mppe_40 = 1;
+	    else
+		try.mppe_40 = (p[5] == 0) ? 1 : 0;
+	}
+	if (ao->mppe_56) {
+	    if ((p[5] & MPPE_56BIT))
+		try.mppe_56 = 1;
+	    else
+		try.mppe_56 = (p[5] == 0) ? 1 : 0;
+	}
+	if (ao->mppe_128) {
+	    if ((p[5] & MPPE_128BIT))
+		try.mppe_128 = 1;
+	    else
+		try.mppe_128 = (p[5] == 0) ? 1 : 0;
+	}
+
+	if (ao->mppe_stateless) {
+	    if ((p[2] & MPPE_STATELESS) || wo->mppe_stateless)
+		try.mppe_stateless = 1;
+	    else
+		try.mppe_stateless = 0;
+	}
+
+	if (!try.mppe_56 && !try.mppe_40 && !try.mppe_128) {
+	    try.mppe = try.mppe_stateless = 0;
+	    if (wo->mppe) {
+		/* we require encryption, but peer doesn't support it
+		   so we close connection */
+		wo->mppc = wo->mppe = wo->mppe_stateless = wo->mppe_40 =
+		    wo->mppe_56 = wo->mppe_128 = 0;
+		lcp_close(f->unit, "MPPE required but cannot negotiate MPPE "
+			  "key length");
+	    }
+        }
+	if (wo->mppe && (wo->mppe_40 != try.mppe_40) &&
+	    (wo->mppe_56 != try.mppe_56) && (wo->mppe_128 != try.mppe_128)) {
+	    /* cannot negotiate key length */
+	    wo->mppc = wo->mppe = wo->mppe_stateless = wo->mppe_40 =
+		wo->mppe_56 = wo->mppe_128 = 0;
+	    lcp_close(f->unit, "Cannot negotiate MPPE key length");
+	}
+	if (try.mppe_40 && try.mppe_56 && try.mppe_128)
+	    try.mppe_40 = try.mppe_56 = 0;
+	else
+	    if (try.mppe_56 && try.mppe_128)
+		try.mppe_56 = 0;
+	    else
+		if (try.mppe_40 && try.mppe_128)
+		    try.mppe_40 = 0;
+		else
+		    if (try.mppe_40 && try.mppe_56)
+			try.mppe_40 = 0;
+
+	p += CILEN_MPPE;
+	len -= CILEN_MPPE;
+    }
+#endif /* MPPE */
+
+    if (go->lzs && len >= CILEN_LZS && p[0] == CI_LZS && p[1] == CILEN_LZS) {
+	no.lzs = 1;
+	if (((p[2]<<8)|p[3]) > 1 || (p[4] != LZS_MODE_SEQ &&
+				     p[4] != LZS_MODE_EXT))
+	    try.lzs = 0;
+	else {
+	    try.lzs_mode = p[4];
+	    try.lzs_hists = (p[2] << 8) | p[3];
+	}
+	p += CILEN_LZS;
+	len -= CILEN_LZS;
+    }
+
     if (go->deflate && len >= CILEN_DEFLATE
 	&& p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
 	&& p[1] == CILEN_DEFLATE) {
@@ -767,31 +1257,71 @@
     if (len == 0 && all_rejected[f->unit])
 	return -1;
 
-    if (go->deflate && len >= CILEN_DEFLATE
-	&& p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
-	&& p[1] == CILEN_DEFLATE) {
+#ifdef MPPE
+    if ((go->mppe || go->mppc) && len >= CILEN_MPPE
+	&& p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+	ccp_options *wo = &ccp_wantoptions[f->unit];
+	if (p[2] != (go->mppe_stateless ? MPPE_STATELESS : 0) ||
+	    p[3] != 0 ||
+	    p[4] != 0 ||
+	    p[5] != ((go->mppe_40 ? MPPE_40BIT : 0) |
+		     (go->mppe_56 ? MPPE_56BIT : 0) |
+		     (go->mppe_128 ? MPPE_128BIT : 0) |
+		     (go->mppc ? MPPE_MPPC : 0)))
+	    return 0;
+	if (go->mppc)
+	    try.mppc = 0;
+	if (go->mppe) {
+	    try.mppe = 0;
+	    if (go->mppe_40)
+		try.mppe_40 = 0;
+	    if (go->mppe_56)
+		try.mppe_56 = 0;
+	    if (go->mppe_128)
+		try.mppe_128 = 0;
+	    if (go->mppe_stateless)
+		try.mppe_stateless = 0;
+	    if (!try.mppe_56 && !try.mppe_40 && !try.mppe_128)
+		try.mppe = try.mppe_stateless = 0;
+	    if (wo->mppe) { /* we want MPPE but cannot negotiate key length */
+		wo->mppc = wo->mppe = wo->mppe_stateless = wo->mppe_40 =
+		    wo->mppe_56 = wo->mppe_128 = 0;
+		lcp_close(f->unit, "MPPE required but cannot negotiate MPPE "
+			  "key length");
+	    }
+	}
+	p += CILEN_MPPE;
+	len -= CILEN_MPPE;
+    }
+#endif /* MPPE */
+    if (go->lzs && len >= CILEN_LZS && p[0] == CI_LZS && p[1] == CILEN_LZS) {
+	if (p[2] != go->lzs_hists>>8 || p[3] != (go->lzs_hists&0xff) 
+	    || p[4] != go->lzs_mode)
+	    return 0;
+	try.lzs = 0;
+	p += CILEN_LZS;
+	len -= CILEN_LZS;
+    }
+    if (go->deflate_correct && len >= CILEN_DEFLATE
+	&& p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
 	if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
 	    || p[3] != DEFLATE_CHK_SEQUENCE)
 	    return 0;		/* Rej is bad */
-	if (go->deflate_correct)
-	    try.deflate_correct = 0;
-	else
-	    try.deflate_draft = 0;
+	try.deflate_correct = 0;
+	p += CILEN_DEFLATE;
+	len -= CILEN_DEFLATE;
+    }
+    if (go->deflate_draft && len >= CILEN_DEFLATE
+	&& p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) {
+	if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+	    || p[3] != DEFLATE_CHK_SEQUENCE)
+	    return 0;		/* Rej is bad */
+	try.deflate_draft = 0;
 	p += CILEN_DEFLATE;
 	len -= CILEN_DEFLATE;
-	if (go->deflate_correct && go->deflate_draft
-	    && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
-	    && p[1] == CILEN_DEFLATE) {
-	    if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
-		|| p[3] != DEFLATE_CHK_SEQUENCE)
-		return 0;		/* Rej is bad */
-	    try.deflate_draft = 0;
-	    p += CILEN_DEFLATE;
-	    len -= CILEN_DEFLATE;
-	}
-	if (!try.deflate_correct && !try.deflate_draft)
-	    try.deflate = 0;
     }
+    if (!try.deflate_correct && !try.deflate_draft)
+	try.deflate = 0;
     if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
 	&& p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
 	if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
@@ -835,10 +1365,15 @@
     int dont_nak;
 {
     int ret, newret, res;
-    u_char *p0, *retp;
+    u_char *p0, *retp, p2, p5;
     int len, clen, type, nb;
     ccp_options *ho = &ccp_hisoptions[f->unit];
     ccp_options *ao = &ccp_allowoptions[f->unit];
+    ccp_options *wo = &ccp_wantoptions[f->unit];
+#ifdef MPPE
+    u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+/*     int mtu; */
+#endif /* MPPE */
 
     ret = CONFACK;
     retp = p0 = p;
@@ -859,6 +1394,307 @@
 	    clen = p[1];
 
 	    switch (type) {
+#ifdef MPPE
+	    case CI_MPPE:
+		if ((!ao->mppc && !ao->mppe) || clen != CILEN_MPPE) {
+		    newret = CONFREJ;
+		    break;
+		}
+
+		p2 = p[2];
+		p5 = p[5];
+		/* not sure what they want, tell 'em what we got */
+		if (((p[2] & ~MPPE_STATELESS) != 0 || p[3] != 0 || p[4] != 0 ||
+		     (p[5] & ~(MPPE_40BIT | MPPE_56BIT | MPPE_128BIT |
+			       MPPE_MPPC)) != 0 || p[5] == 0) ||
+		    (p[2] == 0 && p[3] == 0 && p[4] == 0 &&  p[5] == 0)) {
+		    newret = CONFNAK;
+		    p[2] = (wo->mppe_stateless ? MPPE_STATELESS : 0);
+		    p[3] = 0;
+		    p[4] = 0;
+		    p[5] = (wo->mppe_40 ? MPPE_40BIT : 0) |
+			(wo->mppe_56 ? MPPE_56BIT : 0) |
+			(wo->mppe_128 ? MPPE_128BIT : 0) |
+			(wo->mppc ? MPPE_MPPC : 0);
+		    break;
+		}
+
+		if ((p[5] & MPPE_MPPC)) {
+		    if (ao->mppc) {
+			ho->mppc = 1;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			opt_buf[2] = opt_buf[3] = opt_buf[4] = 0;
+			opt_buf[5] = MPPE_MPPC;
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE, 1) <= 0) {
+			    ho->mppc = 0;
+			    p[5] &= ~MPPE_MPPC;
+			    newret = CONFNAK;
+			}
+		    } else {
+			newret = CONFREJ;
+			if (wo->mppe || ao->mppe) {
+			    p[5] &= ~MPPE_MPPC;
+			    newret = CONFNAK;
+			}
+		    }
+		}
+
+		if (ao->mppe)
+		    ho->mppe = 1;
+
+		if ((p[2] & MPPE_STATELESS)) {
+		    if (ao->mppe_stateless) {
+			if (wo->mppe_stateless)
+			    ho->mppe_stateless = 1;
+			else {
+			    newret = CONFNAK;
+			    if (!dont_nak)
+				p[2] &= ~MPPE_STATELESS;
+			}
+		    } else {
+			newret = CONFNAK;
+			if (!dont_nak)
+			    p[2] &= ~MPPE_STATELESS;
+		    }
+		} else {
+		    if (wo->mppe_stateless && !dont_nak) {
+			wo->mppe_stateless = 0;
+			newret = CONFNAK;
+			p[2] |= MPPE_STATELESS;
+		    }
+		}
+
+		if ((p[5] & ~MPPE_MPPC) == (MPPE_40BIT|MPPE_56BIT|MPPE_128BIT)) {
+		    newret = CONFNAK;
+		    if (ao->mppe_128) {
+			ho->mppe_128 = 1;
+			p[5] &= ~(MPPE_40BIT|MPPE_56BIT);
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_128 = 0;
+			    p[5] |= (MPPE_40BIT|MPPE_56BIT);
+			    p[5] &= ~MPPE_128BIT;
+			    goto check_mppe_56_40;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_128BIT;
+		    goto check_mppe_56_40;
+		}
+		if ((p[5] & ~MPPE_MPPC) == (MPPE_56BIT|MPPE_128BIT)) {
+		    newret = CONFNAK;
+		    if (ao->mppe_128) {
+			ho->mppe_128 = 1;
+			p[5] &= ~MPPE_56BIT;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_128 = 0;
+			    p[5] |= MPPE_56BIT;
+			    p[5] &= ~MPPE_128BIT;
+			    goto check_mppe_56;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_128BIT;
+		    goto check_mppe_56;
+		}
+		if ((p[5] & ~MPPE_MPPC) == (MPPE_40BIT|MPPE_128BIT)) {
+		    newret = CONFNAK;
+		    if (ao->mppe_128) {
+			ho->mppe_128 = 1;
+			p[5] &= ~MPPE_40BIT;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_128 = 0;
+			    p[5] |= MPPE_40BIT;
+			    p[5] &= ~MPPE_128BIT;
+			    goto check_mppe_40;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_128BIT;
+		    goto check_mppe_40;
+		}
+		if ((p[5] & ~MPPE_MPPC) == MPPE_128BIT) {
+		    if (ao->mppe_128) {
+			ho->mppe_128 = 1;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_128 = 0;
+			    p[5] &= ~MPPE_128BIT;
+			    newret = CONFNAK;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_128BIT;
+		    newret = CONFNAK;
+		    goto check_mppe;
+		}
+	    check_mppe_56_40:
+		if ((p[5] & ~MPPE_MPPC) == (MPPE_40BIT|MPPE_56BIT)) {
+		    newret = CONFNAK;
+		    if (ao->mppe_56) {
+			ho->mppe_56 = 1;
+			p[5] &= ~MPPE_40BIT;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_56 = 0;
+			    p[5] |= MPPE_40BIT;
+			    p[5] &= ~MPPE_56BIT;
+			    newret = CONFNAK;
+			    goto check_mppe_40;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_56BIT;
+		    goto check_mppe_40;
+		}
+	    check_mppe_56:
+		if ((p[5] & ~MPPE_MPPC) == MPPE_56BIT) {
+		    if (ao->mppe_56) {
+			ho->mppe_56 = 1;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_56 = 0;
+			    p[5] &= ~MPPE_56BIT;
+			    newret = CONFNAK;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_56BIT;
+		    newret = CONFNAK;
+		    goto check_mppe;
+		}
+	    check_mppe_40:
+		if ((p[5] & ~MPPE_MPPC) == MPPE_40BIT) {
+		    if (ao->mppe_40) {
+			ho->mppe_40 = 1;
+			BCOPY(p, opt_buf, CILEN_MPPE);
+			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+			      MPPE_MAX_KEY_LEN);
+			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
+				     MPPE_MAX_KEY_LEN, 1) <= 0) {
+			    ho->mppe_40 = 0;
+			    p[5] &= ~MPPE_40BIT;
+			    newret = CONFNAK;
+			}
+			goto check_mppe;
+		    }
+		    p[5] &= ~MPPE_40BIT;
+		}
+
+	    check_mppe:
+		if (!ho->mppe_40 && !ho->mppe_56 && !ho->mppe_128) {
+		    if (wo->mppe_40 || wo->mppe_56 || wo->mppe_128) {
+			newret = CONFNAK;
+			p[2] |= (wo->mppe_stateless ? MPPE_STATELESS : 0);
+			p[5] |= (wo->mppe_40 ? MPPE_40BIT : 0) |
+			    (wo->mppe_56 ? MPPE_56BIT : 0) |
+			    (wo->mppe_128 ? MPPE_128BIT : 0) |
+			    (wo->mppc ? MPPE_MPPC : 0);
+		    } else {
+			ho->mppe = ho->mppe_stateless = 0;
+		    }
+		} else {
+		    /* MPPE is not compatible with other compression types */
+		    if (wo->mppe) {
+			ao->bsd_compress = 0;
+			ao->predictor_1 = 0;
+			ao->predictor_2 = 0;
+			ao->deflate = 0;
+			ao->lzs = 0;
+		    }
+		}
+		if ((!ho->mppc || !ao->mppc) && !ho->mppe) {
+		    p[2] = p2;
+		    p[5] = p5;
+		    newret = CONFREJ;
+		    break;
+		}
+
+		/*
+		 * I have commented the code below because according to RFC1547
+		 * MTU is only information for higher level protocols about
+		 * "the maximum allowable length for a packet (q.v.) transmitted
+		 * over a point-to-point link without incurring network layer
+		 * fragmentation." Of course a PPP implementation should be able
+		 * to handle overhead added by MPPE - in our case apropriate code
+		 * is located in drivers/net/ppp_generic.c in the kernel sources.
+		 *
+		 * According to RFC1661:
+		 * - when negotiated MRU is less than 1500 octets, a PPP
+		 *   implementation must still be able to receive at least 1500
+		 *   octets,
+		 * - when PFC is negotiated, a PPP implementation is still
+		 *   required to receive frames with uncompressed protocol field.
+		 *
+		 * So why not to handle MPPE overhead without changing MTU value?
+		 * I am sure that RFC3078, unfortunately silently, assumes that.
+		 */
+
+		/*
+		 * We need to decrease the interface MTU by MPPE_PAD
+		 * because MPPE frames **grow**.  The kernel [must]
+		 * allocate MPPE_PAD extra bytes in xmit buffers.
+		 */
+/*
+		mtu = netif_get_mtu(f->unit);
+		if (mtu) {
+		    netif_set_mtu(f->unit, mtu - MPPE_PAD);
+		} else {
+		    newret = CONFREJ;
+		    if (ccp_wantoptions[f->unit].mppe) {
+			error("Cannot adjust MTU needed by MPPE.");
+			lcp_close(f->unit, "Cannot adjust MTU needed by MPPE.");
+		    }
+		}
+*/
+		break;
+#endif /* MPPE */
+
+	    case CI_LZS:
+		if (!ao->lzs || clen != CILEN_LZS) {
+		    newret = CONFREJ;
+		    break;
+		}
+
+		ho->lzs = 1;
+		ho->lzs_hists = (p[2] << 8) | p[3];
+		ho->lzs_mode = p[4];
+		if ((ho->lzs_hists != ao->lzs_hists) ||
+		    (ho->lzs_mode != ao->lzs_mode)) {
+		    newret = CONFNAK;
+		    if (!dont_nak) {
+			p[2] = ao->lzs_hists >> 8;
+			p[3] = ao->lzs_hists & 0xff;
+			p[4] = ao->lzs_mode;
+		    } else
+			break;
+		}
+
+		if (p == p0 && ccp_test(f->unit, p, CILEN_LZS, 1) <= 0) {
+		    newret = CONFREJ;
+		}
+		break;
+
 	    case CI_DEFLATE:
 	    case CI_DEFLATE_DRAFT:
 		if (!ao->deflate || clen != CILEN_DEFLATE
@@ -1015,6 +1851,41 @@
     if (!ANY_COMPRESS(*opt))
 	return "(none)";
     switch (opt->method) {
+#ifdef MPPE
+    case CI_MPPE:
+    {
+	char *p = result;
+	char *q = result + sizeof(result); /* 1 past result */
+
+	if (opt->mppe) {
+	    if (opt->mppc) {
+		slprintf(p, q - p, "MPPC/MPPE ");
+		p += 10;
+	    } else {
+		slprintf(p, q - p, "MPPE ");
+		p += 5;
+	    }
+	    if (opt->mppe_128) {
+		slprintf(p, q - p, "128-bit ");
+		p += 8;
+	    } else if (opt->mppe_56) {
+		slprintf(p, q - p, "56-bit ");
+		p += 7;
+	    } else if (opt->mppe_40) {
+		slprintf(p, q - p, "40-bit ");
+		p += 7;
+	    }
+	    if (opt->mppe_stateless)
+		slprintf(p, q - p, "stateless");
+	    else
+		slprintf(p, q - p, "stateful");
+	} else if (opt->mppc)
+	    slprintf(p, q - p, "MPPC");
+	break;
+    }
+#endif /* MPPE */
+    case CI_LZS:
+	return "Stac LZS";
     case CI_DEFLATE:
     case CI_DEFLATE_DRAFT:
 	if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
@@ -1069,6 +1940,13 @@
 	    notice("%s receive compression enabled", method_name(go, NULL));
     } else if (ANY_COMPRESS(*ho))
 	notice("%s transmit compression enabled", method_name(ho, NULL));
+#ifdef MPPE
+    if (go->mppe || go->mppc) {
+	BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN);
+	BZERO(mppe_send_key, MPPE_MAX_KEY_LEN);
+	continue_networks(f->unit);		/* Bring up IP et al */
+    }
+#endif /* MPPE */
 }
 
 /*
@@ -1082,6 +1960,16 @@
 	UNTIMEOUT(ccp_rack_timeout, f);
     ccp_localstate[f->unit] = 0;
     ccp_flags_set(f->unit, 1, 0);
+#ifdef MPPE
+    if (ccp_gotoptions[f->unit].mppe) {
+	ccp_gotoptions[f->unit].mppe = 0;
+	if (lcp_fsm[f->unit].state == OPENED) {
+	    /* If LCP is not already going down, make sure it does. */
+	    error("MPPE disabled");
+	    lcp_close(f->unit, "MPPE disabled");
+	}
+    }
+#endif /* MPPE */
 }
 
 /*
@@ -1138,6 +2026,31 @@
 	    len -= optlen;
 	    optend = p + optlen;
 	    switch (code) {
+#ifdef MPPE
+	    case CI_MPPE:
+		if (optlen >= CILEN_MPPE) {
+		    printer(arg, "mppe %s %s %s %s %s %s",
+			    (p[2] & MPPE_STATELESS)? "+H": "-H",
+			    (p[5] & MPPE_56BIT)? "+M": "-M",
+			    (p[5] & MPPE_128BIT)? "+S": "-S",
+			    (p[5] & MPPE_40BIT)? "+L": "-L",
+			    (p[5] & MPPE_D_BIT)? "+D": "-D",
+			    (p[5] & MPPE_MPPC)? "+C": "-C");
+		    if ((p[5] & ~(MPPE_56BIT | MPPE_128BIT | MPPE_40BIT |
+				  MPPE_D_BIT | MPPE_MPPC)) ||
+			(p[2] & ~MPPE_STATELESS))
+			printer(arg, " (%.2x %.2x %.2x %.2x)",
+				p[2], p[3], p[4], p[5]);
+		    p += CILEN_MPPE;
+		}
+		break;
+#endif /* MPPE */
+	    case CI_LZS:
+		if (optlen >= CILEN_LZS) {
+		    printer(arg, "lzs %.2x %.2x %.2x", p[2], p[3], p[4]);
+		    p += CILEN_LZS;
+		}
+		break;
 	    case CI_DEFLATE:
 	    case CI_DEFLATE_DRAFT:
 		if (optlen >= CILEN_DEFLATE) {
@@ -1222,8 +2135,27 @@
 	     */
 	    error("Lost compression sync: disabling compression");
 	    ccp_close(unit, "Lost compression sync");
+#ifdef MPPE
+	    /* My module dosn't need this. J.D., 2003-07-06 */
+	    /*
+	     * If we were doing MPPE, we must also take the link down.
+	     */
+	    if (ccp_gotoptions[unit].mppe) {
+		error("Too many MPPE errors, closing LCP");
+		lcp_close(unit, "Too many MPPE errors");
+	    }
+#endif /* MPPE */
 	} else {
 	    /*
+	     * When LZS or MPPE/MPPC is negotiated we just send CCP_RESETREQ
+	     * and don't wait for CCP_RESETACK
+	     */
+	    if ((ccp_gotoptions[f->unit].method == CI_LZS) ||
+		(ccp_gotoptions[f->unit].method == CI_MPPE)) {
+		fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+		return;
+	    }
+	    /*
 	     * Send a reset-request to reset the peer's compressor.
 	     * We don't do that if we are still waiting for an
 	     * acknowledgement to a previous reset-request.
diff -Naur tomato/release/src/router/pppd/pppd/ccp.h tomato121/release/src/router/pppd/pppd/ccp.h
--- tomato/release/src/router/pppd/pppd/ccp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ccp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,33 @@
 /*
  * ccp.h - Definitions for PPP Compression Control Protocol.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ccp.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: ccp.h,v 1.12 2004/11/04 10:02:26 paulus Exp $
  */
 
 typedef struct ccp_options {
@@ -34,8 +37,17 @@
     bool predictor_2;		/* do Predictor-2? */
     bool deflate_correct;	/* use correct code for deflate? */
     bool deflate_draft;		/* use draft RFC code for deflate? */
+    bool lzs;			/* do Stac LZS? */
+    bool mppc;			/* do MPPC? */
+    bool mppe;			/* do MPPE? */
+    bool mppe_40;		/* allow 40 bit encryption? */
+    bool mppe_56;		/* allow 56 bit encryption? */
+    bool mppe_128;		/* allow 128 bit encryption? */
+    bool mppe_stateless;	/* allow stateless encryption */
     u_short bsd_bits;		/* # bits/code for BSD Compress */
     u_short deflate_size;	/* lg(window size) for Deflate */
+    u_short lzs_mode;		/* LZS check mode */
+    u_short lzs_hists;		/* number of LZS histories */
     short method;		/* code for chosen compression method */
 } ccp_options;
 
diff -Naur tomato/release/src/router/pppd/pppd/chap.c tomato121/release/src/router/pppd/pppd/chap.c
--- tomato/release/src/router/pppd/pppd/chap.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap.c	1970-01-01 10:00:00.000000000 +1000
@@ -1,864 +0,0 @@
-/*
- * chap.c - Challenge Handshake Authentication Protocol.
- *
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University.  The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Copyright (c) 1991 Gregory M. Christy.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Gregory M. Christy.  The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-#define RCSID	"$Id: chap.c,v 1.4 2004/10/06 10:22:59 honor Exp $"
-
-/*
- * TODO:
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include "pppd.h"
-#include "chap.h"
-#include "md5.h"
-#ifdef CHAPMS
-#include "chap_ms.h"
-#endif
-
-static const char rcsid[] = RCSID;
-
-/*
- * Command-line options.
- */
-static option_t chap_option_list[] = {
-    { "chap-restart", o_int, &chap[0].timeouttime,
-      "Set timeout for CHAP", OPT_PRIO },
-    { "chap-max-challenge", o_int, &chap[0].max_transmits,
-      "Set max #xmits for challenge", OPT_PRIO },
-    { "chap-interval", o_int, &chap[0].chal_interval,
-      "Set interval for rechallenge", OPT_PRIO },
-#ifdef MSLANMAN
-    { "ms-lanman", o_bool, &ms_lanman,
-      "Use LanMan passwd when using MS-CHAP", 1 },
-#endif
-    { NULL }
-};
-
-/*
- * Protocol entry points.
- */
-static void ChapInit __P((int));
-static void ChapLowerUp __P((int));
-static void ChapLowerDown __P((int));
-static void ChapInput __P((int, u_char *, int));
-static void ChapProtocolReject __P((int));
-static int  ChapPrintPkt __P((u_char *, int,
-			      void (*) __P((void *, char *, ...)), void *));
-
-struct protent chap_protent = {
-    PPP_CHAP,
-    ChapInit,
-    ChapInput,
-    ChapProtocolReject,
-    ChapLowerUp,
-    ChapLowerDown,
-    NULL,
-    NULL,
-    ChapPrintPkt,
-    NULL,
-    1,
-    "CHAP",
-    NULL,
-    chap_option_list,
-    NULL,
-    NULL,
-    NULL
-};
-
-chap_state chap[NUM_PPP];		/* CHAP state; one for each unit */
-
-static void ChapChallengeTimeout __P((void *));
-static void ChapResponseTimeout __P((void *));
-static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
-static void ChapRechallenge __P((void *));
-static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
-static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
-static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
-static void ChapSendStatus __P((chap_state *, int));
-static void ChapSendChallenge __P((chap_state *));
-static void ChapSendResponse __P((chap_state *));
-static void ChapGenChallenge __P((chap_state *));
-
-extern double drand48 __P((void));
-extern void srand48 __P((long));
-
-/*
- * ChapInit - Initialize a CHAP unit.
- */
-static void
-ChapInit(unit)
-    int unit;
-{
-    chap_state *cstate = &chap[unit];
-
-    BZERO(cstate, sizeof(*cstate));
-    cstate->unit = unit;
-    cstate->clientstate = CHAPCS_INITIAL;
-    cstate->serverstate = CHAPSS_INITIAL;
-    cstate->timeouttime = CHAP_DEFTIMEOUT;
-    cstate->max_transmits = CHAP_DEFTRANSMITS;
-    /* random number generator is initialized in magic_init */
-}
-
-
-/*
- * ChapAuthWithPeer - Authenticate us with our peer (start client).
- *
- */
-void
-ChapAuthWithPeer(unit, our_name, digest)
-    int unit;
-    char *our_name;
-    int digest;
-{
-    chap_state *cstate = &chap[unit];
-
-    cstate->resp_name = our_name;
-    cstate->resp_type = digest;
-
-    if (cstate->clientstate == CHAPCS_INITIAL ||
-	cstate->clientstate == CHAPCS_PENDING) {
-	/* lower layer isn't up - wait until later */
-	cstate->clientstate = CHAPCS_PENDING;
-	return;
-    }
-
-    /*
-     * We get here as a result of LCP coming up.
-     * So even if CHAP was open before, we will 
-     * have to re-authenticate ourselves.
-     */
-    cstate->clientstate = CHAPCS_LISTEN;
-}
-
-
-/*
- * ChapAuthPeer - Authenticate our peer (start server).
- */
-void
-ChapAuthPeer(unit, our_name, digest)
-    int unit;
-    char *our_name;
-    int digest;
-{
-    chap_state *cstate = &chap[unit];
-  
-    cstate->chal_name = our_name;
-    cstate->chal_type = digest;
-
-    if (cstate->serverstate == CHAPSS_INITIAL ||
-	cstate->serverstate == CHAPSS_PENDING) {
-	/* lower layer isn't up - wait until later */
-	cstate->serverstate = CHAPSS_PENDING;
-	return;
-    }
-
-    ChapGenChallenge(cstate);
-    ChapSendChallenge(cstate);		/* crank it up dude! */
-    cstate->serverstate = CHAPSS_INITIAL_CHAL;
-}
-
-
-/*
- * ChapChallengeTimeout - Timeout expired on sending challenge.
- */
-static void
-ChapChallengeTimeout(arg)
-    void *arg;
-{
-    chap_state *cstate = (chap_state *) arg;
-  
-    /* if we aren't sending challenges, don't worry.  then again we */
-    /* probably shouldn't be here either */
-    if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
-	cstate->serverstate != CHAPSS_RECHALLENGE)
-	return;
-
-    if (cstate->chal_transmits >= cstate->max_transmits) {
-	/* give up on peer */
-	error("Peer failed to respond to CHAP challenge");
-	cstate->serverstate = CHAPSS_BADAUTH;
-	auth_peer_fail(cstate->unit, PPP_CHAP);
-	return;
-    }
-
-    ChapSendChallenge(cstate);		/* Re-send challenge */
-}
-
-
-/*
- * ChapResponseTimeout - Timeout expired on sending response.
- */
-static void
-ChapResponseTimeout(arg)
-    void *arg;
-{
-    chap_state *cstate = (chap_state *) arg;
-
-    /* if we aren't sending a response, don't worry. */
-    if (cstate->clientstate != CHAPCS_RESPONSE)
-	return;
-
-    ChapSendResponse(cstate);		/* re-send response */
-}
-
-
-/*
- * ChapRechallenge - Time to challenge the peer again.
- */
-static void
-ChapRechallenge(arg)
-    void *arg;
-{
-    chap_state *cstate = (chap_state *) arg;
-
-    /* if we aren't sending a response, don't worry. */
-    if (cstate->serverstate != CHAPSS_OPEN)
-	return;
-
-    ChapGenChallenge(cstate);
-    ChapSendChallenge(cstate);
-    cstate->serverstate = CHAPSS_RECHALLENGE;
-}
-
-
-/*
- * ChapLowerUp - The lower layer is up.
- *
- * Start up if we have pending requests.
- */
-static void
-ChapLowerUp(unit)
-    int unit;
-{
-    chap_state *cstate = &chap[unit];
-  
-    if (cstate->clientstate == CHAPCS_INITIAL)
-	cstate->clientstate = CHAPCS_CLOSED;
-    else if (cstate->clientstate == CHAPCS_PENDING)
-	cstate->clientstate = CHAPCS_LISTEN;
-
-    if (cstate->serverstate == CHAPSS_INITIAL)
-	cstate->serverstate = CHAPSS_CLOSED;
-    else if (cstate->serverstate == CHAPSS_PENDING) {
-	ChapGenChallenge(cstate);
-	ChapSendChallenge(cstate);
-	cstate->serverstate = CHAPSS_INITIAL_CHAL;
-    }
-}
-
-
-/*
- * ChapLowerDown - The lower layer is down.
- *
- * Cancel all timeouts.
- */
-static void
-ChapLowerDown(unit)
-    int unit;
-{
-    chap_state *cstate = &chap[unit];
-  
-    /* Timeout(s) pending?  Cancel if so. */
-    if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
-	cstate->serverstate == CHAPSS_RECHALLENGE)
-	UNTIMEOUT(ChapChallengeTimeout, cstate);
-    else if (cstate->serverstate == CHAPSS_OPEN
-	     && cstate->chal_interval != 0)
-	UNTIMEOUT(ChapRechallenge, cstate);
-    if (cstate->clientstate == CHAPCS_RESPONSE)
-	UNTIMEOUT(ChapResponseTimeout, cstate);
-
-    cstate->clientstate = CHAPCS_INITIAL;
-    cstate->serverstate = CHAPSS_INITIAL;
-}
-
-
-/*
- * ChapProtocolReject - Peer doesn't grok CHAP.
- */
-static void
-ChapProtocolReject(unit)
-    int unit;
-{
-    chap_state *cstate = &chap[unit];
-
-    if (cstate->serverstate != CHAPSS_INITIAL &&
-	cstate->serverstate != CHAPSS_CLOSED)
-	auth_peer_fail(unit, PPP_CHAP);
-    if (cstate->clientstate != CHAPCS_INITIAL &&
-	cstate->clientstate != CHAPCS_CLOSED)
-	auth_withpeer_fail(unit, PPP_CHAP);
-    ChapLowerDown(unit);		/* shutdown chap */
-}
-
-
-/*
- * ChapInput - Input CHAP packet.
- */
-static void
-ChapInput(unit, inpacket, packet_len)
-    int unit;
-    u_char *inpacket;
-    int packet_len;
-{
-    chap_state *cstate = &chap[unit];
-    u_char *inp;
-    u_char code, id;
-    int len;
-  
-    /*
-     * Parse header (code, id and length).
-     * If packet too short, drop it.
-     */
-    inp = inpacket;
-    if (packet_len < CHAP_HEADERLEN) {
-	CHAPDEBUG(("ChapInput: rcvd short header."));
-	return;
-    }
-    GETCHAR(code, inp);
-    GETCHAR(id, inp);
-    GETSHORT(len, inp);
-    if (len < CHAP_HEADERLEN) {
-	CHAPDEBUG(("ChapInput: rcvd illegal length."));
-	return;
-    }
-    if (len > packet_len) {
-	CHAPDEBUG(("ChapInput: rcvd short packet."));
-	return;
-    }
-    len -= CHAP_HEADERLEN;
-  
-    /*
-     * Action depends on code (as in fact it usually does :-).
-     */
-    switch (code) {
-    case CHAP_CHALLENGE:
-	ChapReceiveChallenge(cstate, inp, id, len);
-	break;
-    
-    case CHAP_RESPONSE:
-	ChapReceiveResponse(cstate, inp, id, len);
-	break;
-    
-    case CHAP_FAILURE:
-	ChapReceiveFailure(cstate, inp, id, len);
-	break;
-
-    case CHAP_SUCCESS:
-	ChapReceiveSuccess(cstate, inp, id, len);
-	break;
-
-    default:				/* Need code reject? */
-	warn("Unknown CHAP code (%d) received.", code);
-	break;
-    }
-}
-
-
-/*
- * ChapReceiveChallenge - Receive Challenge and send Response.
- */
-static void
-ChapReceiveChallenge(cstate, inp, id, len)
-    chap_state *cstate;
-    u_char *inp;
-    int id;
-    int len;
-{
-    int rchallenge_len;
-    u_char *rchallenge;
-    int secret_len;
-    char secret[MAXSECRETLEN];
-    char rhostname[256];
-    MD5_CTX mdContext;
-    u_char hash[MD5_SIGNATURE_SIZE];
- 
-    if (cstate->clientstate == CHAPCS_CLOSED ||
-	cstate->clientstate == CHAPCS_PENDING) {
-	CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
-	return;
-    }
-
-    if (len < 2) {
-	CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
-	return;
-    }
-
-    GETCHAR(rchallenge_len, inp);
-    len -= sizeof (u_char) + rchallenge_len;	/* now name field length */
-    if (len < 0) {
-	CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
-	return;
-    }
-    rchallenge = inp;
-    INCPTR(rchallenge_len, inp);
-
-    if (len >= sizeof(rhostname))
-	len = sizeof(rhostname) - 1;
-    BCOPY(inp, rhostname, len);
-    rhostname[len] = '\000';
-
-    /* Microsoft doesn't send their name back in the PPP packet */
-    if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
-	strlcpy(rhostname, remote_name, sizeof(rhostname));
-	CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
-		   rhostname));
-    }
-
-    /* get secret for authenticating ourselves with the specified host */
-    if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
-		    secret, &secret_len, 0)) {
-	secret_len = 0;		/* assume null secret if can't find one */
-	warn("No CHAP secret found for authenticating us to %q", rhostname);
-    }
-
-    /* cancel response send timeout if necessary */
-    if (cstate->clientstate == CHAPCS_RESPONSE)
-	UNTIMEOUT(ChapResponseTimeout, cstate);
-
-    cstate->resp_id = id;
-    cstate->resp_transmits = 0;
-
-    /*  generate MD based on negotiated type */
-    switch (cstate->resp_type) { 
-
-    case CHAP_DIGEST_MD5:
-	MD5Init(&mdContext);
-	MD5Update(&mdContext, &cstate->resp_id, 1);
-	MD5Update(&mdContext, secret, secret_len);
-	MD5Update(&mdContext, rchallenge, rchallenge_len);
-	MD5Final(hash, &mdContext);
-	BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
-	cstate->resp_length = MD5_SIGNATURE_SIZE;
-	break;
-
-#ifdef CHAPMS
-    case CHAP_MICROSOFT:
-	ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
-	break;
-#endif
-
-    default:
-	CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
-	return;
-    }
-
-    BZERO(secret, sizeof(secret));
-    ChapSendResponse(cstate);
-}
-
-
-/*
- * ChapReceiveResponse - Receive and process response.
- */
-static void
-ChapReceiveResponse(cstate, inp, id, len)
-    chap_state *cstate;
-    u_char *inp;
-    int id;
-    int len;
-{
-    u_char *remmd, remmd_len;
-    int secret_len, old_state;
-    int code;
-    char rhostname[256];
-    MD5_CTX mdContext;
-    char secret[MAXSECRETLEN];
-    u_char hash[MD5_SIGNATURE_SIZE];
-
-    if (cstate->serverstate == CHAPSS_CLOSED ||
-	cstate->serverstate == CHAPSS_PENDING) {
-	CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
-	return;
-    }
-
-    if (id != cstate->chal_id)
-	return;			/* doesn't match ID of last challenge */
-
-    /*
-     * If we have received a duplicate or bogus Response,
-     * we have to send the same answer (Success/Failure)
-     * as we did for the first Response we saw.
-     */
-    if (cstate->serverstate == CHAPSS_OPEN) {
-	ChapSendStatus(cstate, CHAP_SUCCESS);
-	return;
-    }
-    if (cstate->serverstate == CHAPSS_BADAUTH) {
-	ChapSendStatus(cstate, CHAP_FAILURE);
-	return;
-    }
-
-    if (len < 2) {
-	CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
-	return;
-    }
-    GETCHAR(remmd_len, inp);		/* get length of MD */
-    remmd = inp;			/* get pointer to MD */
-    INCPTR(remmd_len, inp);
-
-    len -= sizeof (u_char) + remmd_len;
-    if (len < 0) {
-	CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
-	return;
-    }
-
-    UNTIMEOUT(ChapChallengeTimeout, cstate);
-
-    if (len >= sizeof(rhostname))
-	len = sizeof(rhostname) - 1;
-    BCOPY(inp, rhostname, len);
-    rhostname[len] = '\000';
-
-    /*
-     * Get secret for authenticating them with us,
-     * do the hash ourselves, and compare the result.
-     */
-    code = CHAP_FAILURE;
-    if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
-		    cstate->chal_name, secret, &secret_len, 1)) {
-	warn("No CHAP secret found for authenticating %q", rhostname);
-    } else {
-
-	/*  generate MD based on negotiated type */
-	switch (cstate->chal_type) { 
-
-	case CHAP_DIGEST_MD5:		/* only MD5 is defined for now */
-	    if (remmd_len != MD5_SIGNATURE_SIZE)
-		break;			/* it's not even the right length */
-	    MD5Init(&mdContext);
-	    MD5Update(&mdContext, &cstate->chal_id, 1);
-	    MD5Update(&mdContext, secret, secret_len);
-	    MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
-	    MD5Final(hash, &mdContext); 
-
-	    /* compare local and remote MDs and send the appropriate status */
-	    if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
-		code = CHAP_SUCCESS;	/* they are the same! */
-	    break;
-
-	default:
-	    CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
-	}
-    }
-
-    BZERO(secret, sizeof(secret));
-    ChapSendStatus(cstate, code);
-
-    if (code == CHAP_SUCCESS) {
-	old_state = cstate->serverstate;
-	cstate->serverstate = CHAPSS_OPEN;
-	if (old_state == CHAPSS_INITIAL_CHAL) {
-	    auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
-	}
-	if (cstate->chal_interval != 0)
-	    TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
-	notice("CHAP peer authentication succeeded for %q", rhostname);
-
-    } else {
-	error("CHAP peer authentication failed for remote host %q", rhostname);
-	cstate->serverstate = CHAPSS_BADAUTH;
-	auth_peer_fail(cstate->unit, PPP_CHAP);
-    }
-}
-
-/*
- * ChapReceiveSuccess - Receive Success
- */
-static void
-ChapReceiveSuccess(cstate, inp, id, len)
-    chap_state *cstate;
-    u_char *inp;
-    u_char id;
-    int len;
-{
-
-    if (cstate->clientstate == CHAPCS_OPEN)
-	/* presumably an answer to a duplicate response */
-	return;
-
-    if (cstate->clientstate != CHAPCS_RESPONSE) {
-	/* don't know what this is */
-	CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
-	return;
-    }
-
-    UNTIMEOUT(ChapResponseTimeout, cstate);
-
-    /*
-     * Print message.
-     */
-    if (len > 0)
-	PRINTMSG(inp, len);
-
-    cstate->clientstate = CHAPCS_OPEN;
-
-    auth_withpeer_success(cstate->unit, PPP_CHAP);
-}
-
-
-/*
- * ChapReceiveFailure - Receive failure.
- */
-static void
-ChapReceiveFailure(cstate, inp, id, len)
-    chap_state *cstate;
-    u_char *inp;
-    u_char id;
-    int len;
-{
-    if (cstate->clientstate != CHAPCS_RESPONSE) {
-	/* don't know what this is */
-	CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
-	return;
-    }
-
-    UNTIMEOUT(ChapResponseTimeout, cstate);
-
-    /*
-     * Print message.
-     */
-    if (len > 0)
-	PRINTMSG(inp, len);
-
-    error("CHAP authentication failed");
-
-    //log_to_file("CHAP_AUTH_FAIL");
-    system("ppp_event -t CHAP_AUTH_FAIL &");
-
-    auth_withpeer_fail(cstate->unit, PPP_CHAP);
-}
-
-
-/*
- * ChapSendChallenge - Send an Authenticate challenge.
- */
-static void
-ChapSendChallenge(cstate)
-    chap_state *cstate;
-{
-    u_char *outp;
-    int chal_len, name_len;
-    int outlen;
-
-    chal_len = cstate->chal_len;
-    name_len = strlen(cstate->chal_name);
-    outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
-    outp = outpacket_buf;
-
-    MAKEHEADER(outp, PPP_CHAP);		/* paste in a CHAP header */
-
-    PUTCHAR(CHAP_CHALLENGE, outp);
-    PUTCHAR(cstate->chal_id, outp);
-    PUTSHORT(outlen, outp);
-
-    PUTCHAR(chal_len, outp);		/* put length of challenge */
-    BCOPY(cstate->challenge, outp, chal_len);
-    INCPTR(chal_len, outp);
-
-    BCOPY(cstate->chal_name, outp, name_len);	/* append hostname */
-
-    output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
-  
-    TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
-    ++cstate->chal_transmits;
-}
-
-
-/*
- * ChapSendStatus - Send a status response (ack or nak).
- */
-static void
-ChapSendStatus(cstate, code)
-    chap_state *cstate;
-    int code;
-{
-    u_char *outp;
-    int outlen, msglen;
-    char msg[256];
-
-    if (code == CHAP_SUCCESS)
-	slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
-    else
-	slprintf(msg, sizeof(msg), "I don't like you.  Go 'way.");
-    msglen = strlen(msg);
-
-    outlen = CHAP_HEADERLEN + msglen;
-    outp = outpacket_buf;
-
-    MAKEHEADER(outp, PPP_CHAP);	/* paste in a header */
-  
-    PUTCHAR(code, outp);
-    PUTCHAR(cstate->chal_id, outp);
-    PUTSHORT(outlen, outp);
-    BCOPY(msg, outp, msglen);
-    output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
-}
-
-/*
- * ChapGenChallenge is used to generate a pseudo-random challenge string of
- * a pseudo-random length between min_len and max_len.  The challenge
- * string and its length are stored in *cstate, and various other fields of
- * *cstate are initialized.
- */
-
-static void
-ChapGenChallenge(cstate)
-    chap_state *cstate;
-{
-    int chal_len;
-    u_char *ptr = cstate->challenge;
-    int i;
-
-    /* pick a random challenge length between MIN_CHALLENGE_LENGTH and 
-       MAX_CHALLENGE_LENGTH */  
-    chal_len =  (unsigned) ((drand48() *
-			     (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
-			    MIN_CHALLENGE_LENGTH);
-    cstate->chal_len = chal_len;
-    cstate->chal_id = ++cstate->id;
-    cstate->chal_transmits = 0;
-
-    /* generate a random string */
-    for (i = 0; i < chal_len; i++)
-	*ptr++ = (char) (drand48() * 0xff);
-}
-
-/*
- * ChapSendResponse - send a response packet with values as specified
- * in *cstate.
- */
-/* ARGSUSED */
-static void
-ChapSendResponse(cstate)
-    chap_state *cstate;
-{
-    u_char *outp;
-    int outlen, md_len, name_len;
-
-    md_len = cstate->resp_length;
-    name_len = strlen(cstate->resp_name);
-    outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
-    outp = outpacket_buf;
-
-    MAKEHEADER(outp, PPP_CHAP);
-
-    PUTCHAR(CHAP_RESPONSE, outp);	/* we are a response */
-    PUTCHAR(cstate->resp_id, outp);	/* copy id from challenge packet */
-    PUTSHORT(outlen, outp);		/* packet length */
-
-    PUTCHAR(md_len, outp);		/* length of MD */
-    BCOPY(cstate->response, outp, md_len);	/* copy MD to buffer */
-    INCPTR(md_len, outp);
-
-    BCOPY(cstate->resp_name, outp, name_len); /* append our name */
-
-    /* send the packet */
-    output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
-
-    cstate->clientstate = CHAPCS_RESPONSE;
-    TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
-    ++cstate->resp_transmits;
-}
-
-/*
- * ChapPrintPkt - print the contents of a CHAP packet.
- */
-static char *ChapCodenames[] = {
-    "Challenge", "Response", "Success", "Failure"
-};
-
-static int
-ChapPrintPkt(p, plen, printer, arg)
-    u_char *p;
-    int plen;
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
-{
-    int code, id, len;
-    int clen, nlen;
-    u_char x;
-
-    if (plen < CHAP_HEADERLEN)
-	return 0;
-    GETCHAR(code, p);
-    GETCHAR(id, p);
-    GETSHORT(len, p);
-    if (len < CHAP_HEADERLEN || len > plen)
-	return 0;
-
-    if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
-	printer(arg, " %s", ChapCodenames[code-1]);
-    else
-	printer(arg, " code=0x%x", code);
-    printer(arg, " id=0x%x", id);
-    len -= CHAP_HEADERLEN;
-    switch (code) {
-    case CHAP_CHALLENGE:
-    case CHAP_RESPONSE:
-	if (len < 1)
-	    break;
-	clen = p[0];
-	if (len < clen + 1)
-	    break;
-	++p;
-	nlen = len - clen - 1;
-	printer(arg, " <");
-	for (; clen > 0; --clen) {
-	    GETCHAR(x, p);
-	    printer(arg, "%.2x", x);
-	}
-	printer(arg, ">, name = ");
-	print_string((char *)p, nlen, printer, arg);
-	break;
-    case CHAP_FAILURE:
-    case CHAP_SUCCESS:
-	printer(arg, " ");
-	print_string((char *)p, len, printer, arg);
-	break;
-    default:
-	for (clen = len; clen > 0; --clen) {
-	    GETCHAR(x, p);
-	    printer(arg, " %.2x", x);
-	}
-    }
-
-    return len + CHAP_HEADERLEN;
-}
diff -Naur tomato/release/src/router/pppd/pppd/chap.h tomato121/release/src/router/pppd/pppd/chap.h
--- tomato/release/src/router/pppd/pppd/chap.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap.h	1970-01-01 10:00:00.000000000 +1000
@@ -1,124 +0,0 @@
-/*
- * chap.h - Challenge Handshake Authentication Protocol definitions.
- *
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University.  The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Copyright (c) 1991 Gregory M. Christy
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the author.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: chap.h,v 1.1 2003/07/10 07:43:04 honor Exp $
- */
-
-#ifndef __CHAP_INCLUDE__
-
-/* Code + ID + length */
-#define CHAP_HEADERLEN		4
-
-/*
- * CHAP codes.
- */
-
-#define CHAP_DIGEST_MD5		5	/* use MD5 algorithm */
-#define MD5_SIGNATURE_SIZE	16	/* 16 bytes in a MD5 message digest */
-#define CHAP_MICROSOFT		0x80	/* use Microsoft-compatible alg. */
-#define MS_CHAP_RESPONSE_LEN	49	/* Response length for MS-CHAP */
-
-#define CHAP_CHALLENGE		1
-#define CHAP_RESPONSE		2
-#define CHAP_SUCCESS		3
-#define CHAP_FAILURE    	4
-
-/*
- *  Challenge lengths (for challenges we send) and other limits.
- */
-#define MIN_CHALLENGE_LENGTH	16
-#define MAX_CHALLENGE_LENGTH	24
-#define MAX_RESPONSE_LENGTH	64	/* sufficient for MD5 or MS-CHAP */
-
-/*
- * Each interface is described by a chap structure.
- */
-
-typedef struct chap_state {
-    int unit;			/* Interface unit number */
-    int clientstate;		/* Client state */
-    int serverstate;		/* Server state */
-    u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
-    u_char chal_len;		/* challenge length */
-    u_char chal_id;		/* ID of last challenge */
-    u_char chal_type;		/* hash algorithm for challenges */
-    u_char id;			/* Current id */
-    char *chal_name;		/* Our name to use with challenge */
-    int chal_interval;		/* Time until we challenge peer again */
-    int timeouttime;		/* Timeout time in seconds */
-    int max_transmits;		/* Maximum # of challenge transmissions */
-    int chal_transmits;		/* Number of transmissions of challenge */
-    int resp_transmits;		/* Number of transmissions of response */
-    u_char response[MAX_RESPONSE_LENGTH];	/* Response to send */
-    u_char resp_length;		/* length of response */
-    u_char resp_id;		/* ID for response messages */
-    u_char resp_type;		/* hash algorithm for responses */
-    char *resp_name;		/* Our name to send with response */
-} chap_state;
-
-
-/*
- * Client (peer) states.
- */
-#define CHAPCS_INITIAL		0	/* Lower layer down, not opened */
-#define CHAPCS_CLOSED		1	/* Lower layer up, not opened */
-#define CHAPCS_PENDING		2	/* Auth us to peer when lower up */
-#define CHAPCS_LISTEN		3	/* Listening for a challenge */
-#define CHAPCS_RESPONSE		4	/* Sent response, waiting for status */
-#define CHAPCS_OPEN		5	/* We've received Success */
-
-/*
- * Server (authenticator) states.
- */
-#define CHAPSS_INITIAL		0	/* Lower layer down, not opened */
-#define CHAPSS_CLOSED		1	/* Lower layer up, not opened */
-#define CHAPSS_PENDING		2	/* Auth peer when lower up */
-#define CHAPSS_INITIAL_CHAL	3	/* We've sent the first challenge */
-#define CHAPSS_OPEN		4	/* We've sent a Success msg */
-#define CHAPSS_RECHALLENGE	5	/* We've sent another challenge */
-#define CHAPSS_BADAUTH		6	/* We've sent a Failure msg */
-
-/*
- * Timeouts.
- */
-#define CHAP_DEFTIMEOUT		3	/* Timeout time in seconds */
-#define CHAP_DEFTRANSMITS	10	/* max # times to send challenge */
-
-extern chap_state chap[];
-
-void ChapAuthWithPeer __P((int, char *, int));
-void ChapAuthPeer __P((int, char *, int));
-
-extern struct protent chap_protent;
-
-#define __CHAP_INCLUDE__
-#endif /* __CHAP_INCLUDE__ */
diff -Naur tomato/release/src/router/pppd/pppd/chap-md5.c tomato121/release/src/router/pppd/pppd/chap-md5.c
--- tomato/release/src/router/pppd/pppd/chap-md5.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap-md5.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,117 @@
+/*
+ * chap-md5.c - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 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. 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.
+ */
+
+#define RCSID	"$Id: chap-md5.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
+
+#include <stdlib.h>
+#include <string.h>
+#include "pppd.h"
+#include "chap-new.h"
+#include "chap-md5.h"
+#include "magic.h"
+#include "md5.h"
+
+#define MD5_HASH_SIZE		16
+#define MD5_MIN_CHALLENGE	16
+#define MD5_MAX_CHALLENGE	24
+
+static void
+chap_md5_generate_challenge(unsigned char *cp)
+{
+	int clen;
+
+	clen = (int)(drand48() * (MD5_MAX_CHALLENGE - MD5_MIN_CHALLENGE))
+		+ MD5_MIN_CHALLENGE;
+	*cp++ = clen;
+	random_bytes(cp, clen);
+}
+
+static int
+chap_md5_verify_response(int id, char *name,
+			 unsigned char *secret, int secret_len,
+			 unsigned char *challenge, unsigned char *response,
+			 char *message, int message_space)
+{
+	MD5_CTX ctx;
+	unsigned char idbyte = id;
+	unsigned char hash[MD5_HASH_SIZE];
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;
+	response_len = *response++;
+	if (response_len == MD5_HASH_SIZE) {
+		/* Generate hash of ID, secret, challenge */
+		MD5_Init(&ctx);
+		MD5_Update(&ctx, &idbyte, 1);
+		MD5_Update(&ctx, secret, secret_len);
+		MD5_Update(&ctx, challenge, challenge_len);
+		MD5_Final(hash, &ctx);
+
+		/* Test if our hash matches the peer's response */
+		if (memcmp(hash, response, MD5_HASH_SIZE) == 0) {
+			slprintf(message, message_space, "Access granted");
+			return 1;
+		}
+	}
+	slprintf(message, message_space, "Access denied");
+	return 0;
+}
+
+static void
+chap_md5_make_response(unsigned char *response, int id, char *our_name,
+		       unsigned char *challenge, char *secret, int secret_len,
+		       unsigned char *private)
+{
+	MD5_CTX ctx;
+	unsigned char idbyte = id;
+	int challenge_len = *challenge++;
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, &idbyte, 1);
+	MD5_Update(&ctx, secret, secret_len);
+	MD5_Update(&ctx, challenge, challenge_len);
+	MD5_Final(&response[1], &ctx);
+	response[0] = MD5_HASH_SIZE;
+}
+
+static struct chap_digest_type md5_digest = {
+	CHAP_MD5,		/* code */
+	chap_md5_generate_challenge,
+	chap_md5_verify_response,
+	chap_md5_make_response,
+	NULL,			/* check_success */
+	NULL,			/* handle_failure */
+};
+
+void
+chap_md5_init(void)
+{
+	chap_register_digest(&md5_digest);
+}
diff -Naur tomato/release/src/router/pppd/pppd/chap-md5.h tomato121/release/src/router/pppd/pppd/chap-md5.h
--- tomato/release/src/router/pppd/pppd/chap-md5.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap-md5.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,31 @@
+/*
+ * chap-md5.h - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 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. 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.
+ */
+
+extern void chap_md5_init(void);
diff -Naur tomato/release/src/router/pppd/pppd/chap_ms.c tomato121/release/src/router/pppd/pppd/chap_ms.c
--- tomato/release/src/router/pppd/pppd/chap_ms.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap_ms.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,31 @@
 /*
  * chap_ms.c - Microsoft MS-CHAP compatible implementation.
  *
- * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
- * http://www.strataware.com/
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
  *
- * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Eric Rosenquist.  The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
+ *
+ * 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.
  */
 
 /*
@@ -31,7 +39,42 @@
  *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
  */
 
-#define RCSID	"$Id: chap_ms.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+/*
+ * Modifications by Frank Cusack, frank@google.com, March 2002.
+ *
+ *   Implemented MS-CHAPv2 functionality, heavily based on sample
+ *   implementation in RFC 2759.  Implemented MPPE functionality,
+ *   heavily based on sample implementation in RFC 3079.
+ *
+ * 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
+ * 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.
+ *
+ * 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 RCSID	"$Id: chap_ms.c,v 1.36 2006/05/21 11:56:40 paulus Exp $"
 
 #ifdef CHAPMS
 
@@ -42,42 +85,34 @@
 #include <sys/types.h>
 #include <sys/time.h>
 #include <unistd.h>
-#ifdef HAVE_CRYPT_H
-#include <crypt.h>
-#endif
 
 #include "pppd.h"
-#include "chap.h"
+#include "chap-new.h"
 #include "chap_ms.h"
 #include "md4.h"
-
-#ifndef USE_CRYPT
-#include <des.h>
-#endif
+#include "sha1.h"
+#include "pppcrypt.h"
+#include "magic.h"
 
 static const char rcsid[] = RCSID;
 
-typedef struct {
-    u_char LANManResp[24];
-    u_char NTResp[24];
-    u_char UseNT;		/* If 1, ignore the LANMan response field */
-} MS_ChapResponse;
-/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
-   in case this struct gets padded. */
 
-
-static void	ChallengeResponse __P((u_char *, u_char *, u_char *));
-static void	DesEncrypt __P((u_char *, u_char *, u_char *));
-static void	MakeKey __P((u_char *, u_char *));
-static u_char	Get7Bits __P((u_char *, int));
-static void	ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
+static void	ascii2unicode __P((char[], int, u_char[]));
+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((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((char *, int, char *, int, MS_ChapResponse *));
+static void	ChapMS_LANMan __P((u_char *, char *, int, u_char *));
 #endif
 
-#ifdef USE_CRYPT
-static void	Expand __P((u_char *, u_char *));
-static void	Collapse __P((u_char *, u_char *));
+#ifdef MPPE
+static void	Set_Start_Key __P((u_char *, char *, int));
+static void	SetMasterKeys __P((char *, int, u_char[24], int));
 #endif
 
 #ifdef MSLANMAN
@@ -85,210 +120,452 @@
 			  	/* Has meaning only with MS-CHAP challenges */
 #endif
 
-static void
-ChallengeResponse(challenge, pwHash, response)
-    u_char *challenge;	/* IN   8 octets */
-    u_char *pwHash;	/* IN  16 octets */
-    u_char *response;	/* OUT 24 octets */
-{
-    char    ZPasswordHash[21];
-
-    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
-    BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
-
-#if 0
-    dbglog("ChallengeResponse - ZPasswordHash %.*B",
-	   sizeof(ZPasswordHash), ZPasswordHash);
+#ifdef MPPE
+u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+int mppe_keys_set = 0;		/* Have the MPPE keys been set? */
+
+#ifdef DEBUGMPPEKEY
+/* For MPPE debug */
+/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
+static char *mschap_challenge = NULL;
+/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
+static char *mschap2_peer_challenge = NULL;
+#endif
+
+#include "fsm.h"		/* Need to poke MPPE options */
+#include "ccp.h"
+#include <net/ppp-comp.h>
 #endif
 
-    DesEncrypt(challenge, ZPasswordHash +  0, response + 0);
-    DesEncrypt(challenge, ZPasswordHash +  7, response + 8);
-    DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+/*
+ * Command-line options.
+ */
+static option_t chapms_option_list[] = {
+#ifdef MSLANMAN
+	{ "ms-lanman", o_bool, &ms_lanman,
+	  "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+#ifdef DEBUGMPPEKEY
+	{ "mschap-challenge", o_string, &mschap_challenge,
+	  "specify CHAP challenge" },
+	{ "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
+	  "specify CHAP peer challenge" },
+#endif
+	{ NULL }
+};
 
-#if 0
-    dbglog("ChallengeResponse - response %.24B", response);
+/*
+ * chapms_generate_challenge - generate a challenge for MS-CHAP.
+ * For MS-CHAP the challenge length is fixed at 8 bytes.
+ * The length goes in challenge[0] and the actual challenge starts
+ * at challenge[1].
+ */
+static void
+chapms_generate_challenge(unsigned char *challenge)
+{
+	*challenge++ = 8;
+#ifdef DEBUGMPPEKEY
+	if (mschap_challenge && strlen(mschap_challenge) == 8)
+		memcpy(challenge, mschap_challenge, 8);
+	else
 #endif
+		random_bytes(challenge, 8);
 }
 
-
-#ifdef USE_CRYPT
 static void
-DesEncrypt(clear, key, cipher)
-    u_char *clear;	/* IN  8 octets */
-    u_char *key;	/* IN  7 octets */
-    u_char *cipher;	/* OUT 8 octets */
+chapms2_generate_challenge(unsigned char *challenge)
 {
-    u_char des_key[8];
-    u_char crypt_key[66];
-    u_char des_input[66];
+	*challenge++ = 16;
+#ifdef DEBUGMPPEKEY
+	if (mschap_challenge && strlen(mschap_challenge) == 16)
+		memcpy(challenge, mschap_challenge, 16);
+	else
+#endif
+		random_bytes(challenge, 16);
+}
 
-    MakeKey(key, des_key);
+static int
+chapms_verify_response(int id, char *name,
+		       unsigned char *secret, int secret_len,
+		       unsigned char *challenge, unsigned char *response,
+		       char *message, int message_space)
+{
+	unsigned char md[MS_CHAP_RESPONSE_LEN];
+	int diff;
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;	/* skip length, is 8 */
+	response_len = *response++;
+	if (response_len != MS_CHAP_RESPONSE_LEN)
+		goto bad;
+
+#ifndef MSLANMAN
+	if (!response[MS_CHAP_USENT]) {
+		/* Should really propagate this into the error packet. */
+		notice("Peer request for LANMAN auth not supported");
+		goto bad;
+	}
+#endif
 
-    Expand(des_key, crypt_key);
-    setkey(crypt_key);
+	/* Generate the expected response. */
+	ChapMS(challenge, (char *)secret, secret_len, md);
 
-#if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
-#endif
+#ifdef MSLANMAN
+	/* Determine which part of response to verify against */
+	if (!response[MS_CHAP_USENT])
+		diff = memcmp(&response[MS_CHAP_LANMANRESP],
+			      &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
+	else
+#endif
+		diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
+			      MS_CHAP_NTRESP_LEN);
+
+	if (diff == 0) {
+		slprintf(message, message_space, "Access granted");
+		return 1;
+	}
 
-    Expand(clear, des_input);
-    encrypt(des_input, 0);
-    Collapse(des_input, cipher);
+ bad:
+	/* See comments below for MS-CHAP V2 */
+	slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
+		 challenge_len, challenge);
+	return 0;
+}
 
-#if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
+static int
+chapms2_verify_response(int id, char *name,
+			unsigned char *secret, int secret_len,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space)
+{
+	unsigned char md[MS_CHAP2_RESPONSE_LEN];
+	char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;	/* skip length, is 16 */
+	response_len = *response++;
+	if (response_len != MS_CHAP2_RESPONSE_LEN)
+		goto bad;	/* not even the right length */
+
+	/* Generate the expected response and our mutual auth. */
+	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 */
+	/*
+	 * Per RFC 2759, success message must be formatted as
+	 *     "S=<auth_string> M=<message>"
+	 * where
+	 *     <auth_string> is the Authenticator Response (mutual auth)
+	 *     <message> is a text message
+	 *
+	 * However, some versions of Windows (win98 tested) do not know
+	 * about the M=<message> part (required per RFC 2759) and flag
+	 * it as an error (reported incorrectly as an encryption error
+	 * to the user).  Since the RFC requires it, and it can be
+	 * useful information, we supply it if the peer is a conforming
+	 * system.  Luckily (?), win98 sets the Flags field to 0x04
+	 * (contrary to RFC requirements) so we can use that to
+	 * distinguish between conforming and non-conforming systems.
+	 *
+	 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
+	 * help debugging this.
+	 */
+	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",
+				 saresponse, "Access granted");
+		return 1;
+	}
+
+ bad:
+	/*
+	 * Failure message must be formatted as
+	 *     "E=e R=r C=c V=v M=m"
+	 * where
+	 *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
+	 *     r = retry (we use 1, ok to retry)
+	 *     c = challenge to use for next response, we reuse previous
+	 *     v = Change Password version supported, we use 0
+	 *     m = text message
+	 *
+	 * The M=m part is only for MS-CHAPv2.  Neither win2k nor
+	 * win98 (others untested) display the message to the user anyway.
+	 * They also both ignore the E=e code.
+	 *
+	 * Note that it's safe to reuse the same challenge as we don't
+	 * actually accept another response based on the error message
+	 * (and no clients try to resend a response anyway).
+	 *
+	 * Basically, this whole bit is useless code, even the small
+	 * implementation here is only because of overspecification.
+	 */
+	slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+		 challenge_len, challenge, "Access denied");
+	return 0;
+}
+
+static void
+chapms_make_response(unsigned char *response, int id, char *our_name,
+		     unsigned char *challenge, char *secret, int secret_len,
+		     unsigned char *private)
+{
+	challenge++;	/* skip length, should be 8 */
+	*response++ = MS_CHAP_RESPONSE_LEN;
+	ChapMS(challenge, secret, secret_len, response);
+}
+
+static void
+chapms2_make_response(unsigned char *response, int id, char *our_name,
+		      unsigned char *challenge, char *secret, int secret_len,
+		      unsigned char *private)
+{
+	challenge++;	/* skip length, should be 16 */
+	*response++ = MS_CHAP2_RESPONSE_LEN;
+	ChapMS2(challenge,
+#ifdef DEBUGMPPEKEY
+		mschap2_peer_challenge,
+#else
+		NULL,
 #endif
+		our_name, secret, secret_len, response, private,
+		MS_CHAP2_AUTHENTICATEE);
 }
 
-#else /* USE_CRYPT */
+static int
+chapms2_check_success(unsigned char *msg, int len, unsigned char *private)
+{
+	if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
+	    strncmp((char *)msg, "S=", 2) != 0) {
+		/* Packet does not start with "S=" */
+		error("MS-CHAPv2 Success packet is badly formed.");
+		return 0;
+	}
+	msg += 2;
+	len -= 2;
+	if (len < MS_AUTH_RESPONSE_LENGTH
+	    || memcmp(msg, private, MS_AUTH_RESPONSE_LENGTH)) {
+		/* Authenticator Response did not match expected. */
+		error("MS-CHAPv2 mutual authentication failed.");
+		return 0;
+	}
+	/* Authenticator Response matches. */
+	msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
+	len -= MS_AUTH_RESPONSE_LENGTH;
+	if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
+		msg += 3; /* Eat the delimiter */
+	} else if (len) {
+		/* Packet has extra text which does not begin " M=" */
+		error("MS-CHAPv2 Success packet is badly formed.");
+		return 0;
+	}
+	return 1;
+}
 
 static void
-DesEncrypt(clear, key, cipher)
-    u_char *clear;	/* IN  8 octets */
-    u_char *key;	/* IN  7 octets */
-    u_char *cipher;	/* OUT 8 octets */
+chapms_handle_failure(unsigned char *inp, int len)
 {
-    des_cblock		des_key;
-    des_key_schedule	key_schedule;
+	int err;
+	char *p, *msg;
 
-    MakeKey(key, des_key);
+	/* We want a null-terminated string for strxxx(). */
+	msg = malloc(len + 1);
+	if (!msg) {
+		notice("Out of memory in chapms_handle_failure");
+		return;
+	}
+	BCOPY(inp, msg, len);
+	msg[len] = 0;
+	p = msg;
+
+	/*
+	 * Deal with MS-CHAP formatted failure messages; just print the
+	 * M=<message> part (if any).  For MS-CHAP we're not really supposed
+	 * to use M=<message>, but it shouldn't hurt.  See
+	 * chapms[2]_verify_response.
+	 */
+	if (!strncmp(p, "E=", 2))
+		err = strtol(p+2, NULL, 10); /* Remember the error code. */
+	else
+		goto print_msg; /* Message is badly formatted. */
+
+	if (len && ((p = strstr(p, " M=")) != NULL)) {
+		/* M=<message> field found. */
+		p += 3;
+	} else {
+		/* No M=<message>; use the error code. */
+		switch (err) {
+		case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
+			p = "E=646 Restricted logon hours";
+			break;
+
+		case MS_CHAP_ERROR_ACCT_DISABLED:
+			p = "E=647 Account disabled";
+			break;
+
+		case MS_CHAP_ERROR_PASSWD_EXPIRED:
+			p = "E=648 Password expired";
+			break;
+
+		case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
+			p = "E=649 No dialin permission";
+			break;
+
+		case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
+			p = "E=691 Authentication failure";
+			break;
+
+		case MS_CHAP_ERROR_CHANGING_PASSWORD:
+			/* Should never see this, we don't support Change Password. */
+			p = "E=709 Error changing password";
+			break;
+
+		default:
+			free(msg);
+			error("Unknown MS-CHAP authentication failure: %.*v",
+			      len, inp);
+			return;
+		}
+	}
+print_msg:
+	if (p != NULL)
+		error("MS-CHAP authentication failed: %v", p);
+	free(msg);
+}
 
-    des_set_key(&des_key, key_schedule);
+static void
+ChallengeResponse(u_char *challenge,
+		  u_char PasswordHash[MD4_SIGNATURE_SIZE],
+		  u_char response[24])
+{
+    u_char    ZPasswordHash[21];
+
+    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+    BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
 
 #if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
+    dbglog("ChallengeResponse - ZPasswordHash %.*B",
+	   sizeof(ZPasswordHash), ZPasswordHash);
 #endif
 
-    des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+    (void) DesSetkey(ZPasswordHash + 0);
+    DesEncrypt(challenge, response + 0);
+    (void) DesSetkey(ZPasswordHash + 7);
+    DesEncrypt(challenge, response + 8);
+    (void) DesSetkey(ZPasswordHash + 14);
+    DesEncrypt(challenge, response + 16);
 
 #if 0
-    CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
+    dbglog("ChallengeResponse - response %.24B", response);
 #endif
 }
 
-#endif /* USE_CRYPT */
-
-
-static u_char Get7Bits(input, startBit)
-    u_char *input;
-    int startBit;
+void
+ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
+	      char *username, u_char Challenge[8])
+    
 {
-    register unsigned int	word;
-
-    word  = (unsigned)input[startBit / 8] << 8;
-    word |= (unsigned)input[startBit / 8 + 1];
+    SHA1_CTX	sha1Context;
+    u_char	sha1Hash[SHA1_SIGNATURE_SIZE];
+    char	*user;
+
+    /* remove domain from "domain\username" */
+    if ((user = strrchr(username, '\\')) != NULL)
+	++user;
+    else
+	user = username;
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PeerChallenge, 16);
+    SHA1_Update(&sha1Context, rchallenge, 16);
+    SHA1_Update(&sha1Context, (unsigned char *)user, strlen(user));
+    SHA1_Final(sha1Hash, &sha1Context);
 
-    word >>= 15 - (startBit % 8 + 7);
-
-    return word & 0xFE;
+    BCOPY(sha1Hash, Challenge, 8);
 }
 
-#ifdef USE_CRYPT
-
-/* in == 8-byte string (expanded version of the 56-bit key)
- * out == 64-byte string where each byte is either 1 or 0
- * Note that the low-order "bit" is always ignored by by setkey()
+/*
+ * Convert the ASCII version of the password to Unicode.
+ * This implicitly supports 8-bit ISO8859/1 characters.
+ * This gives us the little-endian representation, which
+ * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
+ * is machine-dependent.)
  */
-static void Expand(in, out)
-    u_char *in;
-    u_char *out;
+static void
+ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
 {
-        int j, c;
-        int i;
+    int i;
 
-        for(i = 0; i < 64; in++){
-		c = *in;
-                for(j = 7; j >= 0; j--)
-                        *out++ = (c >> j) & 01;
-                i += 8;
-        }
+    BZERO(unicode, ascii_len * 2);
+    for (i = 0; i < ascii_len; i++)
+	unicode[i * 2] = (u_char) ascii[i];
 }
 
-/* The inverse of Expand
- */
-static void Collapse(in, out)
-    u_char *in;
-    u_char *out;
+static void
+NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
 {
-        int j;
-        int i;
-	unsigned int c;
+#ifdef __NetBSD__
+    /* NetBSD uses the libc md4 routines which take bytes instead of bits */
+    int			mdlen = secret_len;
+#else
+    int			mdlen = secret_len * 8;
+#endif
+    MD4_CTX		md4Context;
+
+    MD4Init(&md4Context);
+    /* 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);
 
-	for (i = 0; i < 64; i += 8, out++) {
-	    c = 0;
-	    for (j = 7; j >= 0; j--, in++)
-		c |= *in << j;
-	    *out = c & 0xff;
-	}
 }
-#endif
 
-static void MakeKey(key, des_key)
-    u_char *key;	/* IN  56 bit DES key missing parity bits */
-    u_char *des_key;	/* OUT 64 bit DES key with parity bits added */
-{
-    des_key[0] = Get7Bits(key,  0);
-    des_key[1] = Get7Bits(key,  7);
-    des_key[2] = Get7Bits(key, 14);
-    des_key[3] = Get7Bits(key, 21);
-    des_key[4] = Get7Bits(key, 28);
-    des_key[5] = Get7Bits(key, 35);
-    des_key[6] = Get7Bits(key, 42);
-    des_key[7] = Get7Bits(key, 49);
+static void
+ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
+	  u_char NTResponse[24])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
 
-#ifndef USE_CRYPT
-    des_set_odd_parity((des_cblock *)des_key);
-#endif
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
 
-#if 0
-    CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
-    CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
-#endif
+    ChallengeResponse(rchallenge, PasswordHash, NTResponse);
 }
 
 static void
-ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
-    char *rchallenge;
-    int rchallenge_len;
-    char *secret;
-    int secret_len;
-    MS_ChapResponse    *response;
+ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
+	   char *secret, int secret_len, u_char NTResponse[24])
 {
-    int			i;
-#ifdef __NetBSD__
-    /* NetBSD uses the libc md4 routines which take bytes instead of bits */
-    int			mdlen = secret_len * 2;
-#else
-    int			mdlen = secret_len * 2 * 8;
-#endif
-    MD4_CTX		md4Context;
-    u_char		hash[MD4_SIGNATURE_SIZE];
-    u_char		unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	Challenge[8];
 
-    /* Initialize the Unicode version of the secret (== password). */
-    /* This implicitly supports 8-bit ISO8859/1 characters. */
-    BZERO(unicodePassword, sizeof(unicodePassword));
-    for (i = 0; i < secret_len; i++)
-	unicodePassword[i * 2] = (u_char)secret[i];
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
 
-    MD4Init(&md4Context);
-    MD4Update(&md4Context, unicodePassword, mdlen);
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
 
-    MD4Final(hash, &md4Context); 	/* Tell MD4 we're done */
-
-    ChallengeResponse(rchallenge, hash, response->NTResp);
+    ChallengeResponse(Challenge, PasswordHash, NTResponse);
 }
 
 #ifdef MSLANMAN
 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
 
 static void
-ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
-    char *rchallenge;
-    int rchallenge_len;
-    char *secret;
-    int secret_len;
-    MS_ChapResponse	*response;
+ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
+	      unsigned char *response)
 {
     int			i;
     u_char		UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
@@ -298,41 +575,370 @@
     BZERO(UcasePassword, sizeof(UcasePassword));
     for (i = 0; i < secret_len; i++)
        UcasePassword[i] = (u_char)toupper(secret[i]);
-    DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
-    DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
-    ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+    (void) DesSetkey(UcasePassword + 0);
+    DesEncrypt( StdText, PasswordHash + 0 );
+    (void) DesSetkey(UcasePassword + 7);
+    DesEncrypt( StdText, PasswordHash + 8 );
+    ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
 }
 #endif
 
+
 void
-ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
-    chap_state *cstate;
-    char *rchallenge;
-    int rchallenge_len;
-    char *secret;
-    int secret_len;
+GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+			      u_char NTResponse[24], u_char PeerChallenge[16],
+			      u_char *rchallenge, char *username,
+			      u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
 {
-    MS_ChapResponse	response;
+    /*
+     * "Magic" constants used in response generation, from RFC 2759.
+     */
+    u_char Magic1[39] = /* "Magic server to client signing constant" */
+	{ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+	  0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+	  0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+	  0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
+    u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
+	{ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+	  0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+	  0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+	  0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+	  0x6E };
+
+    int		i;
+    SHA1_CTX	sha1Context;
+    u_char	Digest[SHA1_SIGNATURE_SIZE];
+    u_char	Challenge[8];
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(Digest, &sha1Context);
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, Digest, sizeof(Digest));
+    SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
+    SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Convert to ASCII hex string. */
+    for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
+	sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
+}
 
-#if 0
-    CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
-#endif
-    BZERO(&response, sizeof(response));
 
-    /* Calculate both always */
-    ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+static void
+GenerateAuthenticatorResponsePlain
+		(char *secret, int secret_len,
+		 u_char NTResponse[24], u_char PeerChallenge[16],
+		 u_char *rchallenge, char *username,
+		 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash),
+		   PasswordHashHash);
+
+    GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
+				  rchallenge, username, authResponse);
+}
+
+
+#ifdef MPPE
+/*
+ * Set mppe_xxxx_key from the NTPasswordHashHash.
+ * RFC 2548 (RADIUS support) requires us to export this function (ugh).
+ */
+void
+mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE])
+{
+    SHA1_CTX	sha1Context;
+    u_char	Digest[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, rchallenge, 8);
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Same key in both directions. */
+    BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+    BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+
+    mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
+ */
+static void
+Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+    mppe_set_keys(rchallenge, PasswordHashHash);
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ *
+ * This helper function used in the Winbind module, which gets the
+ * NTHashHash from the server.
+ */
+void
+mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+	       u_char NTResponse[24], int IsServer)
+{
+    SHA1_CTX	sha1Context;
+    u_char	MasterKey[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+    u_char	Digest[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+
+    u_char SHApad1[40] =
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+    u_char SHApad2[40] =
+	{ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
+
+    /* "This is the MPPE Master Key" */
+    u_char Magic1[27] =
+	{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+	  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+	  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+    /* "On the client side, this is the send key; "
+       "on the server side, it is the receive key." */
+    u_char Magic2[84] =
+	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+	  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+	  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+	  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	  0x6b, 0x65, 0x79, 0x2e };
+    /* "On the client side, this is the receive key; "
+       "on the server side, it is the send key." */
+    u_char Magic3[84] =
+	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+	  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+	  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+	  0x6b, 0x65, 0x79, 0x2e };
+    u_char *s;
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(MasterKey, &sha1Context);
+
+    /*
+     * generate send key
+     */
+    if (IsServer)
+	s = Magic3;
+    else
+	s = Magic2;
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, MasterKey, 16);
+    SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+    SHA1_Update(&sha1Context, s, 84);
+    SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+    SHA1_Final(Digest, &sha1Context);
+
+    BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+
+    /*
+     * generate recv key
+     */
+    if (IsServer)
+	s = Magic2;
+    else
+	s = Magic3;
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, MasterKey, 16);
+    SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+    SHA1_Update(&sha1Context, s, 84);
+    SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+    SHA1_Final(Digest, &sha1Context);
+
+    BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+
+    mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ */
+static void
+SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+    mppe_set_keys2(PasswordHashHash, NTResponse, IsServer);
+}
+
+#endif /* MPPE */
+
+
+void
+ChapMS(u_char *rchallenge, char *secret, int secret_len,
+       unsigned char *response)
+{
+    BZERO(response, MS_CHAP_RESPONSE_LEN);
+
+    ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
 
 #ifdef MSLANMAN
-    ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+    ChapMS_LANMan(rchallenge, secret, secret_len, &response);
 
-    /* prefered method is set by option  */
-    response.UseNT = !ms_lanman;
+    /* preferred method is set by option  */
+    response[MS_CHAP_USENT] = !ms_lanman;
 #else
-    response.UseNT = 1;
+    response[MS_CHAP_USENT] = 1;
 #endif
 
-    BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
-    cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+#ifdef MPPE
+    Set_Start_Key(rchallenge, secret, secret_len);
+#endif
+}
+
+
+/*
+ * 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.
+ *
+ * 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, unsigned char *response,
+	u_char authResponse[], int authenticator)
+{
+    /* ARGSUSED */
+    u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
+    int i;
+
+    BZERO(response, sizeof(*response));
+
+    /* Generate the Peer-Challenge if requested, or copy it if supplied. */
+    if (!PeerChallenge)
+	for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
+	    *p++ = (u_char) (drand48() * 0xff);
+    else
+	BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
+	      MS_CHAP2_PEER_CHAL_LEN);
+
+    /* Generate the NT-Response */
+    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[MS_CHAP2_NTRESP],
+				       &response[MS_CHAP2_PEER_CHALLENGE],
+				       rchallenge, user, authResponse);
+
+#ifdef MPPE
+    SetMasterKeys(secret, secret_len,
+		  &response[MS_CHAP2_NTRESP], authenticator);
+#endif
+}
+
+#ifdef MPPE
+/*
+ * Set MPPE options from plugins.
+ */
+void
+set_mppe_enc_types(int policy, int types)
+{
+    /* Early exit for unknown policies. */
+    if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
+	policy != MPPE_ENC_POL_ENC_REQUIRED)
+	return;
+
+    /* Don't modify MPPE if it's optional and wasn't already configured. */
+    if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
+	return;
+
+    /*
+     * Disable undesirable encryption types.  Note that we don't ENABLE
+     * any encryption types, to avoid overriding manual configuration.
+     *
+     * It seems that 56 bit keys are unsupported in MS-RADIUS (see RFC 2548)
+     */
+    switch(types) {
+	case MPPE_ENC_TYPES_RC4_40:
+	    ccp_wantoptions[0].mppe_128 = 0;	/* disable 128-bit */
+	    ccp_wantoptions[0].mppe_56 = 0;	/* disable 56-bit */
+	    break;
+	case MPPE_ENC_TYPES_RC4_128:
+	    ccp_wantoptions[0].mppe_56 = 0;	/* disable 56-bit */
+	    ccp_wantoptions[0].mppe_40 = 0;	/* disable 40-bit */
+	    break;
+	default:
+	    break;
+    }
+}
+#endif /* MPPE */
+
+static struct chap_digest_type chapms_digest = {
+	CHAP_MICROSOFT,		/* code */
+	chapms_generate_challenge,
+	chapms_verify_response,
+	chapms_make_response,
+	NULL,			/* check_success */
+	chapms_handle_failure,
+};
+
+static struct chap_digest_type chapms2_digest = {
+	CHAP_MICROSOFT_V2,	/* code */
+	chapms2_generate_challenge,
+	chapms2_verify_response,
+	chapms2_make_response,
+	chapms2_check_success,
+	chapms_handle_failure,
+};
+
+void
+chapms_init(void)
+{
+	chap_register_digest(&chapms_digest);
+	chap_register_digest(&chapms2_digest);
+	add_options(chapms_option_list);
 }
 
 #endif /* CHAPMS */
diff -Naur tomato/release/src/router/pppd/pppd/chap_ms.c.orig tomato121/release/src/router/pppd/pppd/chap_ms.c.orig
--- tomato/release/src/router/pppd/pppd/chap_ms.c.orig	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap_ms.c.orig	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,934 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  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.
+ *
+ * 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.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ *   Implemented LANManager type password response to MS-CHAP challenges.
+ *   Now pppd provides both NT style and LANMan style blocks, and the
+ *   prefered is set by option "ms-lanman". Default is to use NT.
+ *   The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+/*
+ * Modifications by Frank Cusack, frank@google.com, March 2002.
+ *
+ *   Implemented MS-CHAPv2 functionality, heavily based on sample
+ *   implementation in RFC 2759.  Implemented MPPE functionality,
+ *   heavily based on sample implementation in RFC 3079.
+ *
+ * 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
+ * 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.
+ *
+ * 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 RCSID	"$Id: chap_ms.c,v 1.34 2004/11/15 22:13:26 paulus Exp $"
+
+#ifdef CHAPMS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "pppd.h"
+#include "chap-new.h"
+#include "chap_ms.h"
+#include "md4.h"
+#include "sha1.h"
+#include "pppcrypt.h"
+#include "magic.h"
+
+static const char rcsid[] = RCSID;
+
+
+static void	ascii2unicode __P((char[], int, u_char[]));
+static void	NTPasswordHash __P((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((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, u_char *));
+#endif
+
+#ifdef MPPE
+static void	Set_Start_Key __P((u_char *, char *, int));
+static void	SetMasterKeys __P((char *, int, u_char[24], int));
+#endif
+
+#ifdef MSLANMAN
+bool	ms_lanman = 0;    	/* Use LanMan password instead of NT */
+			  	/* Has meaning only with MS-CHAP challenges */
+#endif
+
+#ifdef MPPE
+u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+int mppe_keys_set = 0;		/* Have the MPPE keys been set? */
+
+#ifdef DEBUGMPPEKEY
+/* For MPPE debug */
+/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
+static char *mschap_challenge = NULL;
+/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
+static char *mschap2_peer_challenge = NULL;
+#endif
+
+#include "fsm.h"		/* Need to poke MPPE options */
+#include "ccp.h"
+#include <net/ppp-comp.h>
+#endif
+
+/*
+ * Command-line options.
+ */
+static option_t chapms_option_list[] = {
+#ifdef MSLANMAN
+	{ "ms-lanman", o_bool, &ms_lanman,
+	  "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+#ifdef DEBUGMPPEKEY
+	{ "mschap-challenge", o_string, &mschap_challenge,
+	  "specify CHAP challenge" },
+	{ "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
+	  "specify CHAP peer challenge" },
+#endif
+	{ NULL }
+};
+
+/*
+ * chapms_generate_challenge - generate a challenge for MS-CHAP.
+ * For MS-CHAP the challenge length is fixed at 8 bytes.
+ * The length goes in challenge[0] and the actual challenge starts
+ * at challenge[1].
+ */
+static void
+chapms_generate_challenge(unsigned char *challenge)
+{
+	*challenge++ = 8;
+#ifdef DEBUGMPPEKEY
+	if (mschap_challenge && strlen(mschap_challenge) == 8)
+		memcpy(challenge, mschap_challenge, 8);
+	else
+#endif
+		random_bytes(challenge, 8);
+}
+
+static void
+chapms2_generate_challenge(unsigned char *challenge)
+{
+	*challenge++ = 16;
+#ifdef DEBUGMPPEKEY
+	if (mschap_challenge && strlen(mschap_challenge) == 16)
+		memcpy(challenge, mschap_challenge, 16);
+	else
+#endif
+		random_bytes(challenge, 16);
+}
+
+static int
+chapms_verify_response(int id, char *name,
+		       unsigned char *secret, int secret_len,
+		       unsigned char *challenge, unsigned char *response,
+		       char *message, int message_space)
+{
+	unsigned char md[MS_CHAP_RESPONSE_LEN];
+	int diff;
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;	/* skip length, is 8 */
+	response_len = *response++;
+	if (response_len != MS_CHAP_RESPONSE_LEN)
+		goto bad;
+
+#ifndef MSLANMAN
+	if (!response[MS_CHAP_USENT]) {
+		/* Should really propagate this into the error packet. */
+		notice("Peer request for LANMAN auth not supported");
+		goto bad;
+	}
+#endif
+
+	/* Generate the expected response. */
+	ChapMS(challenge, (char *)secret, secret_len, md);
+
+#ifdef MSLANMAN
+	/* Determine which part of response to verify against */
+	if (!response[MS_CHAP_USENT])
+		diff = memcmp(&response[MS_CHAP_LANMANRESP],
+			      &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
+	else
+#endif
+		diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
+			      MS_CHAP_NTRESP_LEN);
+
+	if (diff == 0) {
+		slprintf(message, message_space, "Access granted");
+		return 1;
+	}
+
+ bad:
+	/* See comments below for MS-CHAP V2 */
+	slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
+		 challenge_len, challenge);
+	return 0;
+}
+
+static int
+chapms2_verify_response(int id, char *name,
+			unsigned char *secret, int secret_len,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space)
+{
+	unsigned char md[MS_CHAP2_RESPONSE_LEN];
+	char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
+	int challenge_len, response_len;
+
+	challenge_len = *challenge++;	/* skip length, is 16 */
+	response_len = *response++;
+	if (response_len != MS_CHAP2_RESPONSE_LEN)
+		goto bad;	/* not even the right length */
+
+	/* Generate the expected response and our mutual auth. */
+	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 */
+	/*
+	 * Per RFC 2759, success message must be formatted as
+	 *     "S=<auth_string> M=<message>"
+	 * where
+	 *     <auth_string> is the Authenticator Response (mutual auth)
+	 *     <message> is a text message
+	 *
+	 * However, some versions of Windows (win98 tested) do not know
+	 * about the M=<message> part (required per RFC 2759) and flag
+	 * it as an error (reported incorrectly as an encryption error
+	 * to the user).  Since the RFC requires it, and it can be
+	 * useful information, we supply it if the peer is a conforming
+	 * system.  Luckily (?), win98 sets the Flags field to 0x04
+	 * (contrary to RFC requirements) so we can use that to
+	 * distinguish between conforming and non-conforming systems.
+	 *
+	 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
+	 * help debugging this.
+	 */
+	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",
+				 saresponse, "Access granted");
+		return 1;
+	}
+
+ bad:
+	/*
+	 * Failure message must be formatted as
+	 *     "E=e R=r C=c V=v M=m"
+	 * where
+	 *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
+	 *     r = retry (we use 1, ok to retry)
+	 *     c = challenge to use for next response, we reuse previous
+	 *     v = Change Password version supported, we use 0
+	 *     m = text message
+	 *
+	 * The M=m part is only for MS-CHAPv2.  Neither win2k nor
+	 * win98 (others untested) display the message to the user anyway.
+	 * They also both ignore the E=e code.
+	 *
+	 * Note that it's safe to reuse the same challenge as we don't
+	 * actually accept another response based on the error message
+	 * (and no clients try to resend a response anyway).
+	 *
+	 * Basically, this whole bit is useless code, even the small
+	 * implementation here is only because of overspecification.
+	 */
+	slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+		 challenge_len, challenge, "Access denied");
+	return 0;
+}
+
+static void
+chapms_make_response(unsigned char *response, int id, char *our_name,
+		     unsigned char *challenge, char *secret, int secret_len,
+		     unsigned char *private)
+{
+	challenge++;	/* skip length, should be 8 */
+	*response++ = MS_CHAP_RESPONSE_LEN;
+	ChapMS(challenge, secret, secret_len, response);
+}
+
+static void
+chapms2_make_response(unsigned char *response, int id, char *our_name,
+		      unsigned char *challenge, char *secret, int secret_len,
+		      unsigned char *private)
+{
+	challenge++;	/* skip length, should be 16 */
+	*response++ = MS_CHAP2_RESPONSE_LEN;
+	ChapMS2(challenge,
+#ifdef DEBUGMPPEKEY
+		mschap2_peer_challenge,
+#else
+		NULL,
+#endif
+		our_name, secret, secret_len, response, private,
+		MS_CHAP2_AUTHENTICATEE);
+}
+
+static int
+chapms2_check_success(unsigned char *msg, int len, unsigned char *private)
+{
+	if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
+	    strncmp((char *)msg, "S=", 2) != 0) {
+		/* Packet does not start with "S=" */
+		error("MS-CHAPv2 Success packet is badly formed.");
+		return 0;
+	}
+	msg += 2;
+	len -= 2;
+	if (len < MS_AUTH_RESPONSE_LENGTH
+	    || memcmp(msg, private, MS_AUTH_RESPONSE_LENGTH)) {
+		/* Authenticator Response did not match expected. */
+		error("MS-CHAPv2 mutual authentication failed.");
+		return 0;
+	}
+	/* Authenticator Response matches. */
+	msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
+	len -= MS_AUTH_RESPONSE_LENGTH;
+	if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
+		msg += 3; /* Eat the delimiter */
+	} else if (len) {
+		/* Packet has extra text which does not begin " M=" */
+		error("MS-CHAPv2 Success packet is badly formed.");
+		return 0;
+	}
+	return 1;
+}
+
+static void
+chapms_handle_failure(unsigned char *inp, int len)
+{
+	int err;
+	char *p, *msg;
+
+	/* We want a null-terminated string for strxxx(). */
+	msg = malloc(len + 1);
+	if (!msg) {
+		notice("Out of memory in chapms_handle_failure");
+		return;
+	}
+	BCOPY(inp, msg, len);
+	msg[len] = 0;
+	p = msg;
+
+	/*
+	 * Deal with MS-CHAP formatted failure messages; just print the
+	 * M=<message> part (if any).  For MS-CHAP we're not really supposed
+	 * to use M=<message>, but it shouldn't hurt.  See
+	 * chapms[2]_verify_response.
+	 */
+	if (!strncmp(p, "E=", 2))
+		err = strtol(p, NULL, 10); /* Remember the error code. */
+	else
+		goto print_msg; /* Message is badly formatted. */
+
+	if (len && ((p = strstr(p, " M=")) != NULL)) {
+		/* M=<message> field found. */
+		p += 3;
+	} else {
+		/* No M=<message>; use the error code. */
+		switch (err) {
+		case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
+			p = "E=646 Restricted logon hours";
+			break;
+
+		case MS_CHAP_ERROR_ACCT_DISABLED:
+			p = "E=647 Account disabled";
+			break;
+
+		case MS_CHAP_ERROR_PASSWD_EXPIRED:
+			p = "E=648 Password expired";
+			break;
+
+		case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
+			p = "E=649 No dialin permission";
+			break;
+
+		case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
+			p = "E=691 Authentication failure";
+			break;
+
+		case MS_CHAP_ERROR_CHANGING_PASSWORD:
+			/* Should never see this, we don't support Change Password. */
+			p = "E=709 Error changing password";
+			break;
+
+		default:
+			free(msg);
+			error("Unknown MS-CHAP authentication failure: %.*v",
+			      len, inp);
+			return;
+		}
+	}
+print_msg:
+	if (p != NULL)
+		error("MS-CHAP authentication failed: %v", p);
+	free(msg);
+}
+
+static void
+ChallengeResponse(u_char *challenge,
+		  u_char PasswordHash[MD4_SIGNATURE_SIZE],
+		  u_char response[24])
+{
+    u_char    ZPasswordHash[21];
+
+    BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+    BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+    dbglog("ChallengeResponse - ZPasswordHash %.*B",
+	   sizeof(ZPasswordHash), ZPasswordHash);
+#endif
+
+    (void) DesSetkey(ZPasswordHash + 0);
+    DesEncrypt(challenge, response + 0);
+    (void) DesSetkey(ZPasswordHash + 7);
+    DesEncrypt(challenge, response + 8);
+    (void) DesSetkey(ZPasswordHash + 14);
+    DesEncrypt(challenge, response + 16);
+
+#if 0
+    dbglog("ChallengeResponse - response %.24B", response);
+#endif
+}
+
+void
+ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
+	      char *username, u_char Challenge[8])
+    
+{
+    SHA1_CTX	sha1Context;
+    u_char	sha1Hash[SHA1_SIGNATURE_SIZE];
+    char	*user;
+
+    /* remove domain from "domain\username" */
+    if ((user = strrchr(username, '\\')) != NULL)
+	++user;
+    else
+	user = username;
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PeerChallenge, 16);
+    SHA1_Update(&sha1Context, rchallenge, 16);
+    SHA1_Update(&sha1Context, (unsigned char *)user, strlen(user));
+    SHA1_Final(sha1Hash, &sha1Context);
+
+    BCOPY(sha1Hash, Challenge, 8);
+}
+
+/*
+ * Convert the ASCII version of the password to Unicode.
+ * This implicitly supports 8-bit ISO8859/1 characters.
+ * This gives us the little-endian representation, which
+ * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
+ * is machine-dependent.)
+ */
+static void
+ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
+{
+    int i;
+
+    BZERO(unicode, ascii_len * 2);
+    for (i = 0; i < ascii_len; i++)
+	unicode[i * 2] = (u_char) ascii[i];
+}
+
+static void
+NTPasswordHash(char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
+{
+#ifdef __NetBSD__
+    /* NetBSD uses the libc md4 routines which take bytes instead of bits */
+    int			mdlen = secret_len;
+#else
+    int			mdlen = secret_len * 8;
+#endif
+    MD4_CTX		md4Context;
+
+    MD4Init(&md4Context);
+    MD4Update(&md4Context, (unsigned char *)secret, mdlen);
+    MD4Final(hash, &md4Context);
+
+}
+
+static void
+ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
+	  u_char NTResponse[24])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash((char *)unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(rchallenge, PasswordHash, NTResponse);
+}
+
+static void
+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, rchallenge, username, Challenge);
+
+    /* Hash the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash((char *)unicodePassword, secret_len * 2, PasswordHash);
+
+    ChallengeResponse(Challenge, PasswordHash, NTResponse);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
+	      unsigned char *response)
+{
+    int			i;
+    u_char		UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+    u_char		PasswordHash[MD4_SIGNATURE_SIZE];
+
+    /* LANMan password is case insensitive */
+    BZERO(UcasePassword, sizeof(UcasePassword));
+    for (i = 0; i < secret_len; i++)
+       UcasePassword[i] = (u_char)toupper(secret[i]);
+    (void) DesSetkey(UcasePassword + 0);
+    DesEncrypt( StdText, PasswordHash + 0 );
+    (void) DesSetkey(UcasePassword + 7);
+    DesEncrypt( StdText, PasswordHash + 8 );
+    ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
+}
+#endif
+
+
+void
+GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+			      u_char NTResponse[24], u_char PeerChallenge[16],
+			      u_char *rchallenge, char *username,
+			      u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+{
+    /*
+     * "Magic" constants used in response generation, from RFC 2759.
+     */
+    u_char Magic1[39] = /* "Magic server to client signing constant" */
+	{ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+	  0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+	  0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+	  0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
+    u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
+	{ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+	  0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+	  0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+	  0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+	  0x6E };
+
+    int		i;
+    SHA1_CTX	sha1Context;
+    u_char	Digest[SHA1_SIGNATURE_SIZE];
+    u_char	Challenge[8];
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(Digest, &sha1Context);
+
+    ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, Digest, sizeof(Digest));
+    SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
+    SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Convert to ASCII hex string. */
+    for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
+	sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
+}
+
+
+static void
+GenerateAuthenticatorResponsePlain
+		(char *secret, int secret_len,
+		 u_char NTResponse[24], u_char PeerChallenge[16],
+		 u_char *rchallenge, char *username,
+		 u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* 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),
+		   PasswordHashHash);
+
+    GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
+				  rchallenge, username, authResponse);
+}
+
+
+#ifdef MPPE
+/*
+ * Set mppe_xxxx_key from the NTPasswordHashHash.
+ * RFC 2548 (RADIUS support) requires us to export this function (ugh).
+ */
+void
+mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE])
+{
+    SHA1_CTX	sha1Context;
+    u_char	Digest[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, rchallenge, 8);
+    SHA1_Final(Digest, &sha1Context);
+
+    /* Same key in both directions. */
+    BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+    BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+
+    mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
+ */
+static void
+Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+    mppe_set_keys(rchallenge, PasswordHashHash);
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ *
+ * This helper function used in the Winbind module, which gets the
+ * NTHashHash from the server.
+ */
+void
+mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+	       u_char NTResponse[24], int IsServer)
+{
+    SHA1_CTX	sha1Context;
+    u_char	MasterKey[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+    u_char	Digest[SHA1_SIGNATURE_SIZE];	/* >= MPPE_MAX_KEY_LEN */
+
+    u_char SHApad1[40] =
+	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+    u_char SHApad2[40] =
+	{ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
+
+    /* "This is the MPPE Master Key" */
+    u_char Magic1[27] =
+	{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+	  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+	  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+    /* "On the client side, this is the send key; "
+       "on the server side, it is the receive key." */
+    u_char Magic2[84] =
+	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+	  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+	  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+	  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	  0x6b, 0x65, 0x79, 0x2e };
+    /* "On the client side, this is the receive key; "
+       "on the server side, it is the send key." */
+    u_char Magic3[84] =
+	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+	  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+	  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+	  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+	  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+	  0x6b, 0x65, 0x79, 0x2e };
+    u_char *s;
+
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+    SHA1_Update(&sha1Context, NTResponse, 24);
+    SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
+    SHA1_Final(MasterKey, &sha1Context);
+
+    /*
+     * generate send key
+     */
+    if (IsServer)
+	s = Magic3;
+    else
+	s = Magic2;
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, MasterKey, 16);
+    SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+    SHA1_Update(&sha1Context, s, 84);
+    SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+    SHA1_Final(Digest, &sha1Context);
+
+    BCOPY(Digest, mppe_send_key, sizeof(mppe_send_key));
+
+    /*
+     * generate recv key
+     */
+    if (IsServer)
+	s = Magic2;
+    else
+	s = Magic3;
+    SHA1_Init(&sha1Context);
+    SHA1_Update(&sha1Context, MasterKey, 16);
+    SHA1_Update(&sha1Context, SHApad1, sizeof(SHApad1));
+    SHA1_Update(&sha1Context, s, 84);
+    SHA1_Update(&sha1Context, SHApad2, sizeof(SHApad2));
+    SHA1_Final(Digest, &sha1Context);
+
+    BCOPY(Digest, mppe_recv_key, sizeof(mppe_recv_key));
+
+    mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ */
+static void
+SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
+{
+    u_char	unicodePassword[MAX_NT_PASSWORD * 2];
+    u_char	PasswordHash[MD4_SIGNATURE_SIZE];
+    u_char	PasswordHashHash[MD4_SIGNATURE_SIZE];
+    /* Hash (x2) the Unicode version of the secret (== password). */
+    ascii2unicode(secret, secret_len, unicodePassword);
+    NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+    NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+    mppe_set_keys2(PasswordHashHash, NTResponse, IsServer);
+}
+
+#endif /* MPPE */
+
+
+void
+ChapMS(u_char *rchallenge, char *secret, int secret_len,
+       unsigned char *response)
+{
+    BZERO(response, MS_CHAP_RESPONSE_LEN);
+
+    ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
+
+#ifdef MSLANMAN
+    ChapMS_LANMan(rchallenge, secret, secret_len, &response);
+
+    /* preferred method is set by option  */
+    response[MS_CHAP_USENT] = !ms_lanman;
+#else
+    response[MS_CHAP_USENT] = 1;
+#endif
+
+#ifdef MPPE
+    Set_Start_Key(rchallenge, secret, secret_len);
+#endif
+}
+
+
+/*
+ * 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.
+ *
+ * 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, unsigned char *response,
+	u_char authResponse[], int authenticator)
+{
+    /* ARGSUSED */
+    u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
+    int i;
+
+    BZERO(response, sizeof(*response));
+
+    /* Generate the Peer-Challenge if requested, or copy it if supplied. */
+    if (!PeerChallenge)
+	for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
+	    *p++ = (u_char) (drand48() * 0xff);
+    else
+	BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
+	      MS_CHAP2_PEER_CHAL_LEN);
+
+    /* Generate the NT-Response */
+    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[MS_CHAP2_NTRESP],
+				       &response[MS_CHAP2_PEER_CHALLENGE],
+				       rchallenge, user, authResponse);
+
+#ifdef MPPE
+    SetMasterKeys(secret, secret_len,
+		  &response[MS_CHAP2_NTRESP], authenticator);
+#endif
+}
+
+#ifdef MPPE
+/*
+ * Set MPPE options from plugins.
+ */
+void
+set_mppe_enc_types(int policy, int types)
+{
+    /* Early exit for unknown policies. */
+    if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
+	policy != MPPE_ENC_POL_ENC_REQUIRED)
+	return;
+
+    /* Don't modify MPPE if it's optional and wasn't already configured. */
+    if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
+	return;
+
+    /*
+     * Disable undesirable encryption types.  Note that we don't ENABLE
+     * any encryption types, to avoid overriding manual configuration.
+     */
+    switch(types) {
+	case MPPE_ENC_TYPES_RC4_40:
+	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_128;	/* disable 128-bit */
+	    break;
+	case MPPE_ENC_TYPES_RC4_128:
+	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_40;	/* disable 40-bit */
+	    break;
+	default:
+	    break;
+    }
+}
+#endif /* MPPE */
+
+static struct chap_digest_type chapms_digest = {
+	CHAP_MICROSOFT,		/* code */
+	chapms_generate_challenge,
+	chapms_verify_response,
+	chapms_make_response,
+	NULL,			/* check_success */
+	chapms_handle_failure,
+};
+
+static struct chap_digest_type chapms2_digest = {
+	CHAP_MICROSOFT_V2,	/* code */
+	chapms2_generate_challenge,
+	chapms2_verify_response,
+	chapms2_make_response,
+	chapms2_check_success,
+	chapms_handle_failure,
+};
+
+void
+chapms_init(void)
+{
+	chap_register_digest(&chapms_digest);
+	chap_register_digest(&chapms2_digest);
+	add_options(chapms_option_list);
+}
+
+#endif /* CHAPMS */
diff -Naur tomato/release/src/router/pppd/pppd/chap_ms.h tomato121/release/src/router/pppd/pppd/chap_ms.h
--- tomato/release/src/router/pppd/pppd/chap_ms.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap_ms.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,33 +1,109 @@
 /*
- * chap.h - Challenge Handshake Authentication Protocol definitions.
+ * chap_ms.h - Challenge Handshake Authentication Protocol definitions.
  *
- * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
- * http://www.strataware.com/
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
  *
- * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Eric Rosenquist.  The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * $Id: chap_ms.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * 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.
+ *
+ * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $
  */
 
 #ifndef __CHAPMS_INCLUDE__
 
 #define MD4_SIGNATURE_SIZE	16	/* 16 bytes in a MD4 message digest */
-#define MAX_NT_PASSWORD	256	/* Maximum number of (Unicode) chars in an NT password */
+#define MAX_NT_PASSWORD		256	/* Max (Unicode) chars in an NT pass */
+
+#define MS_CHAP_RESPONSE_LEN	49	/* Response length for MS-CHAP */
+#define MS_CHAP2_RESPONSE_LEN	49	/* Response length for MS-CHAPv2 */
+#define MS_AUTH_RESPONSE_LENGTH	40	/* MS-CHAPv2 authenticator response, */
+					/* as ASCII */
+
+/* E=eeeeeeeeee error codes for MS-CHAP failure messages. */
+#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS	646
+#define MS_CHAP_ERROR_ACCT_DISABLED		647
+#define MS_CHAP_ERROR_PASSWD_EXPIRED		648
+#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION	649
+#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE	691
+#define MS_CHAP_ERROR_CHANGING_PASSWORD		709
+
+/*
+ * Offsets within the response field for MS-CHAP
+ */
+#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
+
+/*
+ * Offsets within the response field for MS-CHAP2
+ */
+#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 */
+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;
+
+/* These values are the RADIUS attribute values--see RFC 2548. */
+#define MPPE_ENC_POL_ENC_ALLOWED 1
+#define MPPE_ENC_POL_ENC_REQUIRED 2
+#define MPPE_ENC_TYPES_RC4_40 2
+#define MPPE_ENC_TYPES_RC4_128 4
+
+/* used by plugins (using above values) */
+extern void set_mppe_enc_types(int, int);
+#endif
+
+/* Are we the authenticator or authenticatee?  For MS-CHAPv2 key derivation. */
+#define MS_CHAP2_AUTHENTICATEE 0
+#define MS_CHAP2_AUTHENTICATOR 1
+
+void ChapMS __P((u_char *, char *, int, u_char *));
+void ChapMS2 __P((u_char *, u_char *, char *, char *, 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],
+		    u_char NTResponse[24], int IsServer);
+#endif
+
+void	ChallengeHash __P((u_char[16], u_char *, char *, u_char[8]));
+
+void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+			u_char NTResponse[24], u_char PeerChallenge[16],
+			u_char *rchallenge, char *username,
+			u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]);
 
-void ChapMS __P((chap_state *, char *, int, char *, int));
+void chapms_init(void);
 
 #define __CHAPMS_INCLUDE__
 #endif /* __CHAPMS_INCLUDE__ */
diff -Naur tomato/release/src/router/pppd/pppd/chap-new.c tomato121/release/src/router/pppd/pppd/chap-new.c
--- tomato/release/src/router/pppd/pppd/chap-new.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap-new.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,650 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 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. 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.
+ */
+
+#define RCSID	"$Id: chap-new.c,v 1.8 2005/07/13 10:41:58 paulus Exp $"
+
+#include <stdlib.h>
+#include <string.h>
+#include "pppd.h"
+#include "chap-new.h"
+#include "chap-md5.h"
+
+#ifdef CHAPMS
+#include "chap_ms.h"
+#define MDTYPE_ALL (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5)
+#else
+#define MDTYPE_ALL (MDTYPE_MD5)
+#endif
+
+int chap_mdtype_all = MDTYPE_ALL;
+
+/* Hook for a plugin to validate CHAP challenge */
+int (*chap_verify_hook)(char *name, char *ourname, int id,
+			struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space) = NULL;
+
+/*
+ * Option variables.
+ */
+int chap_timeout_time = 3;
+int chap_max_transmits = 10;
+int chap_rechallenge_time = 0;
+
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+	{ "chap-restart", o_int, &chap_timeout_time,
+	  "Set timeout for CHAP", OPT_PRIO },
+	{ "chap-max-challenge", o_int, &chap_max_transmits,
+	  "Set max #xmits for challenge", OPT_PRIO },
+	{ "chap-interval", o_int, &chap_rechallenge_time,
+	  "Set interval for rechallenge", OPT_PRIO },
+	{ NULL }
+};
+
+/*
+ * Internal state.
+ */
+static struct chap_client_state {
+	int flags;
+	char *name;
+	struct chap_digest_type *digest;
+	unsigned char priv[64];		/* private area for digest's use */
+} client;
+
+/*
+ * These limits apply to challenge and response packets we send.
+ * The +4 is the +1 that we actually need rounded up.
+ */
+#define CHAL_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN)
+#define RESP_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN)
+
+static struct chap_server_state {
+	int flags;
+	int id;
+	char *name;
+	struct chap_digest_type *digest;
+	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 */
+#define LOWERUP			1
+#define AUTH_STARTED		2
+#define AUTH_DONE		4
+#define AUTH_FAILED		8
+#define TIMEOUT_PENDING		0x10
+#define CHALLENGE_VALID		0x20
+
+/*
+ * Prototypes.
+ */
+static void chap_init(int unit);
+static void chap_lowerup(int unit);
+static void chap_lowerdown(int unit);
+static void chap_timeout(void *arg);
+static void chap_generate_challenge(struct chap_server_state *ss);
+static void chap_handle_response(struct chap_server_state *ss, int code,
+		unsigned char *pkt, int len);
+static int chap_verify_response(char *name, char *ourname, int id,
+		struct chap_digest_type *digest,
+		unsigned char *challenge, unsigned char *response,
+		char *message, int message_space);
+static void chap_respond(struct chap_client_state *cs, int id,
+		unsigned char *pkt, int len);
+static void chap_handle_status(struct chap_client_state *cs, int code, int id,
+		unsigned char *pkt, int len);
+static void chap_protrej(int unit);
+static void chap_input(int unit, unsigned char *pkt, int pktlen);
+static int chap_print_pkt(unsigned char *p, int plen,
+		void (*printer) __P((void *, char *, ...)), void *arg);
+
+/* List of digest types that we know about */
+static struct chap_digest_type *chap_digests;
+
+/*
+ * chap_init - reset to initial state.
+ */
+static void
+chap_init(int unit)
+{
+	memset(&client, 0, sizeof(client));
+	memset(&server, 0, sizeof(server));
+
+	chap_md5_init();
+#ifdef CHAPMS
+	chapms_init();
+#endif
+}
+
+/*
+ * Add a new digest type to the list.
+ */
+void
+chap_register_digest(struct chap_digest_type *dp)
+{
+	dp->next = chap_digests;
+	chap_digests = dp;
+}
+
+/*
+ * chap_lowerup - we can start doing stuff now.
+ */
+static void
+chap_lowerup(int unit)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+
+	cs->flags |= LOWERUP;
+	ss->flags |= LOWERUP;
+	if (ss->flags & AUTH_STARTED)
+		chap_timeout(ss);
+}
+
+static void
+chap_lowerdown(int unit)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+
+	cs->flags = 0;
+	if (ss->flags & TIMEOUT_PENDING)
+		UNTIMEOUT(chap_timeout, ss);
+	ss->flags = 0;
+}
+
+/*
+ * chap_auth_peer - Start authenticating the peer.
+ * If the lower layer is already up, we start sending challenges,
+ * otherwise we wait for the lower layer to come up.
+ */
+void
+chap_auth_peer(int unit, char *our_name, int digest_code)
+{
+	struct chap_server_state *ss = &server;
+	struct chap_digest_type *dp;
+
+	if (ss->flags & AUTH_STARTED) {
+		error("CHAP: peer authentication already started!");
+		return;
+	}
+	for (dp = chap_digests; dp != NULL; dp = dp->next)
+		if (dp->code == digest_code)
+			break;
+	if (dp == NULL)
+		fatal("CHAP digest 0x%x requested but not available",
+		      digest_code);
+
+	ss->digest = dp;
+	ss->name = our_name;
+	/* Start with a random ID value */
+	ss->id = (unsigned char)(drand48() * 256);
+	ss->flags |= AUTH_STARTED;
+	if (ss->flags & LOWERUP)
+		chap_timeout(ss);
+}
+
+/*
+ * chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
+ * There isn't much to do until we receive a challenge.
+ */
+void
+chap_auth_with_peer(int unit, char *our_name, int digest_code)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_digest_type *dp;
+
+	if (cs->flags & AUTH_STARTED) {
+		error("CHAP: authentication with peer already started!");
+		return;
+	}
+	for (dp = chap_digests; dp != NULL; dp = dp->next)
+		if (dp->code == digest_code)
+			break;
+	if (dp == NULL)
+		fatal("CHAP digest 0x%x requested but not available",
+		      digest_code);
+
+	cs->digest = dp;
+	cs->name = our_name;
+	cs->flags |= AUTH_STARTED;
+}
+
+/*
+ * chap_timeout - It's time to send another challenge to the peer.
+ * This could be either a retransmission of a previous challenge,
+ * or a new challenge to start re-authentication.
+ */
+static void
+chap_timeout(void *arg)
+{
+	struct chap_server_state *ss = arg;
+
+	ss->flags &= ~TIMEOUT_PENDING;
+	if ((ss->flags & CHALLENGE_VALID) == 0) {
+		ss->challenge_xmits = 0;
+		chap_generate_challenge(ss);
+		ss->flags |= CHALLENGE_VALID;
+	} else if (ss->challenge_xmits >= chap_max_transmits) {
+		ss->flags &= ~CHALLENGE_VALID;
+		ss->flags |= AUTH_DONE | AUTH_FAILED;
+		auth_peer_fail(0, PPP_CHAP);
+		return;
+	}
+
+	output(0, ss->challenge, ss->challenge_pktlen);
+	++ss->challenge_xmits;
+	ss->flags |= TIMEOUT_PENDING;
+	TIMEOUT(chap_timeout, arg, chap_timeout_time);
+}
+
+/*
+ * chap_generate_challenge - generate a challenge string and format
+ * the challenge packet in ss->challenge_pkt.
+ */
+static void
+chap_generate_challenge(struct chap_server_state *ss)
+{
+	int clen = 1, nlen, len;
+	unsigned char *p;
+
+	p = ss->challenge;
+	MAKEHEADER(p, PPP_CHAP);
+	p += CHAP_HDRLEN;
+	ss->digest->generate_challenge(p);
+	clen = *p;
+	nlen = strlen(ss->name);
+	memcpy(p + 1 + clen, ss->name, nlen);
+
+	len = CHAP_HDRLEN + 1 + clen + nlen;
+	ss->challenge_pktlen = PPP_HDRLEN + len;
+
+	p = ss->challenge + PPP_HDRLEN;
+	p[0] = CHAP_CHALLENGE;
+	p[1] = ++ss->id;
+	p[2] = len >> 8;
+	p[3] = len;
+}
+
+/*
+ * chap_handle_response - check the response to our challenge.
+ */
+static void
+chap_handle_response(struct chap_server_state *ss, int id,
+		     unsigned char *pkt, int len)
+{
+	int response_len, ok, mlen;
+	unsigned char *response, *p;
+	char *name = NULL;	/* initialized to shut gcc up */
+	int (*verifier)(char *, char *, int, struct chap_digest_type *,
+		unsigned char *, unsigned char *, char *, int);
+	char rname[MAXNAMELEN+1];
+
+	if ((ss->flags & LOWERUP) == 0)
+		return;
+	if (id != ss->challenge[PPP_HDRLEN+1] || len < 2)
+		return;
+	if (ss->flags & CHALLENGE_VALID) {
+		response = pkt;
+		GETCHAR(response_len, pkt);
+		len -= response_len + 1;	/* length of name */
+		name = (char *)pkt + response_len;
+		if (len < 0)
+			return;
+
+		if (ss->flags & TIMEOUT_PENDING) {
+			ss->flags &= ~TIMEOUT_PENDING;
+			UNTIMEOUT(chap_timeout, ss);
+		}
+
+		if (explicit_remote) {
+			name = remote_name;
+		} else {
+			/* Null terminate and clean remote name. */
+			slprintf(rname, sizeof(rname), "%.*v", len, name);
+			name = rname;
+		}
+
+		if (chap_verify_hook)
+			verifier = chap_verify_hook;
+		else
+			verifier = chap_verify_response;
+		ok = (*verifier)(name, ss->name, id, ss->digest,
+				 ss->challenge + PPP_HDRLEN + CHAP_HDRLEN,
+				 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(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, ss->message, mlen);
+	output(0, outpacket_buf, PPP_HDRLEN + len);
+
+	if (ss->flags & CHALLENGE_VALID) {
+		ss->flags &= ~CHALLENGE_VALID;
+		if (ss->flags & AUTH_FAILED) {
+			auth_peer_fail(0, PPP_CHAP);
+		} else {
+			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;
+	}
+}
+
+/*
+ * chap_verify_response - check whether the peer's response matches
+ * what we think it should be.  Returns 1 if it does (authentication
+ * succeeded), or 0 if it doesn't.
+ */
+static int
+chap_verify_response(char *name, char *ourname, int id,
+		     struct chap_digest_type *digest,
+		     unsigned char *challenge, unsigned char *response,
+		     char *message, int message_space)
+{
+	int ok;
+	unsigned char secret[MAXSECRETLEN];
+	int secret_len;
+
+#ifdef CHAPMS
+    char nametmp[MAXNAMELEN];
+    	if (ms_ignore_domain && strrchr(name, '\\')) {
+		strcpy(nametmp, strrchr(name, '\\') + 1);
+		strcpy(name, nametmp);
+	}
+#endif
+	/* Get the secret that the peer is supposed to know */
+	if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) {
+		error("No CHAP secret found for authenticating %q", name);
+		return 0;
+	}
+
+	ok = digest->verify_response(id, name, secret, secret_len, challenge,
+				     response, message, message_space);
+	memset(secret, 0, sizeof(secret));
+
+	return ok;
+}
+
+/*
+ * chap_respond - Generate and send a response to a challenge.
+ */
+static void
+chap_respond(struct chap_client_state *cs, int id,
+	     unsigned char *pkt, int len)
+{
+	int clen, nlen;
+	int secret_len;
+	unsigned char *p;
+	unsigned char response[RESP_MAX_PKTLEN];
+	char rname[MAXNAMELEN+1];
+	char secret[MAXSECRETLEN+1];
+
+	if ((cs->flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
+		return;		/* not ready */
+	if (len < 2 || len < pkt[0] + 1)
+		return;		/* too short */
+	clen = pkt[0];
+	nlen = len - (clen + 1);
+
+	/* Null terminate and clean remote name. */
+	slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
+
+	/* Microsoft doesn't send their name back in the PPP packet */
+	if (explicit_remote || (remote_name[0] != 0 && rname[0] == 0))
+		strlcpy(rname, remote_name, sizeof(rname));
+
+	/* get secret for authenticating ourselves with the specified host */
+	if (!get_secret(0, cs->name, rname, secret, &secret_len, 0)) {
+		secret_len = 0;	/* assume null secret if can't find one */
+		warn("No CHAP secret found for authenticating us to %q", rname);
+	}
+
+	p = response;
+	MAKEHEADER(p, PPP_CHAP);
+	p += CHAP_HDRLEN;
+
+	cs->digest->make_response(p, id, cs->name, pkt,
+				  secret, secret_len, cs->priv);
+	memset(secret, 0, secret_len);
+
+	clen = *p;
+	nlen = strlen(cs->name);
+	memcpy(p + clen + 1, cs->name, nlen);
+
+	p = response + PPP_HDRLEN;
+	len = CHAP_HDRLEN + clen + 1 + nlen;
+	p[0] = CHAP_RESPONSE;
+	p[1] = id;
+	p[2] = len >> 8;
+	p[3] = len;
+
+	output(0, response, PPP_HDRLEN + len);
+}
+
+static void
+chap_handle_status(struct chap_client_state *cs, int code, int id,
+		   unsigned char *pkt, int len)
+{
+	const char *msg = NULL;
+
+	if ((cs->flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
+	    != (AUTH_STARTED|LOWERUP))
+		return;
+	cs->flags |= AUTH_DONE;
+
+	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))
+				code = CHAP_FAILURE;
+		} else
+			msg = "CHAP authentication succeeded";
+	} else {
+		if (cs->digest->handle_failure != NULL)
+			(*cs->digest->handle_failure)(pkt, len);
+		else
+			msg = "CHAP authentication failed";
+	}
+	if (msg) {
+		if (len > 0)
+		{
+			info("%s: %.*v", msg, len, pkt);
+		}
+		else
+		{
+			info("%s", msg);
+		}
+	}
+	if (code == CHAP_SUCCESS)
+		auth_withpeer_success(0, PPP_CHAP, cs->digest->code);
+	else {
+		cs->flags |= AUTH_FAILED;
+		error("CHAP authentication failed");
+		auth_withpeer_fail(0, PPP_CHAP);
+	}
+}
+
+static void
+chap_input(int unit, unsigned char *pkt, int pktlen)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+	unsigned char code, id;
+	int len;
+
+	if (pktlen < CHAP_HDRLEN)
+		return;
+	GETCHAR(code, pkt);
+	GETCHAR(id, pkt);
+	GETSHORT(len, pkt);
+	if (len < CHAP_HDRLEN || len > pktlen)
+		return;
+	len -= CHAP_HDRLEN;
+
+	switch (code) {
+	case CHAP_CHALLENGE:
+		chap_respond(cs, id, pkt, len);
+		break;
+	case CHAP_RESPONSE:
+		chap_handle_response(ss, id, pkt, len);
+		break;
+	case CHAP_FAILURE:
+	case CHAP_SUCCESS:
+		chap_handle_status(cs, code, id, pkt, len);
+		break;
+	}
+}
+
+static void
+chap_protrej(int unit)
+{
+	struct chap_client_state *cs = &client;
+	struct chap_server_state *ss = &server;
+
+	if (ss->flags & TIMEOUT_PENDING) {
+		ss->flags &= ~TIMEOUT_PENDING;
+		UNTIMEOUT(chap_timeout, ss);
+	}
+	if (ss->flags & AUTH_STARTED) {
+		ss->flags = 0;
+		auth_peer_fail(0, PPP_CHAP);
+	}
+	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);
+	}
+}
+
+/*
+ * chap_print_pkt - print the contents of a CHAP packet.
+ */
+static char *chap_code_names[] = {
+	"Challenge", "Response", "Success", "Failure"
+};
+
+static int
+chap_print_pkt(unsigned char *p, int plen,
+	       void (*printer) __P((void *, char *, ...)), void *arg)
+{
+	int code, id, len;
+	int clen, nlen;
+	unsigned char x;
+
+	if (plen < CHAP_HDRLEN)
+		return 0;
+	GETCHAR(code, p);
+	GETCHAR(id, p);
+	GETSHORT(len, p);
+	if (len < CHAP_HDRLEN || len > plen)
+		return 0;
+
+	if (code >= 1 && code <= sizeof(chap_code_names) / sizeof(char *))
+		printer(arg, " %s", chap_code_names[code-1]);
+	else
+		printer(arg, " code=0x%x", code);
+	printer(arg, " id=0x%x", id);
+	len -= CHAP_HDRLEN;
+	switch (code) {
+	case CHAP_CHALLENGE:
+	case CHAP_RESPONSE:
+		if (len < 1)
+			break;
+		clen = p[0];
+		if (len < clen + 1)
+			break;
+		++p;
+		nlen = len - clen - 1;
+		printer(arg, " <");
+		for (; clen > 0; --clen) {
+			GETCHAR(x, p);
+			printer(arg, "%.2x", x);
+		}
+		printer(arg, ">, name = ");
+		print_string((char *)p, nlen, printer, arg);
+		break;
+	case CHAP_FAILURE:
+	case CHAP_SUCCESS:
+		printer(arg, " ");
+		print_string((char *)p, len, printer, arg);
+		break;
+	default:
+		for (clen = len; clen > 0; --clen) {
+			GETCHAR(x, p);
+			printer(arg, " %.2x", x);
+		}
+	}
+
+	return len + CHAP_HDRLEN;
+}
+
+struct protent chap_protent = {
+	PPP_CHAP,
+	chap_init,
+	chap_input,
+	chap_protrej,
+	chap_lowerup,
+	chap_lowerdown,
+	NULL,		/* open */
+	NULL,		/* close */
+	chap_print_pkt,
+	NULL,		/* datainput */
+	1,		/* enabled_flag */
+	"CHAP",		/* name */
+	NULL,		/* data_name */
+	chap_option_list,
+	NULL,		/* check_options */
+};
diff -Naur tomato/release/src/router/pppd/pppd/chap-new.h tomato121/release/src/router/pppd/pppd/chap-new.h
--- tomato/release/src/router/pppd/pppd/chap-new.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/chap-new.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,130 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 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. 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.
+ */
+
+/*
+ * CHAP packets begin with a standard header with code, id, len (2 bytes).
+ */
+#define CHAP_HDRLEN	4
+
+/*
+ * Values for the code field.
+ */
+#define CHAP_CHALLENGE	1
+#define CHAP_RESPONSE	2
+#define CHAP_SUCCESS	3
+#define CHAP_FAILURE	4
+
+/*
+ * CHAP digest codes.
+ */
+#define CHAP_MD5		5
+#define CHAP_MICROSOFT		0x80
+#define CHAP_MICROSOFT_V2	0x81
+
+/*
+ * Semi-arbitrary limits on challenge and response fields.
+ */
+#define MAX_CHALLENGE_LEN	64
+#define MAX_RESPONSE_LEN	64
+
+/* bitmask of supported algorithms */
+#define MDTYPE_MICROSOFT_V2	0x1
+#define MDTYPE_MICROSOFT	0x2
+#define MDTYPE_MD5		0x4
+#define MDTYPE_NONE		0
+
+/* hashes supported by this instance of pppd */
+extern int chap_mdtype_all;
+
+/* Return the digest alg. ID for the most preferred digest type. */
+#define CHAP_DIGEST(mdtype) \
+    ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \
+    ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \
+    ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \
+    0
+
+/* Return the bit flag (lsb set) for our most preferred digest type. */
+#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype)
+
+/* Return the bit flag for a given digest algorithm ID. */
+#define CHAP_MDTYPE_D(digest) \
+    ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \
+    ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \
+    ((digest) == CHAP_MD5)? MDTYPE_MD5: \
+    0
+
+/* Can we do the requested digest? */
+#define CHAP_CANDIGEST(mdtype, digest) \
+    ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \
+    ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \
+    ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \
+    0
+
+/*
+ * The code for each digest type has to supply one of these.
+ */
+struct chap_digest_type {
+	int code;
+
+	/*
+	 * Note: challenge and response arguments below are formatted as
+	 * a length byte followed by the actual challenge/response data.
+	 */
+	void (*generate_challenge)(unsigned char *challenge);
+	int (*verify_response)(int id, char *name,
+		unsigned char *secret, int secret_len,
+		unsigned char *challenge, unsigned char *response,
+		char *message, int message_space);
+	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);
+	void (*handle_failure)(unsigned char *pkt, int len);
+
+	struct chap_digest_type *next;
+};
+
+/* Hook for a plugin to validate CHAP challenge */
+extern int (*chap_verify_hook)(char *name, char *ourname, int id,
+			struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space);
+
+/* Called by digest code to register a digest type */
+extern void chap_register_digest(struct chap_digest_type *);
+
+/* Called by authentication code to start authenticating the peer. */
+extern void chap_auth_peer(int unit, char *our_name, int digest_code);
+
+/* Called by auth. code to start authenticating us to the peer. */
+extern void chap_auth_with_peer(int unit, char *our_name, int digest_code);
+
+/* Represents the CHAP protocol to the main pppd code */
+extern struct protent chap_protent;
diff -Naur tomato/release/src/router/pppd/pppd/demand.c tomato121/release/src/router/pppd/pppd/demand.c
--- tomato/release/src/router/pppd/pppd/demand.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/demand.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,34 @@
 /*
  * demand.c - Support routines for demand-dialling.
  *
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1996-2002 Paul Mackerras. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University.  The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: demand.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: demand.c,v 1.20 2005/08/25 12:14:18 paulus Exp $"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,9 +44,7 @@
 #include <sys/stat.h>
 #include <sys/socket.h>
 #ifdef PPP_FILTER
-#include <net/if.h>
-#include <net/bpf.h>
-#include <pcap.h>
+#include <pcap-bpf.h>
 #endif
 
 #include "pppd.h"
@@ -86,8 +95,9 @@
     fcs = PPP_INITFCS;
 
     netif_set_mtu(0, MIN(lcp_allowoptions[0].mru, PPP_MRU));
-    ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
-    ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+    if (ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0) < 0
+	|| ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0) < 0)
+	    fatal("Couldn't set up demand-dialled PPP interface: %m");
 
 #ifdef PPP_FILTER
     set_filters(&pass_filter, &active_filter);
@@ -331,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)
-	return 0;
-    if (active_filter.bf_len != 0
-	&& bpf_filter(active_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;
+    }
+    p[0] = 0xff;
 #endif
     for (i = 0; (protp = protocols[i]) != NULL; ++i) {
 	if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
diff -Naur tomato/release/src/router/pppd/pppd/eap.c tomato121/release/src/router/pppd/pppd/eap.c
--- tomato/release/src/router/pppd/pppd/eap.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/eap.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,2430 @@
+/*
+ * eap.c - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * This implementation of EAP supports MD5-Challenge and SRP-SHA1
+ * authentication styles.  Note that support of MD5-Challenge is a
+ * requirement of RFC 2284, and that it's essentially just a
+ * reimplementation of regular RFC 1994 CHAP using EAP messages.
+ *
+ * As an authenticator ("server"), there are multiple phases for each
+ * style.  In the first phase of each style, the unauthenticated peer
+ * name is queried using the EAP Identity request type.  If the
+ * "remotename" option is used, then this phase is skipped, because
+ * the peer's name is presumed to be known.
+ *
+ * For MD5-Challenge, there are two phases, and the second phase
+ * consists of sending the challenge itself and handling the
+ * associated response.
+ *
+ * For SRP-SHA1, there are four phases.  The second sends 's', 'N',
+ * and 'g'.  The reply contains 'A'.  The third sends 'B', and the
+ * reply contains 'M1'.  The forth sends the 'M2' value.
+ *
+ * As an authenticatee ("client"), there's just a single phase --
+ * responding to the queries generated by the peer.  EAP is an
+ * authenticator-driven protocol.
+ *
+ * Based on draft-ietf-pppext-eap-srp-03.txt.
+ */
+
+#define RCSID	"$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "md5.h"
+#include "eap.h"
+
+#ifdef USE_SRP
+#include <t_pwd.h>
+#include <t_server.h>
+#include <t_client.h>
+#include "pppcrypt.h"
+#endif /* USE_SRP */
+
+#ifndef SHA_DIGESTSIZE
+#define	SHA_DIGESTSIZE 20
+#endif
+
+static const char rcsid[] = RCSID;
+
+eap_state eap_states[NUM_PPP];		/* EAP state; one for each unit */
+#ifdef USE_SRP
+static char *pn_secret = NULL;		/* Pseudonym generating secret */
+#endif
+
+/*
+ * Command-line options.
+ */
+static option_t eap_option_list[] = {
+    { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout,
+      "Set retransmit timeout for EAP Requests (server)" },
+    { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests,
+      "Set max number of EAP Requests sent (server)" },
+    { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout,
+      "Set time limit for peer EAP authentication" },
+    { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests,
+      "Set max number of EAP Requests allows (client)" },
+    { "eap-interval", o_int, &eap_states[0].es_rechallenge,
+      "Set interval for EAP rechallenge" },
+#ifdef USE_SRP
+    { "srp-interval", o_int, &eap_states[0].es_lwrechallenge,
+      "Set interval for SRP lightweight rechallenge" },
+    { "srp-pn-secret", o_string, &pn_secret,
+      "Long term pseudonym generation secret" },
+    { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo,
+      "Use pseudonym if offered one by server", 1 },
+#endif
+    { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void eap_init __P((int unit));
+static void eap_input __P((int unit, u_char *inp, int inlen));
+static void eap_protrej __P((int unit));
+static void eap_lowerup __P((int unit));
+static void eap_lowerdown __P((int unit));
+static int  eap_printpkt __P((u_char *inp, int inlen,
+    void (*)(void *arg, char *fmt, ...), void *arg));
+
+struct protent eap_protent = {
+	PPP_EAP,		/* protocol number */
+	eap_init,		/* initialization procedure */
+	eap_input,		/* process a received packet */
+	eap_protrej,		/* process a received protocol-reject */
+	eap_lowerup,		/* lower layer has gone up */
+	eap_lowerdown,		/* lower layer has gone down */
+	NULL,			/* open the protocol */
+	NULL,			/* close the protocol */
+	eap_printpkt,		/* print a packet in readable form */
+	NULL,			/* process a received data packet */
+	1,			/* protocol enabled */
+	"EAP",			/* text name of protocol */
+	NULL,			/* text name of corresponding data protocol */
+	eap_option_list,	/* list of command-line options */
+	NULL,			/* check requested options; assign defaults */
+	NULL,			/* configure interface for demand-dial */
+	NULL			/* say whether to bring up link for this pkt */
+};
+
+/*
+ * A well-known 2048 bit modulus.
+ */
+static const u_char wkmodulus[] = {
+	0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+	0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+	0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+	0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+	0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+	0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+	0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+	0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+	0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+	0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+	0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+	0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+	0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+	0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+	0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+	0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+	0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+	0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+	0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+	0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+	0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+	0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+	0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+	0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+	0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+	0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+	0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+	0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+	0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+	0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+	0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+	0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+
+/* Local forward declarations. */
+static void eap_server_timeout __P((void *arg));
+
+/*
+ * Convert EAP state code to printable string for debug.
+ */
+static const char *
+eap_state_name(esc)
+enum eap_state_code esc;
+{
+	static const char *state_names[] = { EAP_STATES };
+
+	return (state_names[(int)esc]);
+}
+
+/*
+ * eap_init - Initialize state for an EAP user.  This is currently
+ * called once by main() during start-up.
+ */
+static void
+eap_init(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	BZERO(esp, sizeof (*esp));
+	esp->es_unit = unit;
+	esp->es_server.ea_timeout = EAP_DEFTIMEOUT;
+	esp->es_server.ea_maxrequests = EAP_DEFTRANSMITS;
+	esp->es_server.ea_id = (u_char)(drand48() * 0x100);
+	esp->es_client.ea_timeout = EAP_DEFREQTIME;
+	esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ;
+}
+
+/*
+ * eap_client_timeout - Give up waiting for the peer to send any
+ * Request messages.
+ */
+static void
+eap_client_timeout(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *) arg;
+
+	if (!eap_client_active(esp))
+		return;
+
+	error("EAP: timeout waiting for Request from peer");
+	auth_withpeer_fail(esp->es_unit, PPP_EAP);
+	esp->es_client.ea_state = eapBadAuth;
+}
+
+/*
+ * eap_authwithpeer - Authenticate to our peer (behave as client).
+ *
+ * Start client state and wait for requests.  This is called only
+ * after eap_lowerup.
+ */
+void
+eap_authwithpeer(unit, localname)
+int unit;
+char *localname;
+{
+	eap_state *esp = &eap_states[unit];
+
+	/* Save the peer name we're given */
+	esp->es_client.ea_name = localname;
+	esp->es_client.ea_namelen = strlen(localname);
+
+	esp->es_client.ea_state = eapListen;
+
+	/*
+	 * Start a timer so that if the other end just goes
+	 * silent, we don't sit here waiting forever.
+	 */
+	if (esp->es_client.ea_timeout > 0)
+		TIMEOUT(eap_client_timeout, (void *)esp,
+		    esp->es_client.ea_timeout);
+}
+
+/*
+ * Format a standard EAP Failure message and send it to the peer.
+ * (Server operation)
+ */
+static void
+eap_send_failure(esp)
+eap_state *esp;
+{
+	u_char *outp;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_FAILURE, outp);
+	esp->es_server.ea_id++;
+	PUTCHAR(esp->es_server.ea_id, outp);
+	PUTSHORT(EAP_HEADERLEN, outp);
+
+	output(esp->es_unit, outpacket_buf, EAP_HEADERLEN + PPP_HDRLEN);
+
+	esp->es_server.ea_state = eapBadAuth;
+	auth_peer_fail(esp->es_unit, PPP_EAP);
+}
+
+/*
+ * Format a standard EAP Success message and send it to the peer.
+ * (Server operation)
+ */
+static void
+eap_send_success(esp)
+eap_state *esp;
+{
+	u_char *outp;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_SUCCESS, outp);
+	esp->es_server.ea_id++;
+	PUTCHAR(esp->es_server.ea_id, outp);
+	PUTSHORT(EAP_HEADERLEN, outp);
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + EAP_HEADERLEN);
+
+	auth_peer_success(esp->es_unit, PPP_EAP, 0,
+	    esp->es_server.ea_peer, esp->es_server.ea_peerlen);
+}
+
+#ifdef USE_SRP
+/*
+ * Set DES key according to pseudonym-generating secret and current
+ * date.
+ */
+static bool
+pncrypt_setkey(int timeoffs)
+{
+	struct tm *tp;
+	char tbuf[9];
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+	time_t reftime;
+
+	if (pn_secret == NULL)
+		return (0);
+	reftime = time(NULL) + timeoffs;
+	tp = localtime(&reftime);
+	SHA1Init(&ctxt);
+	SHA1Update(&ctxt, pn_secret, strlen(pn_secret));
+	strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp);
+	SHA1Update(&ctxt, tbuf, strlen(tbuf));
+	SHA1Final(dig, &ctxt);
+	return (DesSetkey(dig));
+}
+
+static char base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct b64state {
+	u_int32_t bs_bits;
+	int bs_offs;
+};
+
+static int
+b64enc(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+	int outlen = 0;
+
+	while (inlen > 0) {
+		bs->bs_bits = (bs->bs_bits << 8) | *inp++;
+		inlen--;
+		bs->bs_offs += 8;
+		if (bs->bs_offs >= 24) {
+			*outp++ = base64[(bs->bs_bits >> 18) & 0x3F];
+			*outp++ = base64[(bs->bs_bits >> 12) & 0x3F];
+			*outp++ = base64[(bs->bs_bits >> 6) & 0x3F];
+			*outp++ = base64[bs->bs_bits & 0x3F];
+			outlen += 4;
+			bs->bs_offs = 0;
+			bs->bs_bits = 0;
+		}
+	}
+	return (outlen);
+}
+
+static int
+b64flush(bs, outp)
+struct b64state *bs;
+u_char *outp;
+{
+	int outlen = 0;
+
+	if (bs->bs_offs == 8) {
+		*outp++ = base64[(bs->bs_bits >> 2) & 0x3F];
+		*outp++ = base64[(bs->bs_bits << 4) & 0x3F];
+		outlen = 2;
+	} else if (bs->bs_offs == 16) {
+		*outp++ = base64[(bs->bs_bits >> 10) & 0x3F];
+		*outp++ = base64[(bs->bs_bits >> 4) & 0x3F];
+		*outp++ = base64[(bs->bs_bits << 2) & 0x3F];
+		outlen = 3;
+	}
+	bs->bs_offs = 0;
+	bs->bs_bits = 0;
+	return (outlen);
+}
+
+static int
+b64dec(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+	int outlen = 0;
+	char *cp;
+
+	while (inlen > 0) {
+		if ((cp = strchr(base64, *inp++)) == NULL)
+			break;
+		bs->bs_bits = (bs->bs_bits << 6) | (cp - base64);
+		inlen--;
+		bs->bs_offs += 6;
+		if (bs->bs_offs >= 8) {
+			*outp++ = bs->bs_bits >> (bs->bs_offs - 8);
+			outlen++;
+			bs->bs_offs -= 8;
+		}
+	}
+	return (outlen);
+}
+#endif /* USE_SRP */
+
+/*
+ * Assume that current waiting server state is complete and figure
+ * next state to use based on available authentication data.  'status'
+ * indicates if there was an error in handling the last query.  It is
+ * 0 for success and non-zero for failure.
+ */
+static void
+eap_figure_next_state(esp, status)
+eap_state *esp;
+int status;
+{
+#ifdef USE_SRP
+	unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp;
+	struct t_pw tpw;
+	struct t_confent *tce, mytce;
+	char *cp, *cp2;
+	struct t_server *ts;
+	int id, i, plen, toffs;
+	u_char vals[2];
+	struct b64state bs;
+#endif /* USE_SRP */
+
+	esp->es_server.ea_timeout = esp->es_savedtime;
+	switch (esp->es_server.ea_state) {
+	case eapBadAuth:
+		return;
+
+	case eapIdentify:
+#ifdef USE_SRP
+		/* Discard any previous session. */
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status != 0) {
+			esp->es_server.ea_state = eapBadAuth;
+			break;
+		}
+#ifdef USE_SRP
+		/* If we've got a pseudonym, try to decode to real name. */
+		if (esp->es_server.ea_peerlen > SRP_PSEUDO_LEN &&
+		    strncmp(esp->es_server.ea_peer, SRP_PSEUDO_ID,
+			SRP_PSEUDO_LEN) == 0 &&
+		    (esp->es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
+		    sizeof (secbuf)) {
+			BZERO(&bs, sizeof (bs));
+			plen = b64dec(&bs,
+			    esp->es_server.ea_peer + SRP_PSEUDO_LEN,
+			    esp->es_server.ea_peerlen - SRP_PSEUDO_LEN,
+			    secbuf);
+			toffs = 0;
+			for (i = 0; i < 5; i++) {
+				pncrypt_setkey(toffs);
+				toffs -= 86400;
+				if (!DesDecrypt(secbuf, clear)) {
+					dbglog("no DES here; cannot decode "
+					    "pseudonym");
+					return;
+				}
+				id = *(unsigned char *)clear;
+				if (id + 1 <= plen && id + 9 > plen)
+					break;
+			}
+			if (plen % 8 == 0 && i < 5) {
+				/*
+				 * Note that this is always shorter than the
+				 * original stored string, so there's no need
+				 * to realloc.
+				 */
+				if ((i = plen = *(unsigned char *)clear) > 7)
+					i = 7;
+				esp->es_server.ea_peerlen = plen;
+				dp = (unsigned char *)esp->es_server.ea_peer;
+				BCOPY(clear + 1, dp, i);
+				plen -= i;
+				dp += i;
+				sp = secbuf + 8;
+				while (plen > 0) {
+					(void) DesDecrypt(sp, dp);
+					sp += 8;
+					dp += 8;
+					plen -= 8;
+				}
+				esp->es_server.ea_peer[
+					esp->es_server.ea_peerlen] = '\0';
+				dbglog("decoded pseudonym to \"%.*q\"",
+				    esp->es_server.ea_peerlen,
+				    esp->es_server.ea_peer);
+			} else {
+				dbglog("failed to decode real name");
+				/* Stay in eapIdentfy state; requery */
+				break;
+			}
+		}
+		/* Look up user in secrets database. */
+		if (get_srp_secret(esp->es_unit, esp->es_server.ea_peer,
+		    esp->es_server.ea_name, (char *)secbuf, 1) != 0) {
+			/* Set up default in case SRP entry is bad */
+			esp->es_server.ea_state = eapMD5Chall;
+			/* Get t_confent based on index in srp-secrets */
+			id = strtol((char *)secbuf, &cp, 10);
+			if (*cp++ != ':' || id < 0)
+				break;
+			if (id == 0) {
+				mytce.index = 0;
+				mytce.modulus.data = (u_char *)wkmodulus;
+				mytce.modulus.len = sizeof (wkmodulus);
+				mytce.generator.data = (u_char *)"\002";
+				mytce.generator.len = 1;
+				tce = &mytce;
+			} else if ((tce = gettcid(id)) != NULL) {
+				/*
+				 * Client will have to verify this modulus/
+				 * generator combination, and that will take
+				 * a while.  Lengthen the timeout here.
+				 */
+				if (esp->es_server.ea_timeout > 0 &&
+				    esp->es_server.ea_timeout < 30)
+					esp->es_server.ea_timeout = 30;
+			} else {
+				break;
+			}
+			if ((cp2 = strchr(cp, ':')) == NULL)
+				break;
+			*cp2++ = '\0';
+			tpw.pebuf.name = esp->es_server.ea_peer;
+			tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
+			    cp);
+			tpw.pebuf.password.data = tpw.pwbuf;
+			tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
+			    cp2);
+			tpw.pebuf.salt.data = tpw.saltbuf;
+			if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
+				break;
+			esp->es_server.ea_session = (void *)ts;
+			esp->es_server.ea_state = eapSRP1;
+			vals[0] = esp->es_server.ea_id + 1;
+			vals[1] = EAPT_SRP;
+			t_serveraddexdata(ts, vals, 2);
+			/* Generate B; must call before t_servergetkey() */
+			t_servergenexp(ts);
+			break;
+		}
+#endif /* USE_SRP */
+		esp->es_server.ea_state = eapMD5Chall;
+		break;
+
+	case eapSRP1:
+#ifdef USE_SRP
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL && status != 0) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status == 1) {
+			esp->es_server.ea_state = eapMD5Chall;
+		} else if (status != 0 || esp->es_server.ea_session == NULL) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapSRP2;
+		}
+		break;
+
+	case eapSRP2:
+#ifdef USE_SRP
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL && status != 0) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status != 0 || esp->es_server.ea_session == NULL) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapSRP3;
+		}
+		break;
+
+	case eapSRP3:
+	case eapSRP4:
+#ifdef USE_SRP
+		ts = (struct t_server *)esp->es_server.ea_session;
+		if (ts != NULL && status != 0) {
+			t_serverclose(ts);
+			esp->es_server.ea_session = NULL;
+			esp->es_server.ea_skey = NULL;
+		}
+#endif /* USE_SRP */
+		if (status != 0 || esp->es_server.ea_session == NULL) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapOpen;
+		}
+		break;
+
+	case eapMD5Chall:
+		if (status != 0) {
+			esp->es_server.ea_state = eapBadAuth;
+		} else {
+			esp->es_server.ea_state = eapOpen;
+		}
+		break;
+
+	default:
+		esp->es_server.ea_state = eapBadAuth;
+		break;
+	}
+	if (esp->es_server.ea_state == eapBadAuth)
+		eap_send_failure(esp);
+}
+
+/*
+ * Format an EAP Request message and send it to the peer.  Message
+ * type depends on current state.  (Server operation)
+ */
+static void
+eap_send_request(esp)
+eap_state *esp;
+{
+	u_char *outp;
+	u_char *lenloc;
+	u_char *ptr;
+	int outlen;
+	int challen;
+	char *str;
+#ifdef USE_SRP
+	struct t_server *ts;
+	u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp;
+	int i, j;
+	struct b64state b64;
+	SHA1_CTX ctxt;
+#endif /* USE_SRP */
+
+	/* Handle both initial auth and restart */
+	if (esp->es_server.ea_state < eapIdentify &&
+	    esp->es_server.ea_state != eapInitial) {
+		esp->es_server.ea_state = eapIdentify;
+		if (explicit_remote) {
+			/*
+			 * If we already know the peer's
+			 * unauthenticated name, then there's no
+			 * reason to ask.  Go to next state instead.
+			 */
+			esp->es_server.ea_peer = remote_name;
+			esp->es_server.ea_peerlen = strlen(remote_name);
+			eap_figure_next_state(esp, 0);
+		}
+	}
+
+	if (esp->es_server.ea_maxrequests > 0 &&
+	    esp->es_server.ea_requests >= esp->es_server.ea_maxrequests) {
+		if (esp->es_server.ea_responses > 0)
+		{
+			error("EAP: too many Requests sent");
+		}else{
+			error("EAP: no response to Requests");
+		}
+		eap_send_failure(esp);
+		return;
+	}
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_REQUEST, outp);
+	PUTCHAR(esp->es_server.ea_id, outp);
+	lenloc = outp;
+	INCPTR(2, outp);
+
+	switch (esp->es_server.ea_state) {
+	case eapIdentify:
+		PUTCHAR(EAPT_IDENTITY, outp);
+		str = "Name";
+		challen = strlen(str);
+		BCOPY(str, outp, challen);
+		INCPTR(challen, outp);
+		break;
+
+	case eapMD5Chall:
+		PUTCHAR(EAPT_MD5CHAP, outp);
+		/*
+		 * pick a random challenge length between
+		 * MIN_CHALLENGE_LENGTH and MAX_CHALLENGE_LENGTH
+		 */
+		challen = (drand48() *
+		    (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+			    MIN_CHALLENGE_LENGTH;
+		PUTCHAR(challen, outp);
+		esp->es_challen = challen;
+		ptr = esp->es_challenge;
+		while (--challen >= 0)
+			*ptr++ = (u_char) (drand48() * 0x100);
+		BCOPY(esp->es_challenge, outp, esp->es_challen);
+		INCPTR(esp->es_challen, outp);
+		BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
+		INCPTR(esp->es_server.ea_namelen, outp);
+		break;
+
+#ifdef USE_SRP
+	case eapSRP1:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_CHALLENGE, outp);
+
+		PUTCHAR(esp->es_server.ea_namelen, outp);
+		BCOPY(esp->es_server.ea_name, outp, esp->es_server.ea_namelen);
+		INCPTR(esp->es_server.ea_namelen, outp);
+
+		ts = (struct t_server *)esp->es_server.ea_session;
+		assert(ts != NULL);
+		PUTCHAR(ts->s.len, outp);
+		BCOPY(ts->s.data, outp, ts->s.len);
+		INCPTR(ts->s.len, outp);
+
+		if (ts->g.len == 1 && ts->g.data[0] == 2) {
+			PUTCHAR(0, outp);
+		} else {
+			PUTCHAR(ts->g.len, outp);
+			BCOPY(ts->g.data, outp, ts->g.len);
+			INCPTR(ts->g.len, outp);
+		}
+
+		if (ts->n.len != sizeof (wkmodulus) ||
+		    BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
+			BCOPY(ts->n.data, outp, ts->n.len);
+			INCPTR(ts->n.len, outp);
+		}
+		break;
+
+	case eapSRP2:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_SKEY, outp);
+
+		ts = (struct t_server *)esp->es_server.ea_session;
+		assert(ts != NULL);
+		BCOPY(ts->B.data, outp, ts->B.len);
+		INCPTR(ts->B.len, outp);
+		break;
+
+	case eapSRP3:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_SVALIDATOR, outp);
+		PUTLONG(SRPVAL_EBIT, outp);
+		ts = (struct t_server *)esp->es_server.ea_session;
+		assert(ts != NULL);
+		BCOPY(t_serverresponse(ts), outp, SHA_DIGESTSIZE);
+		INCPTR(SHA_DIGESTSIZE, outp);
+
+		if (pncrypt_setkey(0)) {
+			/* Generate pseudonym */
+			optr = outp;
+			cp = (unsigned char *)esp->es_server.ea_peer;
+			if ((j = i = esp->es_server.ea_peerlen) > 7)
+				j = 7;
+			clear[0] = i;
+			BCOPY(cp, clear + 1, j);
+			i -= j;
+			cp += j;
+			if (!DesEncrypt(clear, cipher)) {
+				dbglog("no DES here; not generating pseudonym");
+				break;
+			}
+			BZERO(&b64, sizeof (b64));
+			outp++;		/* space for pseudonym length */
+			outp += b64enc(&b64, cipher, 8, outp);
+			while (i >= 8) {
+				(void) DesEncrypt(cp, cipher);
+				outp += b64enc(&b64, cipher, 8, outp);
+				cp += 8;
+				i -= 8;
+			}
+			if (i > 0) {
+				BCOPY(cp, clear, i);
+				cp += i;
+				while (i < 8) {
+					*cp++ = drand48() * 0x100;
+					i++;
+				}
+				(void) DesEncrypt(clear, cipher);
+				outp += b64enc(&b64, cipher, 8, outp);
+			}
+			outp += b64flush(&b64, outp);
+
+			/* Set length and pad out to next 20 octet boundary */
+			i = outp - optr - 1;
+			*optr = i;
+			i %= SHA_DIGESTSIZE;
+			if (i != 0) {
+				while (i < SHA_DIGESTSIZE) {
+					*outp++ = drand48() * 0x100;
+					i++;
+				}
+			}
+
+			/* Obscure the pseudonym with SHA1 hash */
+			SHA1Init(&ctxt);
+			SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
+			SHA1Update(&ctxt, esp->es_server.ea_skey,
+			    SESSION_KEY_LEN);
+			SHA1Update(&ctxt, esp->es_server.ea_peer,
+			    esp->es_server.ea_peerlen);
+			while (optr < outp) {
+				SHA1Final(dig, &ctxt);
+				cp = dig;
+				while (cp < dig + SHA_DIGESTSIZE)
+					*optr++ ^= *cp++;
+				SHA1Init(&ctxt);
+				SHA1Update(&ctxt, &esp->es_server.ea_id, 1);
+				SHA1Update(&ctxt, esp->es_server.ea_skey,
+				    SESSION_KEY_LEN);
+				SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
+				    SHA_DIGESTSIZE);
+			}
+		}
+		break;
+
+	case eapSRP4:
+		PUTCHAR(EAPT_SRP, outp);
+		PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
+		challen = MIN_CHALLENGE_LENGTH +
+		    ((MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH) * drand48());
+		esp->es_challen = challen;
+		ptr = esp->es_challenge;
+		while (--challen >= 0)
+			*ptr++ = drand48() * 0x100;
+		BCOPY(esp->es_challenge, outp, esp->es_challen);
+		INCPTR(esp->es_challen, outp);
+		break;
+#endif /* USE_SRP */
+
+	default:
+		return;
+	}
+
+	outlen = (outp - outpacket_buf) - PPP_HDRLEN;
+	PUTSHORT(outlen, lenloc);
+
+	output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+	esp->es_server.ea_requests++;
+
+	if (esp->es_server.ea_timeout > 0)
+		TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout);
+}
+
+/*
+ * eap_authpeer - Authenticate our peer (behave as server).
+ *
+ * Start server state and send first request.  This is called only
+ * after eap_lowerup.
+ */
+void
+eap_authpeer(unit, localname)
+int unit;
+char *localname;
+{
+	eap_state *esp = &eap_states[unit];
+
+	/* Save the name we're given. */
+	esp->es_server.ea_name = localname;
+	esp->es_server.ea_namelen = strlen(localname);
+
+	esp->es_savedtime = esp->es_server.ea_timeout;
+
+	/* Lower layer up yet? */
+	if (esp->es_server.ea_state == eapInitial ||
+	    esp->es_server.ea_state == eapPending) {
+		esp->es_server.ea_state = eapPending;
+		return;
+	}
+
+	esp->es_server.ea_state = eapPending;
+
+	/* ID number not updated here intentionally; hashed into M1 */
+	eap_send_request(esp);
+}
+
+/*
+ * eap_server_timeout - Retransmission timer for sending Requests
+ * expired.
+ */
+static void
+eap_server_timeout(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *) arg;
+
+	if (!eap_server_active(esp))
+		return;
+
+	/* EAP ID number must not change on timeout. */
+	eap_send_request(esp);
+}
+
+/*
+ * When it's time to send rechallenge the peer, this timeout is
+ * called.  Once the rechallenge is successful, the response handler
+ * will restart the timer.  If it fails, then the link is dropped.
+ */
+static void
+eap_rechallenge(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *)arg;
+
+	if (esp->es_server.ea_state != eapOpen &&
+	    esp->es_server.ea_state != eapSRP4)
+		return;
+
+	esp->es_server.ea_requests = 0;
+	esp->es_server.ea_state = eapIdentify;
+	eap_figure_next_state(esp, 0);
+	esp->es_server.ea_id++;
+	eap_send_request(esp);
+}
+
+static void
+srp_lwrechallenge(arg)
+void *arg;
+{
+	eap_state *esp = (eap_state *)arg;
+
+	if (esp->es_server.ea_state != eapOpen ||
+	    esp->es_server.ea_type != EAPT_SRP)
+		return;
+
+	esp->es_server.ea_requests = 0;
+	esp->es_server.ea_state = eapSRP4;
+	esp->es_server.ea_id++;
+	eap_send_request(esp);
+}
+
+/*
+ * eap_lowerup - The lower layer is now up.
+ *
+ * This is called before either eap_authpeer or eap_authwithpeer.  See
+ * link_established() in auth.c.  All that's necessary here is to
+ * return to closed state so that those two routines will do the right
+ * thing.
+ */
+static void
+eap_lowerup(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	/* Discard any (possibly authenticated) peer name. */
+	if (esp->es_server.ea_peer != NULL &&
+	    esp->es_server.ea_peer != remote_name)
+		free(esp->es_server.ea_peer);
+	esp->es_server.ea_peer = NULL;
+	if (esp->es_client.ea_peer != NULL)
+		free(esp->es_client.ea_peer);
+	esp->es_client.ea_peer = NULL;
+
+	esp->es_client.ea_state = eapClosed;
+	esp->es_server.ea_state = eapClosed;
+}
+
+/*
+ * eap_lowerdown - The lower layer is now down.
+ *
+ * Cancel all timeouts and return to initial state.
+ */
+static void
+eap_lowerdown(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	if (eap_client_active(esp) && esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+	if (eap_server_active(esp)) {
+		if (esp->es_server.ea_timeout > 0) {
+			UNTIMEOUT(eap_server_timeout, (void *)esp);
+		}
+	} else {
+		if ((esp->es_server.ea_state == eapOpen ||
+		    esp->es_server.ea_state == eapSRP4) &&
+		    esp->es_rechallenge > 0) {
+			UNTIMEOUT(eap_rechallenge, (void *)esp);
+		}
+		if (esp->es_server.ea_state == eapOpen &&
+		    esp->es_lwrechallenge > 0) {
+			UNTIMEOUT(srp_lwrechallenge, (void *)esp);
+		}
+	}
+
+	esp->es_client.ea_state = esp->es_server.ea_state = eapInitial;
+	esp->es_client.ea_requests = esp->es_server.ea_requests = 0;
+}
+
+/*
+ * eap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen.  If it does, it represents authentication
+ * failure.
+ */
+static void
+eap_protrej(unit)
+int unit;
+{
+	eap_state *esp = &eap_states[unit];
+
+	if (eap_client_active(esp)) {
+		error("EAP authentication failed due to Protocol-Reject");
+		auth_withpeer_fail(unit, PPP_EAP);
+	}
+	if (eap_server_active(esp)) {
+		error("EAP authentication of peer failed on Protocol-Reject");
+		auth_peer_fail(unit, PPP_EAP);
+	}
+	eap_lowerdown(unit);
+}
+
+/*
+ * Format and send a regular EAP Response message.
+ */
+static void
+eap_send_response(esp, id, typenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char typenum;
+u_char *str;
+int lenstr;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(typenum, outp);
+	if (lenstr > 0) {
+		BCOPY(str, outp, lenstr);
+	}
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+/*
+ * Format and send an MD5-Challenge EAP Response message.
+ */
+static void
+eap_chap_response(esp, id, hash, name, namelen)
+eap_state *esp;
+u_char id;
+u_char *hash;
+char *name;
+int namelen;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
+	    namelen;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_MD5CHAP, outp);
+	PUTCHAR(MD5_SIGNATURE_SIZE, outp);
+	BCOPY(hash, outp, MD5_SIGNATURE_SIZE);
+	INCPTR(MD5_SIGNATURE_SIZE, outp);
+	if (namelen > 0) {
+		BCOPY(name, outp, namelen);
+	}
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+#ifdef USE_SRP
+/*
+ * Format and send a SRP EAP Response message.
+ */
+static void
+eap_srp_response(esp, id, subtypenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char subtypenum;
+u_char *str;
+int lenstr;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_SRP, outp);
+	PUTCHAR(subtypenum, outp);
+	if (lenstr > 0) {
+		BCOPY(str, outp, lenstr);
+	}
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+/*
+ * Format and send a SRP EAP Client Validator Response message.
+ */
+static void
+eap_srpval_response(esp, id, flags, str)
+eap_state *esp;
+u_char id;
+u_int32_t flags;
+u_char *str;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+    
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u_int32_t) +
+	    SHA_DIGESTSIZE;
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_SRP, outp);
+	PUTCHAR(EAPSRP_CVALIDATOR, outp);
+	PUTLONG(flags, outp);
+	BCOPY(str, outp, SHA_DIGESTSIZE);
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+#endif /* USE_SRP */
+
+static void
+eap_send_nak(esp, id, type)
+eap_state *esp;
+u_char id;
+u_char type;
+{
+	u_char *outp;
+	int msglen;
+
+	outp = outpacket_buf;
+
+	MAKEHEADER(outp, PPP_EAP);
+
+	PUTCHAR(EAP_RESPONSE, outp);
+	PUTCHAR(id, outp);
+	esp->es_client.ea_id = id;
+	msglen = EAP_HEADERLEN + 2 * sizeof (u_char);
+	PUTSHORT(msglen, outp);
+	PUTCHAR(EAPT_NAK, outp);
+	PUTCHAR(type, outp);
+
+	output(esp->es_unit, outpacket_buf, PPP_HDRLEN + msglen);
+}
+
+#ifdef USE_SRP
+static char *
+name_of_pn_file()
+{
+	char *user, *path, *file;
+	struct passwd *pw;
+	size_t pl;
+	static bool pnlogged = 0;
+
+	pw = getpwuid(getuid());
+	if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) {
+		errno = EINVAL;
+		return (NULL);
+	}
+	file = _PATH_PSEUDONYM;
+	pl = strlen(user) + strlen(file) + 2;
+	path = malloc(pl);
+	if (path == NULL)
+		return (NULL);
+	(void) slprintf(path, pl, "%s/%s", user, file);
+	if (!pnlogged) {
+		dbglog("pseudonym file: %s", path);
+		pnlogged = 1;
+	}
+	return (path);
+}
+
+static int
+open_pn_file(modebits)
+mode_t modebits;
+{
+	char *path;
+	int fd, err;
+
+	if ((path = name_of_pn_file()) == NULL)
+		return (-1);
+	fd = open(path, modebits, S_IRUSR | S_IWUSR);
+	err = errno;
+	free(path);
+	errno = err;
+	return (fd);
+}
+
+static void
+remove_pn_file()
+{
+	char *path;
+
+	if ((path = name_of_pn_file()) != NULL) {
+		(void) unlink(path);
+		(void) free(path);
+	}
+}
+
+static void
+write_pseudonym(esp, inp, len, id)
+eap_state *esp;
+u_char *inp;
+int len, id;
+{
+	u_char val;
+	u_char *datp, *digp;
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+	int dsize, fd, olen = len;
+
+	/*
+	 * Do the decoding by working backwards.  This eliminates the need
+	 * to save the decoded output in a separate buffer.
+	 */
+	val = id;
+	while (len > 0) {
+		if ((dsize = len % SHA_DIGESTSIZE) == 0)
+			dsize = SHA_DIGESTSIZE;
+		len -= dsize;
+		datp = inp + len;
+		SHA1Init(&ctxt);
+		SHA1Update(&ctxt, &val, 1);
+		SHA1Update(&ctxt, esp->es_client.ea_skey, SESSION_KEY_LEN);
+		if (len > 0) {
+			SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
+		} else {
+			SHA1Update(&ctxt, esp->es_client.ea_name,
+			    esp->es_client.ea_namelen);
+		}
+		SHA1Final(dig, &ctxt);
+		for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++)
+			*datp++ ^= *digp;
+	}
+
+	/* Now check that the result is sane */
+	if (olen <= 0 || *inp + 1 > olen) {
+		dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp);
+		return;
+	}
+
+	/* Save it away */
+	fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC);
+	if (fd < 0) {
+		dbglog("EAP: error saving pseudonym: %m");
+		return;
+	}
+	len = write(fd, inp + 1, *inp);
+	if (close(fd) != -1 && len == *inp) {
+		dbglog("EAP: saved pseudonym");
+		esp->es_usedpseudo = 0;
+	} else {
+		dbglog("EAP: failed to save pseudonym");
+		remove_pn_file();
+	}
+}
+#endif /* USE_SRP */
+
+/*
+ * eap_request - Receive EAP Request message (client mode).
+ */
+static void
+eap_request(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	u_char typenum;
+	u_char vallen;
+	int secret_len;
+	char secret[MAXWORDLEN];
+	char rhostname[256];
+	MD5_CTX mdContext;
+	u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+	struct t_client *tc;
+	struct t_num sval, gval, Nval, *Ap, Bval;
+	u_char vals[2];
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+	int fd;
+#endif /* USE_SRP */
+
+	/*
+	 * Note: we update es_client.ea_id *only if* a Response
+	 * message is being generated.  Otherwise, we leave it the
+	 * same for duplicate detection purposes.
+	 */
+
+	esp->es_client.ea_requests++;
+	if (esp->es_client.ea_maxrequests != 0 &&
+	    esp->es_client.ea_requests > esp->es_client.ea_maxrequests) {
+		info("EAP: received too many Request messages");
+		if (esp->es_client.ea_timeout > 0) {
+			UNTIMEOUT(eap_client_timeout, (void *)esp);
+		}
+		auth_withpeer_fail(esp->es_unit, PPP_EAP);
+		return;
+	}
+
+	if (len <= 0) {
+		error("EAP: empty Request message discarded");
+		return;
+	}
+
+	GETCHAR(typenum, inp);
+	len--;
+
+	switch (typenum) {
+	case EAPT_IDENTITY:
+		if (len > 0)
+			info("EAP: Identity prompt \"%.*q\"", len, inp);
+#ifdef USE_SRP
+		if (esp->es_usepseudo &&
+		    (esp->es_usedpseudo == 0 ||
+			(esp->es_usedpseudo == 1 &&
+			    id == esp->es_client.ea_id))) {
+			esp->es_usedpseudo = 1;
+			/* Try to get a pseudonym */
+			if ((fd = open_pn_file(O_RDONLY)) >= 0) {
+				strcpy(rhostname, SRP_PSEUDO_ID);
+				len = read(fd, rhostname + SRP_PSEUDO_LEN,
+				    sizeof (rhostname) - SRP_PSEUDO_LEN);
+				/* XXX NAI unsupported */
+				if (len > 0) {
+					eap_send_response(esp, id, typenum,
+					    rhostname, len + SRP_PSEUDO_LEN);
+				}
+				(void) close(fd);
+				if (len > 0)
+					break;
+			}
+		}
+		/* Stop using pseudonym now. */
+		if (esp->es_usepseudo && esp->es_usedpseudo != 2) {
+			remove_pn_file();
+			esp->es_usedpseudo = 2;
+		}
+#endif /* USE_SRP */
+		eap_send_response(esp, id, typenum, esp->es_client.ea_name,
+		    esp->es_client.ea_namelen);
+		break;
+
+	case EAPT_NOTIFICATION:
+		if (len > 0)
+			info("EAP: Notification \"%.*q\"", len, inp);
+		eap_send_response(esp, id, typenum, NULL, 0);
+		break;
+
+	case EAPT_NAK:
+		/*
+		 * Avoid the temptation to send Response Nak in reply
+		 * to Request Nak here.  It can only lead to trouble.
+		 */
+		warn("EAP: unexpected Nak in Request; ignored");
+		/* Return because we're waiting for something real. */
+		return;
+
+	case EAPT_MD5CHAP:
+		if (len < 1) {
+			error("EAP: received MD5-Challenge with no data");
+			/* Bogus request; wait for something real. */
+			return;
+		}
+		GETCHAR(vallen, inp);
+		len--;
+		if (vallen < 8 || vallen > len) {
+			error("EAP: MD5-Challenge with bad length %d (8..%d)",
+			    vallen, len);
+			/* Try something better. */
+			eap_send_nak(esp, id, EAPT_SRP);
+			break;
+		}
+
+		/* Not so likely to happen. */
+		if (vallen >= len + sizeof (rhostname)) {
+			dbglog("EAP: trimming really long peer name down");
+			BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+			rhostname[sizeof (rhostname) - 1] = '\0';
+		} else {
+			BCOPY(inp + vallen, rhostname, len - vallen);
+			rhostname[len - vallen] = '\0';
+		}
+
+		/* In case the remote doesn't give us his name. */
+		if (explicit_remote ||
+		    (remote_name[0] != '\0' && vallen == len))
+			strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+		/*
+		 * Get the secret for authenticating ourselves with
+		 * the specified host.
+		 */
+		if (!get_secret(esp->es_unit, esp->es_client.ea_name,
+		    rhostname, secret, &secret_len, 0)) {
+			dbglog("EAP: no MD5 secret for auth to %q", rhostname);
+			eap_send_nak(esp, id, EAPT_SRP);
+			break;
+		}
+		MD5_Init(&mdContext);
+		typenum = id;
+		MD5_Update(&mdContext, &typenum, 1);
+		MD5_Update(&mdContext, secret, secret_len);
+		BZERO(secret, sizeof (secret));
+		MD5_Update(&mdContext, inp, vallen);
+		MD5_Final(hash, &mdContext);
+		eap_chap_response(esp, id, hash, esp->es_client.ea_name,
+		    esp->es_client.ea_namelen);
+		break;
+
+#ifdef USE_SRP
+	case EAPT_SRP:
+		if (len < 1) {
+			error("EAP: received empty SRP Request");
+			/* Bogus request; wait for something real. */
+			return;
+		}
+
+		/* Get subtype */
+		GETCHAR(vallen, inp);
+		len--;
+		switch (vallen) {
+		case EAPSRP_CHALLENGE:
+			tc = NULL;
+			if (esp->es_client.ea_session != NULL) {
+				tc = (struct t_client *)esp->es_client.
+				    ea_session;
+				/*
+				 * If this is a new challenge, then start
+				 * over with a new client session context.
+				 * Otherwise, just resend last response.
+				 */
+				if (id != esp->es_client.ea_id) {
+					t_clientclose(tc);
+					esp->es_client.ea_session = NULL;
+					tc = NULL;
+				}
+			}
+			/* No session key just yet */
+			esp->es_client.ea_skey = NULL;
+			if (tc == NULL) {
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len) {
+					error("EAP: badly-formed SRP Challenge"
+					    " (name)");
+					/* Ignore badly-formed messages */
+					return;
+				}
+				BCOPY(inp, rhostname, vallen);
+				rhostname[vallen] = '\0';
+				INCPTR(vallen, inp);
+				len -= vallen;
+
+				/*
+				 * In case the remote doesn't give us his name,
+				 * use configured name.
+				 */
+				if (explicit_remote ||
+				    (remote_name[0] != '\0' && vallen == 0)) {
+					strlcpy(rhostname, remote_name,
+					    sizeof (rhostname));
+				}
+
+				if (esp->es_client.ea_peer != NULL)
+					free(esp->es_client.ea_peer);
+				esp->es_client.ea_peer = strdup(rhostname);
+				esp->es_client.ea_peerlen = strlen(rhostname);
+
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len) {
+					error("EAP: badly-formed SRP Challenge"
+					    " (s)");
+					/* Ignore badly-formed messages */
+					return;
+				}
+				sval.data = inp;
+				sval.len = vallen;
+				INCPTR(vallen, inp);
+				len -= vallen;
+
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen > len) {
+					error("EAP: badly-formed SRP Challenge"
+					    " (g)");
+					/* Ignore badly-formed messages */
+					return;
+				}
+				/* If no generator present, then use value 2 */
+				if (vallen == 0) {
+					gval.data = (u_char *)"\002";
+					gval.len = 1;
+				} else {
+					gval.data = inp;
+					gval.len = vallen;
+				}
+				INCPTR(vallen, inp);
+				len -= vallen;
+
+				/*
+				 * If no modulus present, then use well-known
+				 * value.
+				 */
+				if (len == 0) {
+					Nval.data = (u_char *)wkmodulus;
+					Nval.len = sizeof (wkmodulus);
+				} else {
+					Nval.data = inp;
+					Nval.len = len;
+				}
+				tc = t_clientopen(esp->es_client.ea_name,
+				    &Nval, &gval, &sval);
+				if (tc == NULL) {
+					eap_send_nak(esp, id, EAPT_MD5CHAP);
+					break;
+				}
+				esp->es_client.ea_session = (void *)tc;
+
+				/* Add Challenge ID & type to verifier */
+				vals[0] = id;
+				vals[1] = EAPT_SRP;
+				t_clientaddexdata(tc, vals, 2);
+			}
+			Ap = t_clientgenexp(tc);
+			eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data,
+			    Ap->len);
+			break;
+
+		case EAPSRP_SKEY:
+			tc = (struct t_client *)esp->es_client.ea_session;
+			if (tc == NULL) {
+				warn("EAP: peer sent Subtype 2 without 1");
+				eap_send_nak(esp, id, EAPT_MD5CHAP);
+				break;
+			}
+			if (esp->es_client.ea_skey != NULL) {
+				/*
+				 * ID number should not change here.  Warn
+				 * if it does (but otherwise ignore).
+				 */
+				if (id != esp->es_client.ea_id) {
+					warn("EAP: ID changed from %d to %d "
+					    "in SRP Subtype 2 rexmit",
+					    esp->es_client.ea_id, id);
+				}
+			} else {
+				if (get_srp_secret(esp->es_unit,
+				    esp->es_client.ea_name,
+				    esp->es_client.ea_peer, secret, 0) == 0) {
+					/*
+					 * Can't work with this peer because
+					 * the secret is missing.  Just give
+					 * up.
+					 */
+					eap_send_nak(esp, id, EAPT_MD5CHAP);
+					break;
+				}
+				Bval.data = inp;
+				Bval.len = len;
+				t_clientpasswd(tc, secret);
+				BZERO(secret, sizeof (secret));
+				esp->es_client.ea_skey =
+				    t_clientgetkey(tc, &Bval);
+				if (esp->es_client.ea_skey == NULL) {
+					/* Server is rogue; stop now */
+					error("EAP: SRP server is rogue");
+					goto client_failure;
+				}
+			}
+			eap_srpval_response(esp, id, SRPVAL_EBIT,
+			    t_clientresponse(tc));
+			break;
+
+		case EAPSRP_SVALIDATOR:
+			tc = (struct t_client *)esp->es_client.ea_session;
+			if (tc == NULL || esp->es_client.ea_skey == NULL) {
+				warn("EAP: peer sent Subtype 3 without 1/2");
+				eap_send_nak(esp, id, EAPT_MD5CHAP);
+				break;
+			}
+			/*
+			 * If we're already open, then this ought to be a
+			 * duplicate.  Otherwise, check that the server is
+			 * who we think it is.
+			 */
+			if (esp->es_client.ea_state == eapOpen) {
+				if (id != esp->es_client.ea_id) {
+					warn("EAP: ID changed from %d to %d "
+					    "in SRP Subtype 3 rexmit",
+					    esp->es_client.ea_id, id);
+				}
+			} else {
+				len -= sizeof (u_int32_t) + SHA_DIGESTSIZE;
+				if (len < 0 || t_clientverify(tc, inp +
+					sizeof (u_int32_t)) != 0) {
+					error("EAP: SRP server verification "
+					    "failed");
+					goto client_failure;
+				}
+				GETLONG(esp->es_client.ea_keyflags, inp);
+				/* Save pseudonym if user wants it. */
+				if (len > 0 && esp->es_usepseudo) {
+					INCPTR(SHA_DIGESTSIZE, inp);
+					write_pseudonym(esp, inp, len, id);
+				}
+			}
+			/*
+			 * We've verified our peer.  We're now mostly done,
+			 * except for waiting on the regular EAP Success
+			 * message.
+			 */
+			eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0);
+			break;
+
+		case EAPSRP_LWRECHALLENGE:
+			if (len < 4) {
+				warn("EAP: malformed Lightweight rechallenge");
+				return;
+			}
+			SHA1Init(&ctxt);
+			vals[0] = id;
+			SHA1Update(&ctxt, vals, 1);
+			SHA1Update(&ctxt, esp->es_client.ea_skey,
+			    SESSION_KEY_LEN);
+			SHA1Update(&ctxt, inp, len);
+			SHA1Update(&ctxt, esp->es_client.ea_name,
+			    esp->es_client.ea_namelen);
+			SHA1Final(dig, &ctxt);
+			eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
+			    SHA_DIGESTSIZE);
+			break;
+
+		default:
+			error("EAP: unknown SRP Subtype %d", vallen);
+			eap_send_nak(esp, id, EAPT_MD5CHAP);
+			break;
+		}
+		break;
+#endif /* USE_SRP */
+
+	default:
+		info("EAP: unknown authentication type %d; Naking", typenum);
+		eap_send_nak(esp, id, EAPT_SRP);
+		break;
+	}
+
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+		TIMEOUT(eap_client_timeout, (void *)esp,
+		    esp->es_client.ea_timeout);
+	}
+	return;
+
+#ifdef USE_SRP
+client_failure:
+	esp->es_client.ea_state = eapBadAuth;
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+	esp->es_client.ea_session = NULL;
+	t_clientclose(tc);
+	auth_withpeer_fail(esp->es_unit, PPP_EAP);
+#endif /* USE_SRP */
+}
+
+/*
+ * eap_response - Receive EAP Response message (server mode).
+ */
+static void
+eap_response(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	u_char typenum;
+	u_char vallen;
+	int secret_len;
+	char secret[MAXSECRETLEN];
+	char rhostname[256];
+	MD5_CTX mdContext;
+	u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+	struct t_server *ts;
+	struct t_num A;
+	SHA1_CTX ctxt;
+	u_char dig[SHA_DIGESTSIZE];
+#endif /* USE_SRP */
+
+	if (esp->es_server.ea_id != id) {
+		dbglog("EAP: discarding Response %d; expected ID %d", id,
+		    esp->es_server.ea_id);
+		return;
+	}
+
+	esp->es_server.ea_responses++;
+
+	if (len <= 0) {
+		error("EAP: empty Response message discarded");
+		return;
+	}
+
+	GETCHAR(typenum, inp);
+	len--;
+
+	switch (typenum) {
+	case EAPT_IDENTITY:
+		if (esp->es_server.ea_state != eapIdentify) {
+			dbglog("EAP discarding unwanted Identify \"%.q\"", len,
+			    inp);
+			break;
+		}
+		info("EAP: unauthenticated peer name \"%.*q\"", len, inp);
+		if (esp->es_server.ea_peer != NULL &&
+		    esp->es_server.ea_peer != remote_name)
+			free(esp->es_server.ea_peer);
+		esp->es_server.ea_peer = malloc(len + 1);
+		if (esp->es_server.ea_peer == NULL) {
+			esp->es_server.ea_peerlen = 0;
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		BCOPY(inp, esp->es_server.ea_peer, len);
+		esp->es_server.ea_peer[len] = '\0';
+		esp->es_server.ea_peerlen = len;
+		eap_figure_next_state(esp, 0);
+		break;
+
+	case EAPT_NOTIFICATION:
+		dbglog("EAP unexpected Notification; response discarded");
+		break;
+
+	case EAPT_NAK:
+		if (len < 1) {
+			info("EAP: Nak Response with no suggested protocol");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+
+		GETCHAR(vallen, inp);
+		len--;
+
+		if (!explicit_remote && esp->es_server.ea_state == eapIdentify){
+			/* Peer cannot Nak Identify Request */
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+
+		switch (vallen) {
+		case EAPT_SRP:
+			/* Run through SRP validator selection again. */
+			esp->es_server.ea_state = eapIdentify;
+			eap_figure_next_state(esp, 0);
+			break;
+
+		case EAPT_MD5CHAP:
+			esp->es_server.ea_state = eapMD5Chall;
+			break;
+
+		default:
+			dbglog("EAP: peer requesting unknown Type %d", vallen);
+			switch (esp->es_server.ea_state) {
+			case eapSRP1:
+			case eapSRP2:
+			case eapSRP3:
+				esp->es_server.ea_state = eapMD5Chall;
+				break;
+			case eapMD5Chall:
+			case eapSRP4:
+				esp->es_server.ea_state = eapIdentify;
+				eap_figure_next_state(esp, 0);
+				break;
+			default:
+				break;
+			}
+			break;
+		}
+		break;
+
+	case EAPT_MD5CHAP:
+		if (esp->es_server.ea_state != eapMD5Chall) {
+			error("EAP: unexpected MD5-Response");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		if (len < 1) {
+			error("EAP: received MD5-Response with no data");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		GETCHAR(vallen, inp);
+		len--;
+		if (vallen != 16 || vallen > len) {
+			error("EAP: MD5-Response with bad length %d", vallen);
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+
+		/* Not so likely to happen. */
+		if (vallen >= len + sizeof (rhostname)) {
+			dbglog("EAP: trimming really long peer name down");
+			BCOPY(inp + vallen, rhostname, sizeof (rhostname) - 1);
+			rhostname[sizeof (rhostname) - 1] = '\0';
+		} else {
+			BCOPY(inp + vallen, rhostname, len - vallen);
+			rhostname[len - vallen] = '\0';
+		}
+
+		/* In case the remote doesn't give us his name. */
+		if (explicit_remote ||
+		    (remote_name[0] != '\0' && vallen == len))
+			strlcpy(rhostname, remote_name, sizeof (rhostname));
+
+		/*
+		 * Get the secret for authenticating the specified
+		 * host.
+		 */
+		if (!get_secret(esp->es_unit, rhostname,
+		    esp->es_server.ea_name, secret, &secret_len, 1)) {
+			dbglog("EAP: no MD5 secret for auth of %q", rhostname);
+			eap_send_failure(esp);
+			break;
+		}
+		MD5_Init(&mdContext);
+		MD5_Update(&mdContext, &esp->es_server.ea_id, 1);
+		MD5_Update(&mdContext, secret, secret_len);
+		BZERO(secret, sizeof (secret));
+		MD5_Update(&mdContext, esp->es_challenge, esp->es_challen);
+		MD5_Final(hash, &mdContext);
+		if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
+			eap_send_failure(esp);
+			break;
+		}
+		esp->es_server.ea_type = EAPT_MD5CHAP;
+		eap_send_success(esp);
+		eap_figure_next_state(esp, 0);
+		if (esp->es_rechallenge != 0)
+			TIMEOUT(eap_rechallenge, esp, esp->es_rechallenge);
+		break;
+
+#ifdef USE_SRP
+	case EAPT_SRP:
+		if (len < 1) {
+			error("EAP: empty SRP Response");
+			eap_figure_next_state(esp, 1);
+			break;
+		}
+		GETCHAR(typenum, inp);
+		len--;
+		switch (typenum) {
+		case EAPSRP_CKEY:
+			if (esp->es_server.ea_state != eapSRP1) {
+				error("EAP: unexpected SRP Subtype 1 Response");
+				eap_figure_next_state(esp, 1);
+				break;
+			}
+			A.data = inp;
+			A.len = len;
+			ts = (struct t_server *)esp->es_server.ea_session;
+			assert(ts != NULL);
+			esp->es_server.ea_skey = t_servergetkey(ts, &A);
+			if (esp->es_server.ea_skey == NULL) {
+				/* Client's A value is bogus; terminate now */
+				error("EAP: bogus A value from client");
+				eap_send_failure(esp);
+			} else {
+				eap_figure_next_state(esp, 0);
+			}
+			break;
+
+		case EAPSRP_CVALIDATOR:
+			if (esp->es_server.ea_state != eapSRP2) {
+				error("EAP: unexpected SRP Subtype 2 Response");
+				eap_figure_next_state(esp, 1);
+				break;
+			}
+			if (len < sizeof (u_int32_t) + SHA_DIGESTSIZE) {
+				error("EAP: M1 length %d < %d", len,
+				    sizeof (u_int32_t) + SHA_DIGESTSIZE);
+				eap_figure_next_state(esp, 1);
+				break;
+			}
+			GETLONG(esp->es_server.ea_keyflags, inp);
+			ts = (struct t_server *)esp->es_server.ea_session;
+			assert(ts != NULL);
+			if (t_serververify(ts, inp)) {
+				info("EAP: unable to validate client identity");
+				eap_send_failure(esp);
+				break;
+			}
+			eap_figure_next_state(esp, 0);
+			break;
+
+		case EAPSRP_ACK:
+			if (esp->es_server.ea_state != eapSRP3) {
+				error("EAP: unexpected SRP Subtype 3 Response");
+				eap_send_failure(esp);
+				break;
+			}
+			esp->es_server.ea_type = EAPT_SRP;
+			eap_send_success(esp);
+			eap_figure_next_state(esp, 0);
+			if (esp->es_rechallenge != 0)
+				TIMEOUT(eap_rechallenge, esp,
+				    esp->es_rechallenge);
+			if (esp->es_lwrechallenge != 0)
+				TIMEOUT(srp_lwrechallenge, esp,
+				    esp->es_lwrechallenge);
+			break;
+
+		case EAPSRP_LWRECHALLENGE:
+			if (esp->es_server.ea_state != eapSRP4) {
+				info("EAP: unexpected SRP Subtype 4 Response");
+				return;
+			}
+			if (len != SHA_DIGESTSIZE) {
+				error("EAP: bad Lightweight rechallenge "
+				    "response");
+				return;
+			}
+			SHA1Init(&ctxt);
+			vallen = id;
+			SHA1Update(&ctxt, &vallen, 1);
+			SHA1Update(&ctxt, esp->es_server.ea_skey,
+			    SESSION_KEY_LEN);
+			SHA1Update(&ctxt, esp->es_challenge, esp->es_challen);
+			SHA1Update(&ctxt, esp->es_server.ea_peer,
+			    esp->es_server.ea_peerlen);
+			SHA1Final(dig, &ctxt);
+			if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
+				error("EAP: failed Lightweight rechallenge");
+				eap_send_failure(esp);
+				break;
+			}
+			esp->es_server.ea_state = eapOpen;
+			if (esp->es_lwrechallenge != 0)
+				TIMEOUT(srp_lwrechallenge, esp,
+				    esp->es_lwrechallenge);
+			break;
+		}
+		break;
+#endif /* USE_SRP */
+
+	default:
+		/* This can't happen. */
+		error("EAP: unknown Response type %d; ignored", typenum);
+		return;
+	}
+
+	if (esp->es_server.ea_timeout > 0) {
+		UNTIMEOUT(eap_server_timeout, (void *)esp);
+	}
+
+	if (esp->es_server.ea_state != eapBadAuth &&
+	    esp->es_server.ea_state != eapOpen) {
+		esp->es_server.ea_id++;
+		eap_send_request(esp);
+	}
+}
+
+/*
+ * eap_success - Receive EAP Success message (client mode).
+ */
+static void
+eap_success(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) {
+		dbglog("EAP unexpected success message in state %s (%d)",
+		    eap_state_name(esp->es_client.ea_state),
+		    esp->es_client.ea_state);
+		return;
+	}
+
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+
+	if (len > 0) {
+		/* This is odd.  The spec doesn't allow for this. */
+		PRINTMSG(inp, len);
+	}
+
+	esp->es_client.ea_state = eapOpen;
+	auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
+}
+
+/*
+ * eap_failure - Receive EAP Failure message (client mode).
+ */
+static void
+eap_failure(esp, inp, id, len)
+eap_state *esp;
+u_char *inp;
+int id;
+int len;
+{
+	if (!eap_client_active(esp)) {
+		dbglog("EAP unexpected failure message in state %s (%d)",
+		    eap_state_name(esp->es_client.ea_state),
+		    esp->es_client.ea_state);
+	}
+
+	if (esp->es_client.ea_timeout > 0) {
+		UNTIMEOUT(eap_client_timeout, (void *)esp);
+	}
+
+	if (len > 0) {
+		/* This is odd.  The spec doesn't allow for this. */
+		PRINTMSG(inp, len);
+	}
+
+	esp->es_client.ea_state = eapBadAuth;
+
+	error("EAP: peer reports authentication failure");
+	auth_withpeer_fail(esp->es_unit, PPP_EAP);
+}
+
+/*
+ * eap_input - Handle received EAP message.
+ */
+static void
+eap_input(unit, inp, inlen)
+int unit;
+u_char *inp;
+int inlen;
+{
+	eap_state *esp = &eap_states[unit];
+	u_char code, id;
+	int len;
+
+	/*
+	 * Parse header (code, id and length).  If packet too short,
+	 * drop it.
+	 */
+	if (inlen < EAP_HEADERLEN) {
+		error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN);
+		return;
+	}
+	GETCHAR(code, inp);
+	GETCHAR(id, inp);
+	GETSHORT(len, inp);
+	if (len < EAP_HEADERLEN || len > inlen) {
+		error("EAP: packet has illegal length field %d (%d..%d)", len,
+		    EAP_HEADERLEN, inlen);
+		return;
+	}
+	len -= EAP_HEADERLEN;
+
+	/* Dispatch based on message code */
+	switch (code) {
+	case EAP_REQUEST:
+		eap_request(esp, inp, id, len);
+		break;
+
+	case EAP_RESPONSE:
+		eap_response(esp, inp, id, len);
+		break;
+
+	case EAP_SUCCESS:
+		eap_success(esp, inp, id, len);
+		break;
+
+	case EAP_FAILURE:
+		eap_failure(esp, inp, id, len);
+		break;
+
+	default:				/* XXX Need code reject */
+		/* Note: it's not legal to send EAP Nak here. */
+		warn("EAP: unknown code %d received", code);
+		break;
+	}
+}
+
+/*
+ * eap_printpkt - print the contents of an EAP packet.
+ */
+static char *eap_codenames[] = {
+	"Request", "Response", "Success", "Failure"
+};
+
+static char *eap_typenames[] = {
+	"Identity", "Notification", "Nak", "MD5-Challenge",
+	"OTP", "Generic-Token", NULL, NULL,
+	"RSA", "DSS", "KEA", "KEA-Validate",
+	"TLS", "Defender", "Windows 2000", "Arcot",
+	"Cisco", "Nokia", "SRP"
+};
+
+static int
+eap_printpkt(inp, inlen, printer, arg)
+u_char *inp;
+int inlen;
+void (*printer) __P((void *, char *, ...));
+void *arg;
+{
+	int code, id, len, rtype, vallen;
+	u_char *pstart;
+	u_int32_t uval;
+
+	if (inlen < EAP_HEADERLEN)
+		return (0);
+	pstart = inp;
+	GETCHAR(code, inp);
+	GETCHAR(id, inp);
+	GETSHORT(len, inp);
+	if (len < EAP_HEADERLEN || len > inlen)
+		return (0);
+
+	if (code >= 1 && code <= sizeof(eap_codenames) / sizeof(char *))
+		printer(arg, " %s", eap_codenames[code-1]);
+	else
+		printer(arg, " code=0x%x", code);
+	printer(arg, " id=0x%x", id);
+	len -= EAP_HEADERLEN;
+	switch (code) {
+	case EAP_REQUEST:
+		if (len < 1) {
+			printer(arg, " <missing type>");
+			break;
+		}
+		GETCHAR(rtype, inp);
+		len--;
+		if (rtype >= 1 &&
+		    rtype <= sizeof (eap_typenames) / sizeof (char *))
+			printer(arg, " %s", eap_typenames[rtype-1]);
+		else
+			printer(arg, " type=0x%x", rtype);
+		switch (rtype) {
+		case EAPT_IDENTITY:
+		case EAPT_NOTIFICATION:
+			if (len > 0) {
+				printer(arg, " <Message ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			} else {
+				printer(arg, " <No message>");
+			}
+			break;
+
+		case EAPT_MD5CHAP:
+			if (len <= 0)
+				break;
+			GETCHAR(vallen, inp);
+			len--;
+			if (vallen > len)
+				goto truncated;
+			printer(arg, " <Value%.*B>", vallen, inp);
+			INCPTR(vallen, inp);
+			len -= vallen;
+			if (len > 0) {
+				printer(arg, " <Name ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			} else {
+				printer(arg, " <No name>");
+			}
+			break;
+
+		case EAPT_SRP:
+			if (len < 3)
+				goto truncated;
+			GETCHAR(vallen, inp);
+			len--;
+			printer(arg, "-%d", vallen);
+			switch (vallen) {
+			case EAPSRP_CHALLENGE:
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len)
+					goto truncated;
+				if (vallen > 0) {
+					printer(arg, " <Name ");
+					print_string((char *)inp, vallen, printer,
+					    arg);
+					printer(arg, ">");
+				} else {
+					printer(arg, " <No name>");
+				}
+				INCPTR(vallen, inp);
+				len -= vallen;
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen >= len)
+					goto truncated;
+				printer(arg, " <s%.*B>", vallen, inp);
+				INCPTR(vallen, inp);
+				len -= vallen;
+				GETCHAR(vallen, inp);
+				len--;
+				if (vallen > len)
+					goto truncated;
+				if (vallen == 0) {
+					printer(arg, " <Default g=2>");
+				} else {
+					printer(arg, " <g%.*B>", vallen, inp);
+				}
+				INCPTR(vallen, inp);
+				len -= vallen;
+				if (len == 0) {
+					printer(arg, " <Default N>");
+				} else {
+					printer(arg, " <N%.*B>", len, inp);
+					INCPTR(len, inp);
+					len = 0;
+				}
+				break;
+
+			case EAPSRP_SKEY:
+				printer(arg, " <B%.*B>", len, inp);
+				INCPTR(len, inp);
+				len = 0;
+				break;
+
+			case EAPSRP_SVALIDATOR:
+				if (len < sizeof (u_int32_t))
+					break;
+				GETLONG(uval, inp);
+				len -= sizeof (u_int32_t);
+				if (uval & SRPVAL_EBIT) {
+					printer(arg, " E");
+					uval &= ~SRPVAL_EBIT;
+				}
+				if (uval != 0) {
+					printer(arg, " f<%X>", uval);
+				}
+				if ((vallen = len) > SHA_DIGESTSIZE)
+					vallen = SHA_DIGESTSIZE;
+				printer(arg, " <M2%.*B%s>", len, inp,
+				    len < SHA_DIGESTSIZE ? "?" : "");
+				INCPTR(vallen, inp);
+				len -= vallen;
+				if (len > 0) {
+					printer(arg, " <PN%.*B>", len, inp);
+					INCPTR(len, inp);
+					len = 0;
+				}
+				break;
+
+			case EAPSRP_LWRECHALLENGE:
+				printer(arg, " <Challenge%.*B>", len, inp);
+				INCPTR(len, inp);
+				len = 0;
+				break;
+			}
+			break;
+		}
+		break;
+
+	case EAP_RESPONSE:
+		if (len < 1)
+			break;
+		GETCHAR(rtype, inp);
+		len--;
+		if (rtype >= 1 &&
+		    rtype <= sizeof (eap_typenames) / sizeof (char *))
+			printer(arg, " %s", eap_typenames[rtype-1]);
+		else
+			printer(arg, " type=0x%x", rtype);
+		switch (rtype) {
+		case EAPT_IDENTITY:
+			if (len > 0) {
+				printer(arg, " <Name ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			}
+			break;
+
+		case EAPT_NAK:
+			if (len <= 0) {
+				printer(arg, " <missing hint>");
+				break;
+			}
+			GETCHAR(rtype, inp);
+			len--;
+			printer(arg, " <Suggested-type %02X", rtype);
+			if (rtype >= 1 &&
+			    rtype < sizeof (eap_typenames) / sizeof (char *))
+				printer(arg, " (%s)", eap_typenames[rtype-1]);
+			printer(arg, ">");
+			break;
+
+		case EAPT_MD5CHAP:
+			if (len <= 0) {
+				printer(arg, " <missing length>");
+				break;
+			}
+			GETCHAR(vallen, inp);
+			len--;
+			if (vallen > len)
+				goto truncated;
+			printer(arg, " <Value%.*B>", vallen, inp);
+			INCPTR(vallen, inp);
+			len -= vallen;
+			if (len > 0) {
+				printer(arg, " <Name ");
+				print_string((char *)inp, len, printer, arg);
+				printer(arg, ">");
+				INCPTR(len, inp);
+				len = 0;
+			} else {
+				printer(arg, " <No name>");
+			}
+			break;
+
+		case EAPT_SRP:
+			if (len < 1)
+				goto truncated;
+			GETCHAR(vallen, inp);
+			len--;
+			printer(arg, "-%d", vallen);
+			switch (vallen) {
+			case EAPSRP_CKEY:
+				printer(arg, " <A%.*B>", len, inp);
+				INCPTR(len, inp);
+				len = 0;
+				break;
+
+			case EAPSRP_CVALIDATOR:
+				if (len < sizeof (u_int32_t))
+					break;
+				GETLONG(uval, inp);
+				len -= sizeof (u_int32_t);
+				if (uval & SRPVAL_EBIT) {
+					printer(arg, " E");
+					uval &= ~SRPVAL_EBIT;
+				}
+				if (uval != 0) {
+					printer(arg, " f<%X>", uval);
+				}
+				printer(arg, " <M1%.*B%s>", len, inp,
+				    len == SHA_DIGESTSIZE ? "" : "?");
+				INCPTR(len, inp);
+				len = 0;
+				break;
+
+			case EAPSRP_ACK:
+				break;
+
+			case EAPSRP_LWRECHALLENGE:
+				printer(arg, " <Response%.*B%s>", len, inp,
+				    len == SHA_DIGESTSIZE ? "" : "?");
+				if ((vallen = len) > SHA_DIGESTSIZE)
+					vallen = SHA_DIGESTSIZE;
+				INCPTR(vallen, inp);
+				len -= vallen;
+				break;
+			}
+			break;
+		}
+		break;
+
+	case EAP_SUCCESS:	/* No payload expected for these! */
+	case EAP_FAILURE:
+		break;
+
+	truncated:
+		printer(arg, " <truncated>");
+		break;
+	}
+
+	if (len > 8)
+		printer(arg, "%8B...", inp);
+	else if (len > 0)
+		printer(arg, "%.*B", len, inp);
+	INCPTR(len, inp);
+
+	return (inp - pstart);
+}
diff -Naur tomato/release/src/router/pppd/pppd/eap.h tomato121/release/src/router/pppd/pppd/eap.h
--- tomato/release/src/router/pppd/pppd/eap.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/eap.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,158 @@
+/*
+ * eap.h - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $
+ */
+
+#ifndef PPP_EAP_H
+#define	PPP_EAP_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define	EAP_HEADERLEN	4
+
+
+/* EAP message codes. */
+#define	EAP_REQUEST	1
+#define	EAP_RESPONSE	2
+#define	EAP_SUCCESS	3
+#define	EAP_FAILURE	4
+
+/* EAP types */
+#define	EAPT_IDENTITY		1
+#define	EAPT_NOTIFICATION	2
+#define	EAPT_NAK		3	/* (response only) */
+#define	EAPT_MD5CHAP		4
+#define	EAPT_OTP		5	/* One-Time Password; RFC 1938 */
+#define	EAPT_TOKEN		6	/* Generic Token Card */
+/* 7 and 8 are unassigned. */
+#define	EAPT_RSA		9	/* RSA Public Key Authentication */
+#define	EAPT_DSS		10	/* DSS Unilateral */
+#define	EAPT_KEA		11	/* KEA */
+#define	EAPT_KEA_VALIDATE	12	/* KEA-VALIDATE	*/
+#define	EAPT_TLS		13	/* EAP-TLS */
+#define	EAPT_DEFENDER		14	/* Defender Token (AXENT) */
+#define	EAPT_W2K		15	/* Windows 2000 EAP */
+#define	EAPT_ARCOT		16	/* Arcot Systems */
+#define	EAPT_CISCOWIRELESS	17	/* Cisco Wireless */
+#define	EAPT_NOKIACARD		18	/* Nokia IP smart card */
+#define	EAPT_SRP		19	/* Secure Remote Password */
+/* 20 is deprecated */
+
+/* EAP SRP-SHA1 Subtypes */
+#define	EAPSRP_CHALLENGE	1	/* Request 1 - Challenge */
+#define	EAPSRP_CKEY		1	/* Response 1 - Client Key */
+#define	EAPSRP_SKEY		2	/* Request 2 - Server Key */
+#define	EAPSRP_CVALIDATOR	2	/* Response 2 - Client Validator */
+#define	EAPSRP_SVALIDATOR	3	/* Request 3 - Server Validator */
+#define	EAPSRP_ACK		3	/* Response 3 - final ack */
+#define	EAPSRP_LWRECHALLENGE	4	/* Req/resp 4 - Lightweight rechal */
+
+#define	SRPVAL_EBIT	0x00000001	/* Use shared key for ECP */
+
+#define	SRP_PSEUDO_ID	"pseudo_"
+#define	SRP_PSEUDO_LEN	7
+
+#define MD5_SIGNATURE_SIZE	16
+#define MIN_CHALLENGE_LENGTH	16
+#define MAX_CHALLENGE_LENGTH	24
+
+enum eap_state_code {
+	eapInitial = 0,	/* No EAP authentication yet requested */
+	eapPending,	/* Waiting for LCP (no timer) */
+	eapClosed,	/* Authentication not in use */
+	eapListen,	/* Client ready (and timer running) */
+	eapIdentify,	/* EAP Identify sent */
+	eapSRP1,	/* Sent EAP SRP-SHA1 Subtype 1 */
+	eapSRP2,	/* Sent EAP SRP-SHA1 Subtype 2 */
+	eapSRP3,	/* Sent EAP SRP-SHA1 Subtype 3 */
+	eapMD5Chall,	/* Sent MD5-Challenge */
+	eapOpen,	/* Completed authentication */
+	eapSRP4,	/* Sent EAP SRP-SHA1 Subtype 4 */
+	eapBadAuth	/* Failed authentication */
+};
+
+#define	EAP_STATES	\
+	"Initial", "Pending", "Closed", "Listen", "Identify", \
+	"SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth"
+
+#define	eap_client_active(esp)	((esp)->es_client.ea_state == eapListen)
+#define	eap_server_active(esp)	\
+	((esp)->es_server.ea_state >= eapIdentify && \
+	 (esp)->es_server.ea_state <= eapMD5Chall)
+
+struct eap_auth {
+	char *ea_name;		/* Our name */
+	char *ea_peer;		/* Peer's name */
+	void *ea_session;	/* Authentication library linkage */
+	u_char *ea_skey;	/* Shared encryption key */
+	int ea_timeout;		/* Time to wait (for retransmit/fail) */
+	int ea_maxrequests;	/* Max Requests allowed */
+	u_short ea_namelen;	/* Length of our name */
+	u_short ea_peerlen;	/* Length of peer's name */
+	enum eap_state_code ea_state;
+	u_char ea_id;		/* Current id */
+	u_char ea_requests;	/* Number of Requests sent/received */
+	u_char ea_responses;	/* Number of Responses */
+	u_char ea_type;		/* One of EAPT_* */
+	u_int32_t ea_keyflags;	/* SRP shared key usage flags */
+};
+
+/*
+ * Complete EAP state for one PPP session.
+ */
+typedef struct eap_state {
+	int es_unit;			/* Interface unit number */
+	struct eap_auth es_client;	/* Client (authenticatee) data */
+	struct eap_auth es_server;	/* Server (authenticator) data */
+	int es_savedtime;		/* Saved timeout */
+	int es_rechallenge;		/* EAP rechallenge interval */
+	int es_lwrechallenge;		/* SRP lightweight rechallenge inter */
+	bool es_usepseudo;		/* Use SRP Pseudonym if offered one */
+	int es_usedpseudo;		/* Set if we already sent PN */
+	int es_challen;			/* Length of challenge string */
+	u_char es_challenge[MAX_CHALLENGE_LENGTH];
+} eap_state;
+
+/*
+ * Timeouts.
+ */
+#define	EAP_DEFTIMEOUT		3	/* Timeout (seconds) for rexmit */
+#define	EAP_DEFTRANSMITS	10	/* max # times to transmit */
+#define	EAP_DEFREQTIME		20	/* Time to wait for peer request */
+#define	EAP_DEFALLOWREQ		20	/* max # times to accept requests */
+
+extern eap_state eap_states[];
+
+void eap_authwithpeer __P((int unit, char *localname));
+void eap_authpeer __P((int unit, char *localname));
+
+extern struct protent eap_protent;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* PPP_EAP_H */
+
diff -Naur tomato/release/src/router/pppd/pppd/ecp.c tomato121/release/src/router/pppd/pppd/ecp.c
--- tomato/release/src/router/pppd/pppd/ecp.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ecp.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,173 @@
+/*
+ * ecp.c - PPP Encryption Control Protocol.
+ *
+ * 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
+ * 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.
+ *
+ * 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 ccp.c, which is:
+ *
+ * Copyright (c) 1994-2002 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. 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.
+ */
+
+#define RCSID	"$Id: ecp.c,v 1.4 2004/11/04 10:02:26 paulus Exp $"
+
+static const char rcsid[] = RCSID;
+
+#include <string.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ecp.h"
+
+static option_t ecp_option_list[] = {
+    { "noecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation" },
+    { "-ecp", o_bool, &ecp_protent.enabled_flag,
+      "Disable ECP negotiation", OPT_ALIAS },
+
+    { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ecp_init __P((int unit));
+/*
+static void ecp_open __P((int unit));
+static void ecp_close __P((int unit, char *));
+static void ecp_lowerup __P((int unit));
+static void ecp_lowerdown __P((int));
+static void ecp_input __P((int unit, u_char *pkt, int len));
+static void ecp_protrej __P((int unit));
+*/
+static int  ecp_printpkt __P((u_char *pkt, int len,
+			      void (*printer) __P((void *, char *, ...)),
+			      void *arg));
+/*
+static void ecp_datainput __P((int unit, u_char *pkt, int len));
+*/
+
+struct protent ecp_protent = {
+    PPP_ECP,
+    ecp_init,
+    NULL, /* ecp_input, */
+    NULL, /* ecp_protrej, */
+    NULL, /* ecp_lowerup, */
+    NULL, /* ecp_lowerdown, */
+    NULL, /* ecp_open, */
+    NULL, /* ecp_close, */
+    ecp_printpkt,
+    NULL, /* ecp_datainput, */
+    0,
+    "ECP",
+    "Encrypted",
+    ecp_option_list,
+    NULL,
+    NULL,
+    NULL
+};
+
+fsm ecp_fsm[NUM_PPP];
+ecp_options ecp_wantoptions[NUM_PPP];	/* what to request the peer to use */
+ecp_options ecp_gotoptions[NUM_PPP];	/* what the peer agreed to do */
+ecp_options ecp_allowoptions[NUM_PPP];	/* what we'll agree to do */
+ecp_options ecp_hisoptions[NUM_PPP];	/* what we agreed to do */
+
+static fsm_callbacks ecp_callbacks = {
+    NULL, /* ecp_resetci, */
+    NULL, /* ecp_cilen, */
+    NULL, /* ecp_addci, */
+    NULL, /* ecp_ackci, */
+    NULL, /* ecp_nakci, */
+    NULL, /* ecp_rejci, */
+    NULL, /* ecp_reqci, */
+    NULL, /* ecp_up, */
+    NULL, /* ecp_down, */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL, /* ecp_extcode, */
+    "ECP"
+};
+
+/*
+ * ecp_init - initialize ECP.
+ */
+static void
+ecp_init(unit)
+    int unit;
+{
+    fsm *f = &ecp_fsm[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_ECP;
+    f->callbacks = &ecp_callbacks;
+    fsm_init(f);
+
+    memset(&ecp_wantoptions[unit],  0, sizeof(ecp_options));
+    memset(&ecp_gotoptions[unit],   0, sizeof(ecp_options));
+    memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options));
+    memset(&ecp_hisoptions[unit],   0, sizeof(ecp_options));
+
+}
+
+
+static int
+ecp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    return 0;
+}
+
diff -Naur tomato/release/src/router/pppd/pppd/ecp.h tomato121/release/src/router/pppd/pppd/ecp.h
--- tomato/release/src/router/pppd/pppd/ecp.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ecp.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,45 @@
+/*
+ * ecp.h - Definitions for PPP Encryption Control Protocol.
+ *
+ * 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
+ * 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.
+ *
+ * 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.
+ *
+ * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $
+ */
+
+typedef struct ecp_options {
+    bool required;		/* Is ECP required? */
+    unsigned enctype;		/* Encryption type */
+} ecp_options;
+
+extern fsm ecp_fsm[];
+extern ecp_options ecp_wantoptions[];
+extern ecp_options ecp_gotoptions[];
+extern ecp_options ecp_allowoptions[];
+extern ecp_options ecp_hisoptions[];
+
+extern struct protent ecp_protent;
diff -Naur tomato/release/src/router/pppd/pppd/eui64.c tomato121/release/src/router/pppd/pppd/eui64.c
--- tomato/release/src/router/pppd/pppd/eui64.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/eui64.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,24 +1,41 @@
 /*
-    eui64.c - EUI64 routines for IPv6CP.
-    Copyright (C) 1999  Tommi Komulainen <Tommi.Komulainen@iki.fi>
-
-    Redistribution and use in source and binary forms are permitted
-    provided that the above copyright notice and this paragraph are
-    duplicated in all such forms and that any documentation,
-    advertising materials, and other materials related to such
-    distribution and use acknowledge that the software was developed
-    by Tommi Komulainen.  The name of the author may not be used
-    to endorse or promote products derived from this software without
-    specific prior written permission.
-    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-    WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-
-    $Id: eui64.c,v 1.1 2003/07/10 07:43:04 honor Exp $
-*/
+ * eui64.c - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  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 Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * 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.
+ *
+ * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $
+ */
 
-#define RCSID	"$Id: eui64.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $"
 
 #include "pppd.h"
 
diff -Naur tomato/release/src/router/pppd/pppd/eui64.h tomato121/release/src/router/pppd/pppd/eui64.h
--- tomato/release/src/router/pppd/pppd/eui64.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/eui64.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,21 +1,38 @@
 /*
-    eui64.h - EUI64 routines for IPv6CP.
-    Copyright (C) 1999  Tommi Komulainen <Tommi.Komulainen@iki.fi>
-
-    Redistribution and use in source and binary forms are permitted
-    provided that the above copyright notice and this paragraph are
-    duplicated in all such forms and that any documentation,
-    advertising materials, and other materials related to such
-    distribution and use acknowledge that the software was developed
-    by Tommi Komulainen.  The name of the author may not be used
-    to endorse or promote products derived from this software without
-    specific prior written permission.
-    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-    WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-    
-    $Id: eui64.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * eui64.h - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  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 Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * 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.
+ *
+ * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $
 */
 
 #ifndef __EUI64_H__
diff -Naur tomato/release/src/router/pppd/pppd/fsm.c tomato121/release/src/router/pppd/pppd/fsm.c
--- tomato/release/src/router/pppd/pppd/fsm.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/fsm.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,46 @@
 /*
  * fsm.c - {Link, IP} Control Protocol Finite State Machine.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: fsm.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: fsm.c,v 1.23 2004/11/13 02:28:15 paulus Exp $"
 
 /*
  * TODO:
@@ -178,6 +201,44 @@
     }
 }
 
+/*
+ * terminate_layer - Start process of shutting down the FSM
+ *
+ * Cancel any timeout running, notify upper layers we're done, and
+ * send a terminate-request message as configured.
+ */
+static void
+terminate_layer(f, nextstate)
+    fsm *f;
+    int nextstate;
+{
+    if( f->state != OPENED )
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+    else if( f->callbacks->down )
+	(*f->callbacks->down)(f);	/* Inform upper layers we're down */
+
+    /* Init restart counter and send Terminate-Request */
+    f->retransmits = f->maxtermtransmits;
+    fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+	      (u_char *) f->term_reason, f->term_reason_len);
+
+    if (f->retransmits == 0) {
+	/*
+	 * User asked for no terminate requests at all; just close it.
+	 * We've already fired off one Terminate-Request just to be nice
+	 * to the peer, but we're not going to wait for a reply.
+	 */
+	f->state = nextstate == CLOSING ? CLOSED : STOPPED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	return;
+    }
+
+    TIMEOUT(fsm_timeout, f, f->timeouttime);
+    --f->retransmits;
+
+    f->state = nextstate;
+}
 
 /*
  * fsm_close - Start closing connection.
@@ -207,19 +268,7 @@
     case ACKRCVD:
     case ACKSENT:
     case OPENED:
-	if( f->state != OPENED )
-	    UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
-	else if( f->callbacks->down )
-	    (*f->callbacks->down)(f);	/* Inform upper layers we're down */
-
-	/* Init restart counter, send Terminate-Request */
-	f->retransmits = f->maxtermtransmits;
-	fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
-		  (u_char *) f->term_reason, f->term_reason_len);
-	TIMEOUT(fsm_timeout, f, f->timeouttime);
-	--f->retransmits;
-
-	f->state = CLOSING;
+	terminate_layer(f, CLOSING);
 	break;
     }
 }
@@ -383,6 +432,7 @@
 	if( f->callbacks->down )
 	    (*f->callbacks->down)(f);	/* Inform upper layers */
 	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
 	break;
 
     case STOPPED:
@@ -446,6 +496,7 @@
 	return;
     }
     f->seen_ack = 1;
+    f->rnakloops = 0;
 
     switch (f->state) {
     case CLOSED:
@@ -494,17 +545,29 @@
     u_char *inp;
     int len;
 {
-    int (*proc) __P((fsm *, u_char *, int));
     int ret;
+    int treat_as_reject;
 
     if (id != f->reqid || f->seen_ack)	/* Expected id? */
 	return;				/* Nope, toss... */
-    proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
-    if (!proc || !(ret = proc(f, inp, len))) {
-	/* Nak/reject is bad - ignore it */
-	error("Received bad configure-nak/rej: %P", inp, len);
-	return;
+
+    if (code == CONFNAK) {
+	++f->rnakloops;
+	treat_as_reject = (f->rnakloops >= f->maxnakloops);
+	if (f->callbacks->nakci == NULL
+	    || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
+	    error("Received bad configure-nak: %P", inp, len);
+	    return;
+	}
+    } else {
+	f->rnakloops = 0;
+	if (f->callbacks->rejci == NULL
+	    || !(ret = f->callbacks->rejci(f, inp, len))) {
+	    error("Received bad configure-rej: %P", inp, len);
+	    return;
+	}
     }
+
     f->seen_ack = 1;
 
     switch (f->state) {
@@ -562,10 +625,10 @@
 	    info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
 	} else
 	    info("%s terminated by peer", PROTO_NAME(f));
-	if (f->callbacks->down)
-	    (*f->callbacks->down)(f);	/* Inform upper layers */
 	f->retransmits = 0;
 	f->state = STOPPING;
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
 	TIMEOUT(fsm_timeout, f, f->timeouttime);
 	break;
     }
@@ -603,6 +666,7 @@
 	if (f->callbacks->down)
 	    (*f->callbacks->down)(f);	/* Inform upper layers */
 	fsm_sconfreq(f, 0);
+	f->state = REQSENT;
 	break;
     }
 }
@@ -664,17 +728,7 @@
 	break;
 
     case OPENED:
-	if( f->callbacks->down )
-	    (*f->callbacks->down)(f);
-
-	/* Init restart counter, send Terminate-Request */
-	f->retransmits = f->maxtermtransmits;
-	fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
-		  (u_char *) f->term_reason, f->term_reason_len);
-	TIMEOUT(fsm_timeout, f, f->timeouttime);
-	--f->retransmits;
-
-	f->state = STOPPING;
+	terminate_layer(f, STOPPING);
 	break;
 
     default:
@@ -700,6 +754,7 @@
 	if( f->callbacks->resetci )
 	    (*f->callbacks->resetci)(f);
 	f->nakloops = 0;
+	f->rnakloops = 0;
     }
 
     if( !retransmit ){
diff -Naur tomato/release/src/router/pppd/pppd/fsm.h tomato121/release/src/router/pppd/pppd/fsm.h
--- tomato/release/src/router/pppd/pppd/fsm.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/fsm.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,45 @@
 /*
  * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: fsm.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: fsm.h,v 1.10 2004/11/13 02:28:15 paulus Exp $
  */
 
 /*
@@ -53,6 +76,7 @@
     int retransmits;		/* Number of retransmissions left */
     int maxtermtransmits;	/* Maximum Terminate-Request transmissions */
     int nakloops;		/* Number of nak loops since last ack */
+    int rnakloops;		/* Number of naks received */
     int maxnakloops;		/* Maximum number of nak loops tolerated */
     struct fsm_callbacks *callbacks;	/* Callback routines */
     char *term_reason;		/* Reason for closing protocol */
@@ -70,7 +94,7 @@
     int  (*ackci)		/* ACK our Configuration Information */
 		__P((fsm *, u_char *, int));
     int  (*nakci)		/* NAK our Configuration Information */
-		__P((fsm *, u_char *, int));
+		__P((fsm *, u_char *, int, int));
     int  (*rejci)		/* Reject our Configuration Information */
 		__P((fsm *, u_char *, int));
     int  (*reqci)		/* Request peer's Configuration Information */
diff -Naur tomato/release/src/router/pppd/pppd/ipcp.c tomato121/release/src/router/pppd/pppd/ipcp.c
--- tomato/release/src/router/pppd/pppd/ipcp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ipcp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,46 @@
 /*
  * ipcp.c - PPP IP Control Protocol.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: ipcp.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: ipcp.c,v 1.70 2005/08/25 23:59:34 paulus Exp $"
 
 /*
  * TODO:
@@ -59,11 +82,16 @@
 /* Hook for a plugin to choose the remote IP address */
 void (*ip_choose_hook) __P((u_int32_t *)) = NULL;
 
+/* Notifiers for when IPCP goes up and down */
+struct notifier *ip_up_notifier = NULL;
+struct notifier *ip_down_notifier = NULL;
+
 /* local vars */
 static int default_route_set[NUM_PPP];	/* Have set up a default route */
 static int proxy_arp_set[NUM_PPP];	/* Have created proxy arp entry */
 static bool usepeerdns;			/* Ask peer for DNS addrs */
 static int ipcp_is_up;			/* have called np_up() */
+static int ipcp_is_open;		/* haven't called np_finished() */
 static bool ask_for_local;		/* request our address from peer */
 static char vj_value[8];		/* string form of vj option value */
 static char netmask_str[20];		/* string form of netmask value */
@@ -75,7 +103,7 @@
 static int  ipcp_cilen __P((fsm *));	        /* Return length of our CI */
 static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
 static int  ipcp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
-static int  ipcp_nakci __P((fsm *, u_char *, int));	/* Peer nak'd our CI */
+static int  ipcp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */
 static int  ipcp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
 static int  ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
 static void ipcp_up __P((fsm *));		/* We're UP */
@@ -109,7 +137,7 @@
 static int setdnsaddr __P((char **));
 static int setwinsaddr __P((char **));
 static int setnetmask __P((char **));
-static int setipaddr __P((char *, char **, int));
+int setipaddr __P((char *, char **, int));
 static void printipaddr __P((option_t *, void (*)(void *, char *,...),void *));
 
 static option_t ipcp_option_list[] = {
@@ -143,6 +171,12 @@
     { "ipparam", o_string, &ipparam,
       "Set ip script parameter", OPT_PRIO },
 
+    { "ip-up-script", o_string, &ipupcustom,
+      "Specify custom ip-up script", OPT_PRIO },
+
+    { "ip-down-script", o_string, &ipdowncustom,
+      "Specify custom ip-down script", OPT_PRIO },
+
     { "noipdefault", o_bool, &disable_defaultip,
       "Don't use name for default IP adrs", 1 },
 
@@ -184,6 +218,13 @@
     { "netmask", o_special, (void *)setnetmask,
       "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
 
+    { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs,
+      "Disable old-style IP-Addresses usage", OPT_A2CLR,
+      &ipcp_allowoptions[0].old_addrs },
+    { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr,
+      "Disable IP-Address usage", OPT_A2CLR,
+      &ipcp_allowoptions[0].neg_addr },
+
     { "IP addresses", o_wild, (void *) &setipaddr,
       "set local and remote IP addresses",
       OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr },
@@ -229,7 +270,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 *));
 
 /*
@@ -367,8 +408,9 @@
  * setipaddr - Set the IP address
  * If doit is 0, the call is to check whether this option is
  * potentially an IP address specification.
+ * Not static so that plugins can call it to set the addresses
  */
-static int
+int
 setipaddr(arg, argv, doit)
     char *arg;
     char **argv;
@@ -534,7 +576,7 @@
     memset(wo, 0, sizeof(*wo));
     memset(ao, 0, sizeof(*ao));
 
-    wo->neg_addr = 1;
+    wo->neg_addr = wo->old_addrs = 1;
     wo->neg_vj = 1;
     wo->vj_protocol = IPCP_VJ_COMP;
     wo->maxslotindex = MAX_STATES - 1; /* really max index */
@@ -545,7 +587,7 @@
     /* ppp_if.c to 16 and 1, this needs to be changed (among other */
     /* things) gmc */
 
-    ao->neg_addr = 1;
+    ao->neg_addr = ao->old_addrs = 1;
     ao->neg_vj = 1;
     ao->maxslotindex = MAX_STATES - 1;
     ao->cflag = 1;
@@ -567,6 +609,7 @@
     int unit;
 {
     fsm_open(&ipcp_fsm[unit]);
+    ipcp_is_open = 1;
 }
 
 
@@ -640,8 +683,10 @@
 {
     ipcp_options *wo = &ipcp_wantoptions[f->unit];
     ipcp_options *go = &ipcp_gotoptions[f->unit];
+    ipcp_options *ao = &ipcp_allowoptions[f->unit];
 
-    wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+    wo->req_addr = (wo->neg_addr || wo->old_addrs) &&
+	(ao->neg_addr || ao->old_addrs);
     if (wo->ouraddr == 0)
 	wo->accept_local = 1;
     if (wo->hisaddr == 0)
@@ -651,8 +696,13 @@
     *go = *wo;
     if (!ask_for_local)
 	go->ouraddr = 0;
-    if (ip_choose_hook)
+    if (ip_choose_hook) {
 	ip_choose_hook(&wo->hisaddr);
+	if (wo->hisaddr) {
+	    wo->accept_remote = 0;
+	}
+    }
+    BZERO(&ipcp_hisoptions[f->unit], sizeof(ipcp_options));
 }
 
 
@@ -668,19 +718,17 @@
     ipcp_options *wo = &ipcp_wantoptions[f->unit];
     ipcp_options *ho = &ipcp_hisoptions[f->unit];
 
+#define LENCIADDRS(neg)		(neg ? CILEN_ADDRS : 0)
 #define LENCIVJ(neg, old)	(neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
-#define LENCIADDR(neg, old)	(neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+#define LENCIADDR(neg)		(neg ? CILEN_ADDR : 0)
 #define LENCIDNS(neg)		(neg ? (CILEN_ADDR) : 0)
 
     /*
      * First see if we want to change our options to the old
      * forms because we have received old forms from the peer.
      */
-    if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
-	/* use the old style of address negotiation */
-	go->neg_addr = 1;
-	go->old_addrs = 1;
-    }
+    if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs)
+	go->neg_addr = 0;
     if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
 	/* try an older style of VJ negotiation */
 	/* use the old style only if the peer did */
@@ -691,8 +739,9 @@
 	}
     }
 
-    return (LENCIADDR(go->neg_addr, go->old_addrs) +
+    return (LENCIADDRS(!go->neg_addr && go->old_addrs) +
 	    LENCIVJ(go->neg_vj, go->old_vj) +
+	    LENCIADDR(go->neg_addr) +
 	    LENCIDNS(go->req_dns1) +
 	    LENCIDNS(go->req_dns2)) ;
 }
@@ -711,6 +760,21 @@
     ipcp_options *go = &ipcp_gotoptions[f->unit];
     int len = *lenp;
 
+#define ADDCIADDRS(opt, neg, val1, val2) \
+    if (neg) { \
+	if (len >= CILEN_ADDRS) { \
+	    u_int32_t l; \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_ADDRS, ucp); \
+	    l = ntohl(val1); \
+	    PUTLONG(l, ucp); \
+	    l = ntohl(val2); \
+	    PUTLONG(l, ucp); \
+	    len -= CILEN_ADDRS; \
+	} else \
+	    go->old_addrs = 0; \
+    }
+
 #define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
     if (neg) { \
 	int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
@@ -727,20 +791,15 @@
 	    neg = 0; \
     }
 
-#define ADDCIADDR(opt, neg, old, val1, val2) \
+#define ADDCIADDR(opt, neg, val) \
     if (neg) { \
-	int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
-	if (len >= addrlen) { \
+	if (len >= CILEN_ADDR) { \
 	    u_int32_t l; \
 	    PUTCHAR(opt, ucp); \
-	    PUTCHAR(addrlen, ucp); \
-	    l = ntohl(val1); \
+	    PUTCHAR(CILEN_ADDR, ucp); \
+	    l = ntohl(val); \
 	    PUTLONG(l, ucp); \
-	    if (old) { \
-		l = ntohl(val2); \
-		PUTLONG(l, ucp); \
-	    } \
-	    len -= addrlen; \
+	    len -= CILEN_ADDR; \
 	} else \
 	    neg = 0; \
     }
@@ -758,12 +817,14 @@
 	    neg = 0; \
     }
 
-    ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
-	      go->old_addrs, go->ouraddr, go->hisaddr);
+    ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+	       go->hisaddr);
 
     ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
 	    go->maxslotindex, go->cflag);
 
+    ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
     ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
 
     ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
@@ -797,6 +858,26 @@
      * If we find any deviations, then this packet is bad.
      */
 
+#define ACKCIADDRS(opt, neg, val1, val2) \
+    if (neg) { \
+	u_int32_t l; \
+	if ((len -= CILEN_ADDRS) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_ADDRS || \
+	    citype != opt) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (val1 != cilong) \
+	    goto bad; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	if (val2 != cilong) \
+	    goto bad; \
+    }
+
 #define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
     if (neg) { \
 	int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
@@ -820,27 +901,20 @@
 	} \
     }
 
-#define ACKCIADDR(opt, neg, old, val1, val2) \
+#define ACKCIADDR(opt, neg, val) \
     if (neg) { \
-	int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
 	u_int32_t l; \
-	if ((len -= addrlen) < 0) \
+	if ((len -= CILEN_ADDR) < 0) \
 	    goto bad; \
 	GETCHAR(citype, p); \
 	GETCHAR(cilen, p); \
-	if (cilen != addrlen || \
+	if (cilen != CILEN_ADDR || \
 	    citype != opt) \
 	    goto bad; \
 	GETLONG(l, p); \
 	cilong = htonl(l); \
-	if (val1 != cilong) \
+	if (val != cilong) \
 	    goto bad; \
-	if (old) { \
-	    GETLONG(l, p); \
-	    cilong = htonl(l); \
-	    if (val2 != cilong) \
-		goto bad; \
-	} \
     }
 
 #define ACKCIDNS(opt, neg, addr) \
@@ -858,12 +932,14 @@
 	    goto bad; \
     }
 
-    ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
-	      go->old_addrs, go->ouraddr, go->hisaddr);
+    ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+	       go->hisaddr);
 
     ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
 	    go->maxslotindex, go->cflag);
 
+    ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
     ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
 
     ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
@@ -891,10 +967,11 @@
  *	1 - Nak was good.
  */
 static int
-ipcp_nakci(f, p, len)
+ipcp_nakci(f, p, len, treat_as_reject)
     fsm *f;
     u_char *p;
     int len;
+    int treat_as_reject;
 {
     ipcp_options *go = &ipcp_gotoptions[f->unit];
     u_char cimaxslotindex, cicflag;
@@ -912,22 +989,18 @@
      * Check packet length and CI length at each step.
      * If we find any deviations, then this packet is bad.
      */
-#define NAKCIADDR(opt, neg, old, code) \
-    if (go->neg && \
-	len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
-	p[1] == cilen && \
+#define NAKCIADDRS(opt, neg, code) \
+    if ((neg) && \
+	(cilen = p[1]) == CILEN_ADDRS && \
+	len >= cilen && \
 	p[0] == opt) { \
 	len -= cilen; \
 	INCPTR(2, p); \
 	GETLONG(l, p); \
 	ciaddr1 = htonl(l); \
-	if (old) { \
-	    GETLONG(l, p); \
-	    ciaddr2 = htonl(l); \
-	    no.old_addrs = 1; \
-	} else \
-	    ciaddr2 = 0; \
-	no.neg = 1; \
+	GETLONG(l, p); \
+	ciaddr2 = htonl(l); \
+	no.old_addrs = 1; \
 	code \
     }
 
@@ -943,6 +1016,19 @@
         code \
     }
 
+#define NAKCIADDR(opt, neg, code) \
+    if (go->neg && \
+	(cilen = p[1]) == CILEN_ADDR && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETLONG(l, p); \
+	ciaddr1 = htonl(l); \
+	no.neg = 1; \
+	code \
+    }
+
 #define NAKCIDNS(opt, neg, code) \
     if (go->neg && \
 	((cilen = p[1]) == CILEN_ADDR) && \
@@ -960,14 +1046,20 @@
      * Accept the peer's idea of {our,his} address, if different
      * from our idea, only if the accept_{local,remote} flag is set.
      */
-    NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
-	      if (go->accept_local && ciaddr1) { /* Do we know our address? */
-		  try.ouraddr = ciaddr1;
-	      }
-	      if (go->accept_remote && ciaddr2) { /* Does he know his? */
-		  try.hisaddr = ciaddr2;
-	      }
-	      );
+    NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+	       if (treat_as_reject) {
+		   try.old_addrs = 0;
+	       } else {
+		   if (go->accept_local && ciaddr1) {
+		       /* take his idea of our address */
+		       try.ouraddr = ciaddr1;
+		   }
+		   if (go->accept_remote && ciaddr2) {
+		       /* take his idea of his address */
+		       try.hisaddr = ciaddr2;
+		   }
+	       }
+	);
 
     /*
      * Accept the peer's value of maxslotindex provided that it
@@ -976,7 +1068,9 @@
      * the peer wants.
      */
     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
-	    if (cilen == CILEN_VJ) {
+	    if (treat_as_reject) {
+		try.neg_vj = 0;
+	    } else if (cilen == CILEN_VJ) {
 		GETCHAR(cimaxslotindex, p);
 		GETCHAR(cicflag, p);
 		if (cishort == IPCP_VJ_COMP) {
@@ -998,13 +1092,31 @@
 	    }
 	    );
 
+    NAKCIADDR(CI_ADDR, neg_addr,
+	      if (treat_as_reject) {
+		  try.neg_addr = 0;
+		  try.old_addrs = 0;
+	      } else if (go->accept_local && ciaddr1) {
+		  /* take his idea of our address */
+		  try.ouraddr = ciaddr1;
+	      }
+	      );
+
     NAKCIDNS(CI_MS_DNS1, req_dns1,
-	    try.dnsaddr[0] = cidnsaddr;
-	    );
+	     if (treat_as_reject) {
+		 try.req_dns1 = 0;
+	     } else {
+		 try.dnsaddr[0] = cidnsaddr;
+	     }
+	     );
 
     NAKCIDNS(CI_MS_DNS2, req_dns2,
-	    try.dnsaddr[1] = cidnsaddr;
-	    );
+	     if (treat_as_reject) {
+		 try.req_dns2 = 0;
+	     } else {
+		 try.dnsaddr[1] = cidnsaddr;
+	     }
+	     );
 
     /*
      * There may be remaining CIs, if the peer is requesting negotiation
@@ -1012,10 +1124,10 @@
      * If they want to negotiate about IP addresses, we comply.
      * If they want us to ask for compression, we refuse.
      */
-    while (len > CILEN_VOID) {
+    while (len >= CILEN_VOID) {
 	GETCHAR(citype, p);
 	GETCHAR(cilen, p);
-	if( (len -= cilen) < 0 )
+	if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
 	    goto bad;
 	next = p + cilen - 2;
 
@@ -1027,11 +1139,10 @@
 	    no.neg_vj = 1;
 	    break;
 	case CI_ADDRS:
-	    if ((go->neg_addr && go->old_addrs) || no.old_addrs
+	    if ((!go->neg_addr && go->old_addrs) || no.old_addrs
 		|| cilen != CILEN_ADDRS)
 		goto bad;
-	    try.neg_addr = 1;
-	    try.old_addrs = 1;
+	    try.neg_addr = 0;
 	    GETLONG(l, p);
 	    ciaddr1 = htonl(l);
 	    if (ciaddr1 && go->accept_local)
@@ -1095,10 +1206,10 @@
      * Check packet length and CI length at each step.
      * If we find any deviations, then this packet is bad.
      */
-#define REJCIADDR(opt, neg, old, val1, val2) \
-    if (go->neg && \
-	len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
-	p[1] == cilen && \
+#define REJCIADDRS(opt, neg, val1, val2) \
+    if ((neg) && \
+	(cilen = p[1]) == CILEN_ADDRS && \
+	len >= cilen && \
 	p[0] == opt) { \
 	u_int32_t l; \
 	len -= cilen; \
@@ -1108,14 +1219,12 @@
 	/* Check rejected value. */ \
 	if (cilong != val1) \
 	    goto bad; \
-	if (old) { \
-	    GETLONG(l, p); \
-	    cilong = htonl(l); \
-	    /* Check rejected value. */ \
-	    if (cilong != val2) \
-		goto bad; \
-	} \
-	try.neg = 0; \
+	GETLONG(l, p); \
+	cilong = htonl(l); \
+	/* Check rejected value. */ \
+	if (cilong != val2) \
+	    goto bad; \
+	try.old_addrs = 0; \
     }
 
 #define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
@@ -1140,6 +1249,22 @@
 	try.neg = 0; \
      }
 
+#define REJCIADDR(opt, neg, val) \
+    if (go->neg && \
+	(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 != val) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
 #define REJCIDNS(opt, neg, dnsaddr) \
     if (go->neg && \
 	((cilen = p[1]) == CILEN_ADDR) && \
@@ -1157,12 +1282,14 @@
     }
 
 
-    REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
-	      go->old_addrs, go->ouraddr, go->hisaddr);
+    REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+	       go->ouraddr, go->hisaddr);
 
     REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
 	    go->maxslotindex, go->cflag);
 
+    REJCIADDR(CI_ADDR, neg_addr, go->ouraddr);
+
     REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
 
     REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
@@ -1203,7 +1330,6 @@
     ipcp_options *wo = &ipcp_wantoptions[f->unit];
     ipcp_options *ho = &ipcp_hisoptions[f->unit];
     ipcp_options *ao = &ipcp_allowoptions[f->unit];
-    ipcp_options *go = &ipcp_gotoptions[f->unit];
     u_char *cip, *next;		/* Pointer to current and next CIs */
     u_short cilen, citype;	/* Parsed len, type */
     u_short cishort;		/* Parsed short value */
@@ -1244,7 +1370,7 @@
 
 	switch (citype) {		/* Check CI type */
 	case CI_ADDRS:
-	    if (!ao->neg_addr ||
+	    if (!ao->old_addrs || ho->neg_addr ||
 		cilen != CILEN_ADDRS) {	/* Check CI length */
 		orc = CONFREJ;		/* Reject CI */
 		break;
@@ -1290,18 +1416,17 @@
 			PUTLONG(tl, p);
 		    }
 		} else {
-		    go->ouraddr = ciaddr2;	/* accept peer's idea */
+		    wo->ouraddr = ciaddr2;	/* accept peer's idea */
 		}
 	    }
 
-	    ho->neg_addr = 1;
 	    ho->old_addrs = 1;
 	    ho->hisaddr = ciaddr1;
 	    ho->ouraddr = ciaddr2;
 	    break;
 
 	case CI_ADDR:
-	    if (!ao->neg_addr ||
+	    if (!ao->neg_addr || ho->old_addrs ||
 		cilen != CILEN_ADDR) {	/* Check CI length */
 		orc = CONFREJ;		/* Reject CI */
 		break;
@@ -1461,7 +1586,7 @@
      * input buffer is long enough that we can append the extra
      * option safely.
      */
-    if (rc != CONFREJ && !ho->neg_addr &&
+    if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs &&
 	wo->req_addr && !reject_if_disagree) {
 	if (rc == CONFACK) {
 	    rc = CONFNAK;
@@ -1535,6 +1660,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))
@@ -1572,9 +1698,15 @@
     /*
      * We must have a non-zero IP address for both ends of the link.
      */
-    if (!ho->neg_addr)
+    if (!ho->neg_addr && !ho->old_addrs)
 	ho->hisaddr = wo->hisaddr;
 
+    if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs)
+	&& wo->ouraddr != 0) {
+	error("Peer refused to agree to our IP address");
+	ipcp_close(f->unit, "Refused our IP address");
+	return;
+    }
     if (go->ouraddr == 0) {
 	error("Could not determine local IP address");
 	ipcp_close(f->unit, "Could not determine local IP address");
@@ -1588,12 +1720,12 @@
     script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
     script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
 
+    if (go->dnsaddr[0])
+	script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
+    if (go->dnsaddr[1])
+	script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
     if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
 	script_setenv("USEPEERDNS", "1", 0);
-	if (go->dnsaddr[0])
-	    script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
-	if (go->dnsaddr[1])
-	    script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
 	create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
     }
 
@@ -1634,7 +1766,9 @@
 	    mask = GetMask(go->ouraddr);
 	    if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
 		if (debug)
+		{
 		    warn("Interface configuration failed");
+		}
 		ipcp_close(f->unit, "Interface configuration failed");
 		return;
 	    }
@@ -1662,16 +1796,23 @@
 #if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
 	if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
 	    if (debug)
+	    {
 		warn("Interface configuration failed");
+	    }
 	    ipcp_close(f->unit, "Interface configuration failed");
 	    return;
 	}
 #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)
+	    {
 		warn("Interface failed to come up");
+	    }
 	    ipcp_close(f->unit, "Interface configuration failed");
 	    return;
 	}
@@ -1679,7 +1820,9 @@
 #if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
 	if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
 	    if (debug)
+	    {
 		warn("Interface configuration failed");
+	    }
 	    ipcp_close(f->unit, "Interface configuration failed");
 	    return;
 	}
@@ -1706,9 +1849,12 @@
 	    notice("secondary DNS address %I", go->dnsaddr[1]);
     }
 
+    reset_link_stats(f->unit);
+
     np_up(f->unit, PPP_IP);
     ipcp_is_up = 1;
 
+    notify(ip_up_notifier, 0);
     if (ip_up_hook)
 	ip_up_hook();
 
@@ -1718,7 +1864,7 @@
      */
     if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
 	ipcp_script_state = s_up;
-	ipcp_script(_PATH_IPUP);
+	ipcp_script(ipupcustom ? ipupcustom : _PATH_IPUP, 0);
     }
 }
 
@@ -1736,7 +1882,10 @@
     IPCPDEBUG(("ipcp: down"));
     /* XXX a bit IPv4-centric here, we only need to get the stats
      * before the interface is marked down. */
+    /* XXX more correct: we must get the stats before running the notifiers,
+     * at least for the radius plugin */
     update_link_stats(f->unit);
+    notify(ip_down_notifier, 0);
     if (ip_down_hook)
 	ip_down_hook();
     if (ipcp_is_up) {
@@ -1745,6 +1894,10 @@
     }
     sifvjcomp(f->unit, 0, 0, 0);
 
+    print_link_stats(); /* _after_ running the notifiers and ip_down_hook(),
+			 * because print_link_stats() sets link_stats_valid
+			 * to 0 (zero) */
+
     /*
      * If we are doing dial-on-demand, set the interface
      * to queue up outgoing packets (for now).
@@ -1761,7 +1914,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(ipdowncustom ? ipdowncustom : _PATH_IPDOWN, 0);
     }
 }
 
@@ -1795,7 +1948,10 @@
 ipcp_finished(f)
     fsm *f;
 {
-    np_finished(f->unit, PPP_IP);
+	if (ipcp_is_open) {
+		ipcp_is_open = 0;
+		np_finished(f->unit, PPP_IP);
+	}
 }
 
 
@@ -1812,13 +1968,13 @@
     case s_up:
 	if (ipcp_fsm[0].state != OPENED) {
 	    ipcp_script_state = s_down;
-	    ipcp_script(_PATH_IPDOWN);
+	    ipcp_script(ipdowncustom ? ipdowncustom : _PATH_IPDOWN, 0);
 	}
 	break;
     case s_down:
 	if (ipcp_fsm[0].state == OPENED) {
 	    ipcp_script_state = s_up;
-	    ipcp_script(_PATH_IPUP);
+	    ipcp_script(ipupcustom ? ipupcustom : _PATH_IPUP, 0);
 	}
 	break;
     }
@@ -1830,8 +1986,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];
@@ -1848,7 +2005,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);
 }
 
 /*
@@ -2012,7 +2173,9 @@
  */
 #define IP_HDRLEN	20	/* bytes */
 #define IP_OFFMASK	0x1fff
+#ifndef IPPROTO_TCP
 #define IPPROTO_TCP	6
+#endif
 #define TCP_HDRLEN	20
 #define TH_FIN		0x01
 
diff -Naur tomato/release/src/router/pppd/pppd/ipcp.h tomato121/release/src/router/pppd/pppd/ipcp.h
--- tomato/release/src/router/pppd/pppd/ipcp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ipcp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,45 @@
 /*
  * ipcp.h - IP Control Protocol definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ipcp.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/pppd/ipv6cp.c tomato121/release/src/router/pppd/pppd/ipv6cp.c
--- tomato/release/src/router/pppd/pppd/ipv6cp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ipv6cp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,16 +1,38 @@
 /*
-    ipv6cp.c - PPP IPV6 Control Protocol.
-    Copyright (C) 1999  Tommi Komulainen <Tommi.Komulainen@iki.fi>
-
-    Redistribution and use in source and binary forms are permitted
-    provided that the above copyright notice and this paragraph are
-    duplicated in all such forms.  The name of the author may not be
-    used to endorse or promote products derived from this software
-    without specific prior written permission.
-    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-    WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-*/
+ * ipv6cp.c - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  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 Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * 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.
+ *
+ */
 
 /*  Original version, based on RFC2023 :
 
@@ -75,25 +97,48 @@
  *
  * ipcp.c - PPP IP Control Protocol.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * 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
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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/)."
  *
- * $Id: ipv6cp.c,v 1.1 2003/07/10 07:43:04 honor Exp $ 
+ * 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.
+ *
+ * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ 
  */
 
-#define RCSID	"$Id: ipv6cp.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $"
 
 /*
  * TODO: 
@@ -141,7 +186,7 @@
 static int  ipv6cp_cilen __P((fsm *));	        /* Return length of our CI */
 static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
 static int  ipv6cp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
-static int  ipv6cp_nakci __P((fsm *, u_char *, int));	/* Peer nak'd our CI */
+static int  ipv6cp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */
 static int  ipv6cp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
 static int  ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
 static void ipv6cp_up __P((fsm *));		/* We're UP */
@@ -193,7 +238,7 @@
     { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
       "Use (default) IPv4 address as interface identifier", 1 },
 
-#if defined(SOL2)
+#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) */
@@ -330,6 +375,8 @@
     return 1;
 }
 
+char *llv6_ntoa(eui64_t ifaceid);
+
 static void
 printifaceid(opt, printer, arg)
     option_t *opt;
@@ -623,10 +670,11 @@
  *	1 - Nak was good.
  */
 static int
-ipv6cp_nakci(f, p, len)
+ipv6cp_nakci(f, p, len, treat_as_reject)
     fsm *f;
     u_char *p;
     int len;
+    int treat_as_reject;
 {
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
     u_char citype, cilen, *next;
@@ -672,19 +720,21 @@
      * from our idea, only if the accept_{local,remote} flag is set.
      */
     NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
-	      if (go->accept_local) {
-		  while (eui64_iszero(ifaceid) || 
-			 eui64_equals(ifaceid, go->hisid)) /* bad luck */
-		      eui64_magic(ifaceid);
-		  try.ourid = ifaceid;
-		  IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
-	      }
-	      );
+		 if (treat_as_reject) {
+		     try.neg_ifaceid = 0;
+		 } else if (go->accept_local) {
+		     while (eui64_iszero(ifaceid) || 
+			    eui64_equals(ifaceid, go->hisid)) /* bad luck */
+			 eui64_magic(ifaceid);
+		     try.ourid = ifaceid;
+		     IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+		 }
+		 );
 
 #ifdef IPV6CP_COMP
     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
 	    {
-		if (cishort == IPV6CP_COMP) {
+		if (cishort == IPV6CP_COMP && !treat_as_reject) {
 		    try.vj_protocol = cishort;
 		} else {
 		    try.neg_vj = 0;
@@ -705,10 +755,10 @@
      * If they want to negotiate about interface identifier, we comply.
      * If they want us to ask for compression, we refuse.
      */
-    while (len > CILEN_VOID) {
+    while (len >= CILEN_VOID) {
 	GETCHAR(citype, p);
 	GETCHAR(cilen, p);
-	if( (len -= cilen) < 0 )
+	if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
 	    goto bad;
 	next = p + cilen - 2;
 
@@ -1027,7 +1077,7 @@
     if (!ipv6cp_protent.enabled_flag)
 	return;
 
-#if defined(SOL2)
+#if defined(SOL2) || defined(__linux__)
     /*
      * Persistent link-local id is only used when user has not explicitly
      * configure/hard-code the id
@@ -1369,7 +1419,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);
 }
 
 /*
@@ -1475,7 +1526,6 @@
  */
 #define IP6_HDRLEN	40	/* bytes */
 #define IP6_NHDR_FRAG	44	/* fragment IPv6 header */
-#define IPPROTO_TCP	6
 #define TCP_HDRLEN	20
 #define TH_FIN		0x01
 
diff -Naur tomato/release/src/router/pppd/pppd/ipv6cp.h tomato121/release/src/router/pppd/pppd/ipv6cp.h
--- tomato/release/src/router/pppd/pppd/ipv6cp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ipv6cp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,16 +1,38 @@
 /*
-    ipv6cp.h - PPP IPV6 Control Protocol.
-    Copyright (C) 1999  Tommi Komulainen <Tommi.Komulainen@iki.fi>
-
-    Redistribution and use in source and binary forms are permitted
-    provided that the above copyright notice and this paragraph are
-    duplicated in all such forms.  The name of the author may not be
-    used to endorse or promote products derived from this software
-    without specific prior written permission.
-    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-    WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-*/
+ * ipv6cp.h - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  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 Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * 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.
+ *
+ */
 
 /*  Original version, based on RFC2023 :
 
@@ -75,22 +97,45 @@
  *
  * ipcp.h - IP Control Protocol definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * 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/)."
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  *
- * $Id: ipv6cp.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $
  */
 
 /*
@@ -109,7 +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)
+#if defined(SOL2) || defined(__linux__)
     int use_persistent;		/* use uniquely persistent value for address */
 #endif /* defined(SOL2) */
     int neg_vj;			/* Van Jacobson Compression? */
diff -Naur tomato/release/src/router/pppd/pppd/ipxcp.c tomato121/release/src/router/pppd/pppd/ipxcp.c
--- tomato/release/src/router/pppd/pppd/ipxcp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ipxcp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,25 +1,48 @@
 /*
  * ipxcp.c - PPP IPX Control Protocol.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
 #ifdef IPX_CHANGE
 
-#define RCSID	"$Id: ipxcp.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: ipxcp.c,v 1.24 2005/08/25 23:59:34 paulus Exp $"
 
 /*
  * TODO:
@@ -59,7 +82,7 @@
 static int  ipxcp_cilen __P((fsm *));		/* Return length of our CI */
 static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
 static int  ipxcp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
-static int  ipxcp_nakci __P((fsm *, u_char *, int));	/* Peer nak'd our CI */
+static int  ipxcp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */
 static int  ipxcp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
 static int  ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
 static void ipxcp_up __P((fsm *));		/* We're UP */
@@ -740,10 +763,11 @@
  */
 
 static int
-ipxcp_nakci(f, p, len)
+ipxcp_nakci(f, p, len, treat_as_reject)
     fsm *f;
     u_char *p;
     int len;
+    int treat_as_reject;
 {
     u_char citype, cilen, *next;
     u_short s;
@@ -754,11 +778,11 @@
     BZERO(&no, sizeof(no));
     try = *go;
 
-    while (len > CILEN_VOID) {
+    while (len >= CILEN_VOID) {
 	GETCHAR (citype, p);
 	GETCHAR (cilen,	 p);
 	len -= cilen;
-	if (len < 0)
+	if (cilen < CILEN_VOID || len < 0)
 	    goto bad;
 	next = &p [cilen - CILEN_VOID];
 
@@ -769,7 +793,9 @@
 	    no.neg_nn = 1;
 
 	    GETLONG(l, p);
-	    if (l && ao->accept_network)
+	    if (treat_as_reject)
+		try.neg_nn = 0;
+	    else if (l && ao->accept_network)
 		try.our_network = l;
 	    break;
 
@@ -778,8 +804,10 @@
 		goto bad;
 	    no.neg_node = 1;
 
-	    if (!zero_node (p) && ao->accept_local &&
-		! compare_node (p, ho->his_node))
+	    if (treat_as_reject)
+		try.neg_node = 0;
+	    else if (!zero_node (p) && ao->accept_local &&
+		     ! compare_node (p, ho->his_node))
 		copy_node (p, try.our_node);
 	    break;
 
@@ -1309,7 +1337,9 @@
     /* bring the interface up */
     if (!sifup(unit)) {
 	if (debug)
+	{
 	    warn("sifup failed (IPX)");
+	}
 	ipxcp_close(unit, "Interface configuration failed");
 	return;
     }
@@ -1318,7 +1348,9 @@
     /* set the network number for IPX */
     if (!sipxfaddr(unit, go->network, go->our_node)) {
 	if (debug)
+	{
 	    warn("sipxfaddr failed");
+	}
 	ipxcp_close(unit, "Interface configuration failed");
 	return;
     }
@@ -1430,7 +1462,7 @@
     argv[11] = ipparam;
     argv[12] = strpid;
     argv[13] = NULL;
-    run_program(script, argv, 0, NULL, NULL);
+    run_program(script, argv, 0, NULL, NULL, 0);
 }
 
 /*
diff -Naur tomato/release/src/router/pppd/pppd/ipxcp.h tomato121/release/src/router/pppd/pppd/ipxcp.h
--- tomato/release/src/router/pppd/pppd/ipxcp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ipxcp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,45 @@
 /*
  * ipxcp.h - IPX Control Protocol definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ipxcp.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: ipxcp.h,v 1.5 2002/12/04 23:03:32 paulus Exp $
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/pppd/lcp.c tomato121/release/src/router/pppd/pppd/lcp.c
--- tomato/release/src/router/pppd/pppd/lcp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/lcp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,46 @@
 /*
  * lcp.c - PPP Link Control Protocol.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: lcp.c,v 1.3 2004/07/07 07:48:54 honor Exp $"
+#define RCSID	"$Id: lcp.c,v 1.76 2006/05/22 00:04:07 paulus Exp $"
 
 /*
  * TODO:
@@ -30,7 +53,7 @@
 #include "pppd.h"
 #include "fsm.h"
 #include "lcp.h"
-#include "chap.h"
+#include "chap-new.h"
 #include "magic.h"
 
 static const char rcsid[] = RCSID;
@@ -183,7 +206,7 @@
 static int  lcp_cilen __P((fsm *));		/* Return length of our CI */
 static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
 static int  lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
-static int  lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int  lcp_nakci __P((fsm *, u_char *, int, int)); /* Peer nak'd our CI */
 static int  lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
 static int  lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
 static void lcp_up __P((fsm *));		/* We're UP */
@@ -327,7 +350,6 @@
     wo->neg_mru = 1;
     wo->mru = DEFMRU;
     wo->neg_asyncmap = 1;
-    wo->chap_mdtype = CHAP_DIGEST_MD5;
     wo->neg_magicnumber = 1;
     wo->neg_pcompression = 1;
     wo->neg_accompression = 1;
@@ -337,14 +359,12 @@
     ao->mru = MAXMRU;
     ao->neg_asyncmap = 1;
     ao->neg_chap = 1;
-    ao->chap_mdtype = CHAP_DIGEST_MD5;
+    ao->chap_mdtype = chap_mdtype_all;
     ao->neg_upap = 1;
+    ao->neg_eap = 1;
     ao->neg_magicnumber = 1;
     ao->neg_pcompression = 1;
     ao->neg_accompression = 1;
-#ifdef CBCP_SUPPORT
-    ao->neg_cbcp = 1;
-#endif
     ao->neg_endpoint = 1;
 }
 
@@ -378,7 +398,7 @@
 {
     fsm *f = &lcp_fsm[unit];
 
-    if (phase != PHASE_DEAD)
+    if (phase != PHASE_DEAD && phase != PHASE_MASTER)
 	new_phase(PHASE_TERMINATE);
     if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
 	/*
@@ -391,7 +411,7 @@
 	lcp_finished(f);
 
     } else
-	fsm_close(&lcp_fsm[unit], reason);
+	fsm_close(f, reason);
 }
 
 
@@ -410,9 +430,10 @@
      * but accept A/C and protocol compressed packets
      * if we are going to ask for A/C and protocol compression.
      */
-    ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0);
-    ppp_recv_config(unit, PPP_MRU, (lax_recv? 0: 0xffffffff),
-		    wo->neg_pcompression, wo->neg_accompression);
+    if (ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0) < 0
+	|| ppp_recv_config(unit, PPP_MRU, (lax_recv? 0: 0xffffffff),
+			   wo->neg_pcompression, wo->neg_accompression) < 0)
+	    return;
     peer_mru[unit] = PPP_MRU;
 
     if (listen_time != 0) {
@@ -473,7 +494,6 @@
     fsm_input(f, p, len);
 }
 
-
 /*
  * lcp_extcode - Handle a LCP-specific code.
  */
@@ -504,6 +524,8 @@
 	break;
 
     case DISCREQ:
+    case IDENTIF:
+    case TIMEREM:
 	break;
 
     default:
@@ -527,6 +549,7 @@
     int i;
     struct protent *protp;
     u_short prot;
+    const char *pname;
 
     if (len < 2) {
 	LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
@@ -544,16 +567,35 @@
 	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);
+    }
 }
 
 
@@ -636,13 +678,15 @@
 #define LENCILQR(neg)	((neg) ? CILEN_LQR: 0)
 #define LENCICBCP(neg)	((neg) ? CILEN_CBCP: 0)
     /*
-     * NB: we only ask for one of CHAP and UPAP, even if we will
-     * accept either.
+     * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will
+     * accept more than one.  We prefer EAP first, then CHAP, then
+     * PAP.
      */
     return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) +
 	    LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
-	    LENCICHAP(go->neg_chap) +
-	    LENCISHORT(!go->neg_chap && go->neg_upap) +
+	    LENCISHORT(go->neg_eap) +
+	    LENCICHAP(!go->neg_eap && go->neg_chap) +
+	    LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) +
 	    LENCILQR(go->neg_lqr) +
 	    LENCICBCP(go->neg_cbcp) +
 	    LENCILONG(go->neg_magicnumber) +
@@ -677,12 +721,12 @@
 	PUTCHAR(CILEN_SHORT, ucp); \
 	PUTSHORT(val, ucp); \
     }
-#define ADDCICHAP(opt, neg, val, digest) \
+#define ADDCICHAP(opt, neg, val) \
     if (neg) { \
-	PUTCHAR(opt, ucp); \
+	PUTCHAR((opt), ucp); \
 	PUTCHAR(CILEN_CHAP, ucp); \
-	PUTSHORT(val, ucp); \
-	PUTCHAR(digest, ucp); \
+	PUTSHORT(PPP_CHAP, ucp); \
+	PUTCHAR((CHAP_DIGEST(val)), ucp); \
     }
 #define ADDCILONG(opt, neg, val) \
     if (neg) { \
@@ -716,8 +760,10 @@
     ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
     ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
 	      go->asyncmap);
-    ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
-    ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+    ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+    ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+    ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap,
+	       PPP_PAP);
     ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
     ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
     ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
@@ -795,20 +841,20 @@
 	if (cichar != val) \
 	    goto bad; \
     }
-#define ACKCICHAP(opt, neg, val, digest) \
+#define ACKCICHAP(opt, neg, val) \
     if (neg) { \
 	if ((len -= CILEN_CHAP) < 0) \
 	    goto bad; \
 	GETCHAR(citype, p); \
 	GETCHAR(cilen, p); \
 	if (cilen != CILEN_CHAP || \
-	    citype != opt) \
+	    citype != (opt)) \
 	    goto bad; \
 	GETSHORT(cishort, p); \
-	if (cishort != val) \
+	if (cishort != PPP_CHAP) \
 	    goto bad; \
 	GETCHAR(cichar, p); \
-	if (cichar != digest) \
+	if (cichar != (CHAP_DIGEST(val))) \
 	  goto bad; \
     }
 #define ACKCILONG(opt, neg, val) \
@@ -863,8 +909,10 @@
     ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
     ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
 	      go->asyncmap);
-    ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
-    ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+    ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+    ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+    ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap,
+	       PPP_PAP);
     ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
     ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
     ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
@@ -897,10 +945,11 @@
  *	1 - Nak was good.
  */
 static int
-lcp_nakci(f, p, len)
+lcp_nakci(f, p, len, treat_as_reject)
     fsm *f;
     u_char *p;
     int len;
+    int treat_as_reject;
 {
     lcp_options *go = &lcp_gotoptions[f->unit];
     lcp_options *wo = &lcp_wantoptions[f->unit];
@@ -1000,6 +1049,10 @@
     }
 
     /*
+     * NOTE!  There must be no assignments to individual fields of *go in
+     * the code below.  Any such assignment is a BUG!
+     */
+    /*
      * We don't care if they want to send us smaller packets than
      * we want.  Therefore, accept any MRU less than what we asked for,
      * but then ignore the new value when setting the MRU in the kernel.
@@ -1027,41 +1080,60 @@
      * they are proposing a different protocol, or a different
      * hash algorithm for CHAP.
      */
-    if ((go->neg_chap || go->neg_upap)
+    if ((go->neg_chap || go->neg_upap || go->neg_eap)
 	&& len >= CILEN_SHORT
 	&& p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
 	cilen = p[1];
 	len -= cilen;
 	no.neg_chap = go->neg_chap;
 	no.neg_upap = go->neg_upap;
+	no.neg_eap = go->neg_eap;
 	INCPTR(2, p);
-        GETSHORT(cishort, p);
+	GETSHORT(cishort, p);
 	if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+	    /* If we were asking for EAP, then we need to stop that. */
+	    if (go->neg_eap)
+		try.neg_eap = 0;
+
+	    /* If we were asking for CHAP, then we need to stop that. */
+	    else if (go->neg_chap)
+		try.neg_chap = 0;
 	    /*
-	     * If we were asking for CHAP, they obviously don't want to do it.
-	     * If we weren't asking for CHAP, then we were asking for PAP,
-	     * in which case this Nak is bad.
+	     * If we weren't asking for CHAP or EAP, then we were asking for
+	     * PAP, in which case this Nak is bad.
 	     */
-	    if (!go->neg_chap)
+	    else
 		goto bad;
-	    try.neg_chap = 0;
 
 	} else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
 	    GETCHAR(cichar, p);
-	    if (go->neg_chap) {
+	    /* Stop asking for EAP, if we were. */
+	    if (go->neg_eap) {
+		try.neg_eap = 0;
+		/* Try to set up to use their suggestion, if possible */
+		if (CHAP_CANDIGEST(go->chap_mdtype, cichar))
+		    try.chap_mdtype = CHAP_MDTYPE_D(cichar);
+	    } else if (go->neg_chap) {
 		/*
-		 * We were asking for CHAP/MD5; they must want a different
-		 * algorithm.  If they can't do MD5, we can ask for M$-CHAP
-		 * if we support it, otherwise we'll have to stop
-		 * asking for CHAP.
+		 * We were asking for our preferred algorithm, they must
+		 * want something different.
 		 */
-		if (cichar != go->chap_mdtype) {
-#ifdef CHAPMS
-		    if (cichar == CHAP_MICROSOFT)
-			go->chap_mdtype = CHAP_MICROSOFT;
-		    else
-#endif /* CHAPMS */
-			try.neg_chap = 0;
+		if (cichar != CHAP_DIGEST(go->chap_mdtype)) {
+		    if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) {
+			/* Use their suggestion if we support it ... */
+			try.chap_mdtype = CHAP_MDTYPE_D(cichar);
+		    } else {
+			/* ... otherwise, try our next-preferred algorithm. */
+			try.chap_mdtype &= ~(CHAP_MDTYPE(try.chap_mdtype));
+			if (try.chap_mdtype == MDTYPE_NONE) /* out of algos */
+			    try.neg_chap = 0;
+		    }
+		} else {
+		    /*
+		     * Whoops, they Nak'd our algorithm of choice
+		     * but then suggested it back to us.
+		     */
+		    goto bad;
 		}
 	    } else {
 		/*
@@ -1071,11 +1143,21 @@
 	    }
 
 	} else {
+
+	    /*
+	     * If we were asking for EAP, and they're Conf-Naking EAP,
+	     * well, that's just strange.  Nobody should do that.
+	     */
+	    if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap)
+		dbglog("Unexpected Conf-Nak for EAP");
+
 	    /*
 	     * We don't recognize what they're suggesting.
 	     * Stop asking for what we were asking for.
 	     */
-	    if (go->neg_chap)
+	    if (go->neg_eap)
+		try.neg_eap = 0;
+	    else if (go->neg_chap)
 		try.neg_chap = 0;
 	    else
 		try.neg_upap = 0;
@@ -1124,7 +1206,9 @@
      */
     if (go->neg_mrru) {
 	NAKCISHORT(CI_MRRU, neg_mrru,
-		   if (cishort <= wo->mrru)
+		   if (treat_as_reject)
+		       try.neg_mrru = 0;
+		   else if (cishort <= wo->mrru)
 		       try.mrru = cishort;
 		   );
     }
@@ -1157,7 +1241,7 @@
      * An option we don't recognize represents the peer asking to
      * negotiate some option we don't support, so ignore it.
      */
-    while (len > CILEN_VOID) {
+    while (len >= CILEN_VOID) {
 	GETCHAR(citype, p);
 	GETCHAR(cilen, p);
 	if (cilen < CILEN_VOID || (len -= cilen) < 0)
@@ -1181,7 +1265,8 @@
 		goto bad;
 	    break;
 	case CI_AUTHTYPE:
-	    if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
+	    if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap ||
+		go->neg_eap || no.neg_eap)
 		goto bad;
 	    break;
 	case CI_MAGICNUMBER:
@@ -1228,8 +1313,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;
@@ -1294,7 +1379,7 @@
 	    goto bad; \
 	try.neg = 0; \
     }
-#define REJCICHAP(opt, neg, val, digest) \
+#define REJCICHAP(opt, neg, val) \
     if (go->neg && \
 	len >= CILEN_CHAP && \
 	p[1] == CILEN_CHAP && \
@@ -1304,10 +1389,10 @@
 	GETSHORT(cishort, p); \
 	GETCHAR(cichar, p); \
 	/* Check rejected value. */ \
-	if (cishort != val || cichar != digest) \
+	if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
 	    goto bad; \
 	try.neg = 0; \
-	try.neg_upap = 0; \
+	try.neg_eap = try.neg_upap = 0; \
     }
 #define REJCILONG(opt, neg, val) \
     if (go->neg && \
@@ -1370,9 +1455,12 @@
 
     REJCISHORT(CI_MRU, neg_mru, go->mru);
     REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
-    REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
-    if (!go->neg_chap) {
-	REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+    REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP);
+    if (!go->neg_eap) {
+	REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype);
+	if (!go->neg_chap) {
+	    REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+	}
     }
     REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
     REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
@@ -1509,89 +1597,126 @@
 
 	case CI_AUTHTYPE:
 	    if (cilen < CILEN_SHORT ||
-		!(ao->neg_upap || ao->neg_chap)) {
+		!(ao->neg_upap || ao->neg_chap || ao->neg_eap)) {
 		/*
 		 * Reject the option if we're not willing to authenticate.
 		 */
+		dbglog("No auth is possible");
 		orc = CONFREJ;
 		break;
 	    }
 	    GETSHORT(cishort, p);
 
 	    /*
-	     * Authtype must be PAP or CHAP.
+	     * Authtype must be PAP, CHAP, or EAP.
 	     *
-	     * Note: if both ao->neg_upap and ao->neg_chap are set,
-	     * and the peer sends a Configure-Request with two
-	     * authenticate-protocol requests, one for CHAP and one
-	     * for UPAP, then we will reject the second request.
-	     * Whether we end up doing CHAP or UPAP depends then on
+	     * Note: if more than one of ao->neg_upap, ao->neg_chap, and
+	     * ao->neg_eap are set, and the peer sends a Configure-Request
+	     * with two or more authenticate-protocol requests, then we will
+	     * reject the second request.
+	     * Whether we end up doing CHAP, UPAP, or EAP depends then on
 	     * the ordering of the CIs in the peer's Configure-Request.
-	     */
+             */
 
 	    if (cishort == PPP_PAP) {
-		if (ho->neg_chap ||	/* we've already accepted CHAP */
+		/* we've already accepted CHAP or EAP */
+		if (ho->neg_chap || ho->neg_eap ||
 		    cilen != CILEN_SHORT) {
 		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
 		    orc = CONFREJ;
 		    break;
 		}
 		if (!ao->neg_upap) {	/* we don't want to do PAP */
-		    orc = CONFNAK;	/* NAK it and suggest CHAP */
+		    orc = CONFNAK;	/* NAK it and suggest CHAP or EAP */
 		    PUTCHAR(CI_AUTHTYPE, nakp);
-		    PUTCHAR(CILEN_CHAP, nakp);
-		    PUTSHORT(PPP_CHAP, nakp);
-		    PUTCHAR(ao->chap_mdtype, nakp);
-		    /* XXX if we can do CHAP_MICROSOFT as well, we should
-		       probably put in another option saying so */
+		    if (ao->neg_eap) {
+			PUTCHAR(CILEN_SHORT, nakp);
+			PUTSHORT(PPP_EAP, nakp);
+		    } else {
+			PUTCHAR(CILEN_CHAP, nakp);
+			PUTSHORT(PPP_CHAP, nakp);
+			PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+		    }
 		    break;
 		}
 		ho->neg_upap = 1;
 		break;
 	    }
 	    if (cishort == PPP_CHAP) {
-		if (ho->neg_upap ||	/* we've already accepted PAP */
+		/* we've already accepted PAP or EAP */
+		if (ho->neg_upap || ho->neg_eap ||
 		    cilen != CILEN_CHAP) {
 		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
 		    orc = CONFREJ;
 		    break;
 		}
 		if (!ao->neg_chap) {	/* we don't want to do CHAP */
-		    orc = CONFNAK;	/* NAK it and suggest PAP */
+		    orc = CONFNAK;	/* NAK it and suggest EAP or PAP */
 		    PUTCHAR(CI_AUTHTYPE, nakp);
 		    PUTCHAR(CILEN_SHORT, nakp);
-		    PUTSHORT(PPP_PAP, nakp);
+		    if (ao->neg_eap) {
+			PUTSHORT(PPP_EAP, nakp);
+		    } else {
+			PUTSHORT(PPP_PAP, nakp);
+		    }
 		    break;
 		}
-		GETCHAR(cichar, p);	/* get digest type*/
-		if (cichar != CHAP_DIGEST_MD5
-#ifdef CHAPMS
-		    && cichar != CHAP_MICROSOFT
-#endif
-		    ) {
+		GETCHAR(cichar, p);	/* get digest type */
+		if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) {
+		    /*
+		     * We can't/won't do the requested type,
+		     * suggest something else.
+		     */
 		    orc = CONFNAK;
 		    PUTCHAR(CI_AUTHTYPE, nakp);
 		    PUTCHAR(CILEN_CHAP, nakp);
 		    PUTSHORT(PPP_CHAP, nakp);
-		    PUTCHAR(ao->chap_mdtype, nakp);
+		    PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
 		    break;
 		}
-		ho->chap_mdtype = cichar; /* save md type */
+		ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */
 		ho->neg_chap = 1;
 		break;
 	    }
+	    if (cishort == PPP_EAP) {
+		/* we've already accepted CHAP or PAP */
+		if (ho->neg_chap || ho->neg_upap || cilen != CILEN_SHORT) {
+		    LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting..."));
+		    orc = CONFREJ;
+		    break;
+		}
+		if (!ao->neg_eap) {	/* we don't want to do EAP */
+		    orc = CONFNAK;	/* NAK it and suggest CHAP or PAP */
+		    PUTCHAR(CI_AUTHTYPE, nakp);
+		    if (ao->neg_chap) {
+			PUTCHAR(CILEN_CHAP, nakp);
+			PUTSHORT(PPP_CHAP, nakp);
+			PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
+		    } else {
+			PUTCHAR(CILEN_SHORT, nakp);
+			PUTSHORT(PPP_PAP, nakp);
+		    }
+		    break;
+		}
+		ho->neg_eap = 1;
+		break;
+	    }
 
 	    /*
 	     * We don't recognize the protocol they're asking for.
 	     * Nak it with something we're willing to do.
-	     * (At this point we know ao->neg_upap || ao->neg_chap.)
+	     * (At this point we know ao->neg_upap || ao->neg_chap ||
+	     * ao->neg_eap.)
 	     */
 	    orc = CONFNAK;
 	    PUTCHAR(CI_AUTHTYPE, nakp);
-	    if (ao->neg_chap) {
+	    if (ao->neg_eap) {
+		PUTCHAR(CILEN_SHORT, nakp);
+		PUTSHORT(PPP_EAP, nakp);
+	    } else if (ao->neg_chap) {
 		PUTCHAR(CILEN_CHAP, nakp);
 		PUTSHORT(PPP_CHAP, nakp);
-		PUTCHAR(ao->chap_mdtype, nakp);
+		PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakp);
 	    } else {
 		PUTCHAR(CILEN_SHORT, nakp);
 		PUTSHORT(PPP_PAP, nakp);
@@ -1771,8 +1896,7 @@
     lcp_options *ho = &lcp_hisoptions[f->unit];
     lcp_options *go = &lcp_gotoptions[f->unit];
     lcp_options *ao = &lcp_allowoptions[f->unit];
-    int mtu;
-    char runmtu[10];
+    int mtu, mru;
 
     if (!go->neg_magicnumber)
 	go->magicnumber = 0;
@@ -1785,20 +1909,19 @@
      * set our MRU to the larger of value we wanted and
      * the value we got in the negotiation.
      * Note on the MTU: the link MTU can be the MRU the peer wanted,
-     * the interface MTU is set to the lower of that and the
-     * MTU we want to use.
+     * the interface MTU is set to the lowest of that, the
+     * MTU we want to use, and our link MRU.
      */
-    mtu = MIN(ho->neg_mru? ho->mru: PPP_MRU, ao->mru);
-    sprintf(runmtu, "%d", mtu);	// by honor 20040618
-    script_setenv("MTU", runmtu, 1);
+    mtu = ho->neg_mru? ho->mru: PPP_MRU;
+    mru = go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU;
 #ifdef HAVE_MULTILINK
     if (!(multilink && go->neg_mrru && ho->neg_mrru))
 #endif /* HAVE_MULTILINK */
-	netif_set_mtu(f->unit, mtu);
+	netif_set_mtu(f->unit, MIN(MIN(mtu, mru), ao->mru));
     ppp_send_config(f->unit, mtu,
 		    (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
 		    ho->neg_pcompression, ho->neg_accompression);
-    ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU),
+    ppp_recv_config(f->unit, mru,
 		    (lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff),
 		    go->neg_pcompression, go->neg_accompression);
 
@@ -1862,7 +1985,8 @@
 static char *lcp_codenames[] = {
     "ConfReq", "ConfAck", "ConfNak", "ConfRej",
     "TermReq", "TermAck", "CodeRej", "ProtRej",
-    "EchoReq", "EchoRep", "DiscReq"
+    "EchoReq", "EchoRep", "DiscReq", "Ident",
+    "TimeRem"
 };
 
 static int
@@ -1936,19 +2060,27 @@
 			printer(arg, "chap");
 			if (p < optend) {
 			    switch (*p) {
-			    case CHAP_DIGEST_MD5:
+			    case CHAP_MD5:
 				printer(arg, " MD5");
 				++p;
 				break;
 #ifdef CHAPMS
 			    case CHAP_MICROSOFT:
-				printer(arg, " m$oft");
+				printer(arg, " MS");
+				++p;
+				break;
+
+			    case CHAP_MICROSOFT_V2:
+				printer(arg, " MS-v2");
 				++p;
 				break;
 #endif
 			    }
 			}
 			break;
+		    case PPP_EAP:
+			printer(arg, "eap");
+			break;
 		    default:
 			printer(arg, "0x%x", cishort);
 		    }
@@ -2058,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 */
@@ -2088,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");
     }
 }
 
@@ -2109,7 +2262,9 @@
      * Start the timer for the next interval.
      */
     if (lcp_echo_timer_running)
+    {
 	warn("assertion lcp_echo_timer_running==0 failed");
+    }
     TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
     lcp_echo_timer_running = 1;
 }
diff -Naur tomato/release/src/router/pppd/pppd/lcp.h tomato121/release/src/router/pppd/pppd/lcp.h
--- tomato/release/src/router/pppd/pppd/lcp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/lcp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,27 +1,51 @@
 /*
  * lcp.h - Link Control Protocol definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: lcp.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $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 */
@@ -29,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 */
 
 /*
@@ -54,6 +93,7 @@
     bool neg_asyncmap;		/* Negotiate the async map? */
     bool neg_upap;		/* Ask for UPAP authentication? */
     bool neg_chap;		/* Ask for CHAP authentication? */
+    bool neg_eap;		/* Ask for EAP authentication? */
     bool neg_magicnumber;	/* Ask for magic number? */
     bool neg_pcompression;	/* HDLC Protocol Field Compression? */
     bool neg_accompression;	/* HDLC Address/Control Field Compression? */
@@ -64,7 +104,7 @@
     bool neg_endpoint;		/* negotiate endpoint discriminator */
     int  mru;			/* Value of MRU */
     int	 mrru;			/* Value of MRRU, and multilink enable */
-    u_char chap_mdtype;		/* which MD type (hashing algorithm) */
+    u_char chap_mdtype;		/* which MD types (hashing algorithm) */
     u_int32_t asyncmap;		/* Value of async map */
     u_int32_t magicnumber;
     int  numloops;		/* Number of loops during magic number neg. */
diff -Naur tomato/release/src/router/pppd/pppd/magic.c tomato121/release/src/router/pppd/pppd/magic.c
--- tomato/release/src/router/pppd/pppd/magic.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/magic.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,25 +1,49 @@
 /*
  * magic.c - PPP Magic Number routines.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: magic.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: magic.c,v 1.11 2003/06/11 23:56:26 paulus Exp $"
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/time.h>
@@ -59,6 +83,18 @@
     return (u_int32_t) mrand48();
 }
 
+/*
+ * random_bytes - Fill a buffer with random bytes.
+ */
+void
+random_bytes(unsigned char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; ++i)
+		buf[i] = mrand48() >> 24;
+}
+
 #ifdef NO_DRAND48
 /*
  * Substitute procedures for those systems which don't have
diff -Naur tomato/release/src/router/pppd/pppd/magic.h tomato121/release/src/router/pppd/pppd/magic.h
--- tomato/release/src/router/pppd/pppd/magic.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/magic.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,49 @@
 /*
  * magic.h - PPP Magic Number definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: magic.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * 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.
+ *
+ * $Id: magic.h,v 1.5 2003/06/11 23:56:26 paulus Exp $
  */
 
 void magic_init __P((void));	/* Initialize the magic number generator */
 u_int32_t magic __P((void));	/* Returns the next magic number */
+
+/* Fill buffer with random bytes */
+void random_bytes __P((unsigned char *buf, int len));
diff -Naur tomato/release/src/router/pppd/pppd/main.c tomato121/release/src/router/pppd/pppd/main.c
--- tomato/release/src/router/pppd/pppd/main.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/main.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,72 @@
 /*
  * main.c - Point-to-Point Protocol main module
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
+ *
+ * Copyright (c) 1999-2004 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. 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.
  */
 
-#define RCSID	"$Id: main.c,v 1.2 2004/06/30 10:31:51 honor Exp $"
+#define RCSID	"$Id: main.c,v 1.153 2006/06/04 03:52:50 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -51,10 +100,15 @@
 #include "ipv6cp.h"
 #endif
 #include "upap.h"
-#include "chap.h"
+#include "chap-new.h"
+#include "eap.h"
 #include "ccp.h"
+#include "ecp.h"
 #include "pathnames.h"
+
+#ifdef USE_TDB
 #include "tdb.h"
+#endif
 
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
@@ -85,6 +139,7 @@
 struct notifier *phasechange = NULL;
 struct notifier *exitnotify = NULL;
 struct notifier *sigreceived = NULL;
+struct notifier *fork_notifier = NULL;
 
 int hungup;			/* terminal has been hung up */
 int privileged;			/* we're running as real uid root */
@@ -94,25 +149,37 @@
 int unsuccess;			/* # unsuccessful connection attempts */
 int do_callback;		/* != 0 if we should do callback next */
 int doing_callback;		/* != 0 if we are doing callback */
+int ppp_session_number;		/* Session number, for channels with such a
+				   concept (eg PPPoE) */
+int childwait_done;		/* have timed out waiting for children */
+
+#ifdef USE_TDB
 TDB_CONTEXT *pppdb;		/* database for storing status etc. */
+#endif
+
 char db_key[32];
 
 int (*holdoff_hook) __P((void)) = NULL;
 int (*new_phase_hook) __P((int)) = NULL;
+void (*snoop_recv_hook) __P((unsigned char *p, int len)) = NULL;
+void (*snoop_send_hook) __P((unsigned char *p, int len)) = NULL;
 
 static int conn_running;	/* we have a [dis]connector running */
-static int devfd;		/* fd of underlying device */
-static int fd_ppp = -1;		/* fd for talking PPP */
 static int fd_loop;		/* fd for getting demand-dial packets */
 
+int fd_devnull;			/* fd for /dev/null */
+int devfd = -1;			/* fd of underlying device */
+int fd_ppp = -1;		/* fd for talking PPP */
 int phase;			/* where the link is at */
 int kill_link;
+int asked_to_quit;
 int open_ccp_flag;
 int listen_time;
 int got_sigusr2;
 int got_sigterm;
 int got_sighup;
 
+static sigset_t signals_handled;
 static int waiting;
 static sigjmp_buf sigjmp;
 
@@ -134,10 +201,16 @@
 
 static struct timeval start_time;	/* Time when link was started. */
 
+static struct pppd_stats old_link_stats;
 struct pppd_stats link_stats;
-int link_connect_time;
+unsigned link_connect_time;
 int link_stats_valid;
 
+int error_count;
+
+bool bundle_eof;
+bool bundle_terminating;
+
 /*
  * We maintain a list of child process pids and
  * functions to call when they exit.
@@ -155,8 +228,8 @@
 /* Prototypes for procedures local to this file. */
 
 static void setup_signals __P((void));
-static void create_pidfile __P((void));
-static void create_linkpidfile __P((void));
+static void create_pidfile __P((int pid));
+static void create_linkpidfile __P((int pid));
 static void cleanup __P((void));
 static void get_input __P((void));
 static void calltimeout __P((void));
@@ -169,12 +242,19 @@
 static void open_ccp __P((int));
 static void bad_signal __P((int));
 static void holdoff_end __P((void *));
-static int reap_kids __P((int waitfor));
+static void forget_child __P((int pid, int status));
+static int reap_kids __P((void));
+static void childwait_end __P((void *));
+
+#ifdef USE_TDB
 static void update_db_entry __P((void));
 static void add_db_key __P((const char *));
 static void delete_db_key __P((const char *));
 static void cleanup_db __P((void));
+#endif
+
 static void handle_events __P((void));
+void print_link_stats __P((void));
 
 extern	char	*ttyname __P((int));
 extern	char	*getlogin __P((void));
@@ -206,12 +286,14 @@
     &ipv6cp_protent,
 #endif
     &ccp_protent,
+    &ecp_protent,
 #ifdef IPX_CHANGE
     &ipxcp_protent,
 #endif
 #ifdef AT_CHANGE
     &atcp_protent,
 #endif
+    &eap_protent,
     NULL
 };
 
@@ -233,20 +315,9 @@
     struct protent *protp;
     char numbuf[16];
 
+    link_stats_valid = 0;
     new_phase(PHASE_INITIALIZE);
 
-    /*
-     * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else.
-     * This way we can close 0, 1, 2 in detach() without clobbering
-     * a fd that we are using.
-     */
-    if ((i = open("/dev/null", O_RDWR)) >= 0) {
-	while (0 <= i && i <= 2)
-	    i = dup(i);
-	if (i >= 0)
-	    close(i);
-    }
-
     script_env = NULL;
 
     /* Initialize syslog facilities */
@@ -342,8 +413,20 @@
 	init_pr_log(NULL, LOG_INFO);
 	print_options(pr_log, NULL);
 	end_pr_log();
-	if (dryrun)
-	    die(0);
+    }
+
+    if (dryrun)
+	die(0);
+
+    /* Make sure fds 0, 1, 2 are open to somewhere. */
+    fd_devnull = open(_PATH_DEVNULL, O_RDWR);
+    if (fd_devnull < 0)
+	fatal("Couldn't open %s: %m", _PATH_DEVNULL);
+    while (fd_devnull <= 2) {
+	i = dup(fd_devnull);
+	if (i < 0)
+	    fatal("Critical shortage of file descriptors: dup failed: %m");
+	fd_devnull = i;
     }
 
     /*
@@ -351,6 +434,7 @@
      */
     sys_init();
 
+#ifdef USE_TDB
     pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644);
     if (pppdb != NULL) {
 	slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
@@ -362,6 +446,7 @@
 	    multilink = 0;
 	}
     }
+#endif
 
     /*
      * Detach ourselves from the terminal, if required,
@@ -387,9 +472,9 @@
 
     setup_signals();
 
-    waiting = 0;
+    create_linkpidfile(getpid());
 
-    create_linkpidfile();
+    waiting = 0;
 
     /*
      * If we're doing dial-on-demand, set up the interface now.
@@ -398,11 +483,8 @@
 	/*
 	 * Open the loopback channel and set it up to be the ppp interface.
 	 */
-	tdb_writelock(pppdb);
 	fd_loop = open_ppp_loopback();
 	set_ifunit(1);
-	tdb_writeunlock(pppdb);
-
 	/*
 	 * Configure the interface and mark it up, etc.
 	 */
@@ -412,6 +494,8 @@
     do_callback = 0;
     for (;;) {
 
+	bundle_eof = 0;
+	bundle_terminating = 0;
 	listen_time = 0;
 	need_holdoff = 1;
 	devfd = -1;
@@ -429,13 +513,13 @@
 	    add_fd(fd_loop);
 	    for (;;) {
 		handle_events();
-		if (kill_link && !persist)
+		if (asked_to_quit)
 		    break;
 		if (get_loop_output())
 		    break;
 	    }
 	    remove_fd(fd_loop);
-	    if (kill_link && !persist)
+	    if (asked_to_quit)
 		break;
 
 	    /*
@@ -445,46 +529,23 @@
 	    info("Starting link");
 	}
 
-	new_phase(PHASE_SERIALCONN);
-
-	devfd = the_channel->connect();
-	if (devfd < 0)
-	    goto fail;
-
-	/* set up the serial device as a ppp interface */
-	tdb_writelock(pppdb);
-	fd_ppp = the_channel->establish_ppp(devfd);
-	if (fd_ppp < 0) {
-	    tdb_writeunlock(pppdb);
-	    status = EXIT_FATAL_ERROR;
-	    goto disconnect;
-	}
-
-	if (!demand && ifunit >= 0)
-	    set_ifunit(1);
-	tdb_writeunlock(pppdb);
-
-	/*
-	 * Start opening the connection and wait for
-	 * incoming events (reply, timeout, etc.).
-	 */
-	notice("Connect: %s <--> %s", ifname, ppp_devnam);
-	my_gettimeofday(&start_time, NULL);
-	link_stats_valid = 0;
+	gettimeofday(&start_time, NULL);
 	script_unsetenv("CONNECT_TIME");
 	script_unsetenv("BYTES_SENT");
 	script_unsetenv("BYTES_RCVD");
-	lcp_lowerup(0);
 
-	add_fd(fd_ppp);
 	lcp_open(0);		/* Start protocol */
-	status = EXIT_NEGOTIATION_FAILED;
-	new_phase(PHASE_ESTABLISH);
+	start_link(0);
 	while (phase != PHASE_DEAD) {
 	    handle_events();
 	    get_input();
 	    if (kill_link)
 		lcp_close(0, "User request");
+	    if (asked_to_quit) {
+		bundle_terminating = 1;
+		if (phase == PHASE_MASTER)
+		    mp_bundle_terminated();
+	    }
 	    if (open_ccp_flag) {
 		if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
 		    ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
@@ -492,63 +553,10 @@
 		}
 	    }
 	}
+	/* restore FSMs to original state */
+	lcp_close(0, "");
 
-	/*
-	 * Print connect time and statistics.
-	 */
-	if (link_stats_valid) {
-	    int t = (link_connect_time + 5) / 6;    /* 1/10ths of minutes */
-	    info("Connect time %d.%d minutes.", t/10, t%10);
-	    info("Sent %u bytes, received %u bytes.",
-		 link_stats.bytes_out, link_stats.bytes_in);
-	}
-
-	/*
-	 * Delete pid file before disestablishing ppp.  Otherwise it
-	 * can happen that another pppd gets the same unit and then
-	 * we delete its pid file.
-	 */
-	if (!demand) {
-	    if (pidfilename[0] != 0
-		&& unlink(pidfilename) < 0 && errno != ENOENT) 
-		warn("unable to delete pid file %s: %m", pidfilename);
-	    pidfilename[0] = 0;
-	}
-
-	/*
-	 * If we may want to bring the link up again, transfer
-	 * the ppp unit back to the loopback.  Set the
-	 * real serial device back to its normal mode of operation.
-	 */
-	remove_fd(fd_ppp);
-	clean_check();
-	the_channel->disestablish_ppp(devfd);
-	fd_ppp = -1;
-	if (!hungup)
-	    lcp_lowerdown(0);
-	if (!demand)
-	    script_unsetenv("IFNAME");
-
-	/*
-	 * Run disconnector script, if requested.
-	 * XXX we may not be able to do this if the line has hung up!
-	 */
-    disconnect:
-	new_phase(PHASE_DISCONNECT);
-	the_channel->disconnect();
-
-    fail:
-	if (the_channel->cleanup)
-	    (*the_channel->cleanup)();
-
-	if (!demand) {
-	    if (pidfilename[0] != 0
-		&& unlink(pidfilename) < 0 && errno != ENOENT) 
-		warn("unable to delete pid file %s: %m", pidfilename);
-	    pidfilename[0] = 0;
-	}
-
-	if (!persist || (maxfail > 0 && unsuccess >= maxfail))
+	if (!persist || asked_to_quit || (maxfail > 0 && unsuccess >= maxfail))
 	    break;
 
 	if (demand)
@@ -570,16 +578,21 @@
     }
 
     /* Wait for scripts to finish */
-    /* XXX should have a timeout here */
-    while (n_children > 0) {
+    reap_kids();
+    if (n_children > 0) {
+	if (child_wait > 0)
+	    TIMEOUT(childwait_end, NULL, child_wait);
 	if (debug) {
 	    struct subprocess *chp;
 	    dbglog("Waiting for %d child processes...", n_children);
 	    for (chp = children; chp != NULL; chp = chp->next)
 		dbglog("  script %s, pid %d", chp->prog, chp->pid);
 	}
-	if (reap_kids(1) < 0)
-	    break;
+	while (n_children > 0 && !childwait_done) {
+	    handle_events();
+	    if (kill_link && !childwait_done)
+		childwait_end(NULL);
+	}
     }
 
     die(status);
@@ -593,36 +606,38 @@
 handle_events()
 {
     struct timeval timo;
-    sigset_t mask;
 
     kill_link = open_ccp_flag = 0;
     if (sigsetjmp(sigjmp, 1) == 0) {
-	sigprocmask(SIG_BLOCK, &mask, NULL);
+	sigprocmask(SIG_BLOCK, &signals_handled, NULL);
 	if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) {
-	    sigprocmask(SIG_UNBLOCK, &mask, NULL);
+	    sigprocmask(SIG_UNBLOCK, &signals_handled, NULL);
 	} else {
 	    waiting = 1;
-	    sigprocmask(SIG_UNBLOCK, &mask, NULL);
+	    sigprocmask(SIG_UNBLOCK, &signals_handled, NULL);
 	    wait_input(timeleft(&timo));
 	}
     }
     waiting = 0;
     calltimeout();
     if (got_sighup) {
+	info("Hangup (SIGHUP)");
 	kill_link = 1;
 	got_sighup = 0;
 	if (status != EXIT_HANGUP)
 	    status = EXIT_USER_REQUEST;
     }
     if (got_sigterm) {
+	info("Terminating on signal %d", got_sigterm);
 	kill_link = 1;
+	asked_to_quit = 1;
 	persist = 0;
 	status = EXIT_USER_REQUEST;
 	got_sigterm = 0;
     }
     if (got_sigchld) {
-	reap_kids(0);	/* Don't leave dead kids lying around */
 	got_sigchld = 0;
+	reap_kids();	/* Don't leave dead kids lying around */
     }
     if (got_sigusr2) {
 	open_ccp_flag = 1;
@@ -637,19 +652,18 @@
 setup_signals()
 {
     struct sigaction sa;
-    sigset_t mask;
 
     /*
      * Compute mask of all interesting signals and install signal handlers
      * for each.  Only one signal handler may be active at a time.  Therefore,
      * all other signals should be masked when any handler is executing.
      */
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGHUP);
-    sigaddset(&mask, SIGINT);
-    sigaddset(&mask, SIGTERM);
-    sigaddset(&mask, SIGCHLD);
-    sigaddset(&mask, SIGUSR2);
+    sigemptyset(&signals_handled);
+    sigaddset(&signals_handled, SIGHUP);
+    sigaddset(&signals_handled, SIGINT);
+    sigaddset(&signals_handled, SIGTERM);
+    sigaddset(&signals_handled, SIGCHLD);
+    sigaddset(&signals_handled, SIGUSR2);
 
 #define SIGNAL(s, handler)	do { \
 	sa.sa_handler = handler; \
@@ -657,7 +671,7 @@
 	    fatal("Couldn't establish signal handler (%d): %m", s); \
     } while (0)
 
-    sa.sa_mask = mask;
+    sa.sa_mask = signals_handled;
     sa.sa_flags = 0;
     SIGNAL(SIGHUP, hup);		/* Hangup */
     SIGNAL(SIGINT, term);		/* Interrupt */
@@ -726,8 +740,8 @@
     slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
     script_setenv("IFNAME", ifname, iskey);
     if (iskey) {
-	create_pidfile();	/* write pid to file */
-	create_linkpidfile();
+	create_pidfile(getpid());	/* write pid to file */
+	create_linkpidfile(getpid());
     }
 }
 
@@ -739,9 +753,12 @@
 {
     int pid;
     char numbuf[16];
+    int pipefd[2];
 
     if (detached)
 	return;
+    if (pipe(pipefd) == -1)
+	pipefd[0] = pipefd[1] = -1;
     if ((pid = fork()) < 0) {
 	error("Couldn't detach (fork failed: %m)");
 	die(1);			/* or just return? */
@@ -749,23 +766,28 @@
     if (pid != 0) {
 	/* parent */
 	notify(pidchange, pid);
+	/* update pid files if they have been written already */
+	if (pidfilename[0])
+	    create_pidfile(pid);
+	if (linkpidfile[0])
+	    create_linkpidfile(pid);
 	exit(0);		/* parent dies */
     }
     setsid();
     chdir("/");
-    close(0);
-    close(1);
-    close(2);
+    dup2(fd_devnull, 0);
+    dup2(fd_devnull, 1);
+    dup2(fd_devnull, 2);
     detached = 1;
     if (log_default)
 	log_to_fd = -1;
-    /* update pid files if they have been written already */
-    if (pidfilename[0])
-	create_pidfile();
-    if (linkpidfile[0])
-	create_linkpidfile();
     slprintf(numbuf, sizeof(numbuf), "%d", getpid());
     script_setenv("PPPD_PID", numbuf, 1);
+
+    /* wait for parent to finish updating pid & lock files and die */
+    close(pipefd[1]);
+    complete_read(pipefd[0], numbuf, 1);
+    close(pipefd[0]);
 }
 
 /*
@@ -774,26 +796,23 @@
 void
 reopen_log()
 {
-#ifdef ULTRIX
-    openlog("pppd", LOG_PID);
-#else
     openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
     setlogmask(LOG_UPTO(LOG_INFO));
-#endif
 }
 
 /*
  * Create a file containing our process ID.
  */
 static void
-create_pidfile()
+create_pidfile(pid)
+    int pid;
 {
     FILE *pidfile;
 
     slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
 	     _PATH_VARRUN, ifname);
     if ((pidfile = fopen(pidfilename, "w")) != NULL) {
-	fprintf(pidfile, "%d\n", getpid());
+	fprintf(pidfile, "%d\n", pid);
 	(void) fclose(pidfile);
     } else {
 	error("Failed to create pid file %s: %m", pidfilename);
@@ -801,8 +820,9 @@
     }
 }
 
-static void
-create_linkpidfile()
+void
+create_linkpidfile(pid)
+    int pid;
 {
     FILE *pidfile;
 
@@ -812,7 +832,7 @@
     slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid",
 	     _PATH_VARRUN, linkname);
     if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
-	fprintf(pidfile, "%d\n", getpid());
+	fprintf(pidfile, "%d\n", pid);
 	if (ifname[0])
 	    fprintf(pidfile, "%s\n", ifname);
 	(void) fclose(pidfile);
@@ -823,6 +843,19 @@
 }
 
 /*
+ * remove_pidfile - remove our pid files
+ */
+void remove_pidfiles()
+{
+    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+	warn("unable to delete pid file %s: %m", pidfilename);
+    pidfilename[0] = 0;
+    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
+	warn("unable to delete pid file %s: %m", linkpidfile);
+    linkpidfile[0] = 0;
+}
+
+/*
  * holdoff_end - called via a timeout when the holdoff period ends.
  */
 static void
@@ -860,14 +893,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" },
@@ -887,17 +960,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 Protovol" },
+    { 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 },
 };
 
@@ -934,6 +1033,11 @@
 	return;
 
     if (len == 0) {
+	if (bundle_eof && multilink_master) {
+	    notice("Last channel has disconnected");
+	    mp_bundle_terminated();
+	    return;
+	}
 	notice("Modem hangup");
 	hungup = 1;
 	status = EXIT_HANGUP;
@@ -942,14 +1046,14 @@
 	return;
     }
 
-    if (debug /*&& (debugflags & DBG_INPACKET)*/)
-	dbglog("rcvd %P", p, len);
-
     if (len < PPP_HDRLEN) {
-	MAINDEBUG(("io(): Received short packet."));
+	dbglog("received short packet:%.*B", len, p);
 	return;
     }
 
+    dump_packet("rcvd", p, len);
+    if (snoop_recv_hook) snoop_recv_hook(p, len);
+
     p += 2;				/* Skip address and control */
     GETSHORT(protocol, p);
     len -= PPP_HDRLEN;
@@ -958,7 +1062,7 @@
      * Toss all non-LCP packets unless LCP is OPEN.
      */
     if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
-	MAINDEBUG(("get_input: Received non-LCP packet when LCP not open."));
+	dbglog("Discarded non-LCP packet when LCP not open");
 	return;
     }
 
@@ -968,9 +1072,10 @@
      */
     if (phase <= PHASE_AUTHENTICATE
 	&& !(protocol == PPP_LCP || protocol == PPP_LQR
-	     || protocol == PPP_PAP || protocol == PPP_CHAP)) {
-	MAINDEBUG(("get_input: discarding proto 0x%x in phase %d",
-		   protocol, phase));
+	     || protocol == PPP_PAP || protocol == PPP_CHAP ||
+		protocol == PPP_EAP)) {
+	dbglog("discarding proto 0x%x in phase %d",
+		   protocol, phase);
 	return;
     }
 
@@ -992,14 +1097,60 @@
     if (debug) {
 	const char *pname = protocol_name(protocol);
 	if (pname != NULL)
+	{
 	    warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
+	}
 	else
+	{
 	    warn("Unsupported protocol 0x%x received", protocol);
+	}
     }
     lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
 }
 
 /*
+ * ppp_send_config - configure the transmit-side characteristics of
+ * the ppp interface.  Returns -1, indicating an error, if the channel
+ * send_config procedure called error() (or incremented error_count
+ * itself), otherwise 0.
+ */
+int
+ppp_send_config(unit, mtu, accm, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t accm;
+    int pcomp, accomp;
+{
+	int errs;
+
+	if (the_channel->send_config == NULL)
+		return 0;
+	errs = error_count;
+	(*the_channel->send_config)(mtu, accm, pcomp, accomp);
+	return (error_count != errs)? -1: 0;
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.  Returns -1, indicating an error, if the channel
+ * recv_config procedure called error() (or incremented error_count
+ * itself), otherwise 0.
+ */
+int
+ppp_recv_config(unit, mru, accm, pcomp, accomp)
+    int unit, mru;
+    u_int32_t accm;
+    int pcomp, accomp;
+{
+	int errs;
+
+	if (the_channel->recv_config == NULL)
+		return 0;
+	errs = error_count;
+	(*the_channel->recv_config)(mru, accm, pcomp, accomp);
+	return (error_count != errs)? -1: 0;
+}
+
+/*
  * new_phase - signal the start of a new phase of pppd's operation.
  */
 void
@@ -1019,6 +1170,8 @@
 die(status)
     int status;
 {
+    if (!doing_multilink || multilink_master)
+	print_link_stats();
     cleanup();
     notify(exitnotify, status);
     syslog(LOG_INFO, "Exit.");
@@ -1038,16 +1191,40 @@
 	the_channel->disestablish_ppp(devfd);
     if (the_channel->cleanup)
 	(*the_channel->cleanup)();
+    remove_pidfiles();
 
-    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) 
-	warn("unable to delete pid file %s: %m", pidfilename);
-    pidfilename[0] = 0;
-    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT) 
-	warn("unable to delete pid file %s: %m", linkpidfile);
-    linkpidfile[0] = 0;
-
+#ifdef USE_TDB
     if (pppdb != NULL)
 	cleanup_db();
+#endif
+
+}
+
+void
+print_link_stats()
+{
+    /*
+     * Print connect time and statistics.
+     */
+    if (link_stats_valid) {
+       int t = (link_connect_time + 5) / 6;    /* 1/10ths of minutes */
+       info("Connect time %d.%d minutes.", t/10, t%10);
+       info("Sent %u bytes, received %u bytes.",
+	    link_stats.bytes_out, link_stats.bytes_in);
+       link_stats_valid = 0;
+    }
+}
+
+/*
+ * reset_link_stats - "reset" stats when link goes up.
+ */
+void
+reset_link_stats(u)
+    int u;
+{
+    if (!get_ppp_stats(u, &old_link_stats))
+	return;
+    gettimeofday(&start_time, NULL);
 }
 
 /*
@@ -1061,16 +1238,21 @@
     char numbuf[32];
 
     if (!get_ppp_stats(u, &link_stats)
-	|| my_gettimeofday(&now, NULL) < 0)
+	|| gettimeofday(&now, NULL) < 0)
 	return;
     link_connect_time = now.tv_sec - start_time.tv_sec;
     link_stats_valid = 1;
 
-    slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
+    link_stats.bytes_in  -= old_link_stats.bytes_in;
+    link_stats.bytes_out -= old_link_stats.bytes_out;
+    link_stats.pkts_in   -= old_link_stats.pkts_in;
+    link_stats.pkts_out  -= old_link_stats.pkts_out;
+
+    slprintf(numbuf, sizeof(numbuf), "%u", link_connect_time);
     script_setenv("CONNECT_TIME", numbuf, 0);
-    slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
+    slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_out);
     script_setenv("BYTES_SENT", numbuf, 0);
-    slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
+    slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_in);
     script_setenv("BYTES_RCVD", numbuf, 0);
 }
 
@@ -1087,9 +1269,6 @@
 
 /*
  * timeout - Schedule a timeout.
- *
- * Note that this timeout takes the number of milliseconds, NOT hz (as in
- * the kernel).
  */
 void
 timeout(func, arg, secs, usecs)
@@ -1098,10 +1277,7 @@
     int secs, usecs;
 {
     struct callout *newp, *p, **pp;
-  
-    MAINDEBUG(("Timeout %p:%p in %d.%03d seconds.", func, arg,
-	       time / 1000, time % 1000));
-  
+
     /*
      * Allocate timeout.
      */
@@ -1109,7 +1285,7 @@
 	fatal("Out of memory in timeout()!");
     newp->c_arg = arg;
     newp->c_func = func;
-    my_gettimeofday(&timenow, NULL);
+    gettimeofday(&timenow, NULL);
     newp->c_time.tv_sec = timenow.tv_sec + secs;
     newp->c_time.tv_usec = timenow.tv_usec + usecs;
     if (newp->c_time.tv_usec >= 1000000) {
@@ -1139,9 +1315,7 @@
     void *arg;
 {
     struct callout **copp, *freep;
-  
-    MAINDEBUG(("Untimeout %p:%p.", func, arg));
-  
+
     /*
      * Find first matching timeout and remove it from the list.
      */
@@ -1165,7 +1339,7 @@
     while (callout != NULL) {
 	p = callout;
 
-	if (my_gettimeofday(&timenow, NULL) < 0)
+	if (gettimeofday(&timenow, NULL) < 0)
 	    fatal("Failed to get time of day: %m");
 	if (!(p->c_time.tv_sec < timenow.tv_sec
 	      || (p->c_time.tv_sec == timenow.tv_sec
@@ -1190,7 +1364,7 @@
     if (callout == NULL)
 	return NULL;
 
-    my_gettimeofday(&timenow, NULL);
+    gettimeofday(&timenow, NULL);
     tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
     tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
     if (tvp->tv_usec < 0) {
@@ -1206,6 +1380,7 @@
 
 /*
  * kill_my_pg - send a signal to our process group, and ignore it ourselves.
+ * We assume that sig is currently blocked.
  */
 static void
 kill_my_pg(sig)
@@ -1213,9 +1388,21 @@
 {
     struct sigaction act, oldact;
 
+    sigemptyset(&act.sa_mask);		/* unnecessary in fact */
     act.sa_handler = SIG_IGN;
     act.sa_flags = 0;
     kill(0, sig);
+    /*
+     * The kill() above made the signal pending for us, as well as
+     * the rest of our process group, but we don't want it delivered
+     * to us.  It is blocked at the moment.  Setting it to be ignored
+     * will cause the pending signal to be discarded.  If we did the
+     * kill() after setting the signal to be ignored, it is unspecified
+     * (by POSIX) whether the signal is immediately discarded or left
+     * pending, and in fact Linux would leave it pending, and so it
+     * would be delivered after the current signal handler exits,
+     * leading to an infinite loop.
+     */
     sigaction(sig, &act, &oldact);
     sigaction(sig, &oldact, NULL);
 }
@@ -1232,7 +1419,7 @@
 hup(sig)
     int sig;
 {
-    info("Hangup (SIGHUP)");
+    /* can't log a message here, it can deadlock */
     got_sighup = 1;
     if (conn_running)
 	/* Send the signal to the [dis]connector process(es) also */
@@ -1253,8 +1440,8 @@
 term(sig)
     int sig;
 {
-    info("Terminating on signal %d.", sig);
-    got_sigterm = 1;
+    /* can't log a message here, it can deadlock */
+    got_sigterm = sig;
     if (conn_running)
 	/* Send the signal to the [dis]connector process(es) also */
 	kill_my_pg(sig);
@@ -1332,6 +1519,89 @@
     die(127);
 }
 
+/*
+ * safe_fork - Create a child process.  The child closes all the
+ * file descriptors that we don't want to leak to a script.
+ * The parent waits for the child to do this before returning.
+ * This also arranges for the specified fds to be dup'd to
+ * fds 0, 1, 2 in the child.
+ */
+pid_t
+safe_fork(int infd, int outfd, int errfd)
+{
+	pid_t pid;
+	int fd, pipefd[2];
+	char buf[1];
+
+	/* make sure fds 0, 1, 2 are occupied (probably not necessary) */
+	while ((fd = dup(fd_devnull)) >= 0) {
+		if (fd > 2) {
+			close(fd);
+			break;
+		}
+	}
+
+	if (pipe(pipefd) == -1)
+		pipefd[0] = pipefd[1] = -1;
+	pid = fork();
+	if (pid < 0) {
+		error("fork failed: %m");
+		return -1;
+	}
+	if (pid > 0) {
+		/* parent */
+		close(pipefd[1]);
+		/* this read() blocks until the close(pipefd[1]) below */
+		complete_read(pipefd[0], buf, 1);
+		close(pipefd[0]);
+		return pid;
+	}
+
+	/* Executing in the child */
+	sys_close();
+#ifdef USE_TDB
+	tdb_close(pppdb);
+#endif
+
+	/* make sure infd, outfd and errfd won't get tromped on below */
+	if (infd == 1 || infd == 2)
+		infd = dup(infd);
+	if (outfd == 0 || outfd == 2)
+		outfd = dup(outfd);
+	if (errfd == 0 || errfd == 1)
+		errfd = dup(errfd);
+
+	/* dup the in, out, err fds to 0, 1, 2 */
+	if (infd != 0)
+		dup2(infd, 0);
+	if (outfd != 1)
+		dup2(outfd, 1);
+	if (errfd != 2)
+		dup2(errfd, 2);
+
+	closelog();
+	if (log_to_fd > 2)
+		close(log_to_fd);
+	if (the_channel->close)
+		(*the_channel->close)();
+	else
+		close(devfd);	/* some plugins don't have a close function */
+	close(fd_ppp);
+	close(fd_devnull);
+	if (infd != 0)
+		close(infd);
+	if (outfd != 1)
+		close(outfd);
+	if (errfd != 2)
+		close(errfd);
+
+	notify(fork_notifier, 0);
+	close(pipefd[0]);
+	/* this close unblocks the read() call above in the parent */
+	close(pipefd[1]);
+
+	return 0;
+}
 
 /*
  * device_script - run a program to talk to the specified fds
@@ -1344,12 +1614,20 @@
     int in, out;
     int dont_wait;
 {
-    int pid, fd;
+    int pid;
     int status = -1;
     int errfd;
 
+    if (log_to_fd >= 0)
+	errfd = log_to_fd;
+    else
+	errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+
     ++conn_running;
-    pid = fork();
+    pid = safe_fork(in, out, errfd);
+
+    if (pid != 0 && log_to_fd < 0)
+	close(errfd);
 
     if (pid < 0) {
 	--conn_running;
@@ -1373,50 +1651,15 @@
     }
 
     /* here we are executing in the child */
-    /* make sure fds 0, 1, 2 are occupied */
-    while ((fd = dup(in)) >= 0) {
-	if (fd > 2) {
-	    close(fd);
-	    break;
-	}
-    }
-
-    /* dup in and out to fds > 2 */
-    in = dup(in);
-    out = dup(out);
-    if (log_to_fd >= 0) {
-	errfd = dup(log_to_fd);
-    } else {
-	errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
-    }
-
-    /* close fds 0 - 2 and any others we can think of */
-    close(0);
-    close(1);
-    close(2);
-    sys_close();
-    if (the_channel->close)
-	(*the_channel->close)();
-    closelog();
-
-    /* dup the in, out, err fds to 0, 1, 2 */
-    dup2(in, 0);
-    close(in);
-    dup2(out, 1);
-    close(out);
-    if (errfd >= 0) {
-	dup2(errfd, 2);
-	close(errfd);
-    }
 
+    setgid(getgid());
     setuid(uid);
     if (getuid() != uid) {
-	error("setuid failed");
+	fprintf(stderr, "pppd: setuid failed\n");
 	exit(1);
     }
-    setgid(getgid());
     execl("/bin/sh", "sh", "-c", program, (char *)0);
-    error("could not exec /bin/sh: %m");
+    perror("pppd: could not exec /bin/sh");
     exit(99);
     /* NOTREACHED */
 }
@@ -1424,7 +1667,7 @@
 
 /*
  * run-program - execute a program with given arguments,
- * but don't wait for it.
+ * 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
@@ -1433,14 +1676,15 @@
  * 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;
 
     /*
@@ -1457,66 +1701,49 @@
 	return 0;
     }
 
-    pid = fork();
+    pid = safe_fork(fd_devnull, fd_devnull, fd_devnull);
     if (pid == -1) {
 	error("Failed to create child process for %s: %m", prog);
 	return -1;
     }
-    if (pid == 0) {
-	int new_fd;
-
-	/* Leave the current location */
-	(void) setsid();	/* No controlling tty. */
-	(void) umask (S_IRWXG|S_IRWXO);
-	(void) chdir ("/");	/* no current directory. */
-	setuid(0);		/* set real UID = root */
-	setgid(getegid());
-
-	/* Ensure that nothing of our device environment is inherited. */
-	sys_close();
-	closelog();
-	close (0);
-	close (1);
-	close (2);
-	if (the_channel->close)
-	    (*the_channel->close)();
-
-        /* Don't pass handles to the PPP device, even by accident. */
-	new_fd = open (_PATH_DEVNULL, O_RDWR);
-	if (new_fd >= 0) {
-	    if (new_fd != 0) {
-	        dup2  (new_fd, 0); /* stdin <- /dev/null */
-		close (new_fd);
+    if (pid != 0) {
+	if (debug)
+	    dbglog("Script %s started (pid %d)", prog, pid);
+	record_child(pid, prog, done, arg);
+	if (wait) {
+	    while (waitpid(pid, &status, 0) < 0) {
+		if (errno == EINTR)
+		    continue;
+		fatal("error waiting for script %s: %m", prog);
 	    }
-	    dup2 (0, 1); /* stdout -> /dev/null */
-	    dup2 (0, 2); /* stderr -> /dev/null */
+	    forget_child(pid, status);
 	}
-
-#ifdef BSD
-	/* Force the priority back to zero if pppd is running higher. */
-	if (setpriority (PRIO_PROCESS, 0, 0) < 0)
-	    warn("can't reset priority to 0: %m"); 
-#endif
-
-	/* SysV recommends a second fork at this point. */
-
-	/* run the program */
-	execve(prog, args, script_env);
-	if (must_exist || errno != ENOENT) {
-	    /* 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();
-	}
-	_exit(-1);
+	return pid;
     }
 
-    if (debug)
-	dbglog("Script %s started (pid %d)", prog, pid);
-    record_child(pid, prog, done, arg);
+    /* Leave the current location */
+    (void) setsid();	/* No controlling tty. */
+    (void) umask (S_IRWXG|S_IRWXO);
+    (void) chdir ("/");	/* no current directory. */
+    setuid(0);		/* set real UID = root */
+    setgid(getegid());
 
-    return pid;
+#ifdef BSD
+    /* Force the priority back to zero if pppd is running higher. */
+    if (setpriority (PRIO_PROCESS, 0, 0) < 0)
+	warn("can't reset priority to 0: %m");
+#endif
+
+    /* run the program */
+    execve(prog, args, script_env);
+    if (must_exist || errno != ENOENT) {
+	/* 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();
+    }
+    _exit(-1);
 }
 
 
@@ -1548,39 +1775,66 @@
     }
 }
 
+/*
+ * childwait_end - we got fed up waiting for the child processes to
+ * exit, send them all a SIGTERM.
+ */
+static void
+childwait_end(arg)
+    void *arg;
+{
+    struct subprocess *chp;
+
+    for (chp = children; chp != NULL; chp = chp->next) {
+	if (debug)
+	    dbglog("sending SIGTERM to process %d", chp->pid);
+	kill(chp->pid, SIGTERM);
+    }
+    childwait_done = 1;
+}
+
+/*
+ * 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.
  */
 static int
-reap_kids(waitfor)
-    int waitfor;
+reap_kids()
 {
     int pid, status;
-    struct subprocess *chp, **prevp;
 
     if (n_children == 0)
 	return 0;
-    while ((pid = waitpid(-1, &status, (waitfor? 0: 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, status);
-	if (chp && chp->done)
-	    (*chp->done)(chp->arg);
-	if (chp)
-	    free(chp);
+    while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
+        forget_child(pid, status);
     }
     if (pid == -1) {
 	if (errno == ECHILD)
@@ -1633,7 +1887,7 @@
 }
 
 /*
- * notify - call a set of functions registered with add_notify.
+ * notify - call a set of functions registered with add_notifier.
  */
 void
 notify(notif, val)
@@ -1682,13 +1936,17 @@
     if (script_env != 0) {
 	for (i = 0; (p = script_env[i]) != 0; ++i) {
 	    if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+#ifdef USE_TDB
 		if (p[-1] && pppdb != NULL)
 		    delete_db_key(p);
+#endif
 		free(p-1);
 		script_env[i] = newstring;
+#ifdef USE_TDB
 		if (iskey && pppdb != NULL)
 		    add_db_key(newstring);
 		update_db_entry();
+#endif
 		return;
 	    }
 	}
@@ -1715,11 +1973,13 @@
     script_env[i] = newstring;
     script_env[i+1] = 0;
 
+#ifdef USE_TDB
     if (pppdb != NULL) {
 	if (iskey)
 	    add_db_key(newstring);
 	update_db_entry();
     }
+#endif
 }
 
 /*
@@ -1738,19 +1998,59 @@
 	return;
     for (i = 0; (p = script_env[i]) != 0; ++i) {
 	if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+#ifdef USE_TDB
 	    if (p[-1] && pppdb != NULL)
 		delete_db_key(p);
+#endif
 	    free(p-1);
 	    while ((script_env[i] = script_env[i+1]) != 0)
 		++i;
 	    break;
 	}
     }
+#ifdef USE_TDB
     if (pppdb != NULL)
 	update_db_entry();
+#endif
 }
 
 /*
+ * Any arbitrary string used as a key for locking the database.
+ * It doesn't matter what it is as long as all pppds use the same string.
+ */
+#define PPPD_LOCK_KEY	"pppd lock"
+
+/*
+ * lock_db - get an exclusive lock on the TDB database.
+ * Used to ensure atomicity of various lookup/modify operations.
+ */
+void lock_db()
+{
+#ifdef USE_TDB
+	TDB_DATA key;
+
+	key.dptr = PPPD_LOCK_KEY;
+	key.dsize = strlen(key.dptr);
+	tdb_chainlock(pppdb, key);
+#endif
+}
+
+/*
+ * unlock_db - remove the exclusive lock obtained by lock_db.
+ */
+void unlock_db()
+{
+#ifdef USE_TDB
+	TDB_DATA key;
+
+	key.dptr = PPPD_LOCK_KEY;
+	key.dsize = strlen(key.dptr);
+	tdb_chainunlock(pppdb, key);
+#endif
+}
+
+#ifdef USE_TDB
+/*
  * update_db_entry - update our entry in the database.
  */
 static void
@@ -1765,7 +2065,7 @@
     vlen = 0;
     for (i = 0; (p = script_env[i]) != 0; ++i)
 	vlen += strlen(p) + 1;
-    vbuf = malloc(vlen);
+    vbuf = malloc(vlen + 1);
     if (vbuf == 0)
 	novm("database entry");
     q = vbuf;
@@ -1779,6 +2079,9 @@
     if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
 	error("tdb_store failed: %s", tdb_error(pppdb));
 
+    if (vbuf)
+        free(vbuf);
+
 }
 
 /*
@@ -1829,3 +2132,4 @@
 	if (p[-1])
 	    delete_db_key(p);
 }
+#endif /* USE_TDB */
diff -Naur tomato/release/src/router/pppd/pppd/Makefile tomato121/release/src/router/pppd/pppd/Makefile
--- tomato/release/src/router/pppd/pppd/Makefile	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -1,100 +1,166 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile,v 1.2 2003/07/15 03:26:48 honor Exp $
+# $Id: Makefile.linux,v 1.69 2006/06/04 05:07:46 paulus Exp $
 #
 
-CFLAGS   += -DMIPSEL=1
-
 # Default installation locations
-BINDIR = /usr/sbin
-MANDIR = /usr/man
+DESTDIR = @DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+INCDIR = $(DESTDIR)/include
+
+TARGETS = pppd
+
+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
+
+HEADERS = ccp.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
 
-PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-	   ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
-	   demand.c utils.c multilink.c tdb.c tty.c
-HEADERS =  callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
-	   ipxcp.h cbcp.h tdb.h
 MANPAGES = pppd.8
-PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-	   auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
-	   tdb.o tty.o
-
-all: pppd
+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
 
 #
-# include dependancies if present and backup if as a header file
+# include dependencies if present
 ifeq (.depend,$(wildcard .depend))
 include .depend
 endif
 
-#CROSS =mipsel-linux-
-#CC = $(CROSS)gcc -s
-#AR = $(CROSS)ar
-#LD = $(CROSS)ld
-
-COPTS = -O2 -pipe -Wall -g
+# CC = gcc
+#
+#COPTS = -O2 -pipe -Wall -g
 LIBS =
 
-ifneq ($(wildcard /usr/lib/libcrypt.*),)
-LIBS += -lcrypt
-endif
-
 # Uncomment the next 2 lines to include support for Microsoft's
-# MS-CHAP authentication protocol.
-#CHAPMS=y
-#USE_CRYPT=y
-ifneq ($(wildcard /usr/lib/libcrypt.*),)
-HAVE_CRYPT_H=y
-endif
+# MS-CHAP authentication protocol.  Also, edit plugins/radius/Makefile.linux.
+CHAPMS=y
+USE_CRYPT=y
+# Don't use MSLANMAN unless you really know what you're doing.
+#MSLANMAN=y
+# Uncomment the next line to include support for MPPE.  CHAPMS (above) must
+# also be enabled.  Also, edit plugins/radius/Makefile.linux.
+MPPE=y
 
 # Uncomment the next line to include support for PPP packet filtering.
 # This requires that the libpcap library and headers be installed
-# and that the kernel driver support PPP packet filtering, which it
-# doesn't yet.
+# and that the kernel driver support PPP packet filtering.
 #FILTER=y
 
+# Uncomment the next line to enable multilink PPP (enabled by default)
+# Linux distributions: Please leave multilink ENABLED in your builds
+# of pppd!
+#HAVE_MULTILINK=y
+
+# Uncomment the next line to enable the TDB database (enabled by default.)
+# If you enable multilink, then TDB is automatically enabled also.
+# Linux distributions: Please leave TDB ENABLED in your builds.
+#USE_TDB=y
+
 #HAS_SHADOW=y
 #USE_PAM=y
 #HAVE_INET6=y
 
-#PLUGIN=y
+# Enable plugins
+PLUGIN=y
+
+# Enable Microsoft proprietary Callback Control Protocol
+#CBCP=y
+
+# Enable EAP SRP-SHA1 authentication (requires libsrp)
+#USE_SRP=y
+
+MAXOCTETS=y
 
 INCLUDE_DIRS= -I../include
 
-COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H  -DHAVE_MMAP
+COMPILE_FLAGS= -DHAVE_PATHS_H -DHAVE_MMAP
 
-CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) '-DDESTDIR="@DESTDIR@"' -ffunction-sections -fdata-sections -Wl,--gc-sections
 
 ifdef CHAPMS
 CFLAGS   += -DCHAPMS=1
-ifndef USE_CRYPT
-LIBS     := -ldes $(LIBS)
-else
-CFLAGS   += -DUSE_CRYPT=1
-ifneq ($(wildcard /usr/include/crypt.h),)
-CFLAGS   += -DHAVE_CRYPT_H=1
-endif
-endif
+NEEDDES=y
 PPPDOBJS += md4.o chap_ms.o
+HEADERS	+= md4.h chap_ms.h
 ifdef MSLANMAN
 CFLAGS   += -DMSLANMAN=1
 endif
+ifdef MPPE
+CFLAGS   += -DMPPE=1
+endif
+endif
+
+# EAP SRP-SHA1
+ifdef USE_SRP
+CFLAGS	+= -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include
+LIBS	+= -lsrp -L/usr/local/ssl/lib -lcrypto -ffunction-sections -fdata-sections -Wl,--gc-sections
+TARGETS	+= srp-entry
+EXTRAINSTALL = $(INSTALL) -c -m 555 srp-entry $(BINDIR)/srp-entry
+MANPAGES += srp-entry.8
+EXTRACLEAN += srp-entry.o
+NEEDDES=y
+else
+# OpenSSL has an integrated version of SHA-1, and its implementation
+# is incompatible with this local SHA-1 implementation.  We must use
+# one or the other, not both.
+PPPDSRCS += sha1.c
+HEADERS += sha1.h
+PPPDOBJS += sha1.o
 endif
 
 ifdef HAS_SHADOW
 CFLAGS   += -DHAS_SHADOW
-#LIBS     := -lshadow $(LIBS)
+#LIBS     += -lshadow $(LIBS)
+endif
+
+#ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS   += -DHAVE_CRYPT_H=1
+#endif
+#ifneq ($(wildcard /usr/lib/libcrypt.*),)
+LIBS	+= -lcrypt
+#endif
+
+ifdef NEEDDES
+ifndef USE_CRYPT
+LIBS     += -ldes $(LIBS)
+else
+CFLAGS   += -DUSE_CRYPT=1
+endif
+PPPDOBJS += pppcrypt.o
+HEADERS += pppcrypt.h
 endif
 
 # For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
 ifdef USE_PAM
 CFLAGS   += -DUSE_PAM
-LIBS     := -lpam -ldl $(LIBS)
+LIBS     += -lpam -ldl
+endif
+
+# Multi-linnk
+ifdef HAVE_MULTILINK
+	# Multilink implies the use of TDB
+	USE_TDB=y
+
+	CFLAGS += -DHAVE_MULTILINK
+	PPPDSRCS += multilink.c
+	PPPDOBJS += multilink.o
+endif
+
+# TDB
+ifdef USE_TDB
+	CFLAGS += -DUSE_TDB=1
+	PPPDSRCS += tdb.c spinlock.c
+	PPPDOBJS += tdb.o spinlock.o
+	HEADERS += tdb.h spinlock.h
 endif
 
 # Lock library binary for Linux is included in 'linux' subdirectory.
 ifdef LOCKLIB
-LIBS     := -llock $(LIBS)
+LIBS     += -llock
 CFLAGS   += -DLOCKLIB=1
 endif
 
@@ -105,8 +171,10 @@
 endif
 
 ifdef FILTER
+ifneq ($(wildcard /usr/include/pcap-bpf.h),)
 LIBS    += -lpcap
-CFLAGS  += -DPPP_FILTER -I/usr/include/pcap
+CFLAGS  += -DPPP_FILTER
+endif
 endif
 
 ifdef HAVE_INET6
@@ -116,21 +184,41 @@
      CFLAGS   += -DINET6=1
 endif
 
+ifdef CBCP
+     PPPDSRCS += cbcp.c
+     PPPDOBJS += cbcp.o
+     CFLAGS += -DCBCP_SUPPORT
+     HEADERS += cbcp.h
+endif
+
+ifdef MAXOCTETS
+     CFLAGS += -DMAXOCTETS
+endif
+
+INSTALL= install
 
-INSTALL= install -o root
+all: $(TARGETS)
 
 install: pppd
 	mkdir -p $(BINDIR) $(MANDIR)
-	$(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd
+	$(EXTRAINSTALL)
+	$(INSTALL) -c -m 555 pppd $(BINDIR)/pppd
 	if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \
 	  chmod o-rx,u+s $(BINDIR)/pppd; fi
-	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8
+	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)
 
 pppd: $(PPPDOBJS)
 	$(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
 
+srp-entry:	srp-entry.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ srp-entry.c $(LIBS)
+
+install-devel:
+	mkdir -p $(INCDIR)/pppd
+	$(INSTALL) -c -m 644 $(HEADERS) $(INCDIR)/pppd
+
 clean:
-	rm -f $(PPPDOBJS) pppd *~ #* core
+	rm -f $(PPPDOBJS) $(EXTRACLEAN) $(TARGETS) *~ #* core
 
 depend:
 	$(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
diff -Naur tomato/release/src/router/pppd/pppd/Makefile.linux tomato121/release/src/router/pppd/pppd/Makefile.linux
--- tomato/release/src/router/pppd/pppd/Makefile.linux	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -1,100 +1,166 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.1 2003/07/10 07:43:04 honor Exp $
+# $Id: Makefile.linux,v 1.69 2006/06/04 05:07:46 paulus Exp $
 #
 
-CFLAGS   += -DMIPSEL=1
-
 # Default installation locations
-BINDIR = /usr/sbin
-MANDIR = /usr/man
+DESTDIR = @DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+INCDIR = $(DESTDIR)/include
+
+TARGETS = pppd
+
+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
+
+HEADERS = ccp.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
 
-PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-	   ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
-	   demand.c utils.c multilink.c tdb.c tty.c
-HEADERS =  callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
-	   ipxcp.h cbcp.h tdb.h
 MANPAGES = pppd.8
-PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-	   auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
-	   tdb.o tty.o
-
-all: pppd
+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
 
 #
-# include dependancies if present and backup if as a header file
+# include dependencies if present
 ifeq (.depend,$(wildcard .depend))
 include .depend
 endif
 
-CROSS =mipsel-linux-
-CC = $(CROSS)gcc -s
-AR = $(CROSS)ar
-LD = $(CROSS)ld
-
-COPTS = -O2 -pipe -Wall -g
+# CC = gcc
+#
+#COPTS = -O2 -pipe -Wall -g
 LIBS =
 
-ifneq ($(wildcard /usr/lib/libcrypt.*),)
-LIBS += -lcrypt
-endif
-
 # Uncomment the next 2 lines to include support for Microsoft's
-# MS-CHAP authentication protocol.
-#CHAPMS=y
-#USE_CRYPT=y
-ifneq ($(wildcard /usr/lib/libcrypt.*),)
-HAVE_CRYPT_H=y
-endif
+# MS-CHAP authentication protocol.  Also, edit plugins/radius/Makefile.linux.
+CHAPMS=y
+USE_CRYPT=y
+# Don't use MSLANMAN unless you really know what you're doing.
+#MSLANMAN=y
+# Uncomment the next line to include support for MPPE.  CHAPMS (above) must
+# also be enabled.  Also, edit plugins/radius/Makefile.linux.
+MPPE=y
 
 # Uncomment the next line to include support for PPP packet filtering.
 # This requires that the libpcap library and headers be installed
-# and that the kernel driver support PPP packet filtering, which it
-# doesn't yet.
+# and that the kernel driver support PPP packet filtering.
 #FILTER=y
 
+# Uncomment the next line to enable multilink PPP (enabled by default)
+# Linux distributions: Please leave multilink ENABLED in your builds
+# of pppd!
+#HAVE_MULTILINK=y
+
+# Uncomment the next line to enable the TDB database (enabled by default.)
+# If you enable multilink, then TDB is automatically enabled also.
+# Linux distributions: Please leave TDB ENABLED in your builds.
+#USE_TDB=y
+
 #HAS_SHADOW=y
 #USE_PAM=y
 #HAVE_INET6=y
 
-#PLUGIN=y
+# Enable plugins
+PLUGIN=y
+
+# Enable Microsoft proprietary Callback Control Protocol
+#CBCP=y
+
+# Enable EAP SRP-SHA1 authentication (requires libsrp)
+#USE_SRP=y
+
+MAXOCTETS=y
 
 INCLUDE_DIRS= -I../include
 
-COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H  -DHAVE_MMAP
+COMPILE_FLAGS= -DHAVE_PATHS_H -DHAVE_MMAP
 
-CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
+CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) '-DDESTDIR="@DESTDIR@"' -ffunction-sections -fdata-sections -Wl,--gc-sections
 
 ifdef CHAPMS
 CFLAGS   += -DCHAPMS=1
-ifndef USE_CRYPT
-LIBS     := -ldes $(LIBS)
-else
-CFLAGS   += -DUSE_CRYPT=1
-ifneq ($(wildcard /usr/include/crypt.h),)
-CFLAGS   += -DHAVE_CRYPT_H=1
-endif
-endif
+NEEDDES=y
 PPPDOBJS += md4.o chap_ms.o
+HEADERS	+= md4.h chap_ms.h
 ifdef MSLANMAN
 CFLAGS   += -DMSLANMAN=1
 endif
+ifdef MPPE
+CFLAGS   += -DMPPE=1
+endif
+endif
+
+# EAP SRP-SHA1
+ifdef USE_SRP
+CFLAGS	+= -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include
+LIBS	+= -lsrp -L/usr/local/ssl/lib -lcrypto -ffunction-sections -fdata-sections -Wl,--gc-sections
+TARGETS	+= srp-entry
+EXTRAINSTALL = $(INSTALL) -c -m 555 srp-entry $(BINDIR)/srp-entry
+MANPAGES += srp-entry.8
+EXTRACLEAN += srp-entry.o
+NEEDDES=y
+else
+# OpenSSL has an integrated version of SHA-1, and its implementation
+# is incompatible with this local SHA-1 implementation.  We must use
+# one or the other, not both.
+PPPDSRCS += sha1.c
+HEADERS += sha1.h
+PPPDOBJS += sha1.o
 endif
 
 ifdef HAS_SHADOW
 CFLAGS   += -DHAS_SHADOW
-#LIBS     := -lshadow $(LIBS)
+#LIBS     += -lshadow $(LIBS)
+endif
+
+#ifneq ($(wildcard /usr/include/crypt.h),)
+CFLAGS   += -DHAVE_CRYPT_H=1
+#endif
+#ifneq ($(wildcard /usr/lib/libcrypt.*),)
+LIBS	+= -lcrypt
+#endif
+
+ifdef NEEDDES
+ifndef USE_CRYPT
+LIBS     += -ldes $(LIBS)
+else
+CFLAGS   += -DUSE_CRYPT=1
+endif
+PPPDOBJS += pppcrypt.o
+HEADERS += pppcrypt.h
 endif
 
 # For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
 ifdef USE_PAM
 CFLAGS   += -DUSE_PAM
-LIBS     := -lpam -ldl $(LIBS)
+LIBS     += -lpam -ldl
+endif
+
+# Multi-linnk
+ifdef HAVE_MULTILINK
+	# Multilink implies the use of TDB
+	USE_TDB=y
+
+	CFLAGS += -DHAVE_MULTILINK
+	PPPDSRCS += multilink.c
+	PPPDOBJS += multilink.o
+endif
+
+# TDB
+ifdef USE_TDB
+	CFLAGS += -DUSE_TDB=1
+	PPPDSRCS += tdb.c spinlock.c
+	PPPDOBJS += tdb.o spinlock.o
+	HEADERS += tdb.h spinlock.h
 endif
 
 # Lock library binary for Linux is included in 'linux' subdirectory.
 ifdef LOCKLIB
-LIBS     := -llock $(LIBS)
+LIBS     += -llock
 CFLAGS   += -DLOCKLIB=1
 endif
 
@@ -105,8 +171,10 @@
 endif
 
 ifdef FILTER
+ifneq ($(wildcard /usr/include/pcap-bpf.h),)
 LIBS    += -lpcap
-CFLAGS  += -DPPP_FILTER -I/usr/include/pcap
+CFLAGS  += -DPPP_FILTER
+endif
 endif
 
 ifdef HAVE_INET6
@@ -116,21 +184,41 @@
      CFLAGS   += -DINET6=1
 endif
 
+ifdef CBCP
+     PPPDSRCS += cbcp.c
+     PPPDOBJS += cbcp.o
+     CFLAGS += -DCBCP_SUPPORT
+     HEADERS += cbcp.h
+endif
+
+ifdef MAXOCTETS
+     CFLAGS += -DMAXOCTETS
+endif
+
+INSTALL= install
 
-INSTALL= install -o root
+all: $(TARGETS)
 
 install: pppd
 	mkdir -p $(BINDIR) $(MANDIR)
-	$(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd
+	$(EXTRAINSTALL)
+	$(INSTALL) -c -m 555 pppd $(BINDIR)/pppd
 	if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \
 	  chmod o-rx,u+s $(BINDIR)/pppd; fi
-	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8
+	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)
 
 pppd: $(PPPDOBJS)
 	$(CC) $(CFLAGS) $(LDFLAGS) -o pppd $(PPPDOBJS) $(LIBS)
 
+srp-entry:	srp-entry.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ srp-entry.c $(LIBS)
+
+install-devel:
+	mkdir -p $(INCDIR)/pppd
+	$(INSTALL) -c -m 644 $(HEADERS) $(INCDIR)/pppd
+
 clean:
-	rm -f $(PPPDOBJS) pppd *~ #* core
+	rm -f $(PPPDOBJS) $(EXTRACLEAN) $(TARGETS) *~ #* core
 
 depend:
 	$(CPP) -M $(CFLAGS) $(PPPDSRCS) >.depend
diff -Naur tomato/release/src/router/pppd/pppd/Makefile.sol2 tomato121/release/src/router/pppd/pppd/Makefile.sol2
--- tomato/release/src/router/pppd/pppd/Makefile.sol2	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/Makefile.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -1,16 +1,16 @@
 #
 # Makefile for pppd under Solaris 2.
-# $Id: Makefile.sol2,v 1.1 2003/07/10 07:43:04 honor Exp $
+# $Id: Makefile.sol2,v 1.27 2005/08/28 06:49:12 paulus Exp $
 #
 
-include ../solaris/Makedefs
+include ../Makedefs.com
 
-COPTS	+= -xO2 -xspace -W0,-Lt
-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.o md5.o tty.o \
-	ccp.o auth.o options.o demand.o utils.o	sys-solaris.o tdb.o
+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
 
 #
 # uncomment the following to enable plugins
@@ -32,6 +32,14 @@
 #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
+
+# Uncomment for CBCP
+#CFLAGS += -DCBCP_SUPPORT
+#OBJS += cbcp.o
+
 #
 # Make targets
 #
diff -Naur tomato/release/src/router/pppd/pppd/Makefile.sunos4 tomato121/release/src/router/pppd/pppd/Makefile.sunos4
--- tomato/release/src/router/pppd/pppd/Makefile.sunos4	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/Makefile.sunos4	1970-01-01 10:00:00.000000000 +1000
@@ -1,26 +0,0 @@
-#
-# Makefile for pppd under SunOS 4.
-# $Id: Makefile.sunos4,v 1.1 2003/07/10 07:43:04 honor Exp $
-#
-
-include ../sunos4/Makedefs
-
-LIBS =
-
-CFLAGS = $(COPTS) -I../include -DSUNOS4 -DGIDSET_TYPE=int \
-	-DLOCK_DIR=\"/usr/spool/locks\"
-
-all: pppd
-
-OBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-	auth.o options.o demand.o utils.o sys-sunos4.o tty.o
-
-pppd:	$(OBJS)
-	$(CC) -o pppd $(OBJS) $(LIBS)
-
-install:
-	$(INSTALL) -c -m 4555 pppd $(BINDIR)/pppd
-	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)/man8/pppd.8
-
-clean:
-	rm -f $(OBJS) pppd *~ core
diff -Naur tomato/release/src/router/pppd/pppd/md4.c tomato121/release/src/router/pppd/pppd/md4.c
--- tomato/release/src/router/pppd/pppd/md4.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/md4.c	2008-04-22 21:44:05.000000000 +1000
@@ -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 -Naur tomato/release/src/router/pppd/pppd/md5.c tomato121/release/src/router/pppd/pppd/md5.c
--- tomato/release/src/router/pppd/pppd/md5.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/md5.c	2008-04-22 21:44:05.000000000 +1000
@@ -33,15 +33,16 @@
  ***********************************************************************
  */
 
+#include <string.h>
 #include "md5.h"
 
 /*
  ***********************************************************************
  **  Message-digest routines:                                         **
  **  To form the message digest for a message M                       **
- **    (1) Initialize a context buffer mdContext using MD5Init        **
- **    (2) Call MD5Update on mdContext and M                          **
- **    (3) Call MD5Final on mdContext                                 **
+ **    (1) Initialize a context buffer mdContext using MD5_Init       **
+ **    (2) Call MD5_Update on mdContext and M                         **
+ **    (3) Call MD5_Final on mdContext                                **
  **  The message digest is now in mdContext->digest[0...15]           **
  ***********************************************************************
  */
@@ -98,10 +99,10 @@
 #define UL(x)	x
 #endif
 
-/* The routine MD5Init initializes the message-digest context
+/* The routine MD5_Init initializes the message-digest context
    mdContext. All fields are set to zero.
  */
-void MD5Init (mdContext)
+void MD5_Init (mdContext)
 MD5_CTX *mdContext;
 {
   mdContext->i[0] = mdContext->i[1] = (UINT4)0;
@@ -118,7 +119,7 @@
    account for the presence of each of the characters inBuf[0..inLen-1]
    in the message whose digest is being computed.
  */
-void MD5Update (mdContext, inBuf, inLen)
+void MD5_Update (mdContext, inBuf, inLen)
 MD5_CTX *mdContext;
 unsigned char *inBuf;
 unsigned int inLen;
@@ -156,7 +157,7 @@
 /* The routine MD5Final terminates the message-digest computation and
    ends with the desired message digest in mdContext->digest[0...15].
  */
-void MD5Final (hash, mdContext)
+void MD5_Final (hash, mdContext)
 unsigned char hash[];
 MD5_CTX *mdContext;
 {
@@ -174,7 +175,7 @@
 
   /* pad out to 56 mod 64 */
   padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
-  MD5Update (mdContext, PADDING, padLen);
+  MD5_Update (mdContext, PADDING, padLen);
 
   /* append length in bits and transform */
   for (i = 0, ii = 0; i < 14; i++, ii += 4)
diff -Naur tomato/release/src/router/pppd/pppd/md5.h tomato121/release/src/router/pppd/pppd/md5.h
--- tomato/release/src/router/pppd/pppd/md5.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/md5.h	2008-04-22 21:44:05.000000000 +1000
@@ -40,7 +40,14 @@
 #ifndef __MD5_INCLUDE__
 
 /* typedef a 32-bit type */
+#ifdef _LP64
 typedef unsigned int UINT4;
+typedef int          INT4;
+#else
+typedef unsigned long UINT4;
+typedef long          INT4;
+#endif
+#define _UINT4_T
 
 /* Data structure for MD5 (Message-Digest) computation */
 typedef struct {
@@ -50,9 +57,9 @@
   unsigned char digest[16];     /* actual digest after MD5Final call */
 } MD5_CTX;
 
-void MD5Init ();
-void MD5Update ();
-void MD5Final ();
+void MD5_Init ();
+void MD5_Update ();
+void MD5_Final ();
 
 #define __MD5_INCLUDE__
 #endif /* __MD5_INCLUDE__ */
diff -Naur tomato/release/src/router/pppd/pppd/multilink.c tomato121/release/src/router/pppd/pppd/multilink.c
--- tomato/release/src/router/pppd/pppd/multilink.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/multilink.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,17 +1,31 @@
 /*
  * multilink.c - support routines for multilink.
  *
- * Copyright (c) 2000 Paul Mackerras.
- * All rights reserved.
+ * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms.  The name of the author may not be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 #include <string.h>
 #include <ctype.h>
@@ -20,6 +34,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <netinet/in.h>
+#include <unistd.h>
 
 #include "pppd.h"
 #include "fsm.h"
@@ -28,10 +43,17 @@
 
 bool endpoint_specified;	/* user gave explicit endpoint discriminator */
 char *bundle_id;		/* identifier for our bundle */
+char *blinks_id;		/* key for the list of links */
+bool doing_multilink;		/* multilink was enabled and agreed to */
+bool multilink_master;		/* we own the multilink bundle */
 
 extern TDB_CONTEXT *pppdb;
 extern char db_key[];
 
+static void make_bundle_links __P((int append));
+static void remove_bundle_link __P((void));
+static void iterate_bundle_links __P((void (*func) __P((char *))));
+
 static int get_default_epdisc __P((struct epdisc *));
 static int parse_num __P((char *str, const char *key, int *valp));
 static int owns_unit __P((TDB_DATA pid, int unit));
@@ -57,6 +79,7 @@
 	lcp_options *wo = &lcp_wantoptions[0];
 	lcp_options *ao = &lcp_allowoptions[0];
 
+	doing_multilink = 0;
 	if (!multilink)
 		return;
 	/* if we're doing multilink, we have to negotiate MRRU */
@@ -89,6 +112,18 @@
 	char *p;
 	TDB_DATA key, pid, rec;
 
+	if (doing_multilink) {
+		/* have previously joined a bundle */
+		if (!go->neg_mrru || !ho->neg_mrru) {
+			notice("oops, didn't get multilink on renegotiation");
+			lcp_close(0, "multilink required");
+			return 0;
+		}
+		/* XXX should check the peer_authname and ho->endpoint
+		   are the same as previously */
+		return 0;
+	}
+
 	if (!go->neg_mrru || !ho->neg_mrru) {
 		/* not doing multilink */
 		if (go->neg_mrru)
@@ -108,6 +143,8 @@
 		return 0;
 	}
 
+	doing_multilink = 1;
+
 	/*
 	 * Find the appropriate bundle or join a new one.
 	 * First we make up a name for the bundle.
@@ -133,6 +170,13 @@
 	if (bundle_name)
 		p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
 
+	/* Make the key for the list of links belonging to the bundle */
+	l = p - bundle_id;
+	blinks_id = malloc(l + 7);
+	if (blinks_id == NULL)
+		novm("bundle links key");
+	slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
+
 	/*
 	 * For demand mode, we only need to configure the bundle
 	 * and attach the link.
@@ -149,15 +193,17 @@
 	 * Check if the bundle ID is already in the database.
 	 */
 	unit = -1;
-	tdb_writelock(pppdb);
+	lock_db();
 	key.dptr = bundle_id;
 	key.dsize = p - bundle_id;
 	pid = tdb_fetch(pppdb, key);
 	if (pid.dptr != NULL) {
 		/* bundle ID exists, see if the pppd record exists */
 		rec = tdb_fetch(pppdb, pid);
-		if (rec.dptr != NULL) {
-			/* it is, parse the interface number */
+		if (rec.dptr != NULL && rec.dsize > 0) {
+			/* make sure the string is null-terminated */
+			rec.dptr[rec.dsize-1] = 0;
+			/* parse the interface number */
 			parse_num(rec.dptr, "IFNAME=ppp", &unit);
 			/* check the pid value */
 			if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
@@ -174,7 +220,8 @@
 		if (bundle_attach(unit)) {
 			set_ifunit(0);
 			script_setenv("BUNDLE", bundle_id + 7, 0);
-			tdb_writeunlock(pppdb);
+			make_bundle_links(1);
+			unlock_db();
 			info("Link attached to %s", ifname);
 			return 1;
 		}
@@ -186,11 +233,157 @@
 	set_ifunit(1);
 	netif_set_mtu(0, mtu);
 	script_setenv("BUNDLE", bundle_id + 7, 1);
-	tdb_writeunlock(pppdb);
+	make_bundle_links(0);
+	unlock_db();
 	info("New bundle %s created", ifname);
+	multilink_master = 1;
 	return 0;
 }
 
+void mp_exit_bundle()
+{
+	lock_db();
+	remove_bundle_link();
+	unlock_db();
+}
+
+static void sendhup(char *str)
+{
+	int pid;
+
+	if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
+		if (debug)
+			dbglog("sending SIGHUP to process %d", pid);
+		kill(pid, SIGHUP);
+	}
+}
+
+void mp_bundle_terminated()
+{
+	TDB_DATA key;
+
+	bundle_terminating = 1;
+	upper_layers_down(0);
+	notice("Connection terminated.");
+	print_link_stats();
+	if (!demand) {
+		remove_pidfiles();
+		script_unsetenv("IFNAME");
+	}
+
+	lock_db();
+	destroy_bundle();
+	iterate_bundle_links(sendhup);
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	tdb_delete(pppdb, key);
+	unlock_db();
+	
+new_phase(PHASE_DEAD);
+}
+
+static void make_bundle_links(int append)
+{
+	TDB_DATA key, rec;
+	char *p;
+	char entry[32];
+	int l;
+
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	slprintf(entry, sizeof(entry), "%s;", db_key);
+	p = entry;
+	if (append) {
+		rec = tdb_fetch(pppdb, key);
+		if (rec.dptr != NULL && rec.dsize > 0) {
+			rec.dptr[rec.dsize-1] = 0;
+			if (strstr(rec.dptr, db_key) != NULL) {
+				/* already in there? strange */
+				warn("link entry already exists in tdb");
+				return;
+			}
+			l = rec.dsize + strlen(entry);
+			p = malloc(l);
+			if (p == NULL)
+				novm("bundle link list");
+			slprintf(p, l, "%s%s", rec.dptr, entry);
+		} else {
+			warn("bundle link list not found");
+		}
+		if (rec.dptr != NULL)
+			free(rec.dptr);
+	}
+	rec.dptr = p;
+	rec.dsize = strlen(p) + 1;
+	if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+		error("couldn't %s bundle link list",
+		      append? "update": "create");
+	if (p != entry)
+		free(p);
+}
+
+static void remove_bundle_link()
+{
+	TDB_DATA key, rec;
+	char entry[32];
+	char *p, *q;
+	int l;
+
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	slprintf(entry, sizeof(entry), "%s;", db_key);
+
+	rec = tdb_fetch(pppdb, key);
+	if (rec.dptr == NULL || rec.dsize <= 0) {
+		if (rec.dptr != NULL)
+			free(rec.dptr);
+		return;
+	}
+	rec.dptr[rec.dsize-1] = 0;
+	p = strstr(rec.dptr, entry);
+	if (p != NULL) {
+		q = p + strlen(entry);
+		l = strlen(q) + 1;
+		memmove(p, q, l);
+		rec.dsize = p - rec.dptr + l;
+		if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+			error("couldn't update bundle link list (removal)");
+	}
+	free(rec.dptr);
+}
+
+static void iterate_bundle_links(void (*func)(char *))
+{
+	TDB_DATA key, rec, pp;
+	char *p, *q;
+
+	key.dptr = blinks_id;
+	key.dsize = strlen(blinks_id);
+	rec = tdb_fetch(pppdb, key);
+	if (rec.dptr == NULL || rec.dsize <= 0) {
+		error("bundle link list not found (iterating list)");
+		if (rec.dptr != NULL)
+			free(rec.dptr);
+		return;
+	}
+	p = rec.dptr;
+	p[rec.dsize-1] = 0;
+	while ((q = strchr(p, ';')) != NULL) {
+		*q = 0;
+		key.dptr = p;
+		key.dsize = q - p;
+		pp = tdb_fetch(pppdb, key);
+		if (pp.dptr != NULL && pp.dsize > 0) {
+			pp.dptr[pp.dsize-1] = 0;
+			func(pp.dptr);
+		}
+		if (pp.dptr != NULL)
+			free(pp.dptr);
+		p = q + 1;
+	}
+	free(rec.dptr);
+}
+
 static int
 parse_num(str, key, valp)
      char *str;
diff -Naur tomato/release/src/router/pppd/pppd/options.c tomato121/release/src/router/pppd/pppd/options.c
--- tomato/release/src/router/pppd/pppd/options.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/options.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,46 @@
 /*
  * options.c - handles option processing for PPP.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: options.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: options.c,v 1.100 2006/06/18 11:26:00 paulus Exp $"
 
 #include <ctype.h>
 #include <stdio.h>
@@ -31,10 +54,23 @@
 #ifdef PLUGIN
 #include <dlfcn.h>
 #endif
+
 #ifdef PPP_FILTER
 #include <pcap.h>
-#include <pcap-int.h>	/* XXX: To get struct pcap */
+/*
+ * 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_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"
@@ -54,9 +90,6 @@
 /*
  * Option variables and default values.
  */
-#ifdef PPP_FILTER
-int	dflag = 0;		/* Tell libpcap we want debugging */
-#endif
 int	debug = 0;		/* Debug flag */
 int	kdebugflag = 0;		/* Tell kernel to print debug messages */
 int	default_device = 1;	/* Using /dev/tty or equivalent */
@@ -70,6 +103,12 @@
 char	our_name[MAXNAMELEN];	/* Our name for authentication purposes */
 bool	demand = 0;		/* do dial-on-demand */
 char	*ipparam = NULL;	/* Extra parameter for ip up/down scripts */
+char	*ipupcustom = NULL;	/* Custom ip up scripts */
+char	*ipdowncustom = NULL;	/* Custom ip down scripts */
+char	*chapseccustom = NULL;	/* Custom chap-secrets file */
+char	*papseccustom = NULL;	/* Custom pap-secrets file */
+char	*srpseccustom = NULL;	/* Custom pap-secrets file */
+
 int	idle_time_limit = 0;	/* Disconnect if idle for this many seconds */
 int	holdoff = 30;		/* # seconds to pause before reconnecting */
 bool	holdoff_specified;	/* true if a holdoff value has been given */
@@ -85,6 +124,14 @@
 bool	dump_options;		/* print out option values */
 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 */
+
+#ifdef MAXOCTETS
+unsigned int  maxoctets = 0;    /* default - no limit */
+int maxoctets_dir = 0;       /* default - sum of traffic */
+int maxoctets_timeout = 1;   /* default 1 second */ 
+#endif
+
 
 extern option_t auth_options[];
 extern struct stat devstat;
@@ -92,7 +139,6 @@
 #ifdef PPP_FILTER
 struct	bpf_program pass_filter;/* Filter program for packets to pass */
 struct	bpf_program active_filter; /* Filter program for link-active pkts */
-pcap_t  pc;			/* Fake struct pcap so we can compile expr */
 #endif
 
 char *current_option;		/* the name of the option being parsed */
@@ -112,7 +158,8 @@
 static int callfile __P((char **));
 static int showversion __P((char **));
 static int showhelp __P((char **));
-static void usage __P((void));
+#define usage() { }
+//static void usage __P((void));
 static int setlogfile __P((char **));
 #ifdef PLUGIN
 static int loadplugin __P((char **));
@@ -123,6 +170,10 @@
 static int setactivefilter __P((char **));
 #endif
 
+#ifdef MAXOCTETS
+static int setmodir __P((char **));
+#endif
+
 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 *));
@@ -160,7 +211,8 @@
       OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
 
     { "holdoff", o_int, &holdoff,
-      "Set time in seconds before retrying connection", OPT_PRIO },
+      "Set time in seconds before retrying connection",
+      OPT_PRIO, &holdoff_specified },
 
     { "idle", o_int, &idle_time_limit,
       "Set time in seconds before disconnecting idle link", OPT_PRIO },
@@ -232,6 +284,10 @@
     { "dryrun", o_bool, &dryrun,
       "Stop after parsing, printing, and checking options", 1 },
 
+    { "child-timeout", o_int, &child_wait,
+      "Number of seconds to wait for child processes at exit",
+      OPT_PRIO },
+
 #ifdef HAVE_MULTILINK
     { "multilink", o_bool, &multilink,
       "Enable multilink operation", OPT_PRIO | 1 },
@@ -252,16 +308,26 @@
 #endif
 
 #ifdef PPP_FILTER
-    { "pdebug", o_int, &dflag,
-      "libpcap debugging", OPT_PRIO },
-
-    { "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
 
+#ifdef MAXOCTETS
+    { "maxoctets", o_int, &maxoctets,
+      "Set connection traffic limit",
+      OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+    { "mo", o_int, &maxoctets,
+      "Set connection traffic limit",
+      OPT_ALIAS | OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+    { "mo-direction", o_special, setmodir,
+      "Set direction for limit traffic (sum,in,out,max)" },
+    { "mo-timeout", o_int, &maxoctets_timeout,
+      "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 },
+#endif
+
     { NULL }
 };
 
@@ -340,16 +406,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) {
@@ -440,8 +510,8 @@
     size_t pl;
 
     dev = devnam;
-    if (strncmp(dev, "/dev/", 5) == 0)
-	dev += 5;
+    if ((p = strstr(dev, "/dev/")) != NULL)
+	dev = p + 5;
     if (dev[0] == 0 || strcmp(dev, "tty") == 0)
 	return 1;		/* don't look for /etc/ppp/options.tty */
     pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
@@ -578,6 +648,7 @@
     int prio = option_priority;
     option_t *mainopt = opt;
 
+    current_option = opt->name;
     if ((opt->flags & OPT_PRIVFIX) && privileged_option)
 	prio += OPRIO_ROOT;
     while (mainopt->flags & OPT_PRIOSUB)
@@ -623,6 +694,12 @@
 	*(bool *)(opt->addr) = v;
 	if (opt->addr2 && (opt->flags & OPT_A2COPY))
 	    *(bool *)(opt->addr2) = v;
+	else if (opt->addr2 && (opt->flags & OPT_A2CLR))
+	    *(bool *)(opt->addr2) = 0;
+	else if (opt->addr2 && (opt->flags & OPT_A2CLRB))
+	    *(u_char *)(opt->addr2) &= ~v;
+	else if (opt->addr2 && (opt->flags & OPT_A2OR))
+	    *(u_char *)(opt->addr2) |= v;
 	break;
 
     case o_int:
@@ -645,7 +722,7 @@
 		    break;
 		case OPT_LIMITS:
 		    option_error("%s value must be%s between %d and %d",
-				opt->name, opt->lower_limit, opt->upper_limit);
+				opt->name, zok, opt->lower_limit, opt->upper_limit);
 		    break;
 		}
 		return 0;
@@ -724,7 +801,7 @@
     }
 
     if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
-		|OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST)) == 0)
+		|OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0)
 	*(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
 
     mainopt->source = option_source;
@@ -870,7 +947,9 @@
 			void (*oprt) __P((option_t *,
 					  void ((*)__P((void *, char *, ...))),
 					  void *));
-			oprt = opt->addr2;
+			oprt = (void (*) __P((option_t *,
+					 void ((*)__P((void *, char *, ...))),
+					 void *)))opt->addr2;
 			(*oprt)(opt, printer, arg);
 		} else if (opt->flags & OPT_A2STRVAL) {
 			p = (char *) opt->addr2;
@@ -894,7 +973,7 @@
 		break;
 
 	default:
-		printer(arg, "# %s value (type %d??)", opt->name, opt->type);
+		printer(arg, "# %s value (type %d\?\?)", opt->name, opt->type);
 		break;
 	}
 	printer(arg, "\t\t# (from %s)\n", mainopt->source);
@@ -944,12 +1023,12 @@
 /*
  * usage - print out a message telling how to use the program.
  */
-static void
-usage()
-{
-    if (phase == PHASE_INITIALIZE)
-	fprintf(stderr, usage_string, VERSION, progname);
-}
+//static void
+//usage()
+//{
+//    if (phase == PHASE_INITIALIZE)
+//	fprintf(stderr, usage_string, VERSION, progname);
+//}
 
 /*
  * showhelp - print out usage message and exit.
@@ -1383,13 +1462,18 @@
 setpassfilter(argv)
     char **argv;
 {
-    pc.linktype = DLT_PPP;
-    pc.snapshot = PPP_HDRLEN;
- 
-    if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
-	return 1;
-    option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
-    return 0;
+    pcap_t *pc;
+    int ret = 1;
+
+    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 = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
 }
 
 /*
@@ -1399,13 +1483,18 @@
 setactivefilter(argv)
     char **argv;
 {
-    pc.linktype = DLT_PPP;
-    pc.snapshot = PPP_HDRLEN;
- 
-    if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
-	return 1;
-    option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
-    return 0;
+    pcap_t *pc;
+    int ret = 1;
+
+    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 = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
 }
 #endif
 
@@ -1427,21 +1516,24 @@
     return (1);
 }
 
-
 static int
 setlogfile(argv)
     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);
@@ -1456,6 +1548,26 @@
     return 1;
 }
 
+#ifdef MAXOCTETS
+static int
+setmodir(argv)
+    char **argv;
+{
+    if(*argv == NULL)
+	return 0;
+    if(!strcmp(*argv,"in")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_IN;
+    } else if (!strcmp(*argv,"out")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_OUT;
+    } else if (!strcmp(*argv,"max")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_MAXOVERAL;
+    } else {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_SUM;
+    }
+    return 1;
+}
+#endif
+
 #ifdef PLUGIN
 static int
 loadplugin(argv)
@@ -1496,7 +1608,7 @@
 	warn("Warning: plugin %s has no version information", arg);
     } else if (strcmp(vers, VERSION) != 0) {
 	option_error("Plugin %s is for pppd version %s, this is %s",
-		     vers, VERSION);
+		     arg, vers, VERSION);
 	goto errclose;
     }
     info("Plugin %s loaded.", arg);
diff -Naur tomato/release/src/router/pppd/pppd/options.c.orig tomato121/release/src/router/pppd/pppd/options.c.orig
--- tomato/release/src/router/pppd/pppd/options.c.orig	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/options.c.orig	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,1616 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * 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.
+ */
+
+#define RCSID	"$Id: options.c,v 1.98 2005/07/13 12:31:36 paulus Exp $"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <pwd.h>
+#ifdef PLUGIN
+#include <dlfcn.h>
+#endif
+
+#ifdef PPP_FILTER
+#include <pcap.h>
+/*
+ * 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_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"
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup __P((char *));
+#endif
+
+static const char rcsid[] = RCSID;
+
+struct option_value {
+    struct option_value *next;
+    const char *source;
+    char value[1];
+};
+
+/*
+ * Option variables and default values.
+ */
+int	debug = 0;		/* Debug flag */
+int	kdebugflag = 0;		/* Tell kernel to print debug messages */
+int	default_device = 1;	/* Using /dev/tty or equivalent */
+char	devnam[MAXPATHLEN];	/* Device name */
+bool	nodetach = 0;		/* Don't detach from controlling tty */
+bool	updetach = 0;		/* Detach once link is up */
+int	maxconnect = 0;		/* Maximum connect time */
+char	user[MAXNAMELEN];	/* Username for PAP */
+char	passwd[MAXSECRETLEN];	/* Password for PAP */
+bool	persist = 0;		/* Reopen link after it goes down */
+char	our_name[MAXNAMELEN];	/* Our name for authentication purposes */
+bool	demand = 0;		/* do dial-on-demand */
+char	*ipparam = NULL;	/* Extra parameter for ip up/down scripts */
+char	*ipupcustom = NULL;	/* Custom ip up scripts */
+char	*ipdowncustom = NULL;	/* Custom ip down scripts */
+char	*chapseccustom = NULL;	/* Custom chap-secrets file */
+char	*papseccustom = NULL;	/* Custom pap-secrets file */
+char	*srpseccustom = NULL;	/* Custom pap-secrets file */
+
+int	idle_time_limit = 0;	/* Disconnect if idle for this many seconds */
+int	holdoff = 30;		/* # seconds to pause before reconnecting */
+bool	holdoff_specified;	/* true if a holdoff value has been given */
+int	log_to_fd = 1;		/* send log messages to this fd too */
+bool	log_default = 1;	/* log_to_fd is default (stdout) */
+int	maxfail = 10;		/* max # of unsuccessful connection attempts */
+char	linkname[MAXPATHLEN];	/* logical name for link */
+bool	tune_kernel;		/* may alter kernel settings */
+int	connect_delay = 1000;	/* wait this many ms after connect script */
+int	req_unit = -1;		/* requested interface unit */
+bool	multilink = 0;		/* Enable multilink operation */
+char	*bundle_name = NULL;	/* bundle name for multilink */
+bool	dump_options;		/* print out option values */
+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 */
+
+#ifdef MAXOCTETS
+unsigned int  maxoctets = 0;    /* default - no limit */
+int maxoctets_dir = 0;       /* default - sum of traffic */
+int maxoctets_timeout = 1;   /* default 1 second */ 
+#endif
+
+
+extern option_t auth_options[];
+extern struct stat devstat;
+
+#ifdef PPP_FILTER
+struct	bpf_program pass_filter;/* Filter program for packets to pass */
+struct	bpf_program active_filter; /* Filter program for link-active pkts */
+#endif
+
+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 */
+int  option_priority = OPRIO_CFGFILE; /* priority of the current options */
+bool devnam_fixed;		/* can no longer change device name */
+
+static int logfile_fd = -1;	/* fd opened for log file */
+static char logfile_name[MAXPATHLEN];	/* name of log file */
+
+/*
+ * Prototypes
+ */
+static int setdomain __P((char **));
+static int readfile __P((char **));
+static int callfile __P((char **));
+static int showversion __P((char **));
+static int showhelp __P((char **));
+static void usage __P((void));
+static int setlogfile __P((char **));
+#ifdef PLUGIN
+static int loadplugin __P((char **));
+#endif
+
+#ifdef PPP_FILTER
+static int setpassfilter __P((char **));
+static int setactivefilter __P((char **));
+#endif
+
+#ifdef MAXOCTETS
+static int setmodir __P((char **));
+#endif
+
+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 *));
+static int number_option __P((char *, u_int32_t *, int));
+
+/*
+ * Structure to store extra lists of options.
+ */
+struct option_list {
+    option_t *options;
+    struct option_list *next;
+};
+
+static struct option_list *extra_options = NULL;
+
+/*
+ * Valid arguments.
+ */
+option_t general_options[] = {
+    { "debug", o_int, &debug,
+      "Increase debugging level", OPT_INC | OPT_NOARG | 1 },
+    { "-d", o_int, &debug,
+      "Increase debugging level",
+      OPT_ALIAS | OPT_INC | OPT_NOARG | 1 },
+
+    { "kdebug", o_int, &kdebugflag,
+      "Set kernel driver debug level", OPT_PRIO },
+
+    { "nodetach", o_bool, &nodetach,
+      "Don't detach from controlling tty", OPT_PRIO | 1 },
+    { "-detach", o_bool, &nodetach,
+      "Don't detach from controlling tty", OPT_ALIAS | OPT_PRIOSUB | 1 },
+    { "updetach", o_bool, &updetach,
+      "Detach from controlling tty once link is up",
+      OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
+
+    { "holdoff", o_int, &holdoff,
+      "Set time in seconds before retrying connection",
+      OPT_PRIO, &holdoff_specified },
+
+    { "idle", o_int, &idle_time_limit,
+      "Set time in seconds before disconnecting idle link", OPT_PRIO },
+
+    { "maxconnect", o_int, &maxconnect,
+      "Set connection time limit",
+      OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+
+    { "domain", o_special, (void *)setdomain,
+      "Add given domain name to hostname",
+      OPT_PRIO | OPT_PRIV | OPT_A2STRVAL, &domain },
+
+    { "file", o_special, (void *)readfile,
+      "Take options from a file", OPT_NOPRINT },
+    { "call", o_special, (void *)callfile,
+      "Take options from a privileged file", OPT_NOPRINT },
+
+    { "persist", o_bool, &persist,
+      "Keep on reopening connection after close", OPT_PRIO | 1 },
+    { "nopersist", o_bool, &persist,
+      "Turn off persist option", OPT_PRIOSUB },
+
+    { "demand", o_bool, &demand,
+      "Dial on demand", OPT_INITONLY | 1, &persist },
+
+    { "--version", o_special_noarg, (void *)showversion,
+      "Show version number" },
+    { "--help", o_special_noarg, (void *)showhelp,
+      "Show brief listing of options" },
+    { "-h", o_special_noarg, (void *)showhelp,
+      "Show brief listing of options", OPT_ALIAS },
+
+    { "logfile", o_special, (void *)setlogfile,
+      "Append log messages to this file",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, &logfile_name },
+    { "logfd", o_int, &log_to_fd,
+      "Send log messages to this file descriptor",
+      OPT_PRIOSUB | OPT_A2CLR, &log_default },
+    { "nolog", o_int, &log_to_fd,
+      "Don't send log messages to any file",
+      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
+    { "nologfd", o_int, &log_to_fd,
+      "Don't send log messages to any file descriptor",
+      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
+
+    { "linkname", o_string, linkname,
+      "Set logical name for link",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN },
+
+    { "maxfail", o_int, &maxfail,
+      "Maximum number of unsuccessful connection attempts to allow",
+      OPT_PRIO },
+
+    { "ktune", o_bool, &tune_kernel,
+      "Alter kernel settings as necessary", OPT_PRIO | 1 },
+    { "noktune", o_bool, &tune_kernel,
+      "Don't alter kernel settings", OPT_PRIOSUB },
+
+    { "connect-delay", o_int, &connect_delay,
+      "Maximum time (in ms) to wait after connect script finishes",
+      OPT_PRIO },
+
+    { "unit", o_int, &req_unit,
+      "PPP interface unit number to use if possible",
+      OPT_PRIO | OPT_LLIMIT, 0, 0 },
+
+    { "dump", o_bool, &dump_options,
+      "Print out option values after parsing all options", 1 },
+    { "dryrun", o_bool, &dryrun,
+      "Stop after parsing, printing, and checking options", 1 },
+
+    { "child-timeout", o_int, &child_wait,
+      "Number of seconds to wait for child processes at exit",
+      OPT_PRIO },
+
+#ifdef HAVE_MULTILINK
+    { "multilink", o_bool, &multilink,
+      "Enable multilink operation", OPT_PRIO | 1 },
+    { "mp", o_bool, &multilink,
+      "Enable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 1 },
+    { "nomultilink", o_bool, &multilink,
+      "Disable multilink operation", OPT_PRIOSUB | 0 },
+    { "nomp", o_bool, &multilink,
+      "Disable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 0 },
+
+    { "bundle", o_string, &bundle_name,
+      "Bundle name for multilink", OPT_PRIO },
+#endif /* HAVE_MULTILINK */
+
+#ifdef PLUGIN
+    { "plugin", o_special, (void *)loadplugin,
+      "Load a plug-in module into pppd", OPT_PRIV | OPT_A2LIST },
+#endif
+
+#ifdef PPP_FILTER
+    { "pass-filter", o_special, setpassfilter,
+      "set filter for packets to pass", OPT_PRIO },
+
+    { "active-filter", o_special, setactivefilter,
+      "set filter for active pkts", OPT_PRIO },
+#endif
+
+#ifdef MAXOCTETS
+    { "maxoctets", o_int, &maxoctets,
+      "Set connection traffic limit",
+      OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+    { "mo", o_int, &maxoctets,
+      "Set connection traffic limit",
+      OPT_ALIAS | OPT_PRIO | OPT_LLIMIT | OPT_NOINCR | OPT_ZEROINF },
+    { "mo-direction", o_special, setmodir,
+      "Set direction for limit traffic (sum,in,out,max)" },
+    { "mo-timeout", o_int, &maxoctets_timeout,
+      "Check for traffic limit every N seconds", OPT_PRIO | OPT_LLIMIT | 1 },
+#endif
+
+    { NULL }
+};
+
+#ifndef IMPLEMENTATION
+#define IMPLEMENTATION ""
+#endif
+
+static char *usage_string = "\
+pppd version %s\n\
+Usage: %s [ options ], where options are:\n\
+	<device>	Communicate over the named device\n\
+	<speed>		Set the baud rate to <speed>\n\
+	<loc>:<rem>	Set the local and/or remote interface IP\n\
+			addresses.  Either one may be omitted.\n\
+	asyncmap <n>	Set the desired async map to hex <n>\n\
+	auth		Require authentication from peer\n\
+        connect <p>     Invoke shell command <p> to set up the serial line\n\
+	crtscts		Use hardware RTS/CTS flow control\n\
+	defaultroute	Add default route through interface\n\
+	file <f>	Take options from file <f>\n\
+	modem		Use modem control lines\n\
+	mru <n>		Set MRU value to <n> for negotiation\n\
+See pppd(8) for more options.\n\
+";
+
+/*
+ * parse_args - parse a string of arguments from the command line.
+ */
+int
+parse_args(argc, argv)
+    int argc;
+    char **argv;
+{
+    char *arg;
+    option_t *opt;
+    int n;
+
+    privileged_option = privileged;
+    option_source = "command line";
+    option_priority = OPRIO_CMDLINE;
+    while (argc > 0) {
+	arg = *argv++;
+	--argc;
+	opt = find_option(arg);
+	if (opt == NULL) {
+	    option_error("unrecognized option '%s'", arg);
+	    usage();
+	    return 0;
+	}
+	n = n_arguments(opt);
+	if (argc < n) {
+	    option_error("too few parameters for option %s", arg);
+	    return 0;
+	}
+	if (!process_option(opt, arg, argv))
+	    return 0;
+	argc -= n;
+	argv += n;
+    }
+    return 1;
+}
+
+/*
+ * options_from_file - Read a string of options from a file,
+ * and interpret them.
+ */
+int
+options_from_file(filename, must_exist, check_prot, priv)
+    char *filename;
+    int must_exist;
+    int check_prot;
+    int priv;
+{
+    FILE *f;
+    int i, newline, ret, err;
+    option_t *opt;
+    int oldpriv, n;
+    char *oldsource;
+    char *argv[MAXARGS];
+    char args[MAXARGS][MAXWORDLEN];
+    char cmd[MAXWORDLEN];
+
+    if (check_prot)
+	seteuid(getuid());
+    f = fopen(filename, "r");
+    err = errno;
+    if (check_prot)
+	seteuid(0);
+    if (f == NULL) {
+	errno = err;
+	if (!must_exist) {
+	    if (err != ENOENT && err != ENOTDIR)
+		warn("Warning: can't open options file %s: %m", filename);
+	    return 1;
+	}
+	option_error("Can't open options file %s: %m", filename);
+	return 0;
+    }
+
+    oldpriv = privileged_option;
+    privileged_option = priv;
+    oldsource = option_source;
+    option_source = strdup(filename);
+    if (option_source == NULL)
+	option_source = "file";
+    ret = 0;
+    while (getword(f, cmd, &newline, filename)) {
+	opt = find_option(cmd);
+	if (opt == NULL) {
+	    option_error("In file %s: unrecognized option '%s'",
+			 filename, cmd);
+	    goto err;
+	}
+	n = n_arguments(opt);
+	for (i = 0; i < n; ++i) {
+	    if (!getword(f, args[i], &newline, filename)) {
+		option_error(
+			"In file %s: too few parameters for option '%s'",
+			filename, cmd);
+		goto err;
+	    }
+	    argv[i] = args[i];
+	}
+	if (!process_option(opt, cmd, argv))
+	    goto err;
+    }
+    ret = 1;
+
+err:
+    fclose(f);
+    privileged_option = oldpriv;
+    option_source = oldsource;
+    return ret;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+    char *user, *path, *file;
+    int ret;
+    struct passwd *pw;
+    size_t pl;
+
+    pw = getpwuid(getuid());
+    if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
+	return 1;
+    file = _PATH_USEROPT;
+    pl = strlen(user) + strlen(file) + 2;
+    path = malloc(pl);
+    if (path == NULL)
+	novm("init file name");
+    slprintf(path, pl, "%s/%s", user, file);
+    option_priority = OPRIO_CFGFILE;
+    ret = options_from_file(path, 0, 1, privileged);
+    free(path);
+    return ret;
+}
+
+/*
+ * options_for_tty - See if an options file exists for the serial
+ * device, and if so, interpret options from it.
+ * We only allow the per-tty options file to override anything from
+ * the command line if it is something that the user can't override
+ * once it has been set by root; this is done by giving configuration
+ * files a lower priority than the command line.
+ */
+int
+options_for_tty()
+{
+    char *dev, *path, *p;
+    int ret;
+    size_t pl;
+
+    dev = devnam;
+    if ((p = strstr(dev, "/dev/")) != NULL)
+	dev = p + 5;
+    if (dev[0] == 0 || strcmp(dev, "tty") == 0)
+	return 1;		/* don't look for /etc/ppp/options.tty */
+    pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
+    path = malloc(pl);
+    if (path == NULL)
+	novm("tty init file name");
+    slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev);
+    /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+    for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p)
+	if (*p == '/')
+	    *p = '.';
+    option_priority = OPRIO_CFGFILE;
+    ret = options_from_file(path, 0, 0, 1);
+    free(path);
+    return ret;
+}
+
+/*
+ * options_from_list - process a string of options in a wordlist.
+ */
+int
+options_from_list(w, priv)
+    struct wordlist *w;
+    int priv;
+{
+    char *argv[MAXARGS];
+    option_t *opt;
+    int i, n, ret = 0;
+    struct wordlist *w0;
+
+    privileged_option = priv;
+    option_source = "secrets file";
+    option_priority = OPRIO_SECFILE;
+
+    while (w != NULL) {
+	opt = find_option(w->word);
+	if (opt == NULL) {
+	    option_error("In secrets file: unrecognized option '%s'",
+			 w->word);
+	    goto err;
+	}
+	n = n_arguments(opt);
+	w0 = w;
+	for (i = 0; i < n; ++i) {
+	    w = w->next;
+	    if (w == NULL) {
+		option_error(
+			"In secrets file: too few parameters for option '%s'",
+			w0->word);
+		goto err;
+	    }
+	    argv[i] = w->word;
+	}
+	if (!process_option(opt, w0->word, argv))
+	    goto err;
+	w = w->next;
+    }
+    ret = 1;
+
+err:
+    return ret;
+}
+
+/*
+ * match_option - see if this option matches an option_t structure.
+ */
+static int
+match_option(name, opt, dowild)
+    char *name;
+    option_t *opt;
+    int dowild;
+{
+	int (*match) __P((char *, char **, int));
+
+	if (dowild != (opt->type == o_wild))
+		return 0;
+	if (!dowild)
+		return strcmp(name, opt->name) == 0;
+	match = (int (*) __P((char *, char **, int))) opt->addr;
+	return (*match)(name, NULL, 0);
+}
+
+/*
+ * find_option - scan the option lists for the various protocols
+ * looking for an entry with the given name.
+ * This could be optimized by using a hash table.
+ */
+static option_t *
+find_option(name)
+    const char *name;
+{
+	option_t *opt;
+	struct option_list *list;
+	int i, dowild;
+
+	for (dowild = 0; dowild <= 1; ++dowild) {
+		for (opt = general_options; opt->name != NULL; ++opt)
+			if (match_option(name, opt, dowild))
+				return opt;
+		for (opt = auth_options; opt->name != NULL; ++opt)
+			if (match_option(name, opt, dowild))
+				return opt;
+		for (list = extra_options; list != NULL; list = list->next)
+			for (opt = list->options; opt->name != NULL; ++opt)
+				if (match_option(name, opt, dowild))
+					return opt;
+		for (opt = the_channel->options; opt->name != NULL; ++opt)
+			if (match_option(name, opt, dowild))
+				return opt;
+		for (i = 0; protocols[i] != NULL; ++i)
+			if ((opt = protocols[i]->options) != NULL)
+				for (; opt->name != NULL; ++opt)
+					if (match_option(name, opt, dowild))
+						return opt;
+	}
+	return NULL;
+}
+
+/*
+ * process_option - process one new-style option.
+ */
+static int
+process_option(opt, cmd, argv)
+    option_t *opt;
+    char *cmd;
+    char **argv;
+{
+    u_int32_t v;
+    int iv, a;
+    char *sv;
+    int (*parser) __P((char **));
+    int (*wildp) __P((char *, char **, int));
+    char *optopt = (opt->type == o_wild)? "": " option";
+    int prio = option_priority;
+    option_t *mainopt = opt;
+
+    current_option = opt->name;
+    if ((opt->flags & OPT_PRIVFIX) && privileged_option)
+	prio += OPRIO_ROOT;
+    while (mainopt->flags & OPT_PRIOSUB)
+	--mainopt;
+    if (mainopt->flags & OPT_PRIO) {
+	if (prio < mainopt->priority) {
+	    /* new value doesn't override old */
+	    if (prio == OPRIO_CMDLINE && mainopt->priority > OPRIO_ROOT) {
+		option_error("%s%s set in %s cannot be overridden\n",
+			     opt->name, optopt, mainopt->source);
+		return 0;
+	    }
+	    return 1;
+	}
+	if (prio > OPRIO_ROOT && mainopt->priority == OPRIO_CMDLINE)
+	    warn("%s%s from %s overrides command line",
+		 opt->name, optopt, option_source);
+    }
+
+    if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
+	option_error("%s%s cannot be changed after initialization",
+		     opt->name, optopt);
+	return 0;
+    }
+    if ((opt->flags & OPT_PRIV) && !privileged_option) {
+	option_error("using the %s%s requires root privilege",
+		     opt->name, optopt);
+	return 0;
+    }
+    if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) {
+	option_error("%s%s is disabled", opt->name, optopt);
+	return 0;
+    }
+    if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
+	option_error("the %s%s may not be changed in %s",
+		     opt->name, optopt, option_source);
+	return 0;
+    }
+
+    switch (opt->type) {
+    case o_bool:
+	v = opt->flags & OPT_VALUE;
+	*(bool *)(opt->addr) = v;
+	if (opt->addr2 && (opt->flags & OPT_A2COPY))
+	    *(bool *)(opt->addr2) = v;
+	else if (opt->addr2 && (opt->flags & OPT_A2CLR))
+	    *(bool *)(opt->addr2) = 0;
+	else if (opt->addr2 && (opt->flags & OPT_A2CLRB))
+	    *(u_char *)(opt->addr2) &= ~v;
+	else if (opt->addr2 && (opt->flags & OPT_A2OR))
+	    *(u_char *)(opt->addr2) |= v;
+	break;
+
+    case o_int:
+	iv = 0;
+	if ((opt->flags & OPT_NOARG) == 0) {
+	    if (!int_option(*argv, &iv))
+		return 0;
+	    if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit)
+		 || ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit))
+		&& !((opt->flags & OPT_ZEROOK && iv == 0))) {
+		char *zok = (opt->flags & OPT_ZEROOK)? " zero or": "";
+		switch (opt->flags & OPT_LIMITS) {
+		case OPT_LLIMIT:
+		    option_error("%s value must be%s >= %d",
+				 opt->name, zok, opt->lower_limit);
+		    break;
+		case OPT_ULIMIT:
+		    option_error("%s value must be%s <= %d",
+				 opt->name, zok, opt->upper_limit);
+		    break;
+		case OPT_LIMITS:
+		    option_error("%s value must be%s between %d and %d",
+				opt->name, zok, opt->lower_limit, opt->upper_limit);
+		    break;
+		}
+		return 0;
+	    }
+	}
+	a = opt->flags & OPT_VALUE;
+	if (a >= 128)
+	    a -= 256;		/* sign extend */
+	iv += a;
+	if (opt->flags & OPT_INC)
+	    iv += *(int *)(opt->addr);
+	if ((opt->flags & OPT_NOINCR) && !privileged_option) {
+	    int oldv = *(int *)(opt->addr);
+	    if ((opt->flags & OPT_ZEROINF) ?
+		(oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) {
+		option_error("%s value cannot be increased", opt->name);
+		return 0;
+	    }
+	}
+	*(int *)(opt->addr) = iv;
+	if (opt->addr2 && (opt->flags & OPT_A2COPY))
+	    *(int *)(opt->addr2) = iv;
+	break;
+
+    case o_uint32:
+	if (opt->flags & OPT_NOARG) {
+	    v = opt->flags & OPT_VALUE;
+	    if (v & 0x80)
+		    v |= 0xffffff00U;
+	} else if (!number_option(*argv, &v, 16))
+	    return 0;
+	if (opt->flags & OPT_OR)
+	    v |= *(u_int32_t *)(opt->addr);
+	*(u_int32_t *)(opt->addr) = v;
+	if (opt->addr2 && (opt->flags & OPT_A2COPY))
+	    *(u_int32_t *)(opt->addr2) = v;
+	break;
+
+    case o_string:
+	if (opt->flags & OPT_STATIC) {
+	    strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
+	} else {
+	    sv = strdup(*argv);
+	    if (sv == NULL)
+		novm("option argument");
+	    *(char **)(opt->addr) = sv;
+	}
+	break;
+
+    case o_special_noarg:
+    case o_special:
+	parser = (int (*) __P((char **))) opt->addr;
+	if (!(*parser)(argv))
+	    return 0;
+	if (opt->flags & OPT_A2LIST) {
+	    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;
+	    }
+	}
+	break;
+
+    case o_wild:
+	wildp = (int (*) __P((char *, char **, int))) opt->addr;
+	if (!(*wildp)(cmd, argv, 1))
+	    return 0;
+	break;
+    }
+
+    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);
+
+    mainopt->source = option_source;
+    mainopt->priority = prio;
+    mainopt->winner = opt - mainopt;
+
+    return 1;
+}
+
+/*
+ * override_value - if the option priorities would permit us to
+ * override the value of option, return 1 and update the priority
+ * and source of the option value.  Otherwise returns 0.
+ */
+int
+override_value(option, priority, source)
+    const char *option;
+    int priority;
+    const char *source;
+{
+	option_t *opt;
+
+	opt = find_option(option);
+	if (opt == NULL)
+		return 0;
+	while (opt->flags & OPT_PRIOSUB)
+		--opt;
+	if ((opt->flags & OPT_PRIO) && priority < opt->priority)
+		return 0;
+	opt->priority = priority;
+	opt->source = source;
+	opt->winner = -1;
+	return 1;
+}
+
+/*
+ * n_arguments - tell how many arguments an option takes
+ */
+static int
+n_arguments(opt)
+    option_t *opt;
+{
+	return (opt->type == o_bool || opt->type == o_special_noarg
+		|| (opt->flags & OPT_NOARG))? 0: 1;
+}
+
+/*
+ * add_options - add a list of options to the set we grok.
+ */
+void
+add_options(opt)
+    option_t *opt;
+{
+    struct option_list *list;
+
+    list = malloc(sizeof(*list));
+    if (list == 0)
+	novm("option list entry");
+    list->options = opt;
+    list->next = extra_options;
+    extra_options = list;
+}
+
+/*
+ * check_options - check that options are valid and consistent.
+ */
+void
+check_options()
+{
+	if (logfile_fd >= 0 && logfile_fd != log_to_fd)
+		close(logfile_fd);
+}
+
+/*
+ * print_option - print out an option and its value
+ */
+static void
+print_option(opt, mainopt, printer, arg)
+    option_t *opt, *mainopt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	int i, v;
+	char *p;
+
+	if (opt->flags & OPT_NOPRINT)
+		return;
+	switch (opt->type) {
+	case o_bool:
+		v = opt->flags & OPT_VALUE;
+		if (*(bool *)opt->addr != v)
+			/* this can happen legitimately, e.g. lock
+			   option turned off for default device */
+			break;
+		printer(arg, "%s", opt->name);
+		break;
+	case o_int:
+		v = opt->flags & OPT_VALUE;
+		if (v >= 128)
+			v -= 256;
+		i = *(int *)opt->addr;
+		if (opt->flags & OPT_NOARG) {
+			printer(arg, "%s", opt->name);
+			if (i != v) {
+				if (opt->flags & OPT_INC) {
+					for (; i > v; i -= v)
+						printer(arg, " %s", opt->name);
+				} else
+					printer(arg, " # oops: %d not %d\n",
+						i, v);
+			}
+		} else {
+			printer(arg, "%s %d", opt->name, i);
+		}
+		break;
+	case o_uint32:
+		printer(arg, "%s", opt->name);
+		if ((opt->flags & OPT_NOARG) == 0)
+			printer(arg, " %x", *(u_int32_t *)opt->addr);
+		break;
+
+	case o_string:
+		if (opt->flags & OPT_HIDE) {
+			p = "??????";
+		} else {
+			p = (char *) opt->addr;
+			if ((opt->flags & OPT_STATIC) == 0)
+				p = *(char **)p;
+		}
+		printer(arg, "%s %q", opt->name, p);
+		break;
+
+	case o_special:
+	case o_special_noarg:
+	case o_wild:
+		if (opt->type != o_wild) {
+			printer(arg, "%s", opt->name);
+			if (n_arguments(opt) == 0)
+				break;
+			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 *)))opt->addr2;
+			(*oprt)(opt, printer, arg);
+		} else if (opt->flags & OPT_A2STRVAL) {
+			p = (char *) opt->addr2;
+			if ((opt->flags & OPT_STATIC) == 0)
+				p = *(char **)p;
+			printer("%q", p);
+		} else if (opt->flags & OPT_A2LIST) {
+			struct option_value *ovp;
+
+			ovp = (struct option_value *) opt->addr2;
+			for (;;) {
+				printer(arg, "%q", ovp->value);
+				if ((ovp = ovp->next) == NULL)
+					break;
+				printer(arg, "\t\t# (from %s)\n%s ",
+					ovp->source, opt->name);
+			}
+		} else {
+			printer(arg, "xxx # [don't know how to print value]");
+		}
+		break;
+
+	default:
+		printer(arg, "# %s value (type %d\?\?)", opt->name, opt->type);
+		break;
+	}
+	printer(arg, "\t\t# (from %s)\n", mainopt->source);
+}
+
+/*
+ * print_option_list - print out options in effect from an
+ * array of options.
+ */
+static void
+print_option_list(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	while (opt->name != NULL) {
+		if (opt->priority != OPRIO_DEFAULT
+		    && opt->winner != (short int) -1)
+			print_option(opt + opt->winner, opt, printer, arg);
+		do {
+			++opt;
+		} while (opt->flags & OPT_PRIOSUB);
+	}
+}
+
+/*
+ * print_options - print out what options are in effect.
+ */
+void
+print_options(printer, arg)
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+	struct option_list *list;
+	int i;
+
+	printer(arg, "pppd options in effect:\n");
+	print_option_list(general_options, printer, arg);
+	print_option_list(auth_options, printer, arg);
+	for (list = extra_options; list != NULL; list = list->next)
+		print_option_list(list->options, printer, arg);
+	print_option_list(the_channel->options, printer, arg);
+	for (i = 0; protocols[i] != NULL; ++i)
+		print_option_list(protocols[i]->options, printer, arg);
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+static void
+usage()
+{
+    if (phase == PHASE_INITIALIZE)
+	fprintf(stderr, usage_string, VERSION, progname);
+}
+
+/*
+ * showhelp - print out usage message and exit.
+ */
+static int
+showhelp(argv)
+    char **argv;
+{
+    if (phase == PHASE_INITIALIZE) {
+	usage();
+	exit(0);
+    }
+    return 0;
+}
+
+/*
+ * showversion - print out the version number and exit.
+ */
+static int
+showversion(argv)
+    char **argv;
+{
+    if (phase == PHASE_INITIALIZE) {
+	fprintf(stderr, "pppd version %s\n", VERSION);
+	exit(0);
+    }
+    return 0;
+}
+
+/*
+ * option_error - print a message about an error in an option.
+ * The message is logged, and also sent to
+ * stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+    va_list args;
+    char buf[1024];
+
+#if defined(__STDC__)
+    va_start(args, fmt);
+#else
+    char *fmt;
+    va_start(args);
+    fmt = va_arg(args, char *);
+#endif
+    vslprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+    if (phase == PHASE_INITIALIZE)
+	fprintf(stderr, "%s: %s\n", progname, buf);
+    syslog(LOG_ERR, "%s", buf);
+}
+
+#if 0
+/*
+ * readable - check if a file is readable by the real user.
+ */
+int
+readable(fd)
+    int fd;
+{
+    uid_t uid;
+    int i;
+    struct stat sbuf;
+
+    uid = getuid();
+    if (uid == 0)
+	return 1;
+    if (fstat(fd, &sbuf) != 0)
+	return 0;
+    if (sbuf.st_uid == uid)
+	return sbuf.st_mode & S_IRUSR;
+    if (sbuf.st_gid == getgid())
+	return sbuf.st_mode & S_IRGRP;
+    for (i = 0; i < ngroups; ++i)
+	if (sbuf.st_gid == groups[i])
+	    return sbuf.st_mode & S_IRGRP;
+    return sbuf.st_mode & S_IROTH;
+}
+#endif
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (" or ').
+ * Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored.
+ */
+int
+getword(f, word, newlinep, filename)
+    FILE *f;
+    char *word;
+    int *newlinep;
+    char *filename;
+{
+    int c, len, escape;
+    int quoted, comment;
+    int value, digit, got, n;
+
+#define isoctal(c) ((c) >= '0' && (c) < '8')
+
+    *newlinep = 0;
+    len = 0;
+    escape = 0;
+    comment = 0;
+
+    /*
+     * First skip white-space and comments.
+     */
+    for (;;) {
+	c = getc(f);
+	if (c == EOF)
+	    break;
+
+	/*
+	 * A newline means the end of a comment; backslash-newline
+	 * is ignored.  Note that we cannot have escape && comment.
+	 */
+	if (c == '\n') {
+	    if (!escape) {
+		*newlinep = 1;
+		comment = 0;
+	    } else
+		escape = 0;
+	    continue;
+	}
+
+	/*
+	 * Ignore characters other than newline in a comment.
+	 */
+	if (comment)
+	    continue;
+
+	/*
+	 * If this character is escaped, we have a word start.
+	 */
+	if (escape)
+	    break;
+
+	/*
+	 * If this is the escape character, look at the next character.
+	 */
+	if (c == '\\') {
+	    escape = 1;
+	    continue;
+	}
+
+	/*
+	 * If this is the start of a comment, ignore the rest of the line.
+	 */
+	if (c == '#') {
+	    comment = 1;
+	    continue;
+	}
+
+	/*
+	 * A non-whitespace character is the start of a word.
+	 */
+	if (!isspace(c))
+	    break;
+    }
+
+    /*
+     * 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) {
+	if (escape) {
+	    /*
+	     * This character is escaped: backslash-newline is ignored,
+	     * various other characters indicate particular values
+	     * as for C backslash-escapes.
+	     */
+	    escape = 0;
+	    if (c == '\n') {
+	        c = getc(f);
+		continue;
+	    }
+
+	    got = 0;
+	    switch (c) {
+	    case 'a':
+		value = '\a';
+		break;
+	    case 'b':
+		value = '\b';
+		break;
+	    case 'f':
+		value = '\f';
+		break;
+	    case 'n':
+		value = '\n';
+		break;
+	    case 'r':
+		value = '\r';
+		break;
+	    case 's':
+		value = ' ';
+		break;
+	    case 't':
+		value = '\t';
+		break;
+
+	    default:
+		if (isoctal(c)) {
+		    /*
+		     * \ddd octal sequence
+		     */
+		    value = 0;
+		    for (n = 0; n < 3 && isoctal(c); ++n) {
+			value = (value << 3) + (c & 07);
+			c = getc(f);
+		    }
+		    got = 1;
+		    break;
+		}
+
+		if (c == 'x') {
+		    /*
+		     * \x<hex_string> sequence
+		     */
+		    value = 0;
+		    c = getc(f);
+		    for (n = 0; n < 2 && isxdigit(c); ++n) {
+			digit = toupper(c) - '0';
+			if (digit > 10)
+			    digit += '0' + 10 - 'A';
+			value = (value << 4) + digit;
+			c = getc (f);
+		    }
+		    got = 1;
+		    break;
+		}
+
+		/*
+		 * Otherwise the character stands for itself.
+		 */
+		value = c;
+		break;
+	    }
+
+	    /*
+	     * Store the resulting character for the escape sequence.
+	     */
+	    if (len < MAXWORDLEN-1)
+		word[len] = value;
+	    ++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.
+	 */
+	if (c == '\\') {
+	    escape = 1;
+	    c = getc(f);
+	    continue;
+	}
+
+	/*
+	 * An ordinary character: store it in the word and get another.
+	 */
+	if (len < MAXWORDLEN-1)
+	    word[len] = c;
+	++len;
+
+	c = getc(f);
+    }
+
+    /*
+     * End of the word: check for errors.
+     */
+    if (c == EOF) {
+	if (ferror(f)) {
+	    if (errno == 0)
+		errno = EIO;
+	    option_error("Error reading %s: %m", filename);
+	    die(1);
+	}
+	/*
+	 * If len is zero, then we didn't find a word before the
+	 * end of the file.
+	 */
+	if (len == 0)
+	    return 0;
+    }
+
+    /*
+     * Warn if the word was too long, and append a terminating null.
+     */
+    if (len >= MAXWORDLEN) {
+	option_error("warning: word in file %s too long (%.20s...)",
+		     filename, word);
+	len = MAXWORDLEN - 1;
+    }
+    word[len] = 0;
+
+    return 1;
+
+#undef isoctal
+
+}
+
+/*
+ * number_option - parse an unsigned numeric parameter for an option.
+ */
+static int
+number_option(str, valp, base)
+    char *str;
+    u_int32_t *valp;
+    int base;
+{
+    char *ptr;
+
+    *valp = strtoul(str, &ptr, base);
+    if (ptr == str) {
+	option_error("invalid numeric parameter '%s' for %s option",
+		     str, current_option);
+	return 0;
+    }
+    return 1;
+}
+
+
+/*
+ * int_option - like number_option, but valp is int *,
+ * the base is assumed to be 0, and *valp is not changed
+ * if there is an error.
+ */
+int
+int_option(str, valp)
+    char *str;
+    int *valp;
+{
+    u_int32_t v;
+
+    if (!number_option(str, &v, 0))
+	return 0;
+    *valp = (int) v;
+    return 1;
+}
+
+
+/*
+ * The following procedures parse options.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+static int
+readfile(argv)
+    char **argv;
+{
+    return options_from_file(*argv, 1, 1, privileged_option);
+}
+
+/*
+ * callfile - take commands from /etc/ppp/peers/<name>.
+ * Name may not contain /../, start with / or ../, or end in /..
+ */
+static int
+callfile(argv)
+    char **argv;
+{
+    char *fname, *arg, *p;
+    int l, ok;
+
+    arg = *argv;
+    ok = 1;
+    if (arg[0] == '/' || arg[0] == 0)
+	ok = 0;
+    else {
+	for (p = arg; *p != 0; ) {
+	    if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
+		ok = 0;
+		break;
+	    }
+	    while (*p != '/' && *p != 0)
+		++p;
+	    if (*p == '/')
+		++p;
+	}
+    }
+    if (!ok) {
+	option_error("call option value may not contain .. or start with /");
+	return 0;
+    }
+
+    l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
+    if ((fname = (char *) malloc(l)) == NULL)
+	novm("call file name");
+    slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg);
+
+    ok = options_from_file(fname, 1, 1, 1);
+
+    free(fname);
+    return ok;
+}
+
+#ifdef PPP_FILTER
+/*
+ * setpassfilter - Set the pass filter for packets
+ */
+static int
+setpassfilter(argv)
+    char **argv;
+{
+    pcap_t *pc;
+    int ret = 1;
+
+    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 = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setactivefilter(argv)
+    char **argv;
+{
+    pcap_t *pc;
+    int ret = 1;
+
+    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 = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
+}
+#endif
+
+/*
+ * setdomain - Set domain name to append to hostname 
+ */
+static int
+setdomain(argv)
+    char **argv;
+{
+    gethostname(hostname, MAXNAMELEN);
+    if (**argv != 0) {
+	if (**argv != '.')
+	    strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
+	domain = hostname + strlen(hostname);
+	strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+    }
+    hostname[MAXNAMELEN-1] = 0;
+    return (1);
+}
+
+static int
+setlogfile(argv)
+    char **argv;
+{
+    int fd, err;
+
+    if (!privileged_option)
+	seteuid(getuid());
+    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 (fd < 0) {
+	errno = err;
+	option_error("Can't open log file %s: %m", *argv);
+	return 0;
+    }
+    strlcpy(logfile_name, *argv, sizeof(logfile_name));
+    if (logfile_fd >= 0)
+	close(logfile_fd);
+    logfile_fd = fd;
+    log_to_fd = fd;
+    log_default = 0;
+    return 1;
+}
+
+#ifdef MAXOCTETS
+static int
+setmodir(argv)
+    char **argv;
+{
+    if(*argv == NULL)
+	return 0;
+    if(!strcmp(*argv,"in")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_IN;
+    } else if (!strcmp(*argv,"out")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_OUT;
+    } else if (!strcmp(*argv,"max")) {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_MAXOVERAL;
+    } else {
+        maxoctets_dir = PPP_OCTETS_DIRECTION_SUM;
+    }
+    return 1;
+}
+#endif
+
+#ifdef PLUGIN
+static int
+loadplugin(argv)
+    char **argv;
+{
+    char *arg = *argv;
+    void *handle;
+    const char *err;
+    void (*init) __P((void));
+    char *path = arg;
+    const char *vers;
+
+    if (strchr(arg, '/') == 0) {
+	const char *base = _PATH_PLUGIN;
+	int l = strlen(base) + strlen(arg) + 2;
+	path = malloc(l);
+	if (path == 0)
+	    novm("plugin file path");
+	strlcpy(path, base, l);
+	strlcat(path, "/", l);
+	strlcat(path, arg, l);
+    }
+    handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
+    if (handle == 0) {
+	err = dlerror();
+	if (err != 0)
+	    option_error("%s", err);
+	option_error("Couldn't load plugin %s", arg);
+	goto err;
+    }
+    init = (void (*)(void))dlsym(handle, "plugin_init");
+    if (init == 0) {
+	option_error("%s has no initialization entry point", arg);
+	goto errclose;
+    }
+    vers = (const char *) dlsym(handle, "pppd_version");
+    if (vers == 0) {
+	warn("Warning: plugin %s has no version information", arg);
+    } else if (strcmp(vers, VERSION) != 0) {
+	option_error("Plugin %s is for pppd version %s, this is %s",
+		     arg, vers, VERSION);
+	goto errclose;
+    }
+    info("Plugin %s loaded.", arg);
+    (*init)();
+    return 1;
+
+ errclose:
+    dlclose(handle);
+ err:
+    if (path != arg)
+	free(path);
+    return 0;
+}
+#endif /* PLUGIN */
diff -Naur tomato/release/src/router/pppd/pppd/patchlevel.h tomato121/release/src/router/pppd/pppd/patchlevel.h
--- tomato/release/src/router/pppd/pppd/patchlevel.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/patchlevel.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,4 +1,4 @@
-/* $Id: patchlevel.h,v 1.1 2003/07/10 07:43:04 honor Exp $ */
+/* $Id: patchlevel.h,v 1.66 2006/06/28 00:21:23 paulus Exp $ */
 
-#define VERSION		"2.4.1"
-#define DATE		"25 March 2001"
+#define VERSION		"2.4.4"
+#define DATE		"28 June 2006"
diff -Naur tomato/release/src/router/pppd/pppd/pathnames.h tomato121/release/src/router/pppd/pppd/pathnames.h
--- tomato/release/src/router/pppd/pppd/pathnames.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/pathnames.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,7 +1,7 @@
 /*
  * define path names
  *
- * $Id: pathnames.h,v 1.1 2003/07/10 07:43:04 honor Exp $
+ * $Id: pathnames.h,v 1.18 2005/08/25 23:59:34 paulus Exp $
  */
 
 #ifdef HAVE_PATHS_H
@@ -15,15 +15,13 @@
 #endif /* HAVE_PATHS_H */
 
 #ifndef _ROOT_PATH
-#ifdef MIPSEL
 #define _ROOT_PATH "/tmp"
-#else
-#define _ROOT_PATH "/etc"
-#endif
 #endif
 
+
 #define _PATH_UPAPFILE 	 _ROOT_PATH "/ppp/pap-secrets"
 #define _PATH_CHAPFILE 	 _ROOT_PATH "/ppp/chap-secrets"
+#define _PATH_SRPFILE 	 _ROOT_PATH "/ppp/srp-secrets"
 #define _PATH_SYSOPTIONS _ROOT_PATH "/ppp/options"
 #define _PATH_IPUP	 _ROOT_PATH "/ppp/ip-up"
 #define _PATH_IPDOWN	 _ROOT_PATH "/ppp/ip-down"
@@ -33,8 +31,10 @@
 #define _PATH_CONNERRS	 _ROOT_PATH "/ppp/connect-errors"
 #define _PATH_PEERFILES	 _ROOT_PATH "/ppp/peers/"
 #define _PATH_RESOLV	 _ROOT_PATH "/ppp/resolv.conf"
+#define _PATH_IPPREUP	 _ROOT_PATH "/ppp/ip-pre-up"
 
 #define _PATH_USEROPT	 ".ppprc"
+#define	_PATH_PSEUDONYM	 ".ppp_pseudonym"
 
 #ifdef INET6
 #define _PATH_IPV6UP     _ROOT_PATH "/ppp/ipv6-up"
@@ -57,5 +57,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 -Naur tomato/release/src/router/pppd/pppd/plugins/Makefile tomato121/release/src/router/pppd/pppd/plugins/Makefile
--- tomato/release/src/router/pppd/pppd/plugins/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,41 @@
+CC	= gcc
+COPTS	= -O2 -g
+CFLAGS	= $(COPTS) -I.. -I../../include -fPIC
+LDFLAGS	= -shared
+INSTALL	= install
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+SUBDIRS := rp-pppoe pppoatm radius
+# Uncomment the next line to include the radius authentication plugin
+# SUBDIRS += radius
+PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so
+
+# include dependencies if present
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+all:	$(PLUGINS)
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d all; done
+
+%.so: %.c
+	$(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h)
+
+install: $(PLUGINS)
+	$(INSTALL) -d $(LIBDIR)
+	$(INSTALL) $? $(LIBDIR)
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d install; done
+
+clean:
+	rm -f *.o *.so *.a
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d clean; done
+
+depend:
+	$(CPP) -M $(CFLAGS) *.c >.depend
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d depend; done
diff -Naur tomato/release/src/router/pppd/pppd/plugins/Makefile.linux tomato121/release/src/router/pppd/pppd/plugins/Makefile.linux
--- tomato/release/src/router/pppd/pppd/plugins/Makefile.linux	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,41 @@
+CC	= gcc
+COPTS	= -O2 -g
+CFLAGS	= $(COPTS) -I.. -I../../include -fPIC
+LDFLAGS	= -shared
+INSTALL	= install
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+SUBDIRS := rp-pppoe pppoatm radius
+# Uncomment the next line to include the radius authentication plugin
+# SUBDIRS += radius
+PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so
+
+# include dependencies if present
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+all:	$(PLUGINS)
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d all; done
+
+%.so: %.c
+	$(CC) -o $@ $(LDFLAGS) $(CFLAGS) $^
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../patchlevel.h)
+
+install: $(PLUGINS)
+	$(INSTALL) -d $(LIBDIR)
+	$(INSTALL) $? $(LIBDIR)
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d install; done
+
+clean:
+	rm -f *.o *.so *.a
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d clean; done
+
+depend:
+	$(CPP) -M $(CFLAGS) *.c >.depend
+	for d in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$d depend; done
diff -Naur tomato/release/src/router/pppd/pppd/plugins/Makefile.sol2 tomato121/release/src/router/pppd/pppd/plugins/Makefile.sol2
--- tomato/release/src/router/pppd/pppd/plugins/Makefile.sol2	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/Makefile.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,27 @@
+#
+# Makefile for plugins on Solaris 2
+#
+# $Id: Makefile.sol2,v 1.3 2002/09/07 05:15:25 carlsonj Exp $
+#
+
+include ../../Makedefs.com
+
+CFLAGS	= -c -O -I.. -I../../include $(COPTS)
+LDFLAGS	= -G 
+
+all:	minconn.so
+
+minconn.so: minconn.o
+	ld -o $@ $(LDFLAGS) -h $@ minconn.o
+
+minconn.o: minconn.c
+	$(CC) $(CFLAGS) -c $? 
+
+passprompt.so: passprompt.o
+	ld -o $@ $(LDFLAGS) -h $@ passprompt.o
+
+passprompt.o: passprompt.c
+	$(CC) $(CFLAGS) -c $?
+
+clean:
+	rm -f *.o *.so
diff -Naur tomato/release/src/router/pppd/pppd/plugins/minconn.c tomato121/release/src/router/pppd/pppd/plugins/minconn.c
--- tomato/release/src/router/pppd/pppd/plugins/minconn.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/minconn.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,66 @@
+/*
+ * minconn.c - pppd plugin to implement a `minconnect' option.
+ *
+ * Copyright (c) 1999 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.
+ */
+#include <stddef.h>
+#include <time.h>
+#include "pppd.h"
+
+char pppd_version[] = VERSION;
+
+static int minconnect = 0;
+
+static option_t my_options[] = {
+	{ "minconnect", o_int, &minconnect,
+	  "Set minimum connect time before idle timeout applies" },
+	{ NULL }
+};
+
+static int my_get_idle(struct ppp_idle *idle)
+{
+	time_t t;
+
+	if (idle == NULL)
+		return minconnect? minconnect: idle_time_limit;
+	t = idle->xmit_idle;
+	if (idle->recv_idle < t)
+		t = idle->recv_idle;
+	return idle_time_limit - t;
+}
+
+void plugin_init(void)
+{
+	info("plugin_init");
+	add_options(my_options);
+	idle_time_hook = my_get_idle;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/passprompt.c tomato121/release/src/router/pppd/pppd/plugins/passprompt.c
--- tomato/release/src/router/pppd/pppd/plugins/passprompt.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/passprompt.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,110 @@
+/*
+ * passprompt.c - pppd plugin to invoke an external PAP password prompter
+ *
+ * Copyright 1999 Paul Mackerras, Alan Curry.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include "pppd.h"
+
+char pppd_version[] = VERSION;
+
+static char promptprog[PATH_MAX+1];
+
+static option_t options[] = {
+    { "promptprog", o_string, promptprog,
+      "External PAP password prompting program",
+      OPT_STATIC, NULL, PATH_MAX },
+    { NULL }
+};
+
+static int promptpass(char *user, char *passwd)
+{
+    int p[2];
+    pid_t kid;
+    int readgood, wstat;
+    ssize_t red;
+
+    if (promptprog[0] == 0 || access(promptprog, X_OK) < 0)
+	return -1;	/* sorry, can't help */
+
+    if (!passwd)
+	return 1;
+
+    if (pipe(p)) {
+	warn("Can't make a pipe for %s", promptprog);
+	return 0;
+    }
+    if ((kid = fork()) == (pid_t) -1) {
+	warn("Can't fork to run %s", promptprog);
+	close(p[0]);
+	close(p[1]);
+	return 0;
+    }
+    if (!kid) {
+	/* we are the child, exec the program */
+	char *argv[5], fdstr[32];
+	sys_close();
+	closelog();
+	close(p[0]);
+	seteuid(getuid());
+	setegid(getgid());
+	argv[0] = promptprog;
+	argv[1] = user;
+	argv[2] = remote_name;
+	sprintf(fdstr, "%d", p[1]);
+	argv[3] = fdstr;
+	argv[4] = 0;
+	execv(*argv, argv);
+	_exit(127);
+    }
+
+    /* we are the parent, read the password from the pipe */
+    close(p[1]);
+    readgood = 0;
+    do {
+	red = read(p[0], passwd + readgood, MAXSECRETLEN-1 - readgood);
+	if (red == 0)
+	    break;
+	if (red < 0) {
+	    if (errno == EINTR)
+		continue;
+	    error("Can't read secret from %s: %m", promptprog);
+	    readgood = -1;
+	    break;
+	}
+	readgood += red;
+    } while (readgood < MAXSECRETLEN - 1);
+    passwd[readgood] = 0;
+    close(p[0]);
+
+    /* now wait for child to exit */
+    while (waitpid(kid, &wstat, 0) < 0) {
+	if (errno != EINTR) {
+	    warn("error waiting for %s: %m", promptprog);
+	    break;
+	}
+    }
+
+    if (readgood < 0)
+	return 0;
+    if (!WIFEXITED(wstat))
+	warn("%s terminated abnormally", promptprog);
+    if (WEXITSTATUS(wstat))
+	warn("%s exited with code %d", promptprog, WEXITSTATUS(status));
+
+    return 1;
+}
+
+void plugin_init(void)
+{
+    add_options(options);
+    pap_passwd_hook = promptpass;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/passwordfd.c tomato121/release/src/router/pppd/pppd/plugins/passwordfd.c
--- tomato/release/src/router/pppd/pppd/plugins/passwordfd.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/passwordfd.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,82 @@
+
+/*
+ *  Author: Arvin Schnell <arvin@suse.de>
+ *
+ *  This plugin let's you pass the password to the pppd via
+ *  a file descriptor. That's easy and secure - no fiddling
+ *  with pap- and chap-secrets files.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pppd.h"
+
+char pppd_version[] = VERSION;
+
+static int passwdfd = -1;
+static char save_passwd[MAXSECRETLEN];
+
+static option_t options[] = {
+    { "passwordfd", o_int, &passwdfd,
+      "Receive password on this file descriptor" },
+    { NULL }
+};
+
+static int pwfd_check (void)
+{
+    return 1;
+}
+
+static int pwfd_passwd (char *user, char *passwd)
+{
+    int readgood, red;
+
+    if (passwdfd == -1)
+	return -1;
+
+    if (passwd == NULL)
+	return 1;
+
+    if (passwdfd == -2) {
+	strcpy (passwd, save_passwd);
+	return 1;
+    }
+
+    readgood = 0;
+    do {
+	red = read (passwdfd, passwd + readgood, MAXSECRETLEN - 1 - readgood);
+	if (red == 0)
+	    break;
+	if (red < 0) {
+	    error ("Can't read secret from fd\n");
+	    readgood = -1;
+	    break;
+	}
+	readgood += red;
+    } while (readgood < MAXSECRETLEN - 1);
+
+    close (passwdfd);
+
+    if (readgood < 0)
+	return 0;
+
+    passwd[readgood] = 0;
+    strcpy (save_passwd, passwd);
+    passwdfd = -2;
+
+    return 1;
+}
+
+void plugin_init (void)
+{
+    add_options (options);
+
+    pap_check_hook = pwfd_check;
+    pap_passwd_hook = pwfd_passwd;
+
+    chap_check_hook = pwfd_check;
+    chap_passwd_hook = pwfd_passwd;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/ans.c tomato121/release/src/router/pppd/pppd/plugins/pppoatm/ans.c
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/ans.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/ans.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,262 @@
+/* ans.c - Interface for text2atm and atm2text to ANS */
+
+/* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */
+
+
+/*
+ * This stuff is a temporary hack to avoid using gethostbyname_nsap and such
+ * without doing the "full upgrade" to getaddrinfo/getnameinfo. This also
+ * serves as an exercise for me to get all the details right before I propose
+ * a patch that would eventually end up in libc (and that should therefore be
+ * as stable as possible).
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+
+#include "atm.h"
+#include "atmres.h"
+
+
+#define MAX_ANSWER 2048
+#define MAX_NAME   1024
+
+#define MAX_LINE		2048	/* in /etc/e164_cc */
+#define E164_CC_DEFAULT_LEN	   2
+#define E164_CC_FILE		"/etc/e164_cc"
+
+#define GET16(pos) (((pos)[0] << 8) | (pos)[1])
+
+
+static int ans(const char *text,int wanted,void *result,int res_len)
+{
+    unsigned char answer[MAX_ANSWER];
+    unsigned char name[MAX_NAME];
+    unsigned char *pos,*data,*found;
+    int answer_len,name_len,data_len,found_len;
+    int questions,answers;
+
+    found_len = 0; /* gcc wants it */
+    if ((answer_len = res_search(text,C_IN,wanted,answer,MAX_ANSWER)) < 0)
+	return TRY_OTHER;
+    /*
+     * Response header: id, flags, #queries, #answers, #authority,
+     * #additional (all 16 bits)
+     */
+    pos = answer+12;
+    if (answer[3] & 15) return TRY_OTHER; /* rcode != 0 */
+    questions = GET16(answer+4);
+    if (questions != 1) return TRY_OTHER; /* trouble ... */
+    answers = GET16(answer+6);
+    if (answers < 1) return TRY_OTHER;
+    /*
+     * Query: name, type (16), class (16)
+     */
+    if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME)) < 0)
+	return TRY_OTHER;
+    pos += name_len;
+    if (GET16(pos) != wanted || GET16(pos+2) != C_IN) return TRY_OTHER;
+    pos += 4;
+    /*
+     * Iterate over answers until we find something we like, giving priority
+     * to ATMA_AESA (until signaling is fixed to work with E.164 too)
+     */
+    found = NULL;
+    while (answers--) {
+	/*
+	 * RR: name, type (16), class (16), TTL (32), resource_len (16),
+	 * resource_data ...
+	 */
+	if ((name_len = dn_expand(answer,answer+answer_len,pos,name,MAX_NAME))
+	  < 0) return TRY_OTHER;
+	pos += name_len;
+	data_len = GET16(pos+8);
+	data = pos+10;
+	pos = data+data_len;
+	if (GET16(data-10) != wanted || GET16(data-8) != C_IN || !--data_len)
+	    continue;
+	switch (wanted) {
+            case T_NSAP:
+                data_len++;
+                if (data_len != ATM_ESA_LEN) continue;
+                memcpy(((struct sockaddr_atmsvc *) result)->
+                  sas_addr.prv,data,ATM_ESA_LEN);
+                return 0;
+	    case T_ATMA:
+		switch (*data++) {
+		    case ATMA_AESA:
+			if (data_len != ATM_ESA_LEN) continue;
+			memcpy(((struct sockaddr_atmsvc *) result)->
+			  sas_addr.prv,data,ATM_ESA_LEN);
+			return 0;
+		    case ATMA_E164:
+			if (data_len > ATM_E164_LEN) continue;
+			if (!found) {
+			    found = data;
+			    found_len = data_len;
+			}
+			break;
+		    default:
+			continue;
+		}
+	    case T_PTR:
+		    if (dn_expand(answer,answer+answer_len,data,result,
+		      res_len) < 0) return FATAL;
+		    return 0;
+		default:
+		    continue;
+	}
+    }
+    if (!found) return TRY_OTHER;
+    memcpy(((struct sockaddr_atmsvc *) result)->sas_addr.pub,found,
+      found_len);
+    ((struct sockaddr_atmsvc *) result)->sas_addr.pub[found_len] = 0;
+    return 0;
+}
+
+
+int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length,
+  int flags)
+{
+    if (!(flags & T2A_SVC) || length != sizeof(*addr)) return TRY_OTHER; 
+    memset(addr,0,sizeof(*addr));
+    addr->sas_family = AF_ATMSVC;
+    if (!ans(text,T_ATMA,addr,length)) return 0;
+    return ans(text,T_NSAP,addr,length);
+}
+
+
+static int encode_nsap(char *buf,const unsigned char *addr)
+{
+    static int fmt_dcc[] = { 2,12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+      4,2,0 };
+    static int fmt_e164[] = { 2,12,1,1,1,1,1,1,1,1,16,2,0 };
+    int *fmt;
+    int pos,i,j;
+
+    switch (*addr) {
+	case ATM_AFI_DCC:
+	case ATM_AFI_ICD:
+	case ATM_AFI_LOCAL:
+	case ATM_AFI_DCC_GROUP:
+	case ATM_AFI_ICD_GROUP:
+	case ATM_AFI_LOCAL_GROUP:
+	    fmt = fmt_dcc;
+	    break;
+	case ATM_AFI_E164:
+	case ATM_AFI_E164_GROUP:
+	    fmt = fmt_e164;
+	    break;
+	default:
+	    return TRY_OTHER;
+    }
+    pos = 2*ATM_ESA_LEN;
+    for (i = 0; fmt[i]; i++) {
+	pos -= fmt[i];
+	for (j = 0; j < fmt[i]; j++)
+	    sprintf(buf++,"%x",
+	      (addr[(pos+j) >> 1] >> 4*(1-((pos+j) & 1))) & 0xf);
+	*buf++ = '.';
+    }
+    strcpy(buf,"AESA.ATMA.INT.");
+    return 0;
+}
+
+
+static int encode_nsap_new(char *buf,const unsigned char *addr)
+{
+    int i;
+    int digit;
+
+    for (i = 20; i; ) {
+        i--;
+        digit = addr[i] & 0x0F;
+        *(buf++) = digit + (digit >= 10 ? '7' : '0');
+        *(buf++) = '.';
+        digit = ((unsigned char) (addr[i])) >> 4;
+        *(buf++) = digit + (digit >= 10 ? '7' : '0');
+        *(buf++) = '.';
+    }
+    strcpy (buf, "NSAP.INT.");
+    return 0;
+}
+
+
+static int cc_len(int p0,int p1)
+{
+    static char *cc_table = NULL;
+    FILE *file;
+    char buffer[MAX_LINE];
+    char *here;
+    int cc;
+
+    if (!cc_table) {
+	if (!(cc_table = malloc(100))) {
+	    perror("malloc");
+	    return E164_CC_DEFAULT_LEN;
+	}
+	memset(cc_table,E164_CC_DEFAULT_LEN,100);
+	if (!(file = fopen(E164_CC_FILE,"r")))
+	    perror(E164_CC_FILE);
+	else {
+	    while (fgets(buffer,MAX_LINE,file)) {
+		here = strchr(buffer,'#');
+		if (here) *here = 0;
+		if (sscanf(buffer,"%d",&cc) == 1) {
+		    if (cc < 10) cc_table[cc] = 1;
+		    else if (cc < 100) cc_table[cc] = 2;
+			else cc_table[cc/10] = 3;
+		}
+	    }
+	    fclose(file);
+	}
+    }
+    if (cc_table[p0] == 1) return 1;
+    return cc_table[p0*10+p1];
+}
+
+
+static int encode_e164(char *buf,const char *addr)
+{
+    const char *prefix,*here;
+
+    prefix = addr+cc_len(addr[0]-48,addr[1]-48);
+    here = strchr(addr,0);
+    while (here > prefix) {
+	*buf++ = *--here;
+	*buf++ = '.';
+    }
+    while (here > addr) *buf++ = *addr++;
+    strcpy(buf,".E164.ATMA.INT.");
+    return 0;
+}
+
+
+int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr,
+  int flags)
+{
+    char tmp[MAX_NAME]; /* could be smaller ... */
+    int res;
+
+    if (addr->sas_addr.prv) {
+        res = encode_nsap(tmp,addr->sas_addr.prv);
+        if (!res && !ans(tmp,T_PTR,buffer,length)) return 0;
+	res = encode_nsap_new(tmp,addr->sas_addr.prv);
+        if (res < 0) return res;
+	return ans(tmp,T_PTR,buffer,length);
+    } else {
+        res = encode_e164(tmp,addr->sas_addr.pub);
+        if (res < 0) return res;
+        return ans(tmp,T_PTR,buffer,length);
+    }
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/atm.h tomato121/release/src/router/pppd/pppd/plugins/pppoatm/atm.h
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/atm.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/atm.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,108 @@
+/* atm.h - Functions useful for ATM applications */
+ 
+/* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */
+ 
+
+#ifndef _ATM_H
+#define _ATM_H
+
+#include <stdint.h>
+#include <sys/socket.h>
+#include <linux/atm.h>
+
+
+/*
+ * For versions of glibc < 2.1
+ */
+
+#ifndef AF_ATMPVC
+#define AF_ATMPVC	8
+#endif
+
+#ifndef AF_ATMSVC
+#define AF_ATMSVC	20
+#endif
+
+#ifndef PF_ATMPVC
+#define PF_ATMPVC	AF_ATMPVC
+#endif
+
+#ifndef PF_ATMSVC
+#define PF_ATMSVC	AF_ATMSVC
+#endif
+
+#ifndef SOL_ATM
+#define SOL_ATM		264
+#endif
+
+#ifndef SOL_AAL
+#define SOL_AAL		265
+#endif
+
+
+#define HOSTS_ATM "/etc/hosts.atm"
+
+/* text2atm flags */
+#define T2A_PVC		  1	/* address is PVC */
+#define T2A_SVC		  2	/* address is SVC */
+#define T2A_UNSPEC	  4	/* allow unspecified parts in PVC address */
+#define T2A_WILDCARD	  8	/* allow wildcards in PVC or SVC address */
+#define T2A_NNI		 16	/* allow NNI VPI range (PVC) */
+#define T2A_NAME	 32	/* allow name resolution */
+#define T2A_REMOTE	 64	/* OBSOLETE */
+#define T2A_LOCAL	128	/* don't use ANS */
+
+/* atm2text flags */
+#define A2T_PRETTY	 1	/* add syntactic sugar */
+#define A2T_NAME	 2	/* attempt name lookup */
+#define A2T_REMOTE	 4	/* OBSOLETE */
+#define A2T_LOCAL	 8	/* don't use ANS */
+
+/* atm_equal flags */
+#define AXE_WILDCARD	 1	/* allow wildcard match */
+#define AXE_PRVOPT	 2	/* private part of SVC address is optional */
+
+/* text2qos flags */
+#define T2Q_DEFAULTS	 1	/* structure contains default values */
+
+/* text2sap flags */
+#define T2S_NAME	 1	/* attempt name lookup */
+#define T2S_LOCAL	 2	/* we may support NIS or such in the future */
+
+/* sap2text flags */
+#define S2T_NAME	 1	/* attempt name lookup */
+#define S2T_LOCAL	 2	/* we may support NIS or such in the future */
+
+/* sap_equal flags */
+#define SXE_COMPATIBLE	 1	/* check for compatibility instead of identity*/
+#define SXE_NEGOTIATION	 2	/* allow negotiation; requires SXE_COMPATIBLE;
+				   assumes "a" defines the available
+				   capabilities */
+#define SXE_RESULT	 4	/* return selected SAP */
+
+#define MAX_ATM_ADDR_LEN (2*ATM_ESA_LEN+ATM_E164_LEN+5)
+				/* 4 dots, 1 plus */
+#define MAX_ATM_NAME_LEN 256	/* wild guess */
+#define MAX_ATM_QOS_LEN 116	/* 5+4+2*(3+3*(7+9)+2)+1 */
+#define MAX_ATM_SAP_LEN	255	/* BHLI(27)+1+3*BLLI(L2=33,L3=41,+1)+2 */
+
+
+int text2atm(const char *text,struct sockaddr *addr,int length,int flags);
+int atm2text(char *buffer,int length,const struct sockaddr *addr,int flags);
+int atm_equal(const struct sockaddr *a,const struct sockaddr *b,int len,
+  int flags);
+
+int sdu2cell(int s,int sizes,const int *sdu_size,int *num_sdu);
+
+int text2qos(const char *text,struct atm_qos *qos,int flags);
+int qos2text(char *buffer,int length,const struct atm_qos *qos,int flags);
+int qos_equal(const struct atm_qos *a,const struct atm_qos *b);
+
+int text2sap(const char *text,struct atm_sap *sap,int flags);
+int sap2text(char *buffer,int length,const struct atm_sap *sap,int flags);
+int sap_equal(const struct atm_sap *a,const struct atm_sap *b,int flags,...);
+
+int __t2q_get_rate(const char **text,int up);
+int __atmlib_fetch(const char **pos,...); /* internal use only */
+
+#endif
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/atmres.h tomato121/release/src/router/pppd/pppd/plugins/pppoatm/atmres.h
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/atmres.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/atmres.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,36 @@
+/* atmres.h - Common definitions and prototypes for resolver functions */
+
+/* Written 1996,1998 by Werner Almesberger, EPFL-LRC/ICA */
+
+
+#ifndef _ATMRES_H
+#define _ATMRES_H
+
+#include <arpa/nameser.h>
+#include <linux/atm.h>
+
+
+/* Some #defines that may be needed if ANS isn't installed on that system */
+
+#ifndef T_ATMA
+#define T_ATMA		34
+#endif
+#ifndef ATMA_AESA
+#define ATMA_AESA	0
+#endif
+#ifndef ATMA_E164
+#define ATMA_E164	1
+#endif
+
+/* Return codes for text2atm and atm2text */
+
+#define TRY_OTHER -2
+#define FATAL	  -1 /* must be -1 */
+
+
+int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length,
+  int flags);
+int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr,
+  int flags);
+
+#endif
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/atmsap.h tomato121/release/src/router/pppd/pppd/plugins/pppoatm/atmsap.h
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/atmsap.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/atmsap.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,45 @@
+/* atmsap.h - ATM Service Access Point addressing definitions */
+
+/* Written 1996-1998 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#ifndef _ATMSAP_H
+#define _ATMSAP_H
+
+#include <stdint.h>
+#include <linux/atmsap.h>
+
+
+/*
+ * Selected ISO/IEC TR 9577 Network Layer Protocol Identifiers (NLPID)
+ */
+
+#define NLPID_IEEE802_1_SNAP	0x80	/* IEEE 802.1 SNAP */
+
+/*
+ * Selected Organizationally Unique Identifiers (OUIs)
+ */
+
+#define ATM_FORUM_OUI		"\x00\xA0\x3E"	/* ATM Forum */
+#define EPFL_OUI		"\x00\x60\xD7"	/* EPF Lausanne, CH */
+
+/*
+ * Selected vendor-specific application identifiers (for B-HLI). Such an
+ * identifier consists of three bytes containing the OUI, followed by four
+ * bytes assigned by the organization owning the OUI.
+ */
+
+#define ANS_HLT_VS_ID		ATM_FORUM_OUI "\x00\x00\x00\x01"
+					 /* ATM Name System, af-saa-0069.000 */
+#define VOD_HLT_VS_ID		ATM_FORUM_OUI "\x00\x00\x00\x02"
+						     /* VoD, af-saa-0049.001 */
+#define AREQUIPA_HLT_VS_ID	EPFL_OUI "\x01\x00\x00\x01"	/* Arequipa  */
+#define TTCP_HLT_VS_ID		EPFL_OUI "\x01\x00\x00\x03"	/* ttcp_atm */
+
+
+/* Mapping of "well-known" TCP, UDP, etc. port numbers to ATM BHLIs.
+   btd-saa-api-bhli-01.02 */
+
+void atm_tcpip_port_mapping(char *vs_id,uint8_t protocol,uint16_t port);
+
+#endif
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/COPYING tomato121/release/src/router/pppd/pppd/plugins/pppoatm/COPYING
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/COPYING	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/COPYING	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,7 @@
+The files ans.c, atm.h, atmres.h, atmsap.h, misc.c, text2atm.c  and
+text2qos.c are taken from the linux-atm libraries.  These are 
+Copyright 1995-2000 EPFL-LRC/ICA, and are licensed under the GNU Lesser
+General Public License.
+
+The file pppoatm.c contains its own copyright notice, and is licensed
+under the GPL.
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/Makefile tomato121/release/src/router/pppd/pppd/plugins/pppoatm/Makefile
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,46 @@
+CC	= gcc
+COPTS	= -O2 -g
+CFLAGS	= $(COPTS) -I../.. -I../../../include -fPIC
+LDFLAGS	= -shared
+INSTALL	= install
+
+#***********************************************************************
+
+DESTDIR = @DESTDIR@
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
+
+PLUGIN := pppoatm.so
+PLUGIN_OBJS := pppoatm.o 
+
+#*******
+# Do we have the ATM libraries installed? Set HAVE_LIBATM to use them,
+# or leave it unset to build the few routines we actually _use_ into
+# the plugin directly.
+#
+#HAVE_LIBATM=yes
+
+ifdef HAVE_LIBATM
+LIBS := -latm
+else
+CFLAGS += -I.
+PLUGIN_OBJS += text2qos.o text2atm.o misc.o ans.o
+LIBS := -lresolv
+endif
+
+#*********
+all: $(PLUGIN)
+
+$(PLUGIN): $(PLUGIN_OBJS)
+	$(CC) $(CFLAGS) -o $@ -shared $^ $(LIBS)
+
+install: all
+	$(INSTALL) -d -m 755 $(LIBDIR)
+	$(INSTALL) -c -m 4550 $(PLUGIN) $(LIBDIR)
+
+clean:
+	rm -f *.o *.so
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/Makefile.linux tomato121/release/src/router/pppd/pppd/plugins/pppoatm/Makefile.linux
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/Makefile.linux	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,46 @@
+CC	= gcc
+COPTS	= -O2 -g
+CFLAGS	= $(COPTS) -I../.. -I../../../include -fPIC
+LDFLAGS	= -shared
+INSTALL	= install
+
+#***********************************************************************
+
+DESTDIR = @DESTDIR@
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
+
+PLUGIN := pppoatm.so
+PLUGIN_OBJS := pppoatm.o 
+
+#*******
+# Do we have the ATM libraries installed? Set HAVE_LIBATM to use them,
+# or leave it unset to build the few routines we actually _use_ into
+# the plugin directly.
+#
+#HAVE_LIBATM=yes
+
+ifdef HAVE_LIBATM
+LIBS := -latm
+else
+CFLAGS += -I.
+PLUGIN_OBJS += text2qos.o text2atm.o misc.o ans.o
+LIBS := -lresolv
+endif
+
+#*********
+all: $(PLUGIN)
+
+$(PLUGIN): $(PLUGIN_OBJS)
+	$(CC) $(CFLAGS) -o $@ -shared $^ $(LIBS)
+
+install: all
+	$(INSTALL) -d -m 755 $(LIBDIR)
+	$(INSTALL) -c -m 4550 $(PLUGIN) $(LIBDIR)
+
+clean:
+	rm -f *.o *.so
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/misc.c tomato121/release/src/router/pppd/pppd/plugins/pppoatm/misc.c
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/misc.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/misc.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,51 @@
+/* misc.c - Miscellaneous library functions */
+
+/* Written 1997-2000 by Werner Almesberger, EPFL-ICA/ICA */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h> /* for htons */
+
+#include <atm.h>
+#include <atmsap.h>
+
+
+int __atmlib_fetch(const char **pos,...)
+{
+    const char *value;
+    int ref_len,best_len,len;
+    int i,best;
+    va_list ap;
+
+    va_start(ap,pos);
+    ref_len = strlen(*pos);
+    best_len = 0;
+    best = -1;
+    for (i = 0; (value = va_arg(ap,const char *)); i++) {
+	len = strlen(value);
+	if (*value != '!' && len <= ref_len && len > best_len &&
+	  !strncasecmp(*pos,value,len)) {
+	    best = i;
+	    best_len = len;
+	}
+    }
+    va_end(ap);
+    if (best > -1) (*pos) += best_len;
+    return best;
+}
+
+
+void atm_tcpip_port_mapping(char *vs_id,uint8_t protocol,uint16_t port)
+{
+    memcpy(vs_id,ATM_FORUM_OUI "\x01",4);
+    vs_id[4] = protocol; /* e.g. IP_TCP or IP_UDP; from netinet/protocols.h */
+    vs_id[5] = (htons(port) >> 8) & 255;
+    vs_id[6] = htons(port) & 255;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/pppoatm.c tomato121/release/src/router/pppd/pppd/plugins/pppoatm/pppoatm.c
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/pppoatm.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/pppoatm.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,224 @@
+/* pppoatm.c - pppd plugin to implement PPPoATM protocol.
+ *
+ * Copyright 2000 Mitchell Blank Jr.
+ * Based in part on work from Jens Axboe and Paul Mackerras.
+ * Updated to ppp-2.4.1 by Bernhard Kaindl
+ *
+ * Updated to ppp-2.4.2 by David Woodhouse 2004.
+ *  - disconnect method added
+ *  - remove_options() abuse removed.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "pppd.h"
+#include "pathnames.h"
+#include "fsm.h" /* Needed for lcp.h to include cleanly */
+#include "lcp.h"
+#include <atm.h>
+#include <linux/atmdev.h>
+#include <linux/atmppp.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+const char pppd_version[] = VERSION;
+
+static struct sockaddr_atmpvc pvcaddr;
+static char *qosstr = NULL;
+static bool llc_encaps = 0;
+static bool vc_encaps = 0;
+static int device_got_set = 0;
+static int pppoatm_max_mtu, pppoatm_max_mru;
+static int setdevname_pppoatm(const char *cp, const char **argv, int doit);
+struct channel pppoa_channel;
+static int pppoa_fd = -1;
+
+static option_t pppoa_options[] = {
+	{ "device name", o_wild, (void *) &setdevname_pppoatm,
+	  "ATM service provider IDs: VPI.VCI",
+	  OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+	  devnam},
+	{ "llc-encaps", o_bool, &llc_encaps,
+	  "use LLC encapsulation for PPPoATM", 1},
+	{ "vc-encaps", o_bool, &vc_encaps,
+	  "use VC multiplexing for PPPoATM (default)", 1},
+	{ "qos", o_string, &qosstr,
+	  "set QoS for PPPoATM connection", 1},
+	{ NULL }
+};
+
+/* returns:
+ *  -1 if there's a problem with setting the device
+ *   0 if we can't parse "cp" as a valid name of a device
+ *   1 if "cp" is a reasonable thing to name a device
+ * Note that we don't actually open the device at this point
+ * We do need to fill in:
+ *   devnam: a string representation of the device
+ *   devstat: a stat structure of the device.  In this case
+ *     we're not opening a device, so we just make sure
+ *     to set up S_ISCHR(devstat.st_mode) != 1, so we
+ *     don't get confused that we're on stdin.
+ */
+int (*old_setdevname_hook)(const char* cp) = NULL;
+static int setdevname_pppoatm(const char *cp, const char **argv, int doit)
+{
+	struct sockaddr_atmpvc addr;
+	extern struct stat devstat;
+	if (device_got_set)
+		return 0;
+	//info("PPPoATM setdevname_pppoatm: '%s'", cp);
+	memset(&addr, 0, sizeof addr);
+	if (text2atm(cp, (struct sockaddr *) &addr, sizeof(addr),
+	    T2A_PVC | T2A_NAME) < 0) {
+               if(doit)
+                   info("atm does not recognize: %s", cp);
+		return 0;
+           }
+	if (!doit) return 1;
+	//if (!dev_set_ok()) return -1;
+	memcpy(&pvcaddr, &addr, sizeof pvcaddr);
+	strlcpy(devnam, cp, sizeof devnam);
+	devstat.st_mode = S_IFSOCK;
+	if (the_channel != &pppoa_channel) {
+		the_channel = &pppoa_channel;
+		lcp_wantoptions[0].neg_accompression = 0;
+		lcp_allowoptions[0].neg_accompression = 0;
+		lcp_wantoptions[0].neg_asyncmap = 0;
+		lcp_allowoptions[0].neg_asyncmap = 0;
+		lcp_wantoptions[0].neg_pcompression = 0;
+	}
+	info("PPPoATM setdevname_pppoatm - SUCCESS:%s", cp);
+	device_got_set = 1;
+	return 1;
+}
+
+#define pppoatm_overhead() (llc_encaps ? 6 : 2)
+
+static void no_device_given_pppoatm(void)
+{
+	fatal("No vpi.vci specified");
+}
+
+static void set_line_discipline_pppoatm(int fd)
+{
+	struct atm_backend_ppp be;
+	be.backend_num = ATM_BACKEND_PPP;
+	if (!llc_encaps)
+		be.encaps = PPPOATM_ENCAPS_VC;
+	else if (!vc_encaps)
+		be.encaps = PPPOATM_ENCAPS_LLC;
+	else
+		be.encaps = PPPOATM_ENCAPS_AUTODETECT;
+	if (ioctl(fd, ATM_SETBACKEND, &be) < 0)
+		fatal("ioctl(ATM_SETBACKEND): %m");
+}
+
+#if 0
+static void reset_line_discipline_pppoatm(int fd)
+{
+	atm_backend_t be = ATM_BACKEND_RAW;
+	/* 2.4 doesn't support this yet */
+	(void) ioctl(fd, ATM_SETBACKEND, &be);
+}
+#endif
+
+static int connect_pppoatm(void)
+{
+	int fd;
+	struct atm_qos qos;
+
+	system ("/sbin/modprobe -q pppoatm");
+
+	if (!device_got_set)
+		no_device_given_pppoatm();
+	fd = socket(AF_ATMPVC, SOCK_DGRAM, 0);
+	if (fd < 0)
+		fatal("failed to create socket: %m");
+	memset(&qos, 0, sizeof qos);
+	qos.txtp.traffic_class = qos.rxtp.traffic_class = ATM_UBR;
+	/* TODO: support simplified QoS setting */
+	if (qosstr != NULL)
+		if (text2qos(qosstr, &qos, 0))
+			fatal("Can't parse QoS: \"%s\"");
+	qos.txtp.max_sdu = lcp_allowoptions[0].mru + pppoatm_overhead();
+	qos.rxtp.max_sdu = lcp_wantoptions[0].mru + pppoatm_overhead();
+	qos.aal = ATM_AAL5;
+	if (setsockopt(fd, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0)
+		fatal("setsockopt(SO_ATMQOS): %m");
+	/* TODO: accept on SVCs... */
+	if (connect(fd, (struct sockaddr *) &pvcaddr,
+	    sizeof(struct sockaddr_atmpvc)))
+		fatal("connect(%s): %m", devnam);
+	pppoatm_max_mtu = lcp_allowoptions[0].mru;
+	pppoatm_max_mru = lcp_wantoptions[0].mru;
+	set_line_discipline_pppoatm(fd);
+	strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+	pppoa_fd = fd;
+	return fd;
+}
+
+static void disconnect_pppoatm(void)
+{
+	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__)
+	extern int new_style_driver;	/* From sys-linux.c */
+	if (!ppp_available() && !new_style_driver)
+		fatal("Kernel doesn't support ppp_generic - "
+		    "needed for PPPoATM");
+#else
+	fatal("No PPPoATM support on this OS");
+#endif
+	info("PPPoATM plugin_init");
+	add_options(pppoa_options);
+}
+struct channel pppoa_channel = {
+    options: pppoa_options,
+    process_extra_options: NULL,
+    check_options: NULL,
+    connect: &connect_pppoatm,
+    disconnect: &disconnect_pppoatm,
+    establish_ppp: &generic_establish_ppp,
+    disestablish_ppp: &generic_disestablish_ppp,
+    send_config: &send_config_pppoa,
+    recv_config: &recv_config_pppoa,
+    close: NULL,
+    cleanup: NULL
+};
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/text2atm.c tomato121/release/src/router/pppd/pppd/plugins/pppoatm/text2atm.c
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/text2atm.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/text2atm.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,249 @@
+/* text2atm.c - Converts textual representation of ATM address to binary
+		encoding */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#include "atm.h"
+#include "atmsap.h"
+#include "atmres.h"
+
+
+static int try_pvc(const char *text,struct sockaddr_atmpvc *addr,int flags)
+{
+    int part[3];
+    int i;
+
+    part[0] = part[1] = part[2] = 0;
+    i = 0;
+    while (1) {
+	if (!*text) return FATAL; /* empty or ends with a dot */
+	if (i == 3) return TRY_OTHER; /* too long */
+	if (isdigit(*text)) {
+	    if (*text == '0' && isdigit(text[1])) return TRY_OTHER;
+		/* no leading zeroes */
+	    do {
+		if (part[i] > INT_MAX/10) return TRY_OTHER;/* number too big */
+		part[i] = part[i]*10+*text++-'0';
+	    }
+	    while (isdigit(*text));
+	    i++;
+	    if (!*text) break;
+	    if (*text++ != '.') return TRY_OTHER; /* non-PVC character */
+	    continue;
+	}
+	if (*text == '*') {
+	    if (!(flags & T2A_WILDCARD)) return FATAL; /* not allowed */
+	    part[i++] = ATM_ITF_ANY; /* all *_ANY have the same value */
+	}
+	else {
+	    if (*text != '?') return TRY_OTHER; /* invalid character */
+	    if (!(flags & T2A_UNSPEC)) return FATAL; /* not allowed */
+	    part[i++] = ATM_VPI_UNSPEC; /* all *_UNSPEC have the same
+					   value */
+	}
+	if (!*++text) break;
+	if (*text++ != '.') return FATAL; /* dot required */
+    }
+    if (i < 2) return TRY_OTHER; /* no dots */
+    if (i == 2) {
+	part[2] = part[1];
+	part[1] = part[0];
+	part[0] = 0; /* default interface */
+    }
+    if (part[0] > SHRT_MAX || part[2] > ATM_MAX_VCI)
+	return TRY_OTHER; /* too big */
+    if (part[1] > (flags & T2A_NNI ? ATM_MAX_VPI_NNI : ATM_MAX_VPI))
+	return TRY_OTHER; /* too big */
+    if (part[0] == ATM_VPI_UNSPEC) return FATAL; /* bad */
+    addr->sap_family = AF_ATMPVC;
+    addr->sap_addr.itf = part[0];
+    addr->sap_addr.vpi = part[1];
+    addr->sap_addr.vci = part[2];
+    return 0;
+}
+
+
+static int do_try_nsap(const char *text,struct sockaddr_atmsvc *addr,int flags)
+{
+    const char *walk;
+    int count,pos,dot;
+    int offset,len;
+    char value;
+
+    count = dot = 0;
+    for (walk = text; *walk; walk++)
+	if (isdigit(*walk)) {
+	    if (count++ == 15) break;
+	    dot = 1;
+	}
+	else if (*text != '.') break;
+	    else if (!dot) return FATAL; /* two dots in a row */
+		else dot = 0;
+    if (*walk != ':') {
+	pos = 0;
+	offset = 0;
+    }
+    else {
+	if (!dot || *text == '0') return FATAL;
+	addr->sas_addr.prv[0] = ATM_AFI_E164;
+	addr->sas_addr.prv[1] = 0;
+	memset(addr->sas_addr.prv+1,0,8);
+	for (pos = 18-count-1; *text; text++) {
+	    if (*text == '.') continue;
+	    if (*text == ':') break;
+	    else {
+		if (pos & 1) addr->sas_addr.prv[pos >> 1] |= *text-'0';
+		else addr->sas_addr.prv[pos >> 1] = (*text-'0') << 4;
+		pos++;
+	    }
+ 	}
+	addr->sas_addr.prv[8] |= 0xf;
+	text++;
+	pos++;
+	offset = 72;
+    }
+    for (dot = 0; *text; text++)
+	if (isxdigit(*text)) {
+	    if (pos == ATM_ESA_LEN*2) return TRY_OTHER; /* too long */
+	    value = isdigit(*text) ? *text-'0' : (islower(*text) ?
+	      toupper(*text) : *text)-'A'+10;
+	    if (pos & 1) addr->sas_addr.prv[pos >> 1] |= value;
+	    else addr->sas_addr.prv[pos >> 1] = value << 4;
+	    pos++;
+	    dot = 1;
+	}
+	else 
+	    if (*text == '/' && (flags & T2A_WILDCARD)) break;
+	    else if (*text != '.') return TRY_OTHER;
+		else {
+		    if (!dot) return FATAL; /* two dots in a row */
+		    dot = 0;
+		}
+    if (!dot) return FATAL;
+    if (pos > 1 && !*addr->sas_addr.prv)
+	return TRY_OTHER; /* no leading zeroes */
+    if (!*text)
+	return pos != ATM_ESA_LEN*2 ? TRY_OTHER : ATM_ESA_LEN*2;
+	  /* handle bad length */
+    len = 0;
+    while (*++text) {
+	if (!isdigit(*text)) return -1; /* non-digit in length */
+	if (len >= pos*4) return -1; /* too long */
+	len = len*10+*text-'0';
+    }
+    if (len > 7 && addr->sas_addr.prv[0] != ATM_AFI_E164) offset = 72;
+    if (len < offset) return FATAL;
+    return len > pos*4 ? TRY_OTHER : len;
+}
+
+
+static int try_nsap(const char *text,struct sockaddr_atmsvc *addr,int flags)
+{
+    int result;
+
+    result = do_try_nsap(text,addr,flags);
+    if (result < 0) return result;
+    addr->sas_family = AF_ATMSVC;
+    *addr->sas_addr.pub = 0;
+    return result;
+}
+
+
+static int try_e164(const char *text,struct sockaddr_atmsvc *addr,int flags)
+{
+    int i,dot,result;
+
+    if (*text == ':' || *text == '+') text++;
+    for (i = dot = 0; *text; text++)
+	if (isdigit(*text)) {
+	    if (i == ATM_E164_LEN) return TRY_OTHER; /* too long */
+	    addr->sas_addr.pub[i++] = *text;
+	    dot = 1;
+	}
+	else if (*text != '.') break;
+	    else {
+		if (!dot) return TRY_OTHER; /* two dots in a row */
+		dot = 0;
+	    }
+    if (!dot) return TRY_OTHER;
+    addr->sas_addr.pub[i] = 0;
+    *addr->sas_addr.prv = 0;
+    result = 0;
+    if (*text) {
+	if (*text++ != '+') return TRY_OTHER;
+	else {
+	    result = do_try_nsap(text,addr,flags);
+	    if (result < 0) return FATAL;
+	}
+    }
+    addr->sas_family = AF_ATMSVC;
+    return result;
+}
+
+
+static int search(FILE *file,const char *text,struct sockaddr *addr,int length,
+  int flags)
+{
+    char line[MAX_ATM_NAME_LEN+1];
+    const char *here;
+    int result;
+
+    while (fgets(line,MAX_ATM_NAME_LEN,file)) {
+	if (!strtok(line,"\t\n ")) continue;
+	while ((here = strtok(NULL,"\t\n ")))
+	    if (!strcasecmp(here,text)) {
+		here = strtok(line,"\t\n ");
+		result = text2atm(here,addr,length,flags);
+		if (result >= 0) return result;
+	    }
+    }
+    return TRY_OTHER;
+}
+
+
+static int try_name(const char *text,struct sockaddr *addr,int length,
+  int flags)
+{
+    FILE *file;
+    int result;
+
+    if (!(file = fopen(HOSTS_ATM,"r"))) return TRY_OTHER;
+    result = search(file,text,addr,length,flags);
+    (void) fclose(file);
+    return result;
+}
+
+
+int text2atm(const char *text,struct sockaddr *addr,int length,int flags)
+{
+    int result;
+
+    if (!*text) return -1;
+    if (!(flags & (T2A_PVC | T2A_SVC))) flags |= T2A_PVC | T2A_SVC;
+    if (length < sizeof(struct sockaddr_atmpvc)) return -1;
+    if (flags & T2A_PVC) {
+	result = try_pvc(text,(struct sockaddr_atmpvc *) addr,flags);
+	if (result != TRY_OTHER) return result;
+    }
+    if ((flags & T2A_SVC) && length >= sizeof(struct sockaddr_atmsvc)) {
+	result = try_nsap(text,(struct sockaddr_atmsvc *) addr,flags);
+	if (result != TRY_OTHER) return result;
+	result = try_e164(text,(struct sockaddr_atmsvc *) addr,flags);
+	if (result != TRY_OTHER) return result;
+    }
+    if (!(flags & T2A_NAME)) return -1;
+    result = try_name(text,addr,length,flags & ~T2A_NAME);
+    if (result == TRY_OTHER && !(flags & T2A_LOCAL))
+	result = ans_byname(text,(struct sockaddr_atmsvc *) addr,length,flags);
+    if (result != TRY_OTHER) return result;
+    return -1;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/pppoatm/text2qos.c tomato121/release/src/router/pppd/pppd/plugins/pppoatm/text2qos.c
--- tomato/release/src/router/pppd/pppd/plugins/pppoatm/text2qos.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/pppoatm/text2qos.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,180 @@
+/* text2qos.c - Converts textual representation of QOS parameters to binary
+		encoding */
+
+/* Written 1996-2000 by Werner Almesberger, EPFL-LRC/ICA */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "atm.h"
+
+
+#define fetch __atmlib_fetch
+
+
+#define RATE_ERROR -2
+
+
+int __t2q_get_rate(const char **text,int up)
+{
+    const char mult[] = "kKmMgGg";
+    const char *multiplier;
+    char *end;
+    unsigned int rate,fract;
+    int power;
+
+    if (!strncmp(*text,"max",3)) {
+	*text += 3;
+	return ATM_MAX_PCR;
+    }
+    rate = strtoul(*text,&end,10);
+    power = fract = 0;
+    if (*end == '.')
+	for (end++; *end && isdigit(*end); end++) {
+	    fract = fract*10+*end-48;
+	    if (--power == -9) break;
+	}
+    multiplier = NULL;
+    if (*end && (multiplier = strchr(mult,*end))) {
+	while (multiplier >= mult) {
+	    if (rate > UINT_MAX/1000) return RATE_ERROR;
+	    rate *= 1000;
+	    power += 3;
+	    multiplier -= 2;
+	}
+	end++;
+    }
+    while (power && fract)
+	if (power < 0) {
+	    fract /= 10;
+	    power++;
+	}
+	else {
+	    fract *= 10;
+	    power--;
+	}
+    rate += fract;
+    if (strlen(end) < 3) {
+	if (multiplier) return RATE_ERROR;
+    }
+    else if (!strncmp(end,"cps",3)) end += 3;
+	else if (!strncmp(end,"bps",3)) {
+		rate = (rate+(up ? 8*ATM_CELL_PAYLOAD-1 : 0))/8/
+		  ATM_CELL_PAYLOAD;
+		end += 3;
+	    }
+	    else if (multiplier) return RATE_ERROR;
+    if (rate > INT_MAX) return RATE_ERROR;
+    *text = end;
+    return rate;
+}
+
+
+static int params(const char **text,struct atm_trafprm *a,
+  struct atm_trafprm *b)
+{
+    int value;
+    char *end;
+
+    if (*(*text)++ != ':') return -1;
+    while (1) {
+	if (!**text) return -1;
+	switch (fetch(text,"max_pcr=","pcr=","min_pcr=","max_sdu=","sdu=",
+	  NULL)) {
+	    case 0:
+		if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
+		if (a) a->max_pcr = value;
+		if (b) b->max_pcr = value;
+		break;
+	    case 1:
+		if ((value = __t2q_get_rate(text,0)) == RATE_ERROR) return -1;
+		if (a) a->pcr = value;
+		if (b) b->pcr = value;
+		break;
+	    case 2:
+		if ((value = __t2q_get_rate(text,1)) == RATE_ERROR) return -1;
+		if (value == ATM_MAX_PCR) return -1;
+		if (a) a->min_pcr = value;
+		if (b) b->min_pcr = value;
+		break;
+	    case 3:
+	    case 4:
+		value = strtol(*text,&end,10);
+		if (value < 0) return -1;
+		*text = end;
+		if (a) a->max_sdu = value;
+		if (b) b->max_sdu = value;
+		break;
+	    default:
+		return 0;
+	}
+	if (!**text) break;
+	if (*(*text)++ != ',') return -1;
+    }
+    return 0;
+}
+
+
+int text2qos(const char *text,struct atm_qos *qos,int flags)
+{
+    int traffic_class,aal;
+
+    traffic_class = ATM_NONE;
+    aal = ATM_NO_AAL;
+    do {
+	static const unsigned char aal_number[] = { ATM_AAL0, ATM_AAL5 };
+	int item;
+
+	item = fetch(&text,"!none","ubr","cbr","vbr","abr","aal0","aal5",NULL);
+	switch (item) {
+	    case 1:
+	    case 2:
+		/* we don't support VBR yet */
+	    case 4:
+		traffic_class = item;
+		break;
+	    case 5:
+	    case 6:
+		aal = aal_number[item-5];
+		break;
+	    default:
+		return -1;
+	}
+    }
+    while (*text == ',' ? text++ : 0);
+    if (!traffic_class) return -1;
+    if (qos && !(flags & T2Q_DEFAULTS)) memset(qos,0,sizeof(*qos));
+    if (qos) qos->txtp.traffic_class = qos->rxtp.traffic_class = traffic_class;
+    if (qos && aal) qos->aal = aal;
+    if (!*text) return 0;
+    if (params(&text,qos ? &qos->txtp : NULL,qos ? &qos->rxtp : NULL))
+	return -1;
+    if (!*text) return 0;
+    switch (fetch(&text,"tx","rx",NULL)) {
+	case 0:
+	    if (!fetch(&text,":none",NULL)) {
+		if (qos) qos->txtp.traffic_class = ATM_NONE;
+		if (*text == ',') text++;
+		break;
+	    }
+	    if (params(&text,qos ? &qos->txtp : NULL,NULL)) return -1;
+	    break;
+	case 1:
+	    text -= 2;
+	    break;
+	default:
+	    return -1;
+    }
+    if (!*text) return 0;
+    if (fetch(&text,"rx",NULL)) return -1;
+    if (!fetch(&text,":none",NULL) && qos) qos->rxtp.traffic_class = ATM_NONE;
+    else if (params(&text,qos ? &qos->rxtp : NULL,NULL)) return -1;
+    return *text ? -1 : 0;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/avpair.c tomato121/release/src/router/pppd/pppd/plugins/radius/avpair.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/avpair.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/avpair.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,795 @@
+/*
+ * $Id: avpair.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+
+static void rc_extract_vendor_specific_attributes(int attrlen,
+						  unsigned char *ptr,
+						  VALUE_PAIR **vp);
+/*
+ * Function: rc_avpair_add
+ *
+ * Purpose: add an attribute-value pair to the given list.
+ *
+ * Returns: pointer to added a/v pair upon success, NULL pointer upon failure.
+ *
+ * Remarks: Always appends the new pair to the end of the list.
+ *
+ */
+
+VALUE_PAIR *rc_avpair_add (VALUE_PAIR **list, int attrid, void *pval, int len,
+			   int vendorcode)
+{
+	VALUE_PAIR     *vp;
+
+	vp = rc_avpair_new (attrid, pval, len, vendorcode);
+
+	if (vp != (VALUE_PAIR *) NULL)
+	{
+		rc_avpair_insert (list, (VALUE_PAIR *) NULL, vp);
+	}
+
+	return vp;
+
+}
+
+/*
+ * Function: rc_avpair_assign
+ *
+ * Purpose: assign the given value to an attribute-value pair.
+ *
+ * Returns:  0 on success,
+ *	    -1 on failure.
+ *
+ */
+
+int rc_avpair_assign (VALUE_PAIR *vp, void *pval, int len)
+{
+	int	result = -1;
+
+	switch (vp->type)
+	{
+		case PW_TYPE_STRING:
+
+			if (((len == 0) && (strlen ((char *) pval)) > AUTH_STRING_LEN)
+			    || (len > AUTH_STRING_LEN)) {
+				error("rc_avpair_assign: bad attribute length");
+				return result;
+		    }
+
+			if (len > 0) {
+				memcpy(vp->strvalue, (char *)pval, len);
+				vp->strvalue[len] = '\0';
+				vp->lvalue = len;
+			} else {
+			strncpy (vp->strvalue, (char *) pval, AUTH_STRING_LEN);
+			vp->lvalue = strlen((char *) pval);
+			}
+
+			result = 0;
+			break;
+
+		case PW_TYPE_DATE:
+		case PW_TYPE_INTEGER:
+		case PW_TYPE_IPADDR:
+
+			vp->lvalue = * (UINT4 *) pval;
+
+			result = 0;
+			break;
+
+		default:
+			error("rc_avpair_assign: unknown attribute %d", vp->type);
+	}
+	return result;
+}
+
+/*
+ * Function: rc_avpair_new
+ *
+ * Purpose: make a new attribute-value pair with given parameters.
+ *
+ * Returns: pointer to generated a/v pair when successful, NULL when failure.
+ *
+ */
+
+VALUE_PAIR *rc_avpair_new (int attrid, void *pval, int len, int vendorcode)
+{
+	VALUE_PAIR     *vp = (VALUE_PAIR *) NULL;
+	DICT_ATTR      *pda;
+
+	if ((pda = rc_dict_getattr (attrid, vendorcode)) == (DICT_ATTR *) NULL)
+	{
+		error("rc_avpair_new: unknown attribute %d", attrid);
+	}
+	else
+	{
+		if ((vp = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							!= (VALUE_PAIR *) NULL)
+		{
+			strncpy (vp->name, pda->name, sizeof (vp->name));
+			vp->attribute = attrid;
+			vp->vendorcode = vendorcode;
+			vp->next = (VALUE_PAIR *) NULL;
+			vp->type = pda->type;
+			if (rc_avpair_assign (vp, pval, len) == 0)
+			{
+				return vp;
+			}
+			free (vp);
+			vp = (VALUE_PAIR *) NULL;
+		}
+		else
+			novm("rc_avpair_new");
+	}
+	return vp;
+}
+
+/*
+ *
+ * Function: rc_avpair_gen
+ *
+ * Purpose: takes attribute/value pairs from buffer and builds a
+ *	    value_pair list using allocated memory.
+ *
+ * Returns: value_pair list or NULL on failure
+ */
+
+VALUE_PAIR *rc_avpair_gen (AUTH_HDR *auth)
+{
+	int             length;
+	int             x_len;
+	int             attribute;
+	int             attrlen;
+	UINT4           lvalue;
+	unsigned char         *x_ptr;
+	unsigned char         *ptr;
+	DICT_ATTR      *attr;
+	VALUE_PAIR     *vp;
+	VALUE_PAIR     *pair;
+	unsigned char   hex[3];		/* For hex string conversion. */
+	char            buffer[512];
+
+	/*
+	 * Extract attribute-value pairs
+	 */
+	ptr = auth->data;
+	length = ntohs ((unsigned short) auth->length) - AUTH_HDR_LEN;
+	vp = (VALUE_PAIR *) NULL;
+
+	while (length > 0)
+	{
+		attribute = *ptr++;
+		attrlen = *ptr++;
+		attrlen -= 2;
+		if (attrlen < 0)
+		{
+			error("rc_avpair_gen: received attribute with invalid length");
+			break;
+		}
+
+		/* Handle vendor-specific specially */
+		if (attribute == PW_VENDOR_SPECIFIC) {
+		    rc_extract_vendor_specific_attributes(attrlen, ptr, &vp);
+		    ptr += attrlen;
+		    length -= (attrlen + 2);
+		    continue;
+		}
+		if ((attr = rc_dict_getattr (attribute, VENDOR_NONE)) == (DICT_ATTR *) NULL)
+		{
+			*buffer= '\0';	/* Initial length. */
+			for (x_ptr = ptr, x_len = attrlen ;
+				x_len > 0 ;
+				x_len--, x_ptr++)
+			{
+				sprintf (hex, "%2.2X", *x_ptr);
+				strcat (buffer, hex);
+			}
+			warn("rc_avpair_gen: received unknown attribute %d of length %d: 0x%s",
+				attribute, attrlen, buffer);
+		}
+		else
+		{
+			if ((pair =
+				(VALUE_PAIR *) malloc (sizeof (VALUE_PAIR))) ==
+					(VALUE_PAIR *) NULL)
+			{
+				novm("rc_avpair_gen");
+				rc_avpair_free(vp);
+				return NULL;
+			}
+			strcpy (pair->name, attr->name);
+			pair->attribute = attr->value;
+			pair->vendorcode = VENDOR_NONE;
+			pair->type = attr->type;
+			pair->next = (VALUE_PAIR *) NULL;
+
+			switch (attr->type)
+			{
+
+			    case PW_TYPE_STRING:
+				memcpy (pair->strvalue, (char *) ptr, (size_t) attrlen);
+				pair->strvalue[attrlen] = '\0';
+				pair->lvalue = attrlen;
+				rc_avpair_insert (&vp, (VALUE_PAIR *) NULL, pair);
+				break;
+
+			    case PW_TYPE_INTEGER:
+			    case PW_TYPE_IPADDR:
+				memcpy ((char *) &lvalue, (char *) ptr,
+					sizeof (UINT4));
+				pair->lvalue = ntohl (lvalue);
+				rc_avpair_insert (&vp, (VALUE_PAIR *) NULL, pair);
+				break;
+
+			    default:
+				warn("rc_avpair_gen: %s has unknown type", attr->name);
+				free (pair);
+				break;
+			}
+
+		}
+		ptr += attrlen;
+		length -= attrlen + 2;
+	}
+	return (vp);
+}
+
+/*
+ * Function: rc_extract_vendor_specific_attributes
+ *
+ * Purpose: Extracts vendor-specific attributes, assuming they are in
+ *          the "SHOULD" format recommended by RCF 2138.
+ *
+ * Returns: found value_pair
+ *
+ */
+static void rc_extract_vendor_specific_attributes(int attrlen,
+						  unsigned char *ptr,
+						  VALUE_PAIR **vp)
+{
+    int vendor_id;
+    int vtype;
+    int vlen;
+    UINT4 lvalue;
+    DICT_ATTR *attr;
+    VALUE_PAIR *pair;
+
+    /* ptr is sitting at vendor-ID */
+    if (attrlen < 8) {
+	/* Nothing to see here... */
+	return;
+    }
+
+    /* High-order octet of Vendor-Id must be zero (RFC2138) */
+    if (*ptr) {
+	return;
+    }
+
+    /* Extract vendor_id */
+    vendor_id = (int) (
+	((unsigned int) ptr[1]) * 256 * 256 +
+	((unsigned int) ptr[2]) * 256 +
+	((unsigned int) ptr[3]));
+    /* Bump ptr up to contents */
+    ptr += 4;
+
+    /* Set attrlen to length of data */
+    attrlen -= 4;
+    for (; attrlen; attrlen -= vlen+2, ptr += vlen) {
+	vtype = *ptr++;
+	vlen = *ptr++;
+	vlen -= 2;
+	if (vlen < 0 || vlen > attrlen - 2) {
+	    /* Do not log an error.  We are supposed to be able to cope with
+	       arbitrary vendor-specific gunk */
+	    return;
+	}
+	/* Looks plausible... */
+	if ((attr = rc_dict_getattr(vtype, vendor_id)) == NULL) {
+	    continue;
+	}
+
+	/* TODO: Check that length matches data size!!!!! */
+	pair = (VALUE_PAIR *) malloc(sizeof(VALUE_PAIR));
+	if (!pair) {
+	    novm("rc_avpair_gen");
+	    return;
+	}
+	strcpy(pair->name, attr->name);
+	pair->attribute = attr->value;
+	pair->vendorcode = vendor_id;
+	pair->type = attr->type;
+	pair->next = NULL;
+	switch (attr->type) {
+	case PW_TYPE_STRING:
+	    memcpy (pair->strvalue, (char *) ptr, (size_t) vlen);
+	    pair->strvalue[vlen] = '\0';
+	    pair->lvalue = vlen;
+	    rc_avpair_insert (vp, (VALUE_PAIR *) NULL, pair);
+	    break;
+
+	case PW_TYPE_INTEGER:
+	case PW_TYPE_IPADDR:
+	    memcpy ((char *) &lvalue, (char *) ptr,
+		    sizeof (UINT4));
+	    pair->lvalue = ntohl (lvalue);
+	    rc_avpair_insert (vp, (VALUE_PAIR *) NULL, pair);
+	    break;
+
+	default:
+	    warn("rc_avpair_gen: %s has unknown type", attr->name);
+	    free (pair);
+	    break;
+	}
+    }
+}
+
+/*
+ * Function: rc_avpair_get
+ *
+ * Purpose: Find the first attribute value-pair (which matches the given
+ *          attribute) from the specified value-pair list.
+ *
+ * Returns: found value_pair
+ *
+ */
+
+VALUE_PAIR *rc_avpair_get (VALUE_PAIR *vp, UINT4 attr)
+{
+	for (; vp != (VALUE_PAIR *) NULL && vp->attribute != attr; vp = vp->next)
+	{
+		continue;
+	}
+	return (vp);
+}
+
+/*
+ * Function: rc_avpair_copy
+ *
+ * Purpose: Return a copy of the existing list "p" ala strdup().
+ *
+ */
+VALUE_PAIR *rc_avpair_copy(VALUE_PAIR *p)
+{
+	VALUE_PAIR *vp, *fp = NULL, *lp = NULL;
+
+	while (p) {
+		vp = malloc(sizeof(VALUE_PAIR));
+		if (!vp) {
+		    novm("rc_avpair_copy");
+		    return NULL; /* leaks a little but so what */
+		}
+		*vp = *p;
+		if (!fp)
+			fp = vp;
+		if (lp)
+			lp->next = vp;
+		lp = vp;
+		p = p->next;
+	}
+
+	return fp;
+}
+
+/*
+ * Function: rc_avpair_insert
+ *
+ * Purpose: Given the address of an existing list "a" and a pointer
+ *	    to an entry "p" in that list, add the list "b" to
+ *	    the "a" list after the "p" entry.  If "p" is NULL, add
+ *	    the list "b" to the end of "a".
+ *
+ */
+
+void rc_avpair_insert (VALUE_PAIR **a, VALUE_PAIR *p, VALUE_PAIR *b)
+{
+	VALUE_PAIR     *this_node = NULL;
+	VALUE_PAIR     *vp;
+
+	if (*a == (VALUE_PAIR *) NULL)
+	{
+		*a = b;
+		return;
+	}
+
+	if (!b)
+		return;
+
+	vp = *a;
+
+	if ( p == (VALUE_PAIR *) NULL) /* run to end of "a" list */
+	{
+		while (vp != (VALUE_PAIR *) NULL)
+		{
+			this_node = vp;
+			vp = vp->next;
+		}
+	}
+	else /* look for the "p" entry in the "a" list (or run to end) */
+	{
+		this_node = *a;
+		while (this_node != (VALUE_PAIR *) NULL)
+		{
+			if (this_node == p)
+			{
+				break;
+			}
+			this_node = this_node->next;
+		}
+	}
+
+	/* add "b" at this_node */
+	vp = this_node->next;
+	this_node->next = b;
+
+	/* run to end of "b" and connect the rest of "a" */
+	while (b->next)
+		b = b->next;
+	b->next = vp;
+
+	return;
+}
+
+/*
+ * Function: rc_avpair_free
+ *
+ * Purpose: frees all value_pairs in the list
+ *
+ */
+
+void rc_avpair_free (VALUE_PAIR *pair)
+{
+	VALUE_PAIR     *next;
+
+	while (pair != (VALUE_PAIR *) NULL)
+	{
+		next = pair->next;
+		free (pair);
+		pair = next;
+	}
+}
+
+/*
+ * Function: rc_fieldcpy
+ *
+ * Purpose: Copy a data field from the buffer.  Advance the buffer
+ *          past the data field.
+ *
+ */
+
+static void rc_fieldcpy (char *string, char **uptr)
+{
+	char           *ptr;
+
+	ptr = *uptr;
+	if (*ptr == '"')
+	{
+		ptr++;
+		while (*ptr != '"' && *ptr != '\0' && *ptr != '\n')
+		{
+			*string++ = *ptr++;
+		}
+		*string = '\0';
+		if (*ptr == '"')
+		{
+			ptr++;
+		}
+		*uptr = ptr;
+		return;
+	}
+
+	while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
+			*ptr != '=' && *ptr != ',')
+	{
+		*string++ = *ptr++;
+	}
+	*string = '\0';
+	*uptr = ptr;
+	return;
+}
+
+
+/*
+ * Function: rc_avpair_parse
+ *
+ * Purpose: parses the buffer to extract the attribute-value pairs.
+ *
+ * Returns: 0 = successful parse of attribute-value pair,
+ *	   -1 = syntax (or other) error detected.
+ *
+ */
+
+#define PARSE_MODE_NAME		0
+#define PARSE_MODE_EQUAL	1
+#define PARSE_MODE_VALUE	2
+#define PARSE_MODE_INVALID	3
+
+int rc_avpair_parse (char *buffer, VALUE_PAIR **first_pair)
+{
+	int             mode;
+	char            attrstr[AUTH_ID_LEN];
+	char            valstr[AUTH_ID_LEN];
+	DICT_ATTR      *attr = NULL;
+	DICT_VALUE     *dval;
+	VALUE_PAIR     *pair;
+	VALUE_PAIR     *link;
+	struct tm      *tm;
+	time_t          timeval;
+
+	mode = PARSE_MODE_NAME;
+	while (*buffer != '\n' && *buffer != '\0')
+	{
+		if (*buffer == ' ' || *buffer == '\t')
+		{
+			buffer++;
+			continue;
+		}
+
+		switch (mode)
+		{
+		    case PARSE_MODE_NAME:		/* Attribute Name */
+			rc_fieldcpy (attrstr, &buffer);
+			if ((attr =
+				rc_dict_findattr (attrstr)) == (DICT_ATTR *) NULL)
+			{
+				error("rc_avpair_parse: unknown attribute");
+				if (*first_pair) {
+					rc_avpair_free(*first_pair);
+					*first_pair = (VALUE_PAIR *) NULL;
+				}
+				return (-1);
+			}
+			mode = PARSE_MODE_EQUAL;
+			break;
+
+		    case PARSE_MODE_EQUAL:		/* Equal sign */
+			if (*buffer == '=')
+			{
+				mode = PARSE_MODE_VALUE;
+				buffer++;
+			}
+			else
+			{
+				error("rc_avpair_parse: missing or misplaced equal sign");
+				if (*first_pair) {
+					rc_avpair_free(*first_pair);
+					*first_pair = (VALUE_PAIR *) NULL;
+				}
+				return (-1);
+			}
+			break;
+
+		    case PARSE_MODE_VALUE:		/* Value */
+			rc_fieldcpy (valstr, &buffer);
+
+			if ((pair =
+				(VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							== (VALUE_PAIR *) NULL)
+			{
+				novm("rc_avpair_parse");
+				if (*first_pair) {
+					rc_avpair_free(*first_pair);
+					*first_pair = (VALUE_PAIR *) NULL;
+				}
+				return (-1);
+			}
+			strcpy (pair->name, attr->name);
+			pair->attribute = attr->value;
+			pair->type = attr->type;
+			pair->vendorcode = attr->vendorcode;
+
+			switch (pair->type)
+			{
+
+			    case PW_TYPE_STRING:
+				strcpy (pair->strvalue, valstr);
+				pair->lvalue = strlen(valstr);
+				break;
+
+			    case PW_TYPE_INTEGER:
+				if (isdigit (*valstr))
+				{
+					pair->lvalue = atoi (valstr);
+				}
+				else
+				{
+					if ((dval = rc_dict_findval (valstr))
+							== (DICT_VALUE *) NULL)
+					{
+						error("rc_avpair_parse: unknown attribute value: %s", valstr);
+						if (*first_pair) {
+							rc_avpair_free(*first_pair);
+							*first_pair = (VALUE_PAIR *) NULL;
+						}
+						free (pair);
+						return (-1);
+					}
+					else
+					{
+						pair->lvalue = dval->value;
+					}
+				}
+				break;
+
+			    case PW_TYPE_IPADDR:
+				pair->lvalue = rc_get_ipaddr(valstr);
+				break;
+
+			    case PW_TYPE_DATE:
+				timeval = time (0);
+				tm = localtime (&timeval);
+				tm->tm_hour = 0;
+				tm->tm_min = 0;
+				tm->tm_sec = 0;
+				rc_str2tm (valstr, tm);
+#ifdef TIMELOCAL
+				pair->lvalue = (UINT4) timelocal (tm);
+#else	/* TIMELOCAL */
+				pair->lvalue = (UINT4) mktime (tm);
+#endif	/* TIMELOCAL */
+				break;
+
+			    default:
+				error("rc_avpair_parse: unknown attribute type %d", pair->type);
+				if (*first_pair) {
+					rc_avpair_free(*first_pair);
+					*first_pair = (VALUE_PAIR *) NULL;
+				}
+				free (pair);
+				return (-1);
+			}
+			pair->next = (VALUE_PAIR *) NULL;
+
+			if (*first_pair == (VALUE_PAIR *) NULL)
+			{
+				*first_pair = pair;
+			}
+			else
+			{
+				link = *first_pair;
+				while (link->next != (VALUE_PAIR *) NULL)
+				{
+					link = link->next;
+				}
+				link->next = pair;
+			}
+
+			mode = PARSE_MODE_NAME;
+			break;
+
+		    default:
+			mode = PARSE_MODE_NAME;
+			break;
+		}
+	}
+	return (0);
+}
+
+/*
+ * Function: rc_avpair_tostr
+ *
+ * Purpose: Translate an av_pair into two strings
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ */
+
+int rc_avpair_tostr (VALUE_PAIR *pair, char *name, int ln, char *value, int lv)
+{
+	DICT_VALUE     *dval;
+	char            buffer[32];
+	struct in_addr  inad;
+	unsigned char         *ptr;
+
+	*name = *value = '\0';
+
+	if (!pair || pair->name[0] == '\0') {
+		error("rc_avpair_tostr: pair is NULL or empty");
+		return (-1);
+	}
+
+	strncpy(name, pair->name, (size_t) ln);
+
+	switch (pair->type)
+	{
+	    case PW_TYPE_STRING:
+		lv--;
+		ptr = (unsigned char *) pair->strvalue;
+		while (*ptr != '\0')
+		{
+			if (!(isprint (*ptr)))
+			{
+				sprintf (buffer, "\\%03o", *ptr);
+				strncat(value, buffer, (size_t) lv);
+				lv -= 4;
+				if (lv < 0) break;
+			}
+			else
+			{
+				strncat(value, ptr, 1);
+				lv--;
+				if (lv < 0) break;
+			}
+			ptr++;
+		}
+		break;
+
+	    case PW_TYPE_INTEGER:
+		dval = rc_dict_getval (pair->lvalue, pair->name);
+		if (dval != (DICT_VALUE *) NULL)
+		{
+			strncpy(value, dval->name, (size_t) lv-1);
+		}
+		else
+		{
+			sprintf (buffer, "%ld", pair->lvalue);
+			strncpy(value, buffer, (size_t) lv);
+		}
+		break;
+
+	    case PW_TYPE_IPADDR:
+		inad.s_addr = htonl(pair->lvalue);
+		strncpy (value, inet_ntoa (inad), (size_t) lv-1);
+		break;
+
+	    case PW_TYPE_DATE:
+		strftime (buffer, sizeof (buffer), "%m/%d/%y %H:%M:%S",
+			  gmtime ((time_t *) & pair->lvalue));
+		strncpy(value, buffer, lv-1);
+		break;
+
+	    default:
+		error("rc_avpair_tostr: unknown attribute type %d", pair->type);
+		return (-1);
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Function: rc_avpair_readin
+ *
+ * Purpose: get a sequence of attribute value pairs from the file input
+ *	    and make them into a list of value_pairs
+ *
+ */
+
+VALUE_PAIR *rc_avpair_readin(FILE *input)
+{
+	VALUE_PAIR *vp = NULL;
+	char buffer[1024], *q;
+
+	while (fgets(buffer, sizeof(buffer), input) != NULL)
+	{
+		q = buffer;
+
+		while(*q && isspace(*q)) q++;
+
+		if ((*q == '\n') || (*q == '#') || (*q == '\0'))
+			continue;
+
+		if (rc_avpair_parse(q, &vp) < 0) {
+			error("rc_avpair_readin: malformed attribute: %s", buffer);
+			rc_avpair_free(vp);
+			return NULL;
+		}
+	}
+
+	return vp;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/buildreq.c tomato121/release/src/router/pppd/pppd/plugins/radius/buildreq.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/buildreq.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/buildreq.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,446 @@
+/*
+ * $Id: buildreq.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1997 Lars Fenneberg
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+
+unsigned char rc_get_seqnbr(void);
+
+/*
+ * Function: rc_get_nas_id
+ *
+ * Purpose: fills in NAS-Identifier or NAS-IP-Address in request
+ *
+ */
+
+int rc_get_nas_id(VALUE_PAIR **sendpairs)
+{
+	UINT4		client_id;
+	char *nasid;
+
+	nasid = rc_conf_str("nas_identifier");
+	if (strlen(nasid)) {
+		/*
+		 * Fill in NAS-Identifier
+		 */
+		if (rc_avpair_add(sendpairs, PW_NAS_IDENTIFIER, nasid, 0,
+				  VENDOR_NONE) == NULL)
+			return (ERROR_RC);
+
+		return (OK_RC);
+	  
+	} else {
+		/*
+		 * Fill in NAS-IP-Address
+		 */
+		if ((client_id = rc_own_ipaddress()) == 0)
+			return (ERROR_RC);
+
+		if (rc_avpair_add(sendpairs, PW_NAS_IP_ADDRESS, &client_id,
+				  0, VENDOR_NONE) == NULL)
+			return (ERROR_RC);
+	}
+
+	return (OK_RC);
+}
+
+/*
+ * Function: rc_buildreq
+ *
+ * Purpose: builds a skeleton RADIUS request using information from the
+ *	    config file.
+ *
+ */
+
+void rc_buildreq(SEND_DATA *data, int code, char *server, unsigned short port,
+		 int timeout, int retries)
+{
+	data->server = server;
+	data->svc_port = port;
+	data->seq_nbr = rc_get_seqnbr();
+	data->timeout = timeout;
+	data->retries = retries;
+	data->code = code;
+}
+
+/*
+ * Function: rc_guess_seqnbr
+ *
+ * Purpose: return a random sequence number
+ *
+ */
+
+static unsigned char rc_guess_seqnbr(void)
+{
+	return (unsigned char)(magic() & UCHAR_MAX);
+}
+
+/*
+ * Function: rc_get_seqnbr
+ *
+ * Purpose: generate a sequence number
+ *
+ */
+
+unsigned char rc_get_seqnbr(void)
+{
+	FILE *sf;
+	int tries = 1;
+	int seq_nbr, pos;
+	char *seqfile = rc_conf_str("seqfile");
+
+	if ((sf = fopen(seqfile, "a+")) == NULL)
+	{
+		error("rc_get_seqnbr: couldn't open sequence file %s: %s", seqfile, strerror(errno));
+		/* well, so guess a sequence number */
+		return rc_guess_seqnbr();
+	}
+
+	while (do_lock_exclusive(fileno(sf))!= 0)
+	{
+		if (errno != EWOULDBLOCK) {
+			error("rc_get_seqnbr: flock failure: %s: %s", seqfile, strerror(errno));
+			fclose(sf);
+			return rc_guess_seqnbr();
+		}
+		tries++;
+		if (tries <= 10)
+			rc_mdelay(500);
+		else
+			break;
+	}
+
+	if (tries > 10) {
+		error("rc_get_seqnbr: couldn't get lock after %d tries: %s", tries-1, seqfile);
+		fclose(sf);
+		return rc_guess_seqnbr();
+	}
+
+	pos = ftell(sf);
+	rewind(sf);
+	if (fscanf(sf, "%d", &seq_nbr) != 1) {
+		if (pos != ftell(sf)) {
+			/* file was not empty */
+			error("rc_get_seqnbr: fscanf failure: %s", seqfile);
+		}
+		seq_nbr = rc_guess_seqnbr();
+	}
+
+	rewind(sf);
+	ftruncate(fileno(sf),0);
+	fprintf(sf,"%d\n", (seq_nbr+1) & UCHAR_MAX);
+
+	fflush(sf); /* fflush because a process may read it between the do_unlock and fclose */
+
+	if (do_unlock(fileno(sf)) != 0)
+		error("rc_get_seqnbr: couldn't release lock on %s: %s", seqfile, strerror(errno));
+
+	fclose(sf);
+
+	return (unsigned char)seq_nbr;
+}
+
+/*
+ * Function: rc_auth
+ *
+ * Purpose: Builds an authentication request for port id client_port
+ *	    with the value_pairs send and submits it to a server
+ *
+ * Returns: received value_pairs in received, messages from the server in msg
+ *	    and 0 on success, negative on failure as return value
+ *
+ */
+
+int rc_auth(UINT4 client_port, VALUE_PAIR *send, VALUE_PAIR **received,
+	    char *msg, REQUEST_INFO *info)
+{
+    SERVER *authserver = rc_conf_srv("authserver");
+
+    if (!authserver) {
+	return (ERROR_RC);
+    }
+    return rc_auth_using_server(authserver, client_port, send, received,
+				msg, info);
+}
+
+/*
+ * Function: rc_auth_using_server
+ *
+ * Purpose: Builds an authentication request for port id client_port
+ *	    with the value_pairs send and submits it to a server.  You
+ *          explicitly supply a server list.
+ *
+ * Returns: received value_pairs in received, messages from the server in msg
+ *	    and 0 on success, negative on failure as return value
+ *
+ */
+
+int rc_auth_using_server(SERVER *authserver,
+			 UINT4 client_port,
+			 VALUE_PAIR *send,
+			 VALUE_PAIR **received,
+			 char *msg, REQUEST_INFO *info)
+{
+	SEND_DATA       data;
+	int		result;
+	int		i;
+	int		timeout = rc_conf_int("radius_timeout");
+	int		retries = rc_conf_int("radius_retries");
+
+	data.send_pairs = send;
+	data.receive_pairs = NULL;
+
+	/*
+	 * Fill in NAS-IP-Address or NAS-Identifier
+	 */
+
+	if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC)
+	    return (ERROR_RC);
+
+	/*
+	 * Fill in NAS-Port
+	 */
+
+	if (rc_avpair_add(&(data.send_pairs), PW_NAS_PORT, &client_port, 0, VENDOR_NONE) == NULL)
+		return (ERROR_RC);
+
+	result = ERROR_RC;
+	for(i=0; (i<authserver->max) && (result != OK_RC) && (result != BADRESP_RC)
+		; i++)
+	{
+		if (data.receive_pairs != NULL) {
+			rc_avpair_free(data.receive_pairs);
+			data.receive_pairs = NULL;
+		}
+		rc_buildreq(&data, PW_ACCESS_REQUEST, authserver->name[i],
+			    authserver->port[i], timeout, retries);
+
+		result = rc_send_server (&data, msg, info);
+	}
+
+	*received = data.receive_pairs;
+
+	return result;
+}
+
+/*
+ * Function: rc_auth_proxy
+ *
+ * Purpose: Builds an authentication request
+ *	    with the value_pairs send and submits it to a server.
+ *	    Works for a proxy; does not add IP address, and does
+ *	    does not rely on config file.
+ *
+ * Returns: received value_pairs in received, messages from the server in msg
+ *	    and 0 on success, negative on failure as return value
+ *
+ */
+
+int rc_auth_proxy(VALUE_PAIR *send, VALUE_PAIR **received, char *msg)
+{
+	SEND_DATA       data;
+	int		result;
+	int		i;
+	SERVER		*authserver = rc_conf_srv("authserver");
+	int		timeout = rc_conf_int("radius_timeout");
+	int		retries = rc_conf_int("radius_retries");
+
+	data.send_pairs = send;
+	data.receive_pairs = NULL;
+
+	result = ERROR_RC;
+	for(i=0; (i<authserver->max) && (result != OK_RC) && (result != BADRESP_RC)
+		; i++)
+	{
+		if (data.receive_pairs != NULL) {
+			rc_avpair_free(data.receive_pairs);
+			data.receive_pairs = NULL;
+		}
+		rc_buildreq(&data, PW_ACCESS_REQUEST, authserver->name[i],
+			    authserver->port[i], timeout, retries);
+
+		result = rc_send_server (&data, msg, NULL);
+	}
+
+	*received = data.receive_pairs;
+
+	return result;
+}
+
+
+/*
+ * Function: rc_acct_using_server
+ *
+ * Purpose: Builds an accounting request for port id client_port
+ *	    with the value_pairs send.  You explicitly supply server list.
+ *
+ * Remarks: NAS-Identifier/NAS-IP-Address, NAS-Port and Acct-Delay-Time get
+ *	    filled in by this function, the rest has to be supplied.
+ */
+
+int rc_acct_using_server(SERVER *acctserver,
+			 UINT4 client_port,
+			 VALUE_PAIR *send)
+{
+	SEND_DATA       data;
+	VALUE_PAIR	*adt_vp;
+	int		result;
+	time_t		start_time, dtime;
+	char		msg[4096];
+	int		i;
+	int		timeout = rc_conf_int("radius_timeout");
+	int		retries = rc_conf_int("radius_retries");
+
+	data.send_pairs = send;
+	data.receive_pairs = NULL;
+
+	/*
+	 * Fill in NAS-IP-Address or NAS-Identifier
+	 */
+
+	if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC)
+	    return (ERROR_RC);
+
+	/*
+	 * Fill in NAS-Port
+	 */
+
+	if (rc_avpair_add(&(data.send_pairs), PW_NAS_PORT, &client_port, 0, VENDOR_NONE) == NULL)
+		return (ERROR_RC);
+
+	/*
+	 * Fill in Acct-Delay-Time
+	 */
+
+	dtime = 0;
+	if ((adt_vp = rc_avpair_add(&(data.send_pairs), PW_ACCT_DELAY_TIME, &dtime, 0, VENDOR_NONE)) == NULL)
+		return (ERROR_RC);
+
+	start_time = time(NULL);
+	result = ERROR_RC;
+	for(i=0; (i<acctserver->max) && (result != OK_RC) && (result != BADRESP_RC)
+		; i++)
+	{
+		if (data.receive_pairs != NULL) {
+			rc_avpair_free(data.receive_pairs);
+			data.receive_pairs = NULL;
+		}
+		rc_buildreq(&data, PW_ACCOUNTING_REQUEST, acctserver->name[i],
+			    acctserver->port[i], timeout, retries);
+
+		dtime = time(NULL) - start_time;
+		rc_avpair_assign(adt_vp, &dtime, 0);
+
+		result = rc_send_server (&data, msg, NULL);
+	}
+
+	rc_avpair_free(data.receive_pairs);
+
+	return result;
+}
+
+/*
+ * Function: rc_acct
+ *
+ * Purpose: Builds an accounting request for port id client_port
+ *	    with the value_pairs send
+ *
+ * Remarks: NAS-Identifier/NAS-IP-Address, NAS-Port and Acct-Delay-Time get
+ *	    filled in by this function, the rest has to be supplied.
+ */
+
+int rc_acct(UINT4 client_port, VALUE_PAIR *send)
+{
+    SERVER *acctserver = rc_conf_srv("acctserver");
+    if (!acctserver) return (ERROR_RC);
+
+    return rc_acct_using_server(acctserver, client_port, send);
+}
+
+/*
+ * Function: rc_acct_proxy
+ *
+ * Purpose: Builds an accounting request with the value_pairs send
+ *
+ */
+
+int rc_acct_proxy(VALUE_PAIR *send)
+{
+	SEND_DATA       data;
+	int		result;
+	char		msg[4096];
+	int		i;
+	SERVER		*acctserver = rc_conf_srv("authserver");
+	int		timeout = rc_conf_int("radius_timeout");
+	int		retries = rc_conf_int("radius_retries");
+
+	data.send_pairs = send;
+	data.receive_pairs = NULL;
+
+	result = ERROR_RC;
+	for(i=0; (i<acctserver->max) && (result != OK_RC) && (result != BADRESP_RC)
+		; i++)
+	{
+		if (data.receive_pairs != NULL) {
+			rc_avpair_free(data.receive_pairs);
+			data.receive_pairs = NULL;
+		}
+		rc_buildreq(&data, PW_ACCOUNTING_REQUEST, acctserver->name[i],
+			    acctserver->port[i], timeout, retries);
+
+		result = rc_send_server (&data, msg, NULL);
+	}
+
+	rc_avpair_free(data.receive_pairs);
+
+	return result;
+}
+
+/*
+ * Function: rc_check
+ *
+ * Purpose: ask the server hostname on the specified port for a
+ *	    status message
+ *
+ */
+
+int rc_check(char *host, unsigned short port, char *msg)
+{
+	SEND_DATA       data;
+	int		result;
+	UINT4		service_type;
+	int		timeout = rc_conf_int("radius_timeout");
+	int		retries = rc_conf_int("radius_retries");
+
+	data.send_pairs = data.receive_pairs = NULL;
+
+	/*
+	 * Fill in NAS-IP-Address or NAS-Identifier,
+         * although it isn't neccessary
+	 */
+
+	if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC)
+	    return (ERROR_RC);
+
+	/*
+	 * Fill in Service-Type
+	 */
+
+	service_type = PW_ADMINISTRATIVE;
+	rc_avpair_add(&(data.send_pairs), PW_SERVICE_TYPE, &service_type, 0, VENDOR_NONE);
+
+	rc_buildreq(&data, PW_STATUS_SERVER, host, port, timeout, retries);
+	result = rc_send_server (&data, msg, NULL);
+
+	rc_avpair_free(data.receive_pairs);
+
+	return result;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/clientid.c tomato121/release/src/router/pppd/pppd/plugins/radius/clientid.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/clientid.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/clientid.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,121 @@
+/*
+ * $Id: clientid.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996,1997 Lars Fenneberg
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+
+struct map2id_s {
+	char *name;
+	UINT4 id;
+
+	struct map2id_s *next;
+};
+
+static struct map2id_s *map2id_list = NULL;
+
+/*
+ * Function: rc_read_mapfile
+ *
+ * Purpose: Read in the ttyname to port id map file
+ *
+ * Arguments: the file name of the map file
+ *
+ * Returns: zero on success, negative integer on failure
+ */
+
+int rc_read_mapfile(char *filename)
+{
+	char buffer[1024];
+	FILE *mapfd;
+	char *c, *name, *id, *q;
+	struct map2id_s *p;
+	int lnr = 0;
+
+	if ((mapfd = fopen(filename,"r")) == NULL)
+	{
+		error("rc_read_mapfile: can't read %s: %s", filename, strerror(errno));
+		return (-1);
+	}
+
+#define SKIP(p) while(*p && isspace(*p)) p++;
+
+	while (fgets(buffer, sizeof(buffer), mapfd) != NULL)
+	{
+		lnr++;
+
+		q = buffer;
+
+		SKIP(q);
+
+		if ((*q == '\n') || (*q == '#') || (*q == '\0'))
+			continue;
+
+		if (( c = strchr(q, ' ')) || (c = strchr(q,'\t'))) {
+
+			*c = '\0'; c++;
+			SKIP(c);
+
+			name = q;
+			id = c;
+
+			if ((p = (struct map2id_s *)malloc(sizeof(*p))) == NULL) {
+				novm("rc_read_mapfile");
+				return (-1);
+			}
+
+			p->name = strdup(name);
+			p->id = atoi(id);
+			p->next = map2id_list;
+			map2id_list = p;
+
+		} else {
+
+			error("rc_read_mapfile: malformed line in %s, line %d", filename, lnr);
+			return (-1);
+
+		}
+	}
+
+#undef SKIP
+
+	fclose(mapfd);
+
+	return 0;
+}
+
+/*
+ * Function: rc_map2id
+ *
+ * Purpose: Map ttyname to port id
+ *
+ * Arguments: full pathname of the tty
+ *
+ * Returns: port id, zero if no entry found
+ */
+
+UINT4 rc_map2id(char *name)
+{
+	struct map2id_s *p;
+	char ttyname[PATH_MAX];
+
+	*ttyname = '\0';
+	if (*name != '/')
+		strcpy(ttyname, "/dev/");
+
+	strncat(ttyname, name, sizeof(ttyname));
+
+	for(p = map2id_list; p; p = p->next)
+		if (!strcmp(ttyname, p->name)) return p->id;
+
+	warn("rc_map2id: can't find tty %s in map database", ttyname);
+
+	return 0;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/config.c tomato121/release/src/router/pppd/pppd/plugins/radius/config.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/config.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/config.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,550 @@
+/*
+ * $Id: config.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996,1997 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+#include <options.h>
+
+static int test_config(char *);
+
+/*
+ * Function: find_option
+ *
+ * Purpose: find an option in the option list
+ *
+ * Returns: pointer to option on success, NULL otherwise
+ */
+
+static OPTION *find_option(char *optname, unsigned int type)
+{
+	int i;
+
+	/* there're so few options that a binary search seems not necessary */
+	for (i = 0; i < num_options; i++) {
+		if (!strcmp(config_options[i].name, optname) &&
+		    (config_options[i].type & type))
+			return &config_options[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Function: set_option_...
+ *
+ * Purpose: set a specific option doing type conversions
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+
+static int set_option_str(char *filename, int line, OPTION *option, char *p)
+{
+	if (p)
+		option->val = (void *) strdup(p);
+	else
+		option->val = NULL;
+
+	return 0;
+}
+
+static int set_option_int(char *filename, int line, OPTION *option, char *p)
+{
+	int *iptr;
+
+	if (p == NULL) {
+		error("%s: line %d: bogus option value", filename, line);
+		return (-1);
+	}
+
+	if ((iptr = (int *) malloc(sizeof(iptr))) == NULL) {
+		novm("read_config");
+		return (-1);
+	}
+
+	*iptr = atoi(p);
+	option->val = (void *) iptr;
+
+	return 0;
+}
+
+static int set_option_srv(char *filename, int line, OPTION *option, char *p)
+{
+	SERVER *serv;
+	char *q;
+	struct servent *svp;
+	int i;
+
+	if (p == NULL) {
+		error("%s: line %d: bogus option value", filename, line);
+		return (-1);
+	}
+
+	serv = (SERVER *) option->val;
+
+	for (i = 0; i < serv->max; i++) {
+		free(serv->name[i]);
+	}
+	serv->max = 0;
+
+	while ((p = strtok(p, ", \t")) != NULL) {
+
+		if ((q = strchr(p,':')) != NULL) {
+			*q = '\0';
+			q++;
+			serv->port[serv->max] = atoi(q);
+		} else {
+			if (!strcmp(option->name,"authserver"))
+				if ((svp = getservbyname ("radius", "udp")) == NULL)
+					serv->port[serv->max] = PW_AUTH_UDP_PORT;
+				else
+					serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
+			else if (!strcmp(option->name, "acctserver"))
+				if ((svp = getservbyname ("radacct", "udp")) == NULL)
+					serv->port[serv->max] = PW_ACCT_UDP_PORT;
+				else
+					serv->port[serv->max] = ntohs ((unsigned int) svp->s_port);
+			else {
+				error("%s: line %d: no default port for %s", filename, line, option->name);
+				return (-1);
+			}
+		}
+
+		serv->name[serv->max++] = strdup(p);
+
+		p = NULL;
+	}
+
+	return 0;
+}
+
+static int set_option_auo(char *filename, int line, OPTION *option, char *p)
+{
+	int *iptr;
+
+	if (p == NULL) {
+		warn("%s: line %d: bogus option value", filename, line);
+		return (-1);
+	}
+
+	if ((iptr = (int *) malloc(sizeof(iptr))) == NULL) {
+			novm("read_config");
+			return (-1);
+	}
+
+	*iptr = 0;
+	p = strtok(p, ", \t");
+
+	if (!strncmp(p, "local", 5))
+			*iptr = AUTH_LOCAL_FST;
+	else if (!strncmp(p, "radius", 6))
+			*iptr = AUTH_RADIUS_FST;
+	else {
+		error("%s: auth_order: unknown keyword: %s", filename, p);
+		return (-1);
+	}
+
+	p = strtok(NULL, ", \t");
+
+	if (p && (*p != '\0')) {
+		if ((*iptr & AUTH_RADIUS_FST) && !strcmp(p, "local"))
+			*iptr = (*iptr) | AUTH_LOCAL_SND;
+		else if ((*iptr & AUTH_LOCAL_FST) && !strcmp(p, "radius"))
+			*iptr = (*iptr) | AUTH_RADIUS_SND;
+		else {
+			error("%s: auth_order: unknown or unexpected keyword: %s", filename, p);
+			return (-1);
+		}
+	}
+
+	option->val = (void *) iptr;
+
+	return 0;
+}
+
+
+/*
+ * Function: rc_read_config
+ *
+ * Purpose: read the global config file
+ *
+ * Returns: 0 on success, -1 when failure
+ */
+
+int rc_read_config(char *filename)
+{
+	FILE *configfd;
+	char buffer[512], *p;
+	OPTION *option;
+	int line, pos;
+
+	if ((configfd = fopen(filename,"r")) == NULL)
+	{
+		error("rc_read_config: can't open %s: %m", filename);
+		return (-1);
+	}
+
+	line = 0;
+	while ((fgets(buffer, sizeof(buffer), configfd) != NULL))
+	{
+		line++;
+		p = buffer;
+
+		if ((*p == '\n') || (*p == '#') || (*p == '\0'))
+			continue;
+
+		p[strlen(p)-1] = '\0';
+
+
+		if ((pos = strcspn(p, "\t ")) == 0) {
+			error("%s: line %d: bogus format: %s", filename, line, p);
+			return (-1);
+		}
+
+		p[pos] = '\0';
+
+		if ((option = find_option(p, OT_ANY)) == NULL) {
+			error("%s: line %d: unrecognized keyword: %s", filename, line, p);
+			return (-1);
+		}
+
+		if (option->status != ST_UNDEF) {
+			error("%s: line %d: duplicate option line: %s", filename, line, p);
+			return (-1);
+		}
+
+		p += pos+1;
+		while (isspace(*p))
+			p++;
+
+		switch (option->type) {
+			case OT_STR:
+				 if (set_option_str(filename, line, option, p) < 0)
+					return (-1);
+				break;
+			case OT_INT:
+				 if (set_option_int(filename, line, option, p) < 0)
+					return (-1);
+				break;
+			case OT_SRV:
+				 if (set_option_srv(filename, line, option, p) < 0)
+					return (-1);
+				break;
+			case OT_AUO:
+				 if (set_option_auo(filename, line, option, p) < 0)
+					return (-1);
+				break;
+			default:
+				fatal("rc_read_config: impossible case branch!");
+				abort();
+		}
+	}
+	fclose(configfd);
+
+	return test_config(filename);
+}
+
+/*
+ * Function: rc_conf_str, rc_conf_int, rc_conf_src
+ *
+ * Purpose: get the value of a config option
+ *
+ * Returns: config option value
+ */
+
+char *rc_conf_str(char *optname)
+{
+	OPTION *option;
+
+	option = find_option(optname, OT_STR);
+
+	if (option == NULL)
+		fatal("rc_conf_str: unkown config option requested: %s", optname);
+		return (char *)option->val;
+}
+
+int rc_conf_int(char *optname)
+{
+	OPTION *option;
+
+	option = find_option(optname, OT_INT|OT_AUO);
+
+	if (option == NULL)
+		fatal("rc_conf_int: unkown config option requested: %s", optname);
+	return *((int *)option->val);
+}
+
+SERVER *rc_conf_srv(char *optname)
+{
+	OPTION *option;
+
+	option = find_option(optname, OT_SRV);
+
+	if (option == NULL)
+		fatal("rc_conf_srv: unkown config option requested: %s", optname);
+	return (SERVER *)option->val;
+}
+
+/*
+ * Function: test_config
+ *
+ * Purpose: test the configuration the user supplied
+ *
+ * Returns: 0 on success, -1 when failure
+ */
+
+static int test_config(char *filename)
+{
+#if 0
+	struct stat st;
+	char	    *file;
+#endif
+
+	if (!(rc_conf_srv("authserver")->max))
+	{
+		error("%s: no authserver specified", filename);
+		return (-1);
+	}
+	if (!(rc_conf_srv("acctserver")->max))
+	{
+		error("%s: no acctserver specified", filename);
+		return (-1);
+	}
+	if (!rc_conf_str("servers"))
+	{
+		error("%s: no servers file specified", filename);
+		return (-1);
+	}
+	if (!rc_conf_str("dictionary"))
+	{
+		error("%s: no dictionary specified", filename);
+		return (-1);
+	}
+
+	if (rc_conf_int("radius_timeout") <= 0)
+	{
+		error("%s: radius_timeout <= 0 is illegal", filename);
+		return (-1);
+	}
+	if (rc_conf_int("radius_retries") <= 0)
+	{
+		error("%s: radius_retries <= 0 is illegal", filename);
+		return (-1);
+	}
+
+#if 0
+	file = rc_conf_str("login_local");
+	if (stat(file, &st) == 0)
+	{
+		if (!S_ISREG(st.st_mode)) {
+			error("%s: not a regular file: %s", filename, file);
+			return (-1);
+		}
+	} else {
+		error("%s: file not found: %s", filename, file);
+		return (-1);
+	}
+	file = rc_conf_str("login_radius");
+	if (stat(file, &st) == 0)
+	{
+		if (!S_ISREG(st.st_mode)) {
+			error("%s: not a regular file: %s", filename, file);
+			return (-1);
+		}
+	} else {
+		error("%s: file not found: %s", filename, file);
+		return (-1);
+	}
+#endif
+
+#if 0
+	if (rc_conf_int("login_tries") <= 0)
+	{
+		error("%s: login_tries <= 0 is illegal", filename);
+		return (-1);
+	}
+#endif
+	if (rc_conf_str("seqfile") == NULL)
+	{
+		error("%s: seqfile not specified", filename);
+		return (-1);
+	}
+#if 0
+	if (rc_conf_int("login_timeout") <= 0)
+	{
+		error("%s: login_timeout <= 0 is illegal", filename);
+		return (-1);
+	}
+#endif
+	if (rc_conf_str("mapfile") == NULL)
+	{
+		error("%s: mapfile not specified", filename);
+		return (-1);
+	}
+#if 0
+	if (rc_conf_str("nologin") == NULL)
+	{
+		error("%s: nologin not specified", filename);
+		return (-1);
+	}
+#endif
+
+	return 0;
+}
+
+/*
+ * Function: rc_find_match
+ *
+ * Purpose: see if ip_addr is one of the ip addresses of hostname
+ *
+ * Returns: 0 on success, -1 when failure
+ *
+ */
+
+static int find_match (UINT4 *ip_addr, char *hostname)
+{
+	UINT4           addr;
+	char          **paddr;
+	struct hostent *hp;
+
+	if (rc_good_ipaddr (hostname) == 0)
+	{
+		if (*ip_addr == ntohl(inet_addr (hostname)))
+		{
+			return (0);
+		}
+	}
+	else
+	{
+		if ((hp = gethostbyname (hostname)) == (struct hostent *) NULL)
+		{
+			return (-1);
+		}
+		for (paddr = hp->h_addr_list; *paddr; paddr++)
+		{
+			addr = ** (UINT4 **) paddr;
+			if (ntohl(addr) == *ip_addr)
+			{
+				return (0);
+			}
+		}
+	}
+	return (-1);
+}
+
+/*
+ * Function: rc_find_server
+ *
+ * Purpose: search a server in the servers file
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ */
+
+int rc_find_server (char *server_name, UINT4 *ip_addr, char *secret)
+{
+	UINT4	myipaddr = 0;
+	int             len;
+	int             result;
+	FILE           *clientfd;
+	char           *h;
+	char           *s;
+	char           *host2;
+	char            buffer[128];
+	char            hostnm[AUTH_ID_LEN + 1];
+
+	/* Get the IP address of the authentication server */
+	if ((*ip_addr = rc_get_ipaddr (server_name)) == (UINT4) 0)
+		return (-1);
+
+	if ((clientfd = fopen (rc_conf_str("servers"), "r")) == (FILE *) NULL)
+	{
+		error("rc_find_server: couldn't open file: %m: %s", rc_conf_str("servers"));
+		return (-1);
+	}
+
+	myipaddr = rc_own_ipaddress();
+
+	result = 0;
+	while (fgets (buffer, sizeof (buffer), clientfd) != (char *) NULL)
+	{
+		if (*buffer == '#')
+			continue;
+
+		if ((h = strtok (buffer, " \t\n")) == NULL) /* first hostname */
+			continue;
+
+		memset (hostnm, '\0', AUTH_ID_LEN);
+		len = strlen (h);
+		if (len > AUTH_ID_LEN)
+		{
+			len = AUTH_ID_LEN;
+		}
+		strncpy (hostnm, h, (size_t) len);
+		hostnm[AUTH_ID_LEN] = '\0';
+
+		if ((s = strtok (NULL, " \t\n")) == NULL) /* and secret field */
+			continue;
+
+		memset (secret, '\0', MAX_SECRET_LENGTH);
+		len = strlen (s);
+		if (len > MAX_SECRET_LENGTH)
+		{
+			len = MAX_SECRET_LENGTH;
+		}
+		strncpy (secret, s, (size_t) len);
+		secret[MAX_SECRET_LENGTH] = '\0';
+
+		if (!strchr (hostnm, '/')) /* If single name form */
+		{
+			if (find_match (ip_addr, hostnm) == 0)
+			{
+				result++;
+				break;
+			}
+		}
+		else /* <name1>/<name2> "paired" form */
+		{
+			strtok (hostnm, "/");
+			if (find_match (&myipaddr, hostnm) == 0)
+			{	     /* If we're the 1st name, target is 2nd */
+				host2 = strtok (NULL, " ");
+				if (find_match (ip_addr, host2) == 0)
+				{
+					result++;
+					break;
+				}
+			}
+			else	/* If we were 2nd name, target is 1st name */
+			{
+				if (find_match (ip_addr, hostnm) == 0)
+				{
+					result++;
+					break;
+				}
+			}
+		}
+	}
+	fclose (clientfd);
+	if (result == 0)
+	{
+		memset (buffer, '\0', sizeof (buffer));
+		memset (secret, '\0', sizeof (secret));
+		error("rc_find_server: couldn't find RADIUS server %s in %s",
+		      server_name, rc_conf_str("servers"));
+		return (-1);
+	}
+	return 0;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/COPYRIGHT tomato121/release/src/router/pppd/pppd/plugins/radius/COPYRIGHT
--- tomato/release/src/router/pppd/pppd/plugins/radius/COPYRIGHT	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/COPYRIGHT	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,90 @@
+See the respective source files to find out which copyrights apply.
+
+------------------------------------------------------------------------------
+Copyright (C) 2002 Roaring Penguin Software Inc.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose and without fee is hereby granted, provided that this
+copyright and permission notice appear on all copies and supporting
+documentation, the name of Roaring Penguin Software Inc. not be used
+in advertising or publicity pertaining to distribution of the program
+without specific prior permission, and notice be given in supporting
+documentation that copying and distribution is by permission of
+Roaring Penguin Software Inc..
+
+Roaring Penguin Software Inc. makes no representations about the
+suitability of this software for any purpose.  It is provided "as is"
+without express or implied warranty.
+
+------------------------------------------------------------------------------
+Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose and without fee is hereby granted, provided that this copyright and
+permission notice appear on all copies and supporting documentation, the
+name of Lars Fenneberg not be used in advertising or publicity pertaining to
+distribution of the program without specific prior permission, and notice be
+given in supporting documentation that copying and distribution is by
+permission of Lars Fenneberg.
+
+Lars Fenneberg makes no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or implied
+warranty.
+
+------------------------------------------------------------------------------
+Copyright 1992 Livingston Enterprises, Inc.
+Livingston Enterprises, Inc. 6920 Koll Center Parkway Pleasanton, CA  94566
+
+Permission to use, copy, modify, and distribute this software for any
+purpose and without fee is hereby granted, provided that this copyright
+and permission notice appear on all copies and supporting documentation,
+the name of Livingston Enterprises, Inc. not be used in advertising or
+publicity pertaining to distribution of the program without specific
+prior permission, and notice be given in supporting documentation that
+copying and distribution is by permission of Livingston Enterprises, Inc.
+
+Livingston Enterprises, Inc. makes no representations about the suitability
+of this software for any purpose.  It is provided "as is" without express
+or implied warranty.
+------------------------------------------------------------------------------
+[C] The Regents of the University of Michigan and Merit Network, Inc. 1992,
+1993, 1994, 1995 All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice and this permission notice appear in all
+copies of the software and derivative works or modified versions thereof,
+and that both the copyright notice and this permission and disclaimer
+notice appear in supporting documentation.
+
+THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE REGENTS OF THE
+UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE
+FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
+THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE.  The Regents of the
+University of Michigan and Merit Network, Inc. shall not be liable for any
+special, indirect, incidental or consequential damages with respect to any
+claim by Licensee or any third party arising from use of the software.
+------------------------------------------------------------------------------
+Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+All rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+------------------------------------------------------------------------------
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/dict.c tomato121/release/src/router/pppd/pppd/plugins/radius/dict.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/dict.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/dict.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,450 @@
+/*
+ * $Id: dict.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 2002 Roaring Penguin Software Inc.
+ *
+ * Copyright (C) 1995,1996,1997 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+
+static DICT_ATTR *dictionary_attributes = NULL;
+static DICT_VALUE *dictionary_values = NULL;
+static VENDOR_DICT *vendor_dictionaries = NULL;
+
+/*
+ * Function: rc_read_dictionary
+ *
+ * Purpose: Initialize the dictionary.  Read all ATTRIBUTES into
+ *	    the dictionary_attributes list.  Read all VALUES into
+ *	    the dictionary_values list.  Construct VENDOR dictionaries
+ *          as required.
+ *
+ */
+
+int rc_read_dictionary (char *filename)
+{
+	FILE           *dictfd;
+	char            dummystr[AUTH_ID_LEN];
+	char            namestr[AUTH_ID_LEN];
+	char            valstr[AUTH_ID_LEN];
+	char            attrstr[AUTH_ID_LEN];
+	char            typestr[AUTH_ID_LEN];
+	char            vendorstr[AUTH_ID_LEN];
+	int             line_no;
+	DICT_ATTR      *attr;
+	DICT_VALUE     *dval;
+	VENDOR_DICT    *vdict;
+	char            buffer[256];
+	int             value;
+	int             type;
+	int             n;
+	int             retcode;
+	if ((dictfd = fopen (filename, "r")) == (FILE *) NULL)
+	{
+		error( "rc_read_dictionary: couldn't open dictionary %s: %s",
+				filename, strerror(errno));
+		return (-1);
+	}
+
+	line_no = 0;
+	retcode = 0;
+	while (fgets (buffer, sizeof (buffer), dictfd) != (char *) NULL)
+	{
+		line_no++;
+
+		/* Skip empty space */
+		if (*buffer == '#' || *buffer == '\0' || *buffer == '\n')
+		{
+			continue;
+		}
+
+		if (strncmp (buffer, "VENDOR", 6) == 0) {
+		    /* Read the VENDOR line */
+		    if (sscanf(buffer, "%s%s%d", dummystr, namestr, &value) != 3) {
+			error("rc_read_dictionary: invalid vendor on line %d of dictionary %s",
+			      line_no, filename);
+			retcode = -1;
+			break;
+		    }
+		    /* Validate entry */
+		    if (strlen (namestr) > NAME_LENGTH) {
+			error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
+			      line_no, filename);
+			retcode = -1;
+			break;
+		    }
+		    /* Create new vendor entry */
+		    vdict = (VENDOR_DICT *) malloc (sizeof (VENDOR_DICT));
+		    if (!vdict) {
+			novm("rc_read_dictionary");
+			retcode = -1;
+			break;
+		    }
+		    strcpy(vdict->vendorname, namestr);
+		    vdict->vendorcode = value;
+		    vdict->attributes = NULL;
+		    vdict->next = vendor_dictionaries;
+		    vendor_dictionaries = vdict;
+		}
+		else if (strncmp (buffer, "ATTRIBUTE", 9) == 0)
+		{
+
+			/* Read the ATTRIBUTE line.  It is one of:
+			 * ATTRIBUTE attr_name attr_val type         OR
+			 * ATTRIBUTE attr_name attr_val type vendor  */
+			vendorstr[0] = 0;
+			n = sscanf(buffer, "%s%s%s%s%s", dummystr, namestr, valstr, typestr, vendorstr);
+			if (n != 4 && n != 5)
+			{
+				error("rc_read_dictionary: invalid attribute on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			/*
+			 * Validate all entries
+			 */
+			if (strlen (namestr) > NAME_LENGTH)
+			{
+				error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			if (strlen (vendorstr) > NAME_LENGTH)
+			{
+				error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			if (!isdigit (*valstr))
+			{
+				error("rc_read_dictionary: invalid value on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+			value = atoi (valstr);
+
+			if (strcmp (typestr, "string") == 0)
+			{
+				type = PW_TYPE_STRING;
+			}
+			else if (strcmp (typestr, "integer") == 0)
+			{
+				type = PW_TYPE_INTEGER;
+			}
+			else if (strcmp (typestr, "ipaddr") == 0)
+			{
+				type = PW_TYPE_IPADDR;
+			}
+			else if (strcmp (typestr, "date") == 0)
+			{
+				type = PW_TYPE_DATE;
+			}
+			else
+			{
+				error("rc_read_dictionary: invalid type on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			/* Search for vendor if supplied */
+			if (*vendorstr) {
+			    vdict = rc_dict_findvendor(vendorstr);
+			    if (!vdict) {
+				    error("rc_read_dictionary: unknown vendor on line %d of dictionary %s",
+					  line_no, filename);
+				    retcode = -1;
+				    break;
+			    }
+			} else {
+			    vdict = NULL;
+			}
+			/* Create a new attribute for the list */
+			if ((attr =
+				(DICT_ATTR *) malloc (sizeof (DICT_ATTR)))
+							== (DICT_ATTR *) NULL)
+			{
+				novm("rc_read_dictionary");
+				retcode = -1;
+				break;
+			}
+			strcpy (attr->name, namestr);
+			if (vdict) {
+			    attr->vendorcode = vdict->vendorcode;
+			} else {
+			    attr->vendorcode = VENDOR_NONE;
+			}
+			attr->value = value;
+			attr->type = type;
+
+			/* Insert it into the list */
+			if (vdict) {
+			    attr->next = vdict->attributes;
+			    vdict->attributes = attr;
+			} else {
+			    attr->next = dictionary_attributes;
+			    dictionary_attributes = attr;
+			}
+		}
+		else if (strncmp (buffer, "VALUE", 5) == 0)
+		{
+			/* Read the VALUE line */
+			if (sscanf (buffer, "%s%s%s%s", dummystr, attrstr,
+				    namestr, valstr) != 4)
+			{
+				error("rc_read_dictionary: invalid value entry on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			/*
+			 * Validate all entries
+			 */
+			if (strlen (attrstr) > NAME_LENGTH)
+			{
+				error("rc_read_dictionary: invalid attribute length on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			if (strlen (namestr) > NAME_LENGTH)
+			{
+				error("rc_read_dictionary: invalid name length on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+
+			if (!isdigit (*valstr))
+			{
+				error("rc_read_dictionary: invalid value on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+			value = atoi (valstr);
+
+			/* Create a new VALUE entry for the list */
+			if ((dval =
+				(DICT_VALUE *) malloc (sizeof (DICT_VALUE)))
+							== (DICT_VALUE *) NULL)
+			{
+				novm("rc_read_dictionary");
+				retcode = -1;
+				break;
+			}
+			strcpy (dval->attrname, attrstr);
+			strcpy (dval->name, namestr);
+			dval->value = value;
+
+			/* Insert it into the list */
+			dval->next = dictionary_values;
+			dictionary_values = dval;
+		}
+		else if (strncmp (buffer, "INCLUDE", 7) == 0)
+		{
+			/* Read the INCLUDE line */
+			if (sscanf (buffer, "%s%s", dummystr, namestr) != 2)
+			{
+				error("rc_read_dictionary: invalid include entry on line %d of dictionary %s",
+				      line_no, filename);
+				retcode = -1;
+				break;
+			}
+			if (rc_read_dictionary(namestr) == -1)
+			{
+				retcode = -1;
+				break;
+			}
+		}
+	}
+	fclose (dictfd);
+	return retcode;
+}
+
+/*
+ * Function: rc_dict_getattr
+ *
+ * Purpose: Return the full attribute structure based on the
+ *	    attribute id number and vendor code.  If vendor code is VENDOR_NONE,
+ *          non-vendor-specific attributes are used
+ *
+ */
+
+DICT_ATTR *rc_dict_getattr (int attribute, int vendor)
+{
+	DICT_ATTR      *attr;
+	VENDOR_DICT    *dict;
+
+	if (vendor == VENDOR_NONE) {
+	    attr = dictionary_attributes;
+	    while (attr != (DICT_ATTR *) NULL) {
+		if (attr->value == attribute) {
+		    return (attr);
+		}
+		attr = attr->next;
+	    }
+	} else {
+	    dict = rc_dict_getvendor(vendor);
+	    if (!dict) {
+		return NULL;
+	    }
+	    attr = dict->attributes;
+	    while (attr) {
+		if (attr->value == attribute) {
+		    return attr;
+		}
+		attr = attr->next;
+	    }
+	}
+	return NULL;
+}
+
+/*
+ * Function: rc_dict_findattr
+ *
+ * Purpose: Return the full attribute structure based on the
+ *	    attribute name.
+ *
+ */
+
+DICT_ATTR *rc_dict_findattr (char *attrname)
+{
+	DICT_ATTR      *attr;
+	VENDOR_DICT    *dict;
+
+	attr = dictionary_attributes;
+	while (attr != (DICT_ATTR *) NULL)
+	{
+		if (strcasecmp (attr->name, attrname) == 0)
+		{
+			return (attr);
+		}
+		attr = attr->next;
+	}
+
+	/* Search vendor-specific dictionaries */
+	dict = vendor_dictionaries;
+	while (dict) {
+	    attr = dict->attributes;
+	    while (attr) {
+		if (strcasecmp (attr->name, attrname) == 0) {
+		    return (attr);
+		}
+		attr = attr->next;
+	    }
+	    dict = dict->next;
+	}
+	return ((DICT_ATTR *) NULL);
+}
+
+
+/*
+ * Function: rc_dict_findval
+ *
+ * Purpose: Return the full value structure based on the
+ *         value name.
+ *
+ */
+
+DICT_VALUE *rc_dict_findval (char *valname)
+{
+	DICT_VALUE     *val;
+
+	val = dictionary_values;
+	while (val != (DICT_VALUE *) NULL)
+	{
+		if (strcasecmp (val->name, valname) == 0)
+		{
+			return (val);
+		}
+		val = val->next;
+	}
+	return ((DICT_VALUE *) NULL);
+}
+
+/*
+ * Function: dict_getval
+ *
+ * Purpose: Return the full value structure based on the
+ *          actual value and the associated attribute name.
+ *
+ */
+
+DICT_VALUE * rc_dict_getval (UINT4 value, char *attrname)
+{
+	DICT_VALUE     *val;
+
+	val = dictionary_values;
+	while (val != (DICT_VALUE *) NULL)
+	{
+		if (strcmp (val->attrname, attrname) == 0 &&
+				val->value == value)
+		{
+			return (val);
+		}
+		val = val->next;
+	}
+	return ((DICT_VALUE *) NULL);
+}
+
+/*
+ * Function: rc_dict_findvendor
+ *
+ * Purpose: Return the vendor's dictionary given the vendor name.
+ *
+ */
+VENDOR_DICT * rc_dict_findvendor (char *vendorname)
+{
+    VENDOR_DICT *dict;
+
+    dict = vendor_dictionaries;
+    while (dict) {
+	if (!strcmp(vendorname, dict->vendorname)) {
+	    return dict;
+	}
+	dict = dict->next;
+    }
+    return NULL;
+}
+
+/*
+ * Function: rc_dict_getvendor
+ *
+ * Purpose: Return the vendor's dictionary given the vendor ID
+ *
+ */
+VENDOR_DICT * rc_dict_getvendor (int id)
+{
+    VENDOR_DICT *dict;
+
+    dict = vendor_dictionaries;
+    while (dict) {
+	if (id == dict->vendorcode) {
+	    return dict;
+	}
+	dict = dict->next;
+    }
+    return NULL;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,253 @@
+#
+# Updated 97/06/13 to livingston-radius-2.01 miquels@cistron.nl
+#
+#	This file contains dictionary translations for parsing
+#	requests and generating responses.  All transactions are
+#	composed of Attribute/Value Pairs.  The value of each attribute
+#	is specified as one of 4 data types.  Valid data types are:
+#
+#	string - 0-253 octets
+#	ipaddr - 4 octets in network byte order
+#	integer - 32 bit value in big endian order (high byte first)
+#	date - 32 bit value in big endian order - seconds since
+#					00:00:00 GMT,  Jan.  1,  1970
+#
+#	Enumerated values are stored in the user file with dictionary
+#	VALUE translations for easy administration.
+#
+#	Example:
+#
+#	ATTRIBUTE	  VALUE
+#	---------------   -----
+#	Framed-Protocol = PPP
+#	7		= 1	(integer encoding)
+#
+
+# The dictionary format now supports vendor-specific attributes.
+# Vendors are introduced like this:
+#
+#	VENDOR vendor_name vendor_number
+#
+# For example:
+#
+#	VENDOR RoaringPenguin 10055
+#
+# Vendor-specific attributes have a fifth field with the name of the
+# vendor.  For example:
+#
+#       ATTRIBUTE RP-Upstream-Speed-Limit 1 integer RoaringPenguin
+#
+# introduces a Roaring Penguin vendor-specific attribbute with name
+# RP-Upstream-Speed-Limit, number 1, type integer and vendor RoaringPenguin.
+
+#
+#	Following are the proper new names. Use these.
+#
+ATTRIBUTE	User-Name		1	string
+ATTRIBUTE	Password		2	string
+ATTRIBUTE	CHAP-Password		3	string
+ATTRIBUTE	NAS-IP-Address		4	ipaddr
+ATTRIBUTE	NAS-Port-Id		5	integer
+ATTRIBUTE	Service-Type		6	integer
+ATTRIBUTE	Framed-Protocol		7	integer
+ATTRIBUTE	Framed-IP-Address	8	ipaddr
+ATTRIBUTE	Framed-IP-Netmask	9	ipaddr
+ATTRIBUTE	Framed-Routing		10	integer
+ATTRIBUTE	Filter-Id		11	string
+ATTRIBUTE	Framed-MTU		12	integer
+ATTRIBUTE	Framed-Compression	13	integer
+ATTRIBUTE	Login-IP-Host		14	ipaddr
+ATTRIBUTE	Login-Service		15	integer
+ATTRIBUTE	Login-TCP-Port		16	integer
+ATTRIBUTE	Reply-Message		18	string
+ATTRIBUTE	Callback-Number		19	string
+ATTRIBUTE	Callback-Id		20	string
+ATTRIBUTE	Framed-Route		22	string
+ATTRIBUTE	Framed-IPX-Network	23	ipaddr
+ATTRIBUTE	State			24	string
+ATTRIBUTE	Class			25	string
+ATTRIBUTE	Session-Timeout		27	integer
+ATTRIBUTE	Idle-Timeout		28	integer
+ATTRIBUTE	Termination-Action	29	integer
+ATTRIBUTE	Called-Station-Id	30	string
+ATTRIBUTE	Calling-Station-Id	31	string
+ATTRIBUTE	NAS-Identifier		32	string
+ATTRIBUTE	Acct-Status-Type	40	integer
+ATTRIBUTE	Acct-Delay-Time		41	integer
+ATTRIBUTE	Acct-Input-Octets	42	integer
+ATTRIBUTE	Acct-Output-Octets	43	integer
+ATTRIBUTE	Acct-Session-Id		44	string
+ATTRIBUTE	Acct-Authentic		45	integer
+ATTRIBUTE	Acct-Session-Time	46	integer
+ATTRIBUTE	Acct-Input-Packets	47	integer
+ATTRIBUTE	Acct-Output-Packets	48	integer
+ATTRIBUTE	Acct-Terminate-Cause	49	integer
+ATTRIBUTE       Chap-Challenge          60      string
+ATTRIBUTE	NAS-Port-Type		61	integer
+ATTRIBUTE	Port-Limit		62	integer
+ATTRIBUTE	Connect-Info		77	string
+
+# RFC 2869
+ATTRIBUTE	Acct-Interim-Interval	85	integer
+
+#
+#	Experimental Non Protocol Attributes used by Cistron-Radiusd
+#
+ATTRIBUTE	Huntgroup-Name		221	string
+ATTRIBUTE	User-Category		1029	string
+ATTRIBUTE	Group-Name		1030	string
+ATTRIBUTE	Simultaneous-Use	1034	integer
+ATTRIBUTE	Strip-User-Name		1035	integer
+ATTRIBUTE	Fall-Through		1036	integer
+ATTRIBUTE	Add-Port-To-IP-Address	1037	integer
+ATTRIBUTE	Exec-Program		1038	string
+ATTRIBUTE	Exec-Program-Wait	1039	string
+ATTRIBUTE	Hint			1040	string
+
+#
+#	Non-Protocol Attributes
+#	These attributes are used internally by the server
+#
+ATTRIBUTE	Expiration		  21	date
+ATTRIBUTE	Auth-Type		1000	integer
+ATTRIBUTE	Menu			1001	string
+ATTRIBUTE	Termination-Menu	1002	string
+ATTRIBUTE	Prefix			1003	string
+ATTRIBUTE	Suffix			1004	string
+ATTRIBUTE	Group			1005	string
+ATTRIBUTE	Crypt-Password		1006	string
+ATTRIBUTE	Connect-Rate		1007	integer
+
+#
+#       Experimental, implementation specific attributes
+#
+# Limit session traffic
+ATTRIBUTE	Session-Octets-Limit	227	integer
+# What to assume as limit - 0 in+out, 1 in, 2 out, 3 max(in,out)
+ATTRIBUTE	Octets-Direction	228	integer
+
+#
+#	Integer Translations
+#
+
+#	User Types
+
+VALUE		Service-Type		Login-User		1
+VALUE		Service-Type		Framed-User		2
+VALUE		Service-Type		Callback-Login-User	3
+VALUE		Service-Type		Callback-Framed-User	4
+VALUE		Service-Type		Outbound-User		5
+VALUE		Service-Type		Administrative-User	6
+VALUE		Service-Type		NAS-Prompt-User		7
+
+#	Framed Protocols
+
+VALUE		Framed-Protocol		PPP			1
+VALUE		Framed-Protocol		SLIP			2
+
+#	Framed Routing Values
+
+VALUE		Framed-Routing		None			0
+VALUE		Framed-Routing		Broadcast		1
+VALUE		Framed-Routing		Listen			2
+VALUE		Framed-Routing		Broadcast-Listen	3
+
+#	Framed Compression Types
+
+VALUE		Framed-Compression	None			0
+VALUE		Framed-Compression	Van-Jacobson-TCP-IP	1
+
+#	Login Services
+
+VALUE		Login-Service		Telnet			0
+VALUE		Login-Service		Rlogin			1
+VALUE		Login-Service		TCP-Clear		2
+VALUE		Login-Service		PortMaster		3
+
+#	Status Types
+
+VALUE		Acct-Status-Type	Start			1
+VALUE		Acct-Status-Type	Stop			2
+VALUE		Acct-Status-Type	Accounting-On		7
+VALUE		Acct-Status-Type	Accounting-Off		8
+
+#	Authentication Types
+
+VALUE		Acct-Authentic		RADIUS			1
+VALUE		Acct-Authentic		Local			2
+VALUE		Acct-Authentic		PowerLink128		100
+
+#	Termination Options
+
+VALUE		Termination-Action	Default			0
+VALUE		Termination-Action	RADIUS-Request		1
+
+#	NAS Port Types, available in 3.3.1 and later
+
+VALUE		NAS-Port-Type		Async			0
+VALUE		NAS-Port-Type		Sync			1
+VALUE		NAS-Port-Type		ISDN			2
+VALUE		NAS-Port-Type		ISDN-V120		3
+VALUE		NAS-Port-Type		ISDN-V110		4
+
+#	Acct Terminate Causes, available in 3.3.2 and later
+
+VALUE           Acct-Terminate-Cause    User-Request            1
+VALUE           Acct-Terminate-Cause    Lost-Carrier            2
+VALUE           Acct-Terminate-Cause    Lost-Service            3
+VALUE           Acct-Terminate-Cause    Idle-Timeout            4
+VALUE           Acct-Terminate-Cause    Session-Timeout         5
+VALUE           Acct-Terminate-Cause    Admin-Reset             6
+VALUE           Acct-Terminate-Cause    Admin-Reboot            7
+VALUE           Acct-Terminate-Cause    Port-Error              8
+VALUE           Acct-Terminate-Cause    NAS-Error               9
+VALUE           Acct-Terminate-Cause    NAS-Request             10
+VALUE           Acct-Terminate-Cause    NAS-Reboot              11
+VALUE           Acct-Terminate-Cause    Port-Unneeded           12
+VALUE           Acct-Terminate-Cause    Port-Preempted          13
+VALUE           Acct-Terminate-Cause    Port-Suspended          14
+VALUE           Acct-Terminate-Cause    Service-Unavailable     15
+VALUE           Acct-Terminate-Cause    Callback                16
+VALUE           Acct-Terminate-Cause    User-Error              17
+VALUE           Acct-Terminate-Cause    Host-Request            18
+
+#
+#	Non-Protocol Integer Translations
+#
+
+VALUE		Auth-Type		Local			0
+VALUE		Auth-Type		System			1
+VALUE		Auth-Type		SecurID			2
+VALUE		Auth-Type		Crypt-Local		3
+VALUE		Auth-Type		Reject			4
+
+#
+#	Cistron extensions
+#
+VALUE		Auth-Type		Pam			253
+VALUE		Auth-Type		None			254
+
+#
+#	Experimental Non-Protocol Integer Translations for Cistron-Radiusd
+#
+VALUE		Fall-Through		No			0
+VALUE		Fall-Through		Yes			1
+VALUE		Add-Port-To-IP-Address	No			0
+VALUE		Add-Port-To-IP-Address	Yes			1
+
+#
+#	Configuration Values
+#	uncomment these two lines to turn account expiration on
+#
+
+#VALUE		Server-Config		Password-Expiration	30
+#VALUE		Server-Config		Password-Warning	5
+
+#       Octets-Direction
+VALUE		Octets-Direction        Sum			0
+VALUE		Octets-Direction        Input			1
+VALUE		Octets-Direction        Output			2
+VALUE		Octets-Direction        MaxOveral		3
+VALUE		Octets-Direction        MaxSession		4
+
+INCLUDE /etc/dictionary.microsoft
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.ascend tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.ascend
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.ascend	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.ascend	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,295 @@
+#
+# Ascend dictionary.
+#
+#
+# Version:	1.00  21-Jul-1997  Jens Glaser <jens@regio.net>
+#
+
+
+#
+#	Ascend specific extensions
+#	Used by ASCEND MAX/Pipeline products
+#
+ATTRIBUTE	Ascend-FCP-Parameter		119	string
+ATTRIBUTE	Ascend-Modem-PortNo		120	integer
+ATTRIBUTE	Ascend-Modem-SlotNo		121	integer
+ATTRIBUTE	Ascend-Modem-ShelfNo		122	integer
+ATTRIBUTE	Ascend-Call-Attempt-Limit	123	integer
+ATTRIBUTE	Ascend-Call-Block-Duration	124	integer
+ATTRIBUTE	Ascend-Maximum-Call-Duration	125	integer
+ATTRIBUTE	Ascend-Temporary-Rtes		126	integer
+ATTRIBUTE       Tunneling-Protocol              127     integer
+ATTRIBUTE       Ascend-Shared-Profile-Enable    128     integer
+ATTRIBUTE	Ascend-Primary-Home-Agent	129	string
+ATTRIBUTE	Ascend-Secondary-Home-Agent	130	string
+ATTRIBUTE	Ascend-Dialout-Allowed		131	integer
+ATTRIBUTE	Ascend-Client-Gateway		132	ipaddr
+ATTRIBUTE	Ascend-BACP-Enable		133	integer
+ATTRIBUTE	Ascend-DHCP-Maximum-Leases	134	integer
+ATTRIBUTE	Ascend-Client-Primary-DNS	135	ipaddr
+ATTRIBUTE	Ascend-Client-Secondary-DNS	136	ipaddr
+ATTRIBUTE	Ascend-Client-Assign-DNS	137	integer
+ATTRIBUTE	Ascend-User-Acct-Type		138	integer
+ATTRIBUTE	Ascend-User-Acct-Host		139	ipaddr
+ATTRIBUTE	Ascend-User-Acct-Port		140	integer
+ATTRIBUTE	Ascend-User-Acct-Key		141	string
+ATTRIBUTE	Ascend-User-Acct-Base		142	integer
+ATTRIBUTE	Ascend-User-Acct-Time		143	integer
+ATTRIBUTE	Ascend-Assign-IP-Client		144	ipaddr
+ATTRIBUTE	Ascend-Assign-IP-Server		145	ipaddr
+ATTRIBUTE	Ascend-Assign-IP-Global-Pool	146	string
+ATTRIBUTE	Ascend-DHCP-Reply		147	integer
+ATTRIBUTE	Ascend-DHCP-Pool-Number		148	integer
+ATTRIBUTE	Ascend-Expect-Callback		149	integer
+ATTRIBUTE	Ascend-Event-Type		150	integer
+ATTRIBUTE	Ascend-Session-Svr-Key		151	string
+ATTRIBUTE	Ascend-Multicast-Rate-Limit	152	integer
+ATTRIBUTE	Ascend-IF-Netmask		153	ipaddr
+ATTRIBUTE	Ascend-Remote-Addr		154	ipaddr
+ATTRIBUTE	Ascend-Multicast-Client		155	integer
+ATTRIBUTE	Ascend-FR-Circuit-Name		156	string
+ATTRIBUTE	Ascend-FR-LinkUp		157	integer
+ATTRIBUTE	Ascend-FR-Nailed-Grp		158	integer
+ATTRIBUTE	Ascend-FR-Type			159	integer
+ATTRIBUTE	Ascend-FR-Link-Mgt		160	integer
+ATTRIBUTE	Ascend-FR-N391			161	integer
+ATTRIBUTE	Ascend-FR-DCE-N392		162	integer
+ATTRIBUTE	Ascend-FR-DTE-N392		163	integer
+ATTRIBUTE	Ascend-FR-DCE-N393		164	integer
+ATTRIBUTE	Ascend-FR-DTE-N393		165	integer
+ATTRIBUTE	Ascend-FR-T391			166	integer
+ATTRIBUTE	Ascend-FR-T392			167	integer
+ATTRIBUTE	Ascend-Bridge-Address  	 	168	string
+ATTRIBUTE       Ascend-TS-Idle-Limit            169     integer
+ATTRIBUTE       Ascend-TS-Idle-Mode             170     integer
+ATTRIBUTE	Ascend-DBA-Monitor	 	171	integer
+ATTRIBUTE	Ascend-Base-Channel-Count 	172	integer
+ATTRIBUTE	Ascend-Minimum-Channels		173	integer
+ATTRIBUTE	Ascend-IPX-Route		174	string
+ATTRIBUTE	Ascend-FT1-Caller		175	integer
+ATTRIBUTE	Ascend-Backup			176	string
+ATTRIBUTE	Ascend-Call-Type		177	integer
+ATTRIBUTE	Ascend-Group			178	string
+ATTRIBUTE	Ascend-FR-DLCI			179	integer
+ATTRIBUTE	Ascend-FR-Profile-Name		180	string
+ATTRIBUTE	Ascend-Ara-PW			181	string
+ATTRIBUTE	Ascend-IPX-Node-Addr		182	string
+ATTRIBUTE	Ascend-Home-Agent-IP-Addr	183	ipaddr
+ATTRIBUTE	Ascend-Home-Agent-Password	184	string
+ATTRIBUTE	Ascend-Home-Network-Name	185	string
+ATTRIBUTE	Ascend-Home-Agent-UDP-Port	186	integer
+ATTRIBUTE	Ascend-Multilink-ID		187	integer
+ATTRIBUTE	Ascend-Num-In-Multilink		188	integer
+ATTRIBUTE	Ascend-First-Dest		189	ipaddr
+ATTRIBUTE	Ascend-Pre-Input-Octets		190	integer
+ATTRIBUTE	Ascend-Pre-Output-Octets	191	integer
+ATTRIBUTE	Ascend-Pre-Input-Packets	192	integer
+ATTRIBUTE	Ascend-Pre-Output-Packets	193	integer
+ATTRIBUTE	Ascend-Maximum-Time		194	integer
+ATTRIBUTE	Ascend-Disconnect-Cause		195	integer
+ATTRIBUTE	Ascend-Connect-Progress		196	integer
+ATTRIBUTE	Ascend-Data-Rate		197	integer
+ATTRIBUTE	Ascend-PreSession-Time		198	integer
+ATTRIBUTE	Ascend-Token-Idle		199	integer
+ATTRIBUTE	Ascend-Token-Immediate		200	integer
+ATTRIBUTE	Ascend-Require-Auth		201	integer
+ATTRIBUTE	Ascend-Number-Sessions		202	string
+ATTRIBUTE	Ascend-Authen-Alias		203	string
+ATTRIBUTE	Ascend-Token-Expiry		204	integer
+ATTRIBUTE	Ascend-Menu-Selector		205	string
+ATTRIBUTE	Ascend-Menu-Item		206	string
+ATTRIBUTE	Ascend-PW-Warntime		207	integer
+ATTRIBUTE	Ascend-PW-Lifetime		208	integer
+ATTRIBUTE	Ascend-IP-Direct		209	ipaddr
+ATTRIBUTE	Ascend-PPP-VJ-Slot-Comp		210	integer
+ATTRIBUTE	Ascend-PPP-VJ-1172		211	integer
+ATTRIBUTE	Ascend-PPP-Async-Map		212	integer
+ATTRIBUTE	Ascend-Third-Prompt		213	string
+ATTRIBUTE	Ascend-Send-Secret		214	string
+ATTRIBUTE	Ascend-Receive-Secret		215	string
+ATTRIBUTE	Ascend-IPX-Peer-Mode		216	integer
+ATTRIBUTE	Ascend-IP-Pool-Definition	217	string
+ATTRIBUTE	Ascend-Assign-IP-Pool		218	integer
+ATTRIBUTE	Ascend-FR-Direct		219	integer
+ATTRIBUTE	Ascend-FR-Direct-Profile	220	string
+ATTRIBUTE	Ascend-FR-Direct-DLCI		221	integer
+ATTRIBUTE	Ascend-Handle-IPX		222	integer
+ATTRIBUTE	Ascend-Netware-timeout		223	integer
+ATTRIBUTE	Ascend-IPX-Alias		224	integer
+ATTRIBUTE	Ascend-Metric			225	integer
+ATTRIBUTE	Ascend-PRI-Number-Type		226	integer
+ATTRIBUTE	Ascend-Dial-Number		227	string
+ATTRIBUTE	Ascend-Route-IP			228	integer
+ATTRIBUTE	Ascend-Route-IPX		229	integer
+ATTRIBUTE	Ascend-Bridge			230	integer
+ATTRIBUTE	Ascend-Send-Auth		231	integer
+ATTRIBUTE	Ascend-Send-Passwd		232	string
+ATTRIBUTE	Ascend-Link-Compression		233	integer
+ATTRIBUTE	Ascend-Target-Util		234	integer
+ATTRIBUTE	Ascend-Maximum-Channels		235	integer
+ATTRIBUTE	Ascend-Inc-Channel-Count	236	integer
+ATTRIBUTE	Ascend-Dec-Channel-Count	237	integer
+ATTRIBUTE	Ascend-Seconds-Of-History	238	integer
+ATTRIBUTE	Ascend-History-Weigh-Type	239	integer
+ATTRIBUTE	Ascend-Add-Seconds		240	integer
+ATTRIBUTE	Ascend-Remove-Seconds		241	integer
+ATTRIBUTE	Ascend-Idle-Limit		244	integer
+ATTRIBUTE	Ascend-Preempt-Limit		245	integer
+ATTRIBUTE	Ascend-Callback			246	integer
+ATTRIBUTE	Ascend-Data-Svc			247	integer
+ATTRIBUTE	Ascend-Force-56			248	integer
+ATTRIBUTE	Ascend-Billing-Number		249	string
+ATTRIBUTE	Ascend-Call-By-Call		250	integer
+ATTRIBUTE	Ascend-Transit-Number		251	string
+ATTRIBUTE	Ascend-Host-Info		252	string
+ATTRIBUTE	Ascend-PPP-Address		253	ipaddr
+ATTRIBUTE	Ascend-MPP-Idle-Percent		254	integer
+ATTRIBUTE	Ascend-Xmit-Rate		255	integer
+
+
+
+# Ascend protocols
+VALUE		Service-Type		Dialout-Framed-User	5
+VALUE		Framed-Protocol		ARA			255
+VALUE		Framed-Protocol		MPP			256
+VALUE		Framed-Protocol		EURAW			257
+VALUE		Framed-Protocol		EUUI			258
+VALUE		Framed-Protocol		X25			259
+VALUE		Framed-Protocol		COMB			260
+VALUE		Framed-Protocol		FR			261
+VALUE		Framed-Protocol		MP			262
+VALUE		Framed-Protocol		FR-CIR			263
+
+
+#
+#	Ascend specific extensions
+#	Used by ASCEND MAX/Pipeline products (see above)
+#
+
+VALUE		Ascend-FR-Direct	FR-Direct-No		0
+VALUE		Ascend-FR-Direct	FR-Direct-Yes		1
+VALUE		Ascend-Handle-IPX	Handle-IPX-None		0
+VALUE		Ascend-Handle-IPX	Handle-IPX-Client	1
+VALUE		Ascend-Handle-IPX	Handle-IPX-Server	2
+VALUE		Ascend-IPX-Peer-Mode	IPX-Peer-Router		0
+VALUE		Ascend-IPX-Peer-Mode	IPX-Peer-Dialin		1
+VALUE		Ascend-Call-Type	Nailed			1
+VALUE		Ascend-Call-Type	Nailed/Mpp		2
+VALUE		Ascend-Call-Type	Perm/Switched		3
+VALUE		Ascend-FT1-Caller	FT1-No			0
+VALUE		Ascend-FT1-Caller	FT1-Yes			1
+VALUE		Ascend-PRI-Number-Type	Unknown-Number		0
+VALUE		Ascend-PRI-Number-Type	Intl-Number		1
+VALUE		Ascend-PRI-Number-Type	National-Number		2
+VALUE		Ascend-PRI-Number-Type	Local-Number		4
+VALUE		Ascend-PRI-Number-Type	Abbrev-Number		5
+VALUE		Ascend-Route-IPX	Route-IPX-No		0
+VALUE		Ascend-Route-IPX	Route-IPX-Yes		1
+VALUE		Ascend-Bridge		Bridge-No		0
+VALUE		Ascend-Bridge		Bridge-Yes		1
+VALUE  		Ascend-TS-Idle-Mode     TS-Idle-None		0
+VALUE	  	Ascend-TS-Idle-Mode     TS-Idle-Input		1
+VALUE  		Ascend-TS-Idle-Mode     TS-Idle-Input-Output	2
+VALUE		Ascend-Send-Auth	Send-Auth-None		0
+VALUE		Ascend-Send-Auth	Send-Auth-PAP		1
+VALUE		Ascend-Send-Auth	Send-Auth-CHAP		2
+VALUE		Ascend-Send-Auth	Send-Auth-MS-CHAP	3
+VALUE		Ascend-Link-Compression	Link-Comp-None		0
+VALUE		Ascend-Link-Compression	Link-Comp-Stac		1
+VALUE		Ascend-Link-Compression	Link-Comp-Stac-Draft-9	2
+VALUE		Ascend-Link-Compression	Link-Comp-MS-Stac	3
+VALUE		Ascend-History-Weigh-Type	History-Constant	0
+VALUE		Ascend-History-Weigh-Type	History-Linear		1
+VALUE		Ascend-History-Weigh-Type	History-Quadratic	2
+VALUE		Ascend-Callback		Callback-No		0
+VALUE		Ascend-Callback		Callback-Yes		1
+VALUE		Ascend-Expect-Callback	Expect-Callback-No	0
+VALUE		Ascend-Expect-Callback	Expect-Callback-Yes	1
+VALUE		Ascend-Data-Svc		Switched-Voice-Bearer	0
+VALUE		Ascend-Data-Svc		Switched-56KR		1
+VALUE		Ascend-Data-Svc		Switched-64K		2
+VALUE		Ascend-Data-Svc		Switched-64KR		3
+VALUE		Ascend-Data-Svc		Switched-56K		4
+VALUE		Ascend-Data-Svc		Switched-384KR		5
+VALUE		Ascend-Data-Svc		Switched-384K		6
+VALUE		Ascend-Data-Svc		Switched-1536K		7
+VALUE		Ascend-Data-Svc		Switched-1536KR		8
+VALUE		Ascend-Data-Svc		Switched-128K		9
+VALUE		Ascend-Data-Svc		Switched-192K		10
+VALUE		Ascend-Data-Svc		Switched-256K		11
+VALUE		Ascend-Data-Svc		Switched-320K		12
+VALUE		Ascend-Data-Svc		Switched-384K-MR	13
+VALUE		Ascend-Data-Svc		Switched-448K		14
+VALUE		Ascend-Data-Svc		Switched-512K		15
+VALUE		Ascend-Data-Svc		Switched-576K		16
+VALUE		Ascend-Data-Svc		Switched-640K		17
+VALUE		Ascend-Data-Svc		Switched-704K		18
+VALUE		Ascend-Data-Svc		Switched-768K		19
+VALUE		Ascend-Data-Svc		Switched-832K		20
+VALUE		Ascend-Data-Svc		Switched-896K		21
+VALUE		Ascend-Data-Svc		Switched-960K		22
+VALUE		Ascend-Data-Svc		Switched-1024K		23
+VALUE		Ascend-Data-Svc		Switched-1088K		24
+VALUE		Ascend-Data-Svc		Switched-1152K		25
+VALUE		Ascend-Data-Svc		Switched-1216K		26
+VALUE		Ascend-Data-Svc		Switched-1280K		27
+VALUE		Ascend-Data-Svc		Switched-1344K		28
+VALUE		Ascend-Data-Svc		Switched-1408K		29
+VALUE		Ascend-Data-Svc		Switched-1472K		30
+VALUE		Ascend-Data-Svc		Switched-1600K		31
+VALUE		Ascend-Data-Svc		Switched-1664K		32
+VALUE		Ascend-Data-Svc		Switched-1728K		33
+VALUE		Ascend-Data-Svc		Switched-1792K		34
+VALUE		Ascend-Data-Svc		Switched-1856K		35
+VALUE		Ascend-Data-Svc		Switched-1920K		36
+VALUE		Ascend-Data-Svc		Switched-inherited		37
+VALUE		Ascend-Data-Svc		Switched-restricted-bearer-x30  38
+VALUE		Ascend-Data-Svc		Switched-clear-bearer-v110	39
+VALUE		Ascend-Data-Svc		Switched-restricted-64-x30	40
+VALUE		Ascend-Data-Svc		Switched-clear-56-v110		41
+VALUE		Ascend-Data-Svc		Switched-modem			42
+VALUE		Ascend-Data-Svc		Switched-atmodem		43
+VALUE		Ascend-Data-Svc		Nailed-56KR		1
+VALUE		Ascend-Data-Svc		Nailed-64K		2
+VALUE		Ascend-Force-56		Force-56-No		0
+VALUE		Ascend-Force-56		Force-56-Yes		1
+VALUE		Ascend-PW-Lifetime	Lifetime-In-Days	0
+VALUE		Ascend-PW-Warntime	Days-Of-Warning		0
+VALUE		Ascend-PPP-VJ-1172	PPP-VJ-1172		1
+VALUE		Ascend-PPP-VJ-Slot-Comp	VJ-Slot-Comp-No		1
+VALUE		Ascend-Require-Auth	Not-Require-Auth	0
+VALUE		Ascend-Require-Auth	Require-Auth		1
+VALUE		Ascend-Token-Immediate	Tok-Imm-No		0
+VALUE		Ascend-Token-Immediate	Tok-Imm-Yes		1
+VALUE		Ascend-DBA-Monitor		DBA-Transmit		0
+VALUE 		Ascend-DBA-Monitor	DBA-Transmit-Recv	1
+VALUE		Ascend-DBA-Monitor	DBA-None		2
+VALUE		Ascend-FR-Type		Ascend-FR-DTE		0
+VALUE		Ascend-FR-Type		Ascend-FR-DCE		1
+VALUE		Ascend-FR-Type		Ascend-FR-NNI		2
+VALUE		Ascend-FR-Link-Mgt	Ascend-FR-No-Link-Mgt	0
+VALUE		Ascend-FR-Link-Mgt	Ascend-FR-T1-617D	1
+VALUE		Ascend-FR-Link-Mgt	Ascend-FR-Q-933A	2
+VALUE		Ascend-FR-LinkUp	Ascend-LinkUp-Default	0
+VALUE		Ascend-FR-LinkUp	Ascend-LinkUp-AlwaysUp	1
+VALUE		Ascend-Multicast-Client	Multicast-No		0
+VALUE		Ascend-Multicast-Client	Multicast-Yes		1
+VALUE		Ascend-User-Acct-Type	Ascend-User-Acct-None	0
+VALUE		Ascend-User-Acct-Type	Ascend-User-Acct-User	1
+VALUE		Ascend-User-Acct-Type	Ascend-User-Acct-User-Default	2
+VALUE		Ascend-User-Acct-Base	Base-10			0
+VALUE		Ascend-User-Acct-Base	Base-16			1
+VALUE		Ascend-DHCP-Reply	DHCP-Reply-No		0
+VALUE		Ascend-DHCP-Reply	DHCP-Reply-Yes		1
+VALUE		Ascend-Client-Assign-DNS	DNS-Assign-No		0
+VALUE		Ascend-Client-Assign-DNS	DNS-Assign-Yes		1
+VALUE		Ascend-Event-Type	Ascend-ColdStart	1
+VALUE		Ascend-Event-Type	Ascend-Session-Event	2
+VALUE		Ascend-BACP-Enable	BACP-No			0
+VALUE		Ascend-BACP-Enable	BACP-Yes		1
+VALUE		Ascend-Dialout-Allowed	Dialout-Not-Allowed	0
+VALUE		Ascend-Dialout-Allowed	Dialout-Allowed		1
+VALUE		Ascend-Shared-Profile-Enable    Shared-Profile-No       0
+VALUE		Ascend-Shared-Profile-Enable    Shared-Profile-Yes      1
+VALUE		Ascend-Temporary-Rtes	Temp-Rtes-No		0
+VALUE		Ascend-Temporary-Rtes	Temp-Rtes-Yes		1
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.compat tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.compat
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.compat	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.compat	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,45 @@
+#
+#	Obsolete names for backwards compatibility with older users files.
+#
+ATTRIBUTE	Client-Id		4	ipaddr
+ATTRIBUTE	Client-Port-Id		5	integer
+ATTRIBUTE	User-Service-Type	6	integer
+ATTRIBUTE	Framed-Address		8	ipaddr
+ATTRIBUTE	Framed-Netmask		9	ipaddr
+ATTRIBUTE	Framed-Filter-Id	11	string
+ATTRIBUTE	Login-Host		14	ipaddr
+ATTRIBUTE	Login-Port		16	integer
+ATTRIBUTE	Old-Password		17	string
+ATTRIBUTE	Port-Message		18	string
+ATTRIBUTE	Dialback-No		19	string
+ATTRIBUTE	Dialback-Name		20	string
+ATTRIBUTE	Challenge-State		24	string
+VALUE		Framed-Compression	Van-Jacobsen-TCP-IP	1
+VALUE		Framed-Compression	VJ-TCP-IP		1
+VALUE		Service-Type		Shell-User		6
+VALUE		Auth-Type		Unix			1
+VALUE		Service-Type		Dialback-Login-User	3
+VALUE		Service-Type		Dialback-Framed-User	4
+
+#
+#	For compatibility with MERIT users files.
+#
+ATTRIBUTE	NAS-Port		5	integer
+ATTRIBUTE	Login-Host		14	ipaddr
+ATTRIBUTE	Login-Callback-Number	19	string
+ATTRIBUTE	Framed-Callback-Id	20	string
+ATTRIBUTE	Client-Port-DNIS	30	string
+ATTRIBUTE	Caller-ID		31	string
+VALUE		Service-Type		Login			1
+VALUE		Service-Type		Framed			2
+VALUE		Service-Type		Callback-Login		3
+VALUE		Service-Type		Callback-Framed		4
+VALUE		Service-Type		Exec-User		7
+
+#
+#	For compatibility with ESVA RADIUS, Old Cistron RADIUS
+#
+ATTRIBUTE	Session			1034	integer
+ATTRIBUTE	User-Name-Is-Star	1035	integer
+VALUE		User-Name-Is-Star	No			0
+VALUE		User-Name-Is-Star	Yes			1
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.merit tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.merit
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.merit	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.merit	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,17 @@
+#
+#	Experimental extensions, configuration only (for check-items)
+#	Names/numbers as per the MERIT extensions (if possible).
+#
+ATTRIBUTE	NAS-Identifier		32	string
+ATTRIBUTE	Proxy-State		33	string
+ATTRIBUTE	Login-LAT-Service	34	string
+ATTRIBUTE	Login-LAT-Node		35	string
+ATTRIBUTE	Login-LAT-Group		36	string
+ATTRIBUTE	Framed-AppleTalk-Link	37	integer
+ATTRIBUTE	Framed-AppleTalk-Network 38	integer
+ATTRIBUTE	Framed-AppleTalk-Zone	39	string
+ATTRIBUTE       Acct-Input-Packets	47	integer
+ATTRIBUTE       Acct-Output-Packets	48	integer
+# 8 is a MERIT extension.
+VALUE		Service-Type		Authenticate-Only	8
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.microsoft tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.microsoft
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.microsoft	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/dictionary.microsoft	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,81 @@
+#
+#	Microsoft's VSA's, from RFC 2548
+#
+#	$Id: dictionary.microsoft,v 1.1 2004/11/14 07:26:26 paulus Exp $
+#
+
+VENDOR		Microsoft	311	Microsoft
+
+ATTRIBUTE	MS-CHAP-Response	1	string	Microsoft
+ATTRIBUTE	MS-CHAP-Error		2	string	Microsoft
+ATTRIBUTE	MS-CHAP-CPW-1		3	string	Microsoft
+ATTRIBUTE	MS-CHAP-CPW-2		4	string	Microsoft
+ATTRIBUTE	MS-CHAP-LM-Enc-PW	5	string	Microsoft
+ATTRIBUTE	MS-CHAP-NT-Enc-PW	6	string	Microsoft
+ATTRIBUTE	MS-MPPE-Encryption-Policy 7	string	Microsoft
+# This is referred to as both singular and plural in the RFC.
+# Plural seems to make more sense.
+ATTRIBUTE	MS-MPPE-Encryption-Type 8	string	Microsoft
+ATTRIBUTE	MS-MPPE-Encryption-Types  8	string	Microsoft
+ATTRIBUTE	MS-RAS-Vendor		9	integer	Microsoft
+ATTRIBUTE	MS-CHAP-Domain		10	string	Microsoft
+ATTRIBUTE	MS-CHAP-Challenge	11	string	Microsoft
+ATTRIBUTE	MS-CHAP-MPPE-Keys	12	string	Microsoft
+ATTRIBUTE	MS-BAP-Usage		13	integer	Microsoft
+ATTRIBUTE	MS-Link-Utilization-Threshold 14 integer	Microsoft
+ATTRIBUTE	MS-Link-Drop-Time-Limit	15	integer	Microsoft
+ATTRIBUTE	MS-MPPE-Send-Key	16	string	Microsoft
+ATTRIBUTE	MS-MPPE-Recv-Key	17	string	Microsoft
+ATTRIBUTE	MS-RAS-Version		18	string	Microsoft
+ATTRIBUTE	MS-Old-ARAP-Password	19	string	Microsoft
+ATTRIBUTE	MS-New-ARAP-Password	20	string	Microsoft
+ATTRIBUTE	MS-ARAP-PW-Change-Reason 21	integer	Microsoft
+
+ATTRIBUTE	MS-Filter		22	string	Microsoft
+ATTRIBUTE	MS-Acct-Auth-Type	23	integer	Microsoft
+ATTRIBUTE	MS-Acct-EAP-Type	24	integer	Microsoft
+
+ATTRIBUTE	MS-CHAP2-Response	25	string	Microsoft
+ATTRIBUTE	MS-CHAP2-Success	26	string	Microsoft
+ATTRIBUTE	MS-CHAP2-CPW		27	string	Microsoft
+
+ATTRIBUTE	MS-Primary-DNS-Server	28	ipaddr	Microsoft
+ATTRIBUTE	MS-Secondary-DNS-Server	29	ipaddr	Microsoft
+ATTRIBUTE	MS-Primary-NBNS-Server	30	ipaddr	Microsoft
+ATTRIBUTE	MS-Secondary-NBNS-Server 31	ipaddr	Microsoft
+
+#ATTRIBUTE	MS-ARAP-Challenge	33	string	Microsoft
+
+
+#
+#	Integer Translations
+#
+
+#	MS-BAP-Usage Values
+
+VALUE		MS-BAP-Usage		Not-Allowed	0
+VALUE		MS-BAP-Usage		Allowed		1
+VALUE		MS-BAP-Usage		Required	2
+
+#	MS-ARAP-Password-Change-Reason Values
+
+VALUE	MS-ARAP-PW-Change-Reason	Just-Change-Password		1
+VALUE	MS-ARAP-PW-Change-Reason	Expired-Password		2
+VALUE	MS-ARAP-PW-Change-Reason	Admin-Requires-Password-Change	3
+VALUE	MS-ARAP-PW-Change-Reason	Password-Too-Short		4
+
+#	MS-Acct-Auth-Type Values
+
+VALUE		MS-Acct-Auth-Type	PAP		1
+VALUE		MS-Acct-Auth-Type	CHAP		2
+VALUE		MS-Acct-Auth-Type	MS-CHAP-1	3
+VALUE		MS-Acct-Auth-Type	MS-CHAP-2	4
+VALUE		MS-Acct-Auth-Type	EAP		5
+
+#	MS-Acct-EAP-Type Values
+
+VALUE		MS-Acct-EAP-Type	MD5		4
+VALUE		MS-Acct-EAP-Type	OTP		5
+VALUE		MS-Acct-EAP-Type	Generic-Token-Card	6
+VALUE		MS-Acct-EAP-Type	TLS		13
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/issue tomato121/release/src/router/pppd/pppd/plugins/radius/etc/issue
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/issue	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/issue	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,5 @@
+(\I)
+-----------------------------------------------------
+\S \R (\N) (port \L)
+-----------------------------------------------------
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/port-id-map tomato121/release/src/router/pppd/pppd/plugins/radius/etc/port-id-map
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/port-id-map	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/port-id-map	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,24 @@
+#
+# port-id-map
+#
+# This file describes the ttyname to port id mapping. The port id
+# is reported as part of a RADIUS authentication or accouting request.
+#
+#ttyname (as returned by ttyname(3))	port-id
+/dev/tty1	1
+/dev/tty2	2
+/dev/tty3	3
+/dev/tty4	4
+/dev/tty5	5
+/dev/tty6	6
+/dev/tty7	7
+/dev/tty8	8
+/dev/ttyS0	9
+/dev/ttyS1	10
+/dev/ttyS2	11
+/dev/ttyS3	12
+/dev/ttyS4	13
+/dev/ttyS5	14
+/dev/ttyS6	15
+/dev/ttyS7	16
+ 
\ No newline at end of file
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf tomato121/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,91 @@
+# General settings
+
+# specify which authentication comes first respectively which
+# authentication is used. possible values are: "radius" and "local".
+# if you specify "radius,local" then the RADIUS server is asked
+# first then the local one. if only one keyword is specified only
+# this server is asked.
+auth_order	radius
+
+# maximum login tries a user has (default 4)
+login_tries	4
+
+# timeout for all login tries (default 60)
+# if this time is exceeded the user is kicked out 
+login_timeout	60
+
+# name of the nologin file which when it exists disables logins.
+# it may be extended by the ttyname which will result in
+# a terminal specific lock (e.g. /etc/nologin.ttyS2 will disable
+# logins on /dev/ttyS2)   (default /etc/nologin)
+nologin /etc/nologin
+
+# name of the issue file. it's only display when no username is passed
+# on the radlogin command line  (default /etc/radiusclient/issue)
+issue	/usr/local/etc/radiusclient/issue
+
+# RADIUS settings
+
+# RADIUS server to use for authentication requests. this config
+# item can appear more then one time. if multiple servers are
+# defined they are tried in a round robin fashion if one
+# server is not answering.
+# optionally you can specify a the port number on which is remote
+# RADIUS listens separated by a colon from the hostname. if
+# no port is specified /etc/services is consulted of the radius
+# service. if this fails also a compiled in default is used.
+authserver 	localhost:1812
+
+# RADIUS server to use for accouting requests. All that I
+# said for authserver applies, too. 
+#
+acctserver 	localhost:1813
+
+# file holding shared secrets used for the communication
+# between the RADIUS client and server
+servers		/usr/local/etc/radiusclient/servers
+
+# dictionary of allowed attributes and values
+# just like in the normal RADIUS distributions
+dictionary 	/usr/local/etc/radiusclient/dictionary
+
+# program to call for a RADIUS authenticated login 
+# (default /usr/sbin/login.radius)
+login_radius	/usr/local/sbin/login.radius
+
+# file which holds sequence number for communication with the
+# RADIUS server
+seqfile		/var/run/radius.seq
+
+# file which specifies mapping between ttyname and NAS-Port attribute
+mapfile		/usr/local/etc/radiusclient/port-id-map
+
+# default authentication realm to append to all usernames if no
+# realm was explicitly specified by the user
+# the radiusd directly form Livingston doesnt use any realms, so leave
+# it blank then
+default_realm
+
+# time to wait for a reply from the RADIUS server
+radius_timeout	10
+
+# resend request this many times before trying the next server
+radius_retries	3
+
+# NAS-Identifier
+#
+# If supplied, this option will cause the client to send the given string
+# as the contents of the NAS-Identifier attribute in RADIUS requests.  No
+# NAS-IP-Address attribute will be sent in this case.
+#
+# The default behavior is to send a NAS-IP-Address option and not send
+# a NAS-Identifier.  The value of the NAS-IP-Address option is chosen
+# by resolving the system hostname.
+
+# nas_identifier MyUniqueNASName
+
+# LOCAL settings
+
+# program to execute for local login
+# it must support the -f flag for preauthenticated login
+login_local	/bin/login
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf.in tomato121/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf.in
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf.in	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/radiusclient.conf.in	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,91 @@
+# General settings
+
+# specify which authentication comes first respectively which
+# authentication is used. possible values are: "radius" and "local".
+# if you specify "radius,local" then the RADIUS server is asked
+# first then the local one. if only one keyword is specified only
+# this server is asked.
+auth_order	radius
+
+# maximum login tries a user has (default 4)
+login_tries	4
+
+# timeout for all login tries (default 60)
+# if this time is exceeded the user is kicked out 
+login_timeout	60
+
+# name of the nologin file which when it exists disables logins.
+# it may be extended by the ttyname which will result in
+# a terminal specific lock (e.g. /etc/nologin.ttyS2 will disable
+# logins on /dev/ttyS2)   (default /etc/nologin)
+nologin /etc/nologin
+
+# name of the issue file. it's only display when no username is passed
+# on the radlogin command line  (default /etc/radiusclient/issue)
+issue	@pkgsysconfdir@/issue
+
+# RADIUS settings
+
+# RADIUS server to use for authentication requests. this config
+# item can appear more then one time. if multiple servers are
+# defined they are tried in a round robin fashion if one
+# server is not answering.
+# optionally you can specify a the port number on which is remote
+# RADIUS listens separated by a colon from the hostname. if
+# no port is specified /etc/services is consulted of the radius
+# service. if this fails also a compiled in default is used.
+authserver 	localhost:1812
+
+# RADIUS server to use for accouting requests. All that I
+# said for authserver applies, too. 
+#
+acctserver 	localhost:1813
+
+# file holding shared secrets used for the communication
+# between the RADIUS client and server
+servers		@pkgsysconfdir@/servers
+
+# dictionary of allowed attributes and values
+# just like in the normal RADIUS distributions
+dictionary 	@pkgsysconfdir@/dictionary
+
+# program to call for a RADIUS authenticated login 
+# (default /usr/sbin/login.radius)
+login_radius	@sbindir@/login.radius
+
+# file which holds sequence number for communication with the
+# RADIUS server
+seqfile		/var/run/radius.seq
+
+# file which specifies mapping between ttyname and NAS-Port attribute
+mapfile		@pkgsysconfdir@/port-id-map
+
+# default authentication realm to append to all usernames if no
+# realm was explicitly specified by the user
+# the radiusd directly form Livingston doesnt use any realms, so leave
+# it blank then
+default_realm
+
+# time to wait for a reply from the RADIUS server
+radius_timeout	10
+
+# resend request this many times before trying the next server
+radius_retries	3
+
+# NAS-Identifier
+#
+# If supplied, this option will cause the client to send the given string
+# as the contents of the NAS-Identifier attribute in RADIUS requests.  No
+# NAS-IP-Address attribute will be sent in this case.
+#
+# The default behavior is to send a NAS-IP-Address option and not send
+# a NAS-Identifier.  The value of the NAS-IP-Address option is chosen
+# by resolving the system hostname.
+
+# nas_identifier MyUniqueNASName
+
+# LOCAL settings
+
+# program to execute for local login
+# it must support the -f flag for preauthenticated login
+login_local	/bin/login
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/realms tomato121/release/src/router/pppd/pppd/plugins/radius/etc/realms
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/realms	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/realms	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,22 @@
+# /etc/radiusclient/realms
+#
+# Handle realm @netservers.co.uk on an internal RADIUS server
+# (note the server must be told to strip the realm)
+
+#authserver netservers.co.uk 192.168.1.1:1812
+#acctserver netservers.co.uk 192.168.1.1:1813
+
+# users in realm @example.com are handled by separate servers
+
+#authserver example.com 10.0.0.1:1812
+#acctserver example.com 10.0.0.2:1813
+
+# the DEFAULT realm matches users that do not supply a realm
+
+#authserver DEFAULT 192.168.1.1:1812
+#acctserver DEFAULT 192.168.1.1:1813
+
+# Any realms that do not match in the realms file automatically fall 
+# through to the standard radius plugin which uses the servers in the 
+# radiusclient.conf file.  Note that this is different than the
+# DEFAULT realm match, above.
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/etc/servers tomato121/release/src/router/pppd/pppd/plugins/radius/etc/servers
--- tomato/release/src/router/pppd/pppd/plugins/radius/etc/servers	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/etc/servers	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,4 @@
+#Server Name or Client/Server pair		Key
+#----------------				---------------
+#portmaster.elemental.net			hardlyasecret
+#portmaster2.elemental.net	    		donttellanyone
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/includes.h tomato121/release/src/router/pppd/pppd/plugins/radius/includes.h
--- tomato/release/src/router/pppd/pppd/plugins/radius/includes.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/includes.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,54 @@
+/*
+ * $Id: includes.h,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1997 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netdb.h>
+#include <syslog.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <limits.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX        1024
+#endif
+
+#ifndef UCHAR_MAX
+# define UCHAR_MAX       255
+#endif
+
+#include <pwd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <time.h>
+
+#include "magic.h"
+
+/* rlib/lock.c */
+int do_lock_exclusive(int);
+int do_unlock(int);
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/ip_util.c tomato121/release/src/router/pppd/pppd/plugins/radius/ip_util.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/ip_util.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/ip_util.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,137 @@
+/*
+ * $Id: ip_util.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996,1997 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+
+/*
+ * Function: rc_get_ipaddr
+ *
+ * Purpose: return an IP address in host long notation from a host
+ *          name or address in dot notation.
+ *
+ * Returns: 0 on failure
+ */
+
+UINT4 rc_get_ipaddr (char *host)
+{
+	struct hostent *hp;
+
+	if (rc_good_ipaddr (host) == 0)
+	{
+		return ntohl(inet_addr (host));
+	}
+	else if ((hp = gethostbyname (host)) == (struct hostent *) NULL)
+	{
+		error("rc_get_ipaddr: couldn't resolve hostname: %s", host);
+		return ((UINT4) 0);
+	}
+	return ntohl((*(UINT4 *) hp->h_addr));
+}
+
+/*
+ * Function: rc_good_ipaddr
+ *
+ * Purpose: check for valid IP address in standard dot notation.
+ *
+ * Returns: 0 on success, -1 when failure
+ *
+ */
+
+int rc_good_ipaddr (char *addr)
+{
+	int             dot_count;
+	int             digit_count;
+
+	if (addr == NULL)
+		return (-1);
+
+	dot_count = 0;
+	digit_count = 0;
+	while (*addr != '\0' && *addr != ' ')
+	{
+		if (*addr == '.')
+		{
+			dot_count++;
+			digit_count = 0;
+		}
+		else if (!isdigit (*addr))
+		{
+			dot_count = 5;
+		}
+		else
+		{
+			digit_count++;
+			if (digit_count > 3)
+			{
+				dot_count = 5;
+			}
+		}
+		addr++;
+	}
+	if (dot_count != 3)
+	{
+		return (-1);
+	}
+	else
+	{
+		return (0);
+	}
+}
+
+/*
+ * Function: rc_ip_hostname
+ *
+ * Purpose: Return a printable host name (or IP address in dot notation)
+ *	    for the supplied IP address.
+ *
+ */
+
+const char *rc_ip_hostname (UINT4 h_ipaddr)
+{
+	struct hostent  *hp;
+	UINT4           n_ipaddr = htonl (h_ipaddr);
+
+	if ((hp = gethostbyaddr ((char *) &n_ipaddr, sizeof (struct in_addr),
+			    AF_INET)) == NULL) {
+		error("rc_ip_hostname: couldn't look up host by addr: %08lX", h_ipaddr);
+	}
+
+	return ((hp==NULL)?"unknown":hp->h_name);
+}
+
+/*
+ * Function: rc_own_ipaddress
+ *
+ * Purpose: get the IP address of this host in host order
+ *
+ * Returns: IP address on success, 0 on failure
+ *
+ */
+
+UINT4 rc_own_ipaddress(void)
+{
+	static UINT4 this_host_ipaddr = 0;
+
+	if (!this_host_ipaddr) {
+		if ((this_host_ipaddr = rc_get_ipaddr (hostname)) == 0) {
+			error("rc_own_ipaddress: couldn't get own IP address");
+			return 0;
+		}
+	}
+
+	return this_host_ipaddr;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/lock.c tomato121/release/src/router/pppd/pppd/plugins/radius/lock.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/lock.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/lock.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,46 @@
+/*
+ * $Id: lock.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1997 Lars Fenneberg
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include "includes.h"
+#include <unistd.h>
+#include <fcntl.h>
+
+int do_lock_exclusive(int fd)
+{
+	struct flock fl;
+	int res;
+
+	memset((void *)&fl, 0, sizeof(fl));
+
+	fl.l_type = F_WRLCK;
+	fl.l_whence = fl.l_start = 0;
+	fl.l_len = 0; /* 0 means "to end of file" */
+
+	res = fcntl(fd, F_SETLK, &fl);
+
+	if ((res == -1) && (errno == EAGAIN))
+		errno = EWOULDBLOCK;
+
+	return res;
+}
+
+int do_unlock(int fd)
+{
+	struct flock fl;
+
+	memset((void *)&fl, 0, sizeof(fl));
+
+	fl.l_type = F_UNLCK;
+	fl.l_whence = fl.l_start = 0;
+	fl.l_len = 0; /* 0 means "to end of file" */
+
+	return fcntl(fd, F_SETLK, &fl);
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/Makefile tomato121/release/src/router/pppd/pppd/plugins/radius/Makefile
--- tomato/release/src/router/pppd/pppd/plugins/radius/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,66 @@
+# Makefile for RADIUS plugin
+#
+# Copyright 2002 Roaring Penguin Software Inc.
+#
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+MANDIR = $(DESTDIR)/share/man/man8
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
+
+INSTALL	= install
+
+PLUGIN=radius.so radattr.so radrealms.so
+COPTS = -O2
+CFLAGS=-I. -I../.. -I../../../include $(COPTS) -fPIC -DRC_LOG_FACILITY=LOG_DAEMON
+
+# Uncomment the next line to include support for Microsoft's
+# MS-CHAP authentication protocol.
+CHAPMS=y
+# Uncomment the next line to include support for MPPE.
+MPPE=y
+# Uncomment the next lint to include support for traffic limiting
+MAXOCTETS=y
+
+ifdef CHAPMS
+CFLAGS += -DCHAPMS=1
+ifdef MPPE
+CFLAGS += -DMPPE=1
+endif
+endif
+ifdef MAXOCTETS
+CFLAGS += -DMAXOCTETS=1
+endif
+
+all: $(PLUGIN)
+
+install: all
+	$(INSTALL) -d -m 755 $(LIBDIR)
+	$(INSTALL) -c -m 755 radius.so $(LIBDIR)
+	$(INSTALL) -c -m 755 radattr.so $(LIBDIR)
+	$(INSTALL) -c -m 755 radrealms.so $(LIBDIR)
+	$(INSTALL) -c -m 444 pppd-radius.8 $(MANDIR)
+	$(INSTALL) -c -m 444 pppd-radattr.8 $(MANDIR)
+
+radius.so: radius.o libradiusclient.a
+	$(CC) -o radius.so -shared radius.o libradiusclient.a
+
+radattr.so: radattr.o
+	$(CC) -o radattr.so -shared radattr.o
+
+radrealms.so: radrealms.o
+	$(CC) -o radrealms.so -shared radrealms.o
+
+CLIENTOBJS = avpair.o buildreq.o config.o dict.o ip_util.o \
+	clientid.o sendserver.o lock.o util.o md5.o
+libradiusclient.a: $(CLIENTOBJS)
+	$(AR) rv $@ $?
+
+clean:
+	rm -f *.o *.so *.a
+
+distclean:
+	rm -f *.o *.so *.a
+
+dist-clean: distclean
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/Makefile.linux tomato121/release/src/router/pppd/pppd/plugins/radius/Makefile.linux
--- tomato/release/src/router/pppd/pppd/plugins/radius/Makefile.linux	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,66 @@
+# Makefile for RADIUS plugin
+#
+# Copyright 2002 Roaring Penguin Software Inc.
+#
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+MANDIR = $(DESTDIR)/share/man/man8
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
+
+INSTALL	= install
+
+PLUGIN=radius.so radattr.so radrealms.so
+COPTS = -O2
+CFLAGS=-I. -I../.. -I../../../include $(COPTS) -fPIC -DRC_LOG_FACILITY=LOG_DAEMON
+
+# Uncomment the next line to include support for Microsoft's
+# MS-CHAP authentication protocol.
+CHAPMS=y
+# Uncomment the next line to include support for MPPE.
+MPPE=y
+# Uncomment the next lint to include support for traffic limiting
+MAXOCTETS=y
+
+ifdef CHAPMS
+CFLAGS += -DCHAPMS=1
+ifdef MPPE
+CFLAGS += -DMPPE=1
+endif
+endif
+ifdef MAXOCTETS
+CFLAGS += -DMAXOCTETS=1
+endif
+
+all: $(PLUGIN)
+
+install: all
+	$(INSTALL) -d -m 755 $(LIBDIR)
+	$(INSTALL) -c -m 755 radius.so $(LIBDIR)
+	$(INSTALL) -c -m 755 radattr.so $(LIBDIR)
+	$(INSTALL) -c -m 755 radrealms.so $(LIBDIR)
+	$(INSTALL) -c -m 444 pppd-radius.8 $(MANDIR)
+	$(INSTALL) -c -m 444 pppd-radattr.8 $(MANDIR)
+
+radius.so: radius.o libradiusclient.a
+	$(CC) -o radius.so -shared radius.o libradiusclient.a
+
+radattr.so: radattr.o
+	$(CC) -o radattr.so -shared radattr.o
+
+radrealms.so: radrealms.o
+	$(CC) -o radrealms.so -shared radrealms.o
+
+CLIENTOBJS = avpair.o buildreq.o config.o dict.o ip_util.o \
+	clientid.o sendserver.o lock.o util.o md5.o
+libradiusclient.a: $(CLIENTOBJS)
+	$(AR) rv $@ $?
+
+clean:
+	rm -f *.o *.so *.a
+
+distclean:
+	rm -f *.o *.so *.a
+
+dist-clean: distclean
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/md5.c tomato121/release/src/router/pppd/pppd/plugins/radius/md5.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/md5.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/md5.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,13 @@
+/*
+ * $Id: md5.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ */
+#include "md5.h"
+
+void rc_md5_calc (unsigned char *output, unsigned char *input, unsigned int inlen)
+{
+	MD5_CTX         context;
+
+	MD5_Init (&context);
+	MD5_Update (&context, input, inlen);
+	MD5_Final (output, &context);
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/options.h tomato121/release/src/router/pppd/pppd/plugins/radius/options.h
--- tomato/release/src/router/pppd/pppd/plugins/radius/options.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/options.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,59 @@
+/*
+ * $Id: options.h,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1996 Lars Fenneberg
+ *
+ * See the file COPYRIGHT for the respective terms and conditions. 
+ * If the file is missing contact me at lf@elemental.net 
+ * and I'll send you a copy.
+ *
+ */
+
+#define OPTION_LEN	64
+
+/* ids for different option types */
+#define OT_STR		(1<<0)	  /* string */
+#define OT_INT		(1<<1)	  /* integer */
+#define OT_SRV		(1<<2)	  /* server list */
+#define OT_AUO		(1<<3)    /* authentication order */
+
+#define OT_ANY		((unsigned int)~0) /* used internally */
+
+/* status types */
+#define ST_UNDEF	(1<<0)	  /* option is undefined */
+
+typedef struct _option {
+	char name[OPTION_LEN];	  /* name of the option */
+	int type, status;	  /* type and status    */
+	void *val;		  /* pointer to option value */
+} OPTION;
+
+static SERVER acctserver = {0};
+static SERVER authserver = {0};
+
+static OPTION config_options[] = {
+/* internally used options */
+{"config_file",		OT_STR, ST_UNDEF, NULL},
+/* General options */
+{"auth_order",	 	OT_AUO, ST_UNDEF, NULL},
+{"login_tries",	 	OT_INT, ST_UNDEF, NULL},
+{"login_timeout",	OT_INT, ST_UNDEF, NULL},
+{"nologin",		OT_STR, ST_UNDEF, NULL},
+{"issue",		OT_STR, ST_UNDEF, NULL},
+/* RADIUS specific options */
+{"authserver",		OT_SRV, ST_UNDEF, &authserver},
+{"acctserver",		OT_SRV, ST_UNDEF, &acctserver},
+{"servers",		OT_STR, ST_UNDEF, NULL},
+{"dictionary",		OT_STR, ST_UNDEF, NULL},
+{"login_radius",	OT_STR, ST_UNDEF, NULL},
+{"seqfile",		OT_STR, ST_UNDEF, NULL},
+{"mapfile",		OT_STR, ST_UNDEF, NULL},
+{"default_realm",	OT_STR, ST_UNDEF, NULL},
+{"radius_timeout",	OT_INT, ST_UNDEF, NULL},
+{"radius_retries",	OT_INT,	ST_UNDEF, NULL},
+{"nas_identifier",      OT_STR, ST_UNDEF, ""},
+/* local options */
+{"login_local",		OT_STR, ST_UNDEF, NULL},
+};
+
+static int num_options = ((sizeof(config_options))/(sizeof(config_options[0])));
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/pathnames.h tomato121/release/src/router/pppd/pppd/plugins/radius/pathnames.h
--- tomato/release/src/router/pppd/pppd/plugins/radius/pathnames.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/pathnames.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,28 @@
+/*
+ * $Id: pathnames.h,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#define _PATH_DEV_URANDOM	"/dev/urandom"		/* Linux only */
+#define _PATH_ETC_ISSUE		"/etc/issue"
+
+/* normally defined in the Makefile */
+#ifndef _PATH_ETC_RADIUSCLIENT_CONF
+#define _PATH_ETC_RADIUSCLIENT_CONF       "/tmp/ppp/radius/radiusclient.conf"
+#endif
+
+#endif /* PATHNAMES_H */
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/pppd-radattr.8 tomato121/release/src/router/pppd/pppd/plugins/radius/pppd-radattr.8
--- tomato/release/src/router/pppd/pppd/plugins/radius/pppd-radattr.8	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/pppd-radattr.8	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,44 @@
+.\" manual page [] for RADATTR plugin for pppd 2.4
+.\" $Id: pppd-radattr.8,v 1.2 2003/04/25 07:33:20 fcusack Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH PPPD-RADATTR 8
+.SH NAME
+radattr.so \- RADIUS utility plugin for
+.BR pppd (8)
+.SH SYNOPSIS
+.B pppd
+[
+.I options
+]
+plugin radius.so plugin radattr.so
+.SH DESCRIPTION
+.LP
+The radattr plugin for pppd causes all radius attributes returned by
+the RADIUS server at authentication time to be stored in the file
+.I /var/run/radattr.pppN
+where
+.I pppN
+is the name of the PPP interface.  The RADIUS attributes are stored
+one per line in the format "Attribute-Name Attribute-Value".  This
+format is convenient for use in /etc/ppp/ip-up and /etc/ppp/ip-down
+scripts.
+.LP
+Note that you
+.I must
+load the radius.so plugin before loading the radattr.so plugin;
+radattr.so depends on symbols defined in radius.so.
+
+.SH USAGE
+To use the plugin, simply supply the
+.B plugin radius.so plugin radattr.so
+options to pppd.
+
+.SH SEE ALSO
+.BR pppd (8) " pppd-radius" (8)
+
+.SH AUTHOR
+David F. Skoll <dfs@roaringpenguin.com>
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/pppd-radius.8 tomato121/release/src/router/pppd/pppd/plugins/radius/pppd-radius.8
--- tomato/release/src/router/pppd/pppd/plugins/radius/pppd-radius.8	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/pppd-radius.8	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,67 @@
+.\" manual page [] for RADIUS plugin for pppd 2.4
+.\" $Id: pppd-radius.8,v 1.5 2004/03/26 13:27:17 kad Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH PPPD-RADIUS 8
+.SH NAME
+radius.so \- RADIUS authentication plugin for
+.BR pppd (8)
+.SH SYNOPSIS
+.B pppd
+[
+.I options
+]
+plugin radius.so
+.SH DESCRIPTION
+.LP
+The RADIUS plugin for pppd permits pppd to perform PAP, CHAP, MS-CHAP and
+MS-CHAPv2 authentication against a RADIUS server instead of the usual
+.I /etc/ppp/pap-secrets
+and
+.I /etc/ppp/chap-secrets
+files.
+.LP
+The RADIUS plugin is built on a library called
+.B radiusclient
+which has its own configuration files (usually in \fI/etc/radiusclient\fR),
+consult those files for more information on configuring the RADIUS
+plugin
+
+.SH OPTIONS
+The RADIUS plugin introduces one additional pppd option:
+.TP
+.BI "radius-config-file " filename
+The file
+.I filename
+is taken as the radiusclient configuration file.  If this option is not
+used, then the plugin uses
+.I /etc/radiusclient/radiusclient.conf
+as the configuration file.
+.TP
+.BI "avpair " attribute=value
+Adds an Attribute-Value pair to be passed on to the RADIUS server on each request.
+.TP
+.BI map-to-ifname
+Sets Radius NAS-Port attribute to number equal to interface name (Default)
+.TP
+.BI map-to-ttyname
+Sets Radius NAS-Port attribute value via libradiusclient library
+
+.SH USAGE
+To use the plugin, simply supply the
+.B plugin radius.so
+option to pppd, and edit
+.I /etc/radiusclient/radiusclient.conf
+appropriately.  If you use the RADIUS plugin, the normal pppd authentication
+schemes (login, checking the /etc/ppp/*-secrets files) are skipped.  The
+RADIUS server should assign an IP address to the peer using the RADIUS
+Framed-IP-Address attribute.
+
+.SH SEE ALSO
+.BR pppd (8) " pppd-radattr" (8)
+
+.SH AUTHOR
+David F. Skoll <dfs@roaringpenguin.com>
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/radattr.c tomato121/release/src/router/pppd/pppd/plugins/radius/radattr.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/radattr.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/radattr.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,111 @@
+/***********************************************************************
+*
+* radattr.c
+*
+* A plugin which is stacked on top of radius.so.  This plugin writes
+* all RADIUS attributes from the server's authentication confirmation
+* into /var/run/radattr.pppN.  These attributes are available for
+* consumption by /etc/ppp/ip-{up,down} scripts.
+*
+* Copyright (C) 2002 Roaring Penguin Software Inc.
+*
+* This plugin may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: radattr.c,v 1.2 2004/10/28 00:24:40 paulus Exp $";
+
+#include "pppd.h"
+#include "radiusclient.h"
+#include <stdio.h>
+
+extern void (*radius_attributes_hook)(VALUE_PAIR *);
+static void print_attributes(VALUE_PAIR *);
+static void cleanup(void *opaque, int arg);
+
+char pppd_version[] = VERSION;
+
+/**********************************************************************
+* %FUNCTION: plugin_init
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Initializes radattr plugin.
+***********************************************************************/
+void
+plugin_init(void)
+{
+    radius_attributes_hook = print_attributes;
+
+#if 0
+    /* calling cleanup() on link down is problematic because print_attributes()
+       is called only after PAP or CHAP authentication, but not when the link
+       should go up again for any other reason */
+    add_notifier(&link_down_notifier, cleanup, NULL);
+#endif
+
+    /* Just in case... */
+    add_notifier(&exitnotify, cleanup, NULL);
+    info("RADATTR plugin initialized.");
+}
+
+/**********************************************************************
+* %FUNCTION: print_attributes
+* %ARGUMENTS:
+*  vp -- linked-list of RADIUS attribute-value pairs
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Prints the attribute pairs to /var/run/radattr.pppN.  Each line of the
+*  file contains "name value" pairs.
+***********************************************************************/
+static void
+print_attributes(VALUE_PAIR *vp)
+{
+    FILE *fp;
+    char fname[512];
+    char name[2048];
+    char value[2048];
+    int cnt = 0;
+
+    slprintf(fname, sizeof(fname), "/var/run/radattr.%s", ifname);
+    fp = fopen(fname, "w");
+    if (!fp) {
+	warn("radattr plugin: Could not open %s for writing: %m", fname);
+	return;
+    }
+
+    for (; vp; vp=vp->next) {
+	if (rc_avpair_tostr(vp, name, sizeof(name), value, sizeof(value)) < 0) {
+	    continue;
+	}
+	fprintf(fp, "%s %s\n", name, value);
+	cnt++;
+    }
+    fclose(fp);
+    dbglog("RADATTR plugin wrote %d line(s) to file %s.", cnt, fname);
+}
+
+/**********************************************************************
+* %FUNCTION: cleanup
+* %ARGUMENTS:
+*  opaque -- not used
+*  arg -- not used
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Deletes /var/run/radattr.pppN
+***********************************************************************/
+static void
+cleanup(void *opaque, int arg)
+{
+    char fname[512];
+
+    slprintf(fname, sizeof(fname), "/var/run/radattr.%s", ifname);
+    (void) remove(fname);
+    dbglog("RADATTR plugin removed file %s.", fname);
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/radius.c tomato121/release/src/router/pppd/pppd/plugins/radius/radius.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/radius.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/radius.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,1303 @@
+/***********************************************************************
+*
+* radius.c
+*
+* RADIUS plugin for pppd.  Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
+* authentication using RADIUS.
+*
+* Copyright (C) 2002 Roaring Penguin Software Inc.
+*
+* Based on a patch for ipppd, which is:
+*    Copyright (C) 1996, Matjaz Godec <gody@elgo.si>
+*    Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de>
+*    Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net>
+*
+* Uses radiusclient library, which is:
+*    Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
+*    Copyright (C) 2002 Roaring Penguin Software Inc.
+*
+* MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with
+* modification from Frank Cusack, <frank@google.com>.
+*
+* This plugin may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+***********************************************************************/
+static char const RCSID[] =
+"$Id: radius.c,v 1.31 2006/05/22 00:01:40 paulus Exp $";
+
+#include "pppd.h"
+#include "chap-new.h"
+#ifdef CHAPMS
+#include "chap_ms.h"
+#ifdef MPPE
+#include "md5.h"
+#endif
+#endif
+#include "radiusclient.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#define BUF_LEN 1024
+
+#define MD5_HASH_SIZE	16
+
+static char *config_file = NULL;
+static int add_avp(char **);
+static struct avpopt {
+    char *vpstr;
+    struct avpopt *next;
+} *avpopt = NULL;
+static bool portnummap = 0;
+
+static option_t Options[] = {
+    { "radius-config-file", o_string, &config_file },
+    { "avpair", o_special, add_avp },
+    { "map-to-ttyname", o_bool, &portnummap,
+	"Set Radius NAS-Port attribute value via libradiusclient library", OPT_PRIO | 1 },
+    { "map-to-ifname", o_bool, &portnummap,
+	"Set Radius NAS-Port attribute to number as in interface name (Default)", OPT_PRIOSUB | 0 },
+    { NULL }
+};
+
+static int radius_secret_check(void);
+static int radius_pap_auth(char *user,
+			   char *passwd,
+			   char **msgp,
+			   struct wordlist **paddrs,
+			   struct wordlist **popts);
+static int radius_chap_verify(char *user, char *ourname, int id,
+			      struct chap_digest_type *digest,
+			      unsigned char *challenge,
+			      unsigned char *response,
+			      char *message, int message_space);
+
+static void radius_ip_up(void *opaque, int arg);
+static void radius_ip_down(void *opaque, int arg);
+static void make_username_realm(char *user);
+static int radius_setparams(VALUE_PAIR *vp, char *msg, REQUEST_INFO *req_info,
+			    struct chap_digest_type *digest,
+			    unsigned char *challenge,
+			    char *message, int message_space);
+static void radius_choose_ip(u_int32_t *addrp);
+static int radius_init(char *msg);
+static int get_client_port(char *ifname);
+static int radius_allowed_address(u_int32_t addr);
+static void radius_acct_interim(void *);
+#ifdef MPPE
+static int radius_setmppekeys(VALUE_PAIR *vp, REQUEST_INFO *req_info,
+			      unsigned char *);
+static int radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info);
+#endif
+
+#ifndef MAXSESSIONID
+#define MAXSESSIONID 32
+#endif
+
+#ifndef MAXCLASSLEN
+#define MAXCLASSLEN 500
+#endif
+
+struct radius_state {
+    int accounting_started;
+    int initialized;
+    int client_port;
+    int choose_ip;
+    int any_ip_addr_ok;
+    int done_chap_once;
+    u_int32_t ip_addr;
+    char user[MAXNAMELEN];
+    char config_file[MAXPATHLEN];
+    char session_id[MAXSESSIONID + 1];
+    time_t start_time;
+    int acct_interim_interval;
+    SERVER *authserver;		/* Authentication server to use */
+    SERVER *acctserver;		/* Accounting server to use */
+    int class_len;
+    char class[MAXCLASSLEN];
+    VALUE_PAIR *avp;	/* Additional (user supplied) vp's to send to server */
+};
+
+void (*radius_attributes_hook)(VALUE_PAIR *) = NULL;
+
+/* The pre_auth_hook MAY set authserver and acctserver if it wants.
+   In that case, they override the values in the radiusclient.conf file */
+void (*radius_pre_auth_hook)(char const *user,
+			     SERVER **authserver,
+			     SERVER **acctserver) = NULL;
+
+static struct radius_state rstate;
+
+char pppd_version[] = VERSION;
+
+/**********************************************************************
+* %FUNCTION: plugin_init
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Initializes RADIUS plugin.
+***********************************************************************/
+void
+plugin_init(void)
+{
+    pap_check_hook = radius_secret_check;
+    pap_auth_hook = radius_pap_auth;
+
+    chap_check_hook = radius_secret_check;
+    chap_verify_hook = radius_chap_verify;
+
+    ip_choose_hook = radius_choose_ip;
+    allowed_address_hook = radius_allowed_address;
+
+    add_notifier(&ip_up_notifier, radius_ip_up, NULL);
+    add_notifier(&ip_down_notifier, radius_ip_down, NULL);
+
+    memset(&rstate, 0, sizeof(rstate));
+
+    strlcpy(rstate.config_file, "/tmp/ppp/radius/radiusclient.conf",
+	    sizeof(rstate.config_file));
+
+    add_options(Options);
+
+    info("RADIUS plugin initialized.");
+}
+
+/**********************************************************************
+* %FUNCTION: add_avp
+* %ARGUMENTS:
+*  argv -- the <attribute=value> pair to add
+* %RETURNS:
+*  1
+* %DESCRIPTION:
+*  Adds an av pair to be passed on to the RADIUS server on each request.
+***********************************************************************/
+static int
+add_avp(char **argv)
+{
+    struct avpopt *p = malloc(sizeof(struct avpopt));
+
+    /* Append to a list of vp's for later parsing */
+    p->vpstr = strdup(*argv);
+    p->next = avpopt;
+    avpopt = p;
+
+    return 1;
+}
+
+/**********************************************************************
+* %FUNCTION: radius_secret_check
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  1 -- we are ALWAYS willing to supply a secret. :-)
+* %DESCRIPTION:
+* Tells pppd that we will try to authenticate the peer, and not to
+* worry about looking in /etc/ppp/*-secrets
+***********************************************************************/
+static int
+radius_secret_check(void)
+{
+    return 1;
+}
+
+/**********************************************************************
+* %FUNCTION: radius_choose_ip
+* %ARGUMENTS:
+*  addrp -- where to store the IP address
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  If RADIUS server has specified an IP address, it is stored in *addrp.
+***********************************************************************/
+static void
+radius_choose_ip(u_int32_t *addrp)
+{
+    if (rstate.choose_ip) {
+	*addrp = rstate.ip_addr;
+    }
+}
+
+/**********************************************************************
+* %FUNCTION: radius_pap_auth
+* %ARGUMENTS:
+*  user -- user-name of peer
+*  passwd -- password supplied by peer
+*  msgp -- Message which will be sent in PAP response
+*  paddrs -- set to a list of possible peer IP addresses
+*  popts -- set to a list of additional pppd options
+* %RETURNS:
+*  1 if we can authenticate, -1 if we cannot.
+* %DESCRIPTION:
+* Performs PAP authentication using RADIUS
+***********************************************************************/
+static int
+radius_pap_auth(char *user,
+		char *passwd,
+		char **msgp,
+		struct wordlist **paddrs,
+		struct wordlist **popts)
+{
+    VALUE_PAIR *send, *received;
+    UINT4 av_type;
+    int result;
+    static char radius_msg[BUF_LEN];
+
+    radius_msg[0] = 0;
+    *msgp = radius_msg;
+
+    if (radius_init(radius_msg) < 0) {
+	return 0;
+    }
+
+    /* Put user with potentially realm added in rstate.user */
+    make_username_realm(user);
+
+    if (radius_pre_auth_hook) {
+	radius_pre_auth_hook(rstate.user,
+			     &rstate.authserver,
+			     &rstate.acctserver);
+    }
+
+    send = NULL;
+    received = NULL;
+
+    /* Hack... the "port" is the ppp interface number.  Should really be
+       the tty */
+    rstate.client_port = get_client_port(portnummap ? devnam : ifname);
+
+    av_type = PW_FRAMED;
+    rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_PPP;
+    rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
+
+    rc_avpair_add(&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE);
+    rc_avpair_add(&send, PW_USER_PASSWORD, passwd, 0, VENDOR_NONE);
+    if (*remote_number) {
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0,
+		       VENDOR_NONE);
+    } else if (ipparam)
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE);
+
+    /* Add user specified vp's */
+    if (rstate.avp)
+	rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp));
+
+    if (rstate.authserver) {
+	result = rc_auth_using_server(rstate.authserver,
+				      rstate.client_port, send,
+				      &received, radius_msg, NULL);
+    } else {
+	result = rc_auth(rstate.client_port, send, &received, radius_msg, NULL);
+    }
+
+    if (result == OK_RC) {
+	if (radius_setparams(received, radius_msg, NULL, NULL, NULL, NULL, 0) < 0) {
+	    result = ERROR_RC;
+	}
+    }
+
+    /* free value pairs */
+    rc_avpair_free(received);
+    rc_avpair_free(send);
+
+    return (result == OK_RC) ? 1 : 0;
+}
+
+/**********************************************************************
+* %FUNCTION: radius_chap_verify
+* %ARGUMENTS:
+*  user -- name of the peer
+*  ourname -- name for this machine
+*  id -- the ID byte in the challenge
+*  digest -- points to the structure representing the digest type
+*  challenge -- the challenge string we sent (length in first byte)
+*  response -- the response (hash) the peer sent back (length in 1st byte)
+*  message -- space for a message to be returned to the peer
+*  message_space -- number of bytes available at *message.
+* %RETURNS:
+*  1 if the response is good, 0 if it is bad
+* %DESCRIPTION:
+* Performs CHAP, MS-CHAP and MS-CHAPv2 authentication using RADIUS.
+***********************************************************************/
+static int
+radius_chap_verify(char *user, char *ourname, int id,
+		   struct chap_digest_type *digest,
+		   unsigned char *challenge, unsigned char *response,
+		   char *message, int message_space)
+{
+    VALUE_PAIR *send, *received;
+    UINT4 av_type;
+    static char radius_msg[BUF_LEN];
+    int result;
+    int challenge_len, response_len;
+    u_char cpassword[MAX_RESPONSE_LEN + 1];
+#ifdef MPPE
+    /* Need the RADIUS secret and Request Authenticator to decode MPPE */
+    REQUEST_INFO request_info, *req_info = &request_info;
+#else
+    REQUEST_INFO *req_info = NULL;
+#endif
+
+    challenge_len = *challenge++;
+    response_len = *response++;
+
+    radius_msg[0] = 0;
+
+    if (radius_init(radius_msg) < 0) {
+	error("%s", radius_msg);
+	return 0;
+    }
+
+    /* return error for types we can't handle */
+    if ((digest->code != CHAP_MD5)
+#ifdef CHAPMS
+	&& (digest->code != CHAP_MICROSOFT)
+	&& (digest->code != CHAP_MICROSOFT_V2)
+#endif
+	) {
+	error("RADIUS: Challenge type %u unsupported", digest->code);
+	return 0;
+    }
+
+    /* Put user with potentially realm added in rstate.user */
+    if (!rstate.done_chap_once) {
+	make_username_realm(user);
+	rstate.client_port = get_client_port (portnummap ? devnam : ifname);
+	if (radius_pre_auth_hook) {
+	    radius_pre_auth_hook(rstate.user,
+				 &rstate.authserver,
+				 &rstate.acctserver);
+	}
+    }
+
+    send = received = NULL;
+
+    av_type = PW_FRAMED;
+    rc_avpair_add (&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_PPP;
+    rc_avpair_add (&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
+
+    rc_avpair_add (&send, PW_USER_NAME, rstate.user , 0, VENDOR_NONE);
+
+    /*
+     * add the challenge and response fields
+     */
+    switch (digest->code) {
+    case CHAP_MD5:
+	/* CHAP-Challenge and CHAP-Password */
+	if (response_len != MD5_HASH_SIZE)
+	    return 0;
+	cpassword[0] = id;
+	memcpy(&cpassword[1], response, MD5_HASH_SIZE);
+
+	rc_avpair_add(&send, PW_CHAP_CHALLENGE,
+		      challenge, challenge_len, VENDOR_NONE);
+	rc_avpair_add(&send, PW_CHAP_PASSWORD,
+		      cpassword, MD5_HASH_SIZE + 1, VENDOR_NONE);
+	break;
+
+#ifdef CHAPMS
+    case CHAP_MICROSOFT:
+    {
+	/* MS-CHAP-Challenge and MS-CHAP-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 */
+	*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);
+	rc_avpair_add(&send, PW_MS_CHAP_RESPONSE,
+		      cpassword, MS_CHAP_RESPONSE_LEN + 1, VENDOR_MICROSOFT);
+	break;
+    }
+
+    case CHAP_MICROSOFT_V2:
+    {
+	/* MS-CHAP-Challenge and MS-CHAP2-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 */
+	*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);
+	rc_avpair_add(&send, PW_MS_CHAP2_RESPONSE,
+		      cpassword, MS_CHAP2_RESPONSE_LEN + 1, VENDOR_MICROSOFT);
+	break;
+    }
+#endif
+    }
+
+    if (*remote_number) {
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, remote_number, 0,
+		       VENDOR_NONE);
+    } else if (ipparam)
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE);
+
+    /* Add user specified vp's */
+    if (rstate.avp)
+	rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp));
+
+    /*
+     * make authentication with RADIUS server
+     */
+
+    if (rstate.authserver) {
+	result = rc_auth_using_server(rstate.authserver,
+				      rstate.client_port, send,
+				      &received, radius_msg, req_info);
+    } else {
+	result = rc_auth(rstate.client_port, send, &received, radius_msg,
+			 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,
+				 challenge, message, message_space) < 0) {
+		error("%s", radius_msg);
+		result = ERROR_RC;
+	    } else {
+		rstate.done_chap_once = 1;
+	    }
+	}
+    }
+
+    rc_avpair_free(received);
+    rc_avpair_free (send);
+    return (result == OK_RC);
+}
+
+/**********************************************************************
+* %FUNCTION: make_username_realm
+* %ARGUMENTS:
+*  user -- the user given to pppd
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Copies user into rstate.user.  If it lacks a realm (no "@domain" part),
+* then the default realm from the radiusclient config file is added.
+***********************************************************************/
+static void
+make_username_realm(char *user)
+{
+    char *default_realm;
+
+    if ( user != NULL ) {
+	strlcpy(rstate.user, user, sizeof(rstate.user));
+    }  else {
+	rstate.user[0] = 0;
+    }
+
+    default_realm = rc_conf_str("default_realm");
+
+    if (!strchr(rstate.user, '@') &&
+	default_realm &&
+	(*default_realm != '\0')) {
+	strlcat(rstate.user, "@", sizeof(rstate.user));
+	strlcat(rstate.user, default_realm, sizeof(rstate.user));
+    }
+}
+
+/**********************************************************************
+* %FUNCTION: radius_setparams
+* %ARGUMENTS:
+*  vp -- received value-pairs
+*  msg -- buffer in which to place error message.  Holds up to BUF_LEN chars
+* %RETURNS:
+*  >= 0 on success; -1 on failure
+* %DESCRIPTION:
+*  Parses attributes sent by RADIUS server and sets them in pppd.
+***********************************************************************/
+static int
+radius_setparams(VALUE_PAIR *vp, char *msg, REQUEST_INFO *req_info,
+		 struct chap_digest_type *digest, unsigned char *challenge,
+		 char *message, int message_space)
+{
+    u_int32_t remote;
+    int ms_chap2_success = 0;
+#ifdef MPPE
+    int mppe_enc_keys = 0;	/* whether or not these were received */
+    int mppe_enc_policy = 0;
+    int mppe_enc_types = 0;
+#endif
+
+    /* Send RADIUS attributes to anyone else who might be interested */
+    if (radius_attributes_hook) {
+	(*radius_attributes_hook)(vp);
+    }
+
+    /*
+     * service type (if not framed then quit),
+     * new IP address (RADIUS can define static IP for some users),
+     */
+
+    while (vp) {
+	if (vp->vendorcode == VENDOR_NONE) {
+	    switch (vp->attribute) {
+	    case PW_SERVICE_TYPE:
+		/* check for service type       */
+		/* if not FRAMED then exit      */
+		if (vp->lvalue != PW_FRAMED) {
+		    slprintf(msg, BUF_LEN, "RADIUS: wrong service type %ld for %s",
+			     vp->lvalue, rstate.user);
+		    return -1;
+		}
+		break;
+
+	    case PW_FRAMED_PROTOCOL:
+		/* check for framed protocol type       */
+		/* if not PPP then also exit            */
+		if (vp->lvalue != PW_PPP) {
+		    slprintf(msg, BUF_LEN, "RADIUS: wrong framed protocol %ld for %s",
+			     vp->lvalue, rstate.user);
+		    return -1;
+		}
+		break;
+
+	    case PW_SESSION_TIMEOUT:
+		/* Session timeout */
+		maxconnect = vp->lvalue;
+		break;
+#ifdef MAXOCTETS
+	    case PW_SESSION_OCTETS_LIMIT:
+		/* Session traffic limit */
+		maxoctets = vp->lvalue;
+		break;
+	    case PW_OCTETS_DIRECTION:
+		/* Session traffic limit direction check */
+		maxoctets_dir = ( vp->lvalue > 4 ) ? 0 : vp->lvalue ;
+		break;
+#endif
+	    case PW_ACCT_INTERIM_INTERVAL:
+		/* Send accounting updates every few seconds */
+		rstate.acct_interim_interval = vp->lvalue;
+		/* RFC says it MUST NOT be less than 60 seconds */
+		/* We use "0" to signify not sending updates */
+		if (rstate.acct_interim_interval &&
+		    rstate.acct_interim_interval < 60) {
+		    rstate.acct_interim_interval = 60;
+		}
+		break;
+	    case PW_FRAMED_IP_ADDRESS:
+		/* seting up remote IP addresses */
+		remote = vp->lvalue;
+		if (remote == 0xffffffff) {
+		    /* 0xffffffff means user should be allowed to select one */
+		    rstate.any_ip_addr_ok = 1;
+		} else if (remote != 0xfffffffe) {
+		    /* 0xfffffffe means NAS should select an ip address */
+		    remote = htonl(vp->lvalue);
+		    if (bad_ip_adrs (remote)) {
+			slprintf(msg, BUF_LEN, "RADIUS: bad remote IP address %I for %s",
+				 remote, rstate.user);
+			return -1;
+		    }
+		    rstate.choose_ip = 1;
+		    rstate.ip_addr = remote;
+		}
+		break;
+	    case PW_CLASS:
+		/* Save Class attribute to pass it in accounting request */
+		if (vp->lvalue <= MAXCLASSLEN) {
+		    rstate.class_len=vp->lvalue;
+		    memcpy(rstate.class, vp->strvalue, rstate.class_len);
+		} /* else too big for our buffer - ignore it */
+		break;
+	    }
+
+
+#ifdef CHAPMS
+	} else if (vp->vendorcode == VENDOR_MICROSOFT) {
+	    switch (vp->attribute) {
+	    case PW_MS_CHAP2_SUCCESS:
+		if ((vp->lvalue != 43) || strncmp(vp->strvalue + 1, "S=", 2)) {
+		    slprintf(msg,BUF_LEN,"RADIUS: bad MS-CHAP2-Success packet");
+		    return -1;
+		}
+		if (message != NULL)
+		    strlcpy(message, vp->strvalue + 1, message_space);
+		ms_chap2_success = 1;
+		break;
+
+#ifdef MPPE
+	    case PW_MS_CHAP_MPPE_KEYS:
+		if (radius_setmppekeys(vp, req_info, challenge) < 0) {
+		    slprintf(msg, BUF_LEN,
+			     "RADIUS: bad MS-CHAP-MPPE-Keys attribute");
+		    return -1;
+		}
+		mppe_enc_keys = 1;
+		break;
+
+	    case PW_MS_MPPE_SEND_KEY:
+	    case PW_MS_MPPE_RECV_KEY:
+		if (radius_setmppekeys2(vp, req_info) < 0) {
+		    slprintf(msg, BUF_LEN,
+			     "RADIUS: bad MS-MPPE-%s-Key attribute",
+			     (vp->attribute == PW_MS_MPPE_SEND_KEY)?
+			     "Send": "Recv");
+		    return -1;
+		}
+		mppe_enc_keys = 1;
+		break;
+
+	    case PW_MS_MPPE_ENCRYPTION_POLICY:
+		mppe_enc_policy = vp->lvalue;	/* save for later */
+		break;
+
+	    case PW_MS_MPPE_ENCRYPTION_TYPES:
+		mppe_enc_types = vp->lvalue;	/* save for later */
+		break;
+
+#endif /* MPPE */
+#if 0
+	    case PW_MS_PRIMARY_DNS_SERVER:
+	    case PW_MS_SECONDARY_DNS_SERVER:
+	    case PW_MS_PRIMARY_NBNS_SERVER:
+	    case PW_MS_SECONDARY_NBNS_SERVER:
+		break;
+#endif
+	    }
+#endif /* CHAPMS */
+	}
+	vp = vp->next;
+    }
+
+    /* Require a valid MS-CHAP2-SUCCESS for MS-CHAPv2 auth */
+    if (digest && (digest->code == CHAP_MICROSOFT_V2) && !ms_chap2_success)
+	return -1;
+
+#ifdef MPPE
+    /*
+     * Require both policy and key attributes to indicate a valid key.
+     * Note that if the policy value was '0' we don't set the key!
+     */
+    if (mppe_enc_policy && mppe_enc_keys) {
+	mppe_keys_set = 1;
+	/* Set/modify allowed encryption types. */
+	if (mppe_enc_types)
+	    set_mppe_enc_types(mppe_enc_policy, mppe_enc_types);
+    }
+#endif
+
+    return 0;
+}
+
+#ifdef MPPE
+/**********************************************************************
+* %FUNCTION: radius_setmppekeys
+* %ARGUMENTS:
+*  vp -- value pair holding MS-CHAP-MPPE-KEYS attribute
+*  req_info -- radius request information used for encryption
+* %RETURNS:
+*  >= 0 on success; -1 on failure
+* %DESCRIPTION:
+*  Decrypt the "key" provided by the RADIUS server for MPPE encryption.
+*  See RFC 2548.
+***********************************************************************/
+static int
+radius_setmppekeys(VALUE_PAIR *vp, REQUEST_INFO *req_info,
+		   unsigned char *challenge)
+{
+    int i;
+    MD5_CTX Context;
+    u_char  plain[32];
+    u_char  buf[16];
+
+    if (vp->lvalue != 32) {
+	error("RADIUS: Incorrect attribute length (%d) for MS-CHAP-MPPE-Keys",
+	      vp->lvalue);
+	return -1;
+    }
+
+    memcpy(plain, vp->strvalue, sizeof(plain));
+
+    MD5_Init(&Context);
+    MD5_Update(&Context, req_info->secret, strlen(req_info->secret));
+    MD5_Update(&Context, req_info->request_vector, AUTH_VECTOR_LEN);
+    MD5_Final(buf, &Context);
+
+    for (i = 0; i < 16; i++)
+	plain[i] ^= buf[i];
+
+    MD5_Init(&Context);
+    MD5_Update(&Context, req_info->secret, strlen(req_info->secret));
+    MD5_Update(&Context, vp->strvalue, 16);
+    MD5_Final(buf, &Context);
+
+    for(i = 0; i < 16; i++)
+	plain[i + 16] ^= buf[i];
+
+    /*
+     * Annoying.  The "key" returned is just the NTPasswordHashHash, which
+     * the NAS (us) doesn't need; we only need the start key.  So we have
+     * to generate the start key, sigh.  NB: We do not support the LM-Key.
+     */
+    mppe_set_keys(challenge, &plain[8]);
+
+    return 0;    
+}
+
+/**********************************************************************
+* %FUNCTION: radius_setmppekeys2
+* %ARGUMENTS:
+*  vp -- value pair holding MS-MPPE-SEND-KEY or MS-MPPE-RECV-KEY attribute
+*  req_info -- radius request information used for encryption
+* %RETURNS:
+*  >= 0 on success; -1 on failure
+* %DESCRIPTION:
+*  Decrypt the key provided by the RADIUS server for MPPE encryption.
+*  See RFC 2548.
+***********************************************************************/
+static int
+radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info)
+{
+    int i;
+    MD5_CTX Context;
+    u_char  *salt = vp->strvalue;
+    u_char  *crypt = vp->strvalue + 2;
+    u_char  plain[32];
+    u_char  buf[MD5_HASH_SIZE];
+    char    *type = "Send";
+
+    if (vp->attribute == PW_MS_MPPE_RECV_KEY)
+	type = "Recv";
+
+    if (vp->lvalue != 34) {
+	error("RADIUS: Incorrect attribute length (%d) for MS-MPPE-%s-Key",
+	      vp->lvalue, type);
+	return -1;
+    }
+
+    if ((salt[0] & 0x80) == 0) {
+	error("RADIUS: Illegal salt value for MS-MPPE-%s-Key attribute", type);
+	return -1;
+    }
+
+    memcpy(plain, crypt, 32);
+
+    MD5_Init(&Context);
+    MD5_Update(&Context, req_info->secret, strlen(req_info->secret));
+    MD5_Update(&Context, req_info->request_vector, AUTH_VECTOR_LEN);
+    MD5_Update(&Context, salt, 2);
+    MD5_Final(buf, &Context);
+
+    for (i = 0; i < 16; i++)
+	plain[i] ^= buf[i];
+
+    if (plain[0] != sizeof(mppe_send_key) /* 16 */) {
+	error("RADIUS: Incorrect key length (%d) for MS-MPPE-%s-Key attribute",
+	      (int) plain[0], type);
+	return -1;
+    }
+
+    MD5_Init(&Context);
+    MD5_Update(&Context, req_info->secret, strlen(req_info->secret));
+    MD5_Update(&Context, crypt, 16);
+    MD5_Final(buf, &Context);
+
+    plain[16] ^= buf[0]; /* only need the first byte */
+
+    if (vp->attribute == PW_MS_MPPE_SEND_KEY)
+	memcpy(mppe_send_key, plain + 1, 16);
+    else
+	memcpy(mppe_recv_key, plain + 1, 16);
+
+    return 0;
+}
+#endif /* MPPE */
+
+/**********************************************************************
+* %FUNCTION: radius_acct_start
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Sends a "start" accounting message to the RADIUS server.
+***********************************************************************/
+static void
+radius_acct_start(void)
+{
+    UINT4 av_type;
+    int result;
+    VALUE_PAIR *send = NULL;
+    ipcp_options *ho = &ipcp_hisoptions[0];
+    u_int32_t hisaddr;
+
+    if (!rstate.initialized) {
+	return;
+    }
+
+    rstate.start_time = time(NULL);
+
+    strncpy(rstate.session_id, rc_mksid(), sizeof(rstate.session_id));
+
+    rc_avpair_add(&send, PW_ACCT_SESSION_ID,
+		   rstate.session_id, 0, VENDOR_NONE);
+    rc_avpair_add(&send, PW_USER_NAME,
+		   rstate.user, 0, VENDOR_NONE);
+
+    if (rstate.class_len > 0)
+	rc_avpair_add(&send, PW_CLASS,
+		      rstate.class, rstate.class_len, VENDOR_NONE);
+
+    av_type = PW_STATUS_START;
+    rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_FRAMED;
+    rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_PPP;
+    rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
+
+    if (*remote_number) {
+	rc_avpair_add(&send, PW_CALLING_STATION_ID,
+		       remote_number, 0, VENDOR_NONE);
+    } else if (ipparam)
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE);
+
+    av_type = PW_RADIUS;
+    rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE);
+
+
+    av_type = ( using_pty ? PW_VIRTUAL : ( sync_serial ? PW_SYNC : PW_ASYNC ) );
+    rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE);
+
+    hisaddr = ho->hisaddr;
+    av_type = htonl(hisaddr);
+    rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
+
+    /* Add user specified vp's */
+    if (rstate.avp)
+	rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp));
+
+    if (rstate.acctserver) {
+	result = rc_acct_using_server(rstate.acctserver,
+				      rstate.client_port, send);
+    } else {
+	result = rc_acct(rstate.client_port, send);
+    }
+
+    rc_avpair_free(send);
+
+    if (result != OK_RC) {
+	/* RADIUS server could be down so make this a warning */
+	syslog(LOG_WARNING,
+		"Accounting START failed for %s", rstate.user);
+    } else {
+	rstate.accounting_started = 1;
+	/* Kick off periodic accounting reports */
+	if (rstate.acct_interim_interval) {
+	    TIMEOUT(radius_acct_interim, NULL, rstate.acct_interim_interval);
+	}
+    }
+}
+
+/**********************************************************************
+* %FUNCTION: radius_acct_stop
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Sends a "stop" accounting message to the RADIUS server.
+***********************************************************************/
+static void
+radius_acct_stop(void)
+{
+    UINT4 av_type;
+    VALUE_PAIR *send = NULL;
+    ipcp_options *ho = &ipcp_hisoptions[0];
+    u_int32_t hisaddr;
+    int result;
+
+    if (!rstate.initialized) {
+	return;
+    }
+
+    if (!rstate.accounting_started) {
+	return;
+    }
+
+    rstate.accounting_started = 0;
+    rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id,
+		   0, VENDOR_NONE);
+
+    rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE);
+
+    av_type = PW_STATUS_STOP;
+    rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_FRAMED;
+    rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_PPP;
+    rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_RADIUS;
+    rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE);
+
+
+    if (link_stats_valid) {
+	av_type = link_connect_time;
+	rc_avpair_add(&send, PW_ACCT_SESSION_TIME, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.bytes_out;
+	rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.bytes_in;
+	rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.pkts_out;
+	rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.pkts_in;
+	rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0, VENDOR_NONE);
+    }
+
+    if (*remote_number) {
+	rc_avpair_add(&send, PW_CALLING_STATION_ID,
+		       remote_number, 0, VENDOR_NONE);
+    } else if (ipparam)
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE);
+
+    av_type = ( using_pty ? PW_VIRTUAL : ( sync_serial ? PW_SYNC : PW_ASYNC ) );
+    rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_NAS_ERROR;
+    switch( status ) {
+	case EXIT_OK:
+	case EXIT_USER_REQUEST:
+	    av_type = PW_USER_REQUEST;
+	    break;
+
+	case EXIT_HANGUP:
+	case EXIT_PEER_DEAD:
+	case EXIT_CONNECT_FAILED:
+	    av_type = PW_LOST_CARRIER;
+	    break;
+
+	case EXIT_INIT_FAILED:
+	case EXIT_OPEN_FAILED:
+	case EXIT_LOCK_FAILED:
+	case EXIT_PTYCMD_FAILED:
+	    av_type = PW_PORT_ERROR;
+	    break;
+
+	case EXIT_PEER_AUTH_FAILED:
+	case EXIT_AUTH_TOPEER_FAILED:
+	case EXIT_NEGOTIATION_FAILED:
+	case EXIT_CNID_AUTH_FAILED:
+	    av_type = PW_SERVICE_UNAVAILABLE;
+	    break;
+
+	case EXIT_IDLE_TIMEOUT:
+	    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;
+	    
+#ifdef MAXOCTETS
+	case EXIT_TRAFFIC_LIMIT:
+	    av_type = PW_NAS_REQUEST;
+	    break;
+#endif
+
+	default:
+	    av_type = PW_NAS_ERROR;
+	    break;
+    }
+    rc_avpair_add(&send, PW_ACCT_TERMINATE_CAUSE, &av_type, 0, VENDOR_NONE);
+
+    hisaddr = ho->hisaddr;
+    av_type = htonl(hisaddr);
+    rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
+
+    /* Add user specified vp's */
+    if (rstate.avp)
+	rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp));
+
+    if (rstate.acctserver) {
+	result = rc_acct_using_server(rstate.acctserver,
+				      rstate.client_port, send);
+    } else {
+	result = rc_acct(rstate.client_port, send);
+    }
+
+    if (result != OK_RC) {
+	/* RADIUS server could be down so make this a warning */
+	syslog(LOG_WARNING,
+		"Accounting STOP failed for %s", rstate.user);
+    }
+    rc_avpair_free(send);
+}
+
+/**********************************************************************
+* %FUNCTION: radius_acct_interim
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Sends an interim accounting message to the RADIUS server
+***********************************************************************/
+static void
+radius_acct_interim(void *ignored)
+{
+    UINT4 av_type;
+    VALUE_PAIR *send = NULL;
+    ipcp_options *ho = &ipcp_hisoptions[0];
+    u_int32_t hisaddr;
+    int result;
+
+    if (!rstate.initialized) {
+	return;
+    }
+
+    if (!rstate.accounting_started) {
+	return;
+    }
+
+    rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id,
+		   0, VENDOR_NONE);
+
+    rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE);
+
+    av_type = PW_STATUS_ALIVE;
+    rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_FRAMED;
+    rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_PPP;
+    rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_RADIUS;
+    rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE);
+
+    /* Update link stats */
+    update_link_stats(0);
+
+    if (link_stats_valid) {
+	link_stats_valid = 0; /* Force later code to update */
+
+	av_type = link_connect_time;
+	rc_avpair_add(&send, PW_ACCT_SESSION_TIME, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.bytes_out;
+	rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.bytes_in;
+	rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.pkts_out;
+	rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0, VENDOR_NONE);
+
+	av_type = link_stats.pkts_in;
+	rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0, VENDOR_NONE);
+    }
+
+    if (*remote_number) {
+	rc_avpair_add(&send, PW_CALLING_STATION_ID,
+		       remote_number, 0, VENDOR_NONE);
+    } else if (ipparam)
+	rc_avpair_add(&send, PW_CALLING_STATION_ID, ipparam, 0, VENDOR_NONE);
+
+    av_type = ( using_pty ? PW_VIRTUAL : ( sync_serial ? PW_SYNC : PW_ASYNC ) );
+    rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE);
+
+    hisaddr = ho->hisaddr;
+    av_type = htonl(hisaddr);
+    rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
+
+    /* Add user specified vp's */
+    if (rstate.avp)
+	rc_avpair_insert(&send, NULL, rc_avpair_copy(rstate.avp));
+
+    if (rstate.acctserver) {
+	result = rc_acct_using_server(rstate.acctserver,
+				      rstate.client_port, send);
+    } else {
+	result = rc_acct(rstate.client_port, send);
+    }
+
+    if (result != OK_RC) {
+	/* RADIUS server could be down so make this a warning */
+	syslog(LOG_WARNING,
+		"Interim accounting failed for %s", rstate.user);
+    }
+    rc_avpair_free(send);
+
+    /* Schedule another one */
+    TIMEOUT(radius_acct_interim, NULL, rstate.acct_interim_interval);
+}
+
+/**********************************************************************
+* %FUNCTION: radius_ip_up
+* %ARGUMENTS:
+*  opaque -- ignored
+*  arg -- ignored
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Called when IPCP is up.  We'll do a start-accounting record.
+***********************************************************************/
+static void
+radius_ip_up(void *opaque, int arg)
+{
+    radius_acct_start();
+}
+
+/**********************************************************************
+* %FUNCTION: radius_ip_down
+* %ARGUMENTS:
+*  opaque -- ignored
+*  arg -- ignored
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Called when IPCP is down.  We'll do a stop-accounting record.
+***********************************************************************/
+static void
+radius_ip_down(void *opaque, int arg)
+{
+    radius_acct_stop();
+}
+
+/**********************************************************************
+* %FUNCTION: radius_init
+* %ARGUMENTS:
+*  msg -- buffer of size BUF_LEN for error message
+* %RETURNS:
+*  negative on failure; non-negative on success
+* %DESCRIPTION:
+*  Initializes radiusclient library
+***********************************************************************/
+static int
+radius_init(char *msg)
+{
+    if (rstate.initialized) {
+	return 0;
+    }
+
+    if (config_file && *config_file) {
+	strlcpy(rstate.config_file, config_file, MAXPATHLEN-1);
+    }
+
+    rstate.initialized = 1;
+
+    if (rc_read_config(rstate.config_file) != 0) {
+	slprintf(msg, BUF_LEN, "RADIUS: Can't read config file %s",
+		 rstate.config_file);
+	return -1;
+    }
+
+    if (rc_read_dictionary(rc_conf_str("dictionary")) != 0) {
+	slprintf(msg, BUF_LEN, "RADIUS: Can't read dictionary file %s",
+		 rc_conf_str("dictionary"));
+	return -1;
+    }
+
+    if (rc_read_mapfile(rc_conf_str("mapfile")) != 0)	{
+	slprintf(msg, BUF_LEN, "RADIUS: Can't read map file %s",
+		 rc_conf_str("mapfile"));
+	return -1;
+    }
+
+    /* Add av pairs saved during option parsing */
+    while (avpopt) {
+	struct avpopt *n = avpopt->next;
+
+	rc_avpair_parse(avpopt->vpstr, &rstate.avp);
+	free(avpopt->vpstr);
+	free(avpopt);
+	avpopt = n;
+    }
+    return 0;
+}
+
+/**********************************************************************
+* %FUNCTION: get_client_port
+* %ARGUMENTS:
+*  ifname -- PPP interface name (e.g. "ppp7")
+* %RETURNS:
+*  The NAS port number (e.g. 7)
+* %DESCRIPTION:
+*  Extracts the port number from the interface name
+***********************************************************************/
+static int
+get_client_port(char *ifname)
+{
+    int port;
+    if (sscanf(ifname, "ppp%d", &port) == 1) {
+	return port;
+    }
+    return rc_map2id(ifname);
+}
+
+/**********************************************************************
+* %FUNCTION: radius_allowed_address
+* %ARGUMENTS:
+*  addr -- IP address
+* %RETURNS:
+*  1 if we're allowed to use that IP address; 0 if not; -1 if we do
+*  not know.
+***********************************************************************/
+static int
+radius_allowed_address(u_int32_t addr)
+{
+    ipcp_options *wo = &ipcp_wantoptions[0];
+
+    if (!rstate.choose_ip) {
+	/* If RADIUS server said any address is OK, then fine... */
+	if (rstate.any_ip_addr_ok) {
+	    return 1;
+	}
+
+	/* Sigh... if an address was supplied for remote host in pppd
+	   options, it has to match that.  */
+	if (wo->hisaddr != 0 && wo->hisaddr == addr) {
+	    return 1;
+	}
+
+	return 0;
+    }
+    if (addr == rstate.ip_addr) return 1;
+    return 0;
+}
+
+/* Useful for other plugins */
+char *radius_logged_in_user(void)
+{
+    return rstate.user;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/radiusclient.h tomato121/release/src/router/pppd/pppd/plugins/radius/radiusclient.h
--- tomato/release/src/router/pppd/pppd/plugins/radius/radiusclient.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/radiusclient.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,455 @@
+/*
+ * $Id: radiusclient.h,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#ifndef RADIUSCLIENT_H
+#define RADIUSCLIENT_H
+
+#include	<sys/types.h>
+#include	<stdio.h>
+#include	<time.h>
+#include "pppd.h"
+
+#ifndef _UINT4_T
+/* This works for all machines that Linux runs on... */
+typedef unsigned int UINT4;
+typedef int          INT4;
+#endif
+
+#define AUTH_VECTOR_LEN		16
+#define AUTH_PASS_LEN		(3 * 16) /* multiple of 16 */
+#define AUTH_ID_LEN		64
+#define AUTH_STRING_LEN		128	 /* maximum of 253 */
+
+#define	BUFFER_LEN		8192
+
+#define NAME_LENGTH		32
+#define	GETSTR_LENGTH		128	/* must be bigger than AUTH_PASS_LEN */
+
+/* codes for radius_buildreq, radius_getport, etc. */
+#define AUTH			0
+#define ACCT			1
+
+/* defines for config.c */
+
+#define SERVER_MAX 8
+
+#define AUTH_LOCAL_FST	(1<<0)
+#define AUTH_RADIUS_FST (1<<1)
+#define AUTH_LOCAL_SND  (1<<2)
+#define AUTH_RADIUS_SND (1<<3)
+
+typedef struct server {
+	int max;
+	char *name[SERVER_MAX];
+	unsigned short port[SERVER_MAX];
+} SERVER;
+
+typedef struct pw_auth_hdr
+{
+	u_char          code;
+	u_char          id;
+	u_short         length;
+	u_char          vector[AUTH_VECTOR_LEN];
+	u_char          data[2];
+} AUTH_HDR;
+
+#define AUTH_HDR_LEN			20
+#define MAX_SECRET_LENGTH		(3 * 16) /* MUST be multiple of 16 */
+#define CHAP_VALUE_LENGTH		16
+
+#define PW_AUTH_UDP_PORT		1812
+#define PW_ACCT_UDP_PORT		1813
+
+#define PW_TYPE_STRING			0
+#define PW_TYPE_INTEGER			1
+#define PW_TYPE_IPADDR			2
+#define PW_TYPE_DATE			3
+
+/* standard RADIUS codes */
+
+#define	PW_ACCESS_REQUEST		1
+#define	PW_ACCESS_ACCEPT		2
+#define	PW_ACCESS_REJECT		3
+#define	PW_ACCOUNTING_REQUEST		4
+#define	PW_ACCOUNTING_RESPONSE		5
+#define	PW_ACCOUNTING_STATUS		6
+#define	PW_PASSWORD_REQUEST		7
+#define	PW_PASSWORD_ACK			8
+#define	PW_PASSWORD_REJECT		9
+#define	PW_ACCOUNTING_MESSAGE		10
+#define	PW_ACCESS_CHALLENGE		11
+#define	PW_STATUS_SERVER		12
+#define	PW_STATUS_CLIENT		13
+
+
+/* standard RADIUS attribute-value pairs */
+
+#define	PW_USER_NAME			1	/* string */
+#define	PW_USER_PASSWORD		2	/* string */
+#define	PW_CHAP_PASSWORD		3	/* string */
+#define	PW_NAS_IP_ADDRESS		4	/* ipaddr */
+#define	PW_NAS_PORT			5	/* integer */
+#define	PW_SERVICE_TYPE			6	/* integer */
+#define	PW_FRAMED_PROTOCOL		7	/* integer */
+#define	PW_FRAMED_IP_ADDRESS		8	/* ipaddr */
+#define	PW_FRAMED_IP_NETMASK		9	/* ipaddr */
+#define	PW_FRAMED_ROUTING		10	/* integer */
+#define	PW_FILTER_ID		        11	/* string */
+#define	PW_FRAMED_MTU			12	/* integer */
+#define	PW_FRAMED_COMPRESSION		13	/* integer */
+#define	PW_LOGIN_IP_HOST		14	/* ipaddr */
+#define	PW_LOGIN_SERVICE		15	/* integer */
+#define	PW_LOGIN_PORT			16	/* integer */
+#define	PW_OLD_PASSWORD			17	/* string */ /* deprecated */
+#define	PW_REPLY_MESSAGE		18	/* string */
+#define	PW_LOGIN_CALLBACK_NUMBER	19	/* string */
+#define	PW_FRAMED_CALLBACK_ID		20	/* string */
+#define	PW_EXPIRATION			21	/* date */ /* deprecated */
+#define	PW_FRAMED_ROUTE			22	/* string */
+#define	PW_FRAMED_IPX_NETWORK		23	/* integer */
+#define	PW_STATE			24	/* string */
+#define	PW_CLASS			25	/* string */
+#define	PW_VENDOR_SPECIFIC		26	/* string */
+#define	PW_SESSION_TIMEOUT		27	/* integer */
+#define	PW_IDLE_TIMEOUT			28	/* integer */
+#define	PW_TERMINATION_ACTION		29	/* integer */
+#define	PW_CALLED_STATION_ID            30      /* string */
+#define	PW_CALLING_STATION_ID           31      /* string */
+#define	PW_NAS_IDENTIFIER		32	/* string */
+#define	PW_PROXY_STATE			33	/* string */
+#define	PW_LOGIN_LAT_SERVICE		34	/* string */
+#define	PW_LOGIN_LAT_NODE		35	/* string */
+#define	PW_LOGIN_LAT_GROUP		36	/* string */
+#define	PW_FRAMED_APPLETALK_LINK	37	/* integer */
+#define	PW_FRAMED_APPLETALK_NETWORK	38	/* integer */
+#define	PW_FRAMED_APPLETALK_ZONE	39	/* string */
+#define	PW_CHAP_CHALLENGE               60      /* string */
+#define	PW_NAS_PORT_TYPE                61      /* integer */
+#define	PW_PORT_LIMIT                   62      /* integer */
+#define PW_LOGIN_LAT_PORT               63      /* string */
+
+/* Vendor RADIUS attribute-value pairs */
+#define PW_MS_CHAP_CHALLENGE		11	/* string */
+#define PW_MS_CHAP_RESPONSE		1	/* string */
+#define PW_MS_CHAP2_RESPONSE		25	/* string */
+#define PW_MS_CHAP2_SUCCESS		26	/* string */
+#define PW_MS_MPPE_ENCRYPTION_POLICY	7	/* string */
+#define PW_MS_MPPE_ENCRYPTION_TYPE	8	/* string */
+#define PW_MS_MPPE_ENCRYPTION_TYPES PW_MS_MPPE_ENCRYPTION_TYPE
+#define PW_MS_CHAP_MPPE_KEYS		12	/* string */
+#define PW_MS_MPPE_SEND_KEY		16	/* string */
+#define PW_MS_MPPE_RECV_KEY		17	/* string */
+
+/*	Accounting */
+
+#define	PW_ACCT_STATUS_TYPE		40	/* integer */
+#define	PW_ACCT_DELAY_TIME		41	/* integer */
+#define	PW_ACCT_INPUT_OCTETS		42	/* integer */
+#define	PW_ACCT_OUTPUT_OCTETS		43	/* integer */
+#define	PW_ACCT_SESSION_ID		44	/* string */
+#define	PW_ACCT_AUTHENTIC		45	/* integer */
+#define	PW_ACCT_SESSION_TIME		46	/* integer */
+#define	PW_ACCT_INPUT_PACKETS		47	/* integer */
+#define	PW_ACCT_OUTPUT_PACKETS		48	/* integer */
+#define PW_ACCT_TERMINATE_CAUSE		49	/* integer */
+#define PW_ACCT_MULTI_SESSION_ID	50	/* string */
+#define PW_ACCT_LINK_COUNT		51	/* integer */
+
+/* From RFC 2869 */
+#define PW_ACCT_INTERIM_INTERVAL        85	/* integer */
+
+/*	Merit Experimental Extensions */
+
+#define PW_USER_ID                      222     /* string */
+#define PW_USER_REALM                   223     /* string */
+
+
+/*      Session limits */
+#define PW_SESSION_OCTETS_LIMIT		227    /* integer */
+#define PW_OCTETS_DIRECTION		228    /* integer */
+
+/*	Integer Translations */
+
+/*	SERVICE TYPES	*/
+
+#define	PW_LOGIN			1
+#define	PW_FRAMED			2
+#define	PW_CALLBACK_LOGIN		3
+#define	PW_CALLBACK_FRAMED		4
+#define	PW_OUTBOUND			5
+#define	PW_ADMINISTRATIVE		6
+#define PW_NAS_PROMPT                   7
+#define PW_AUTHENTICATE_ONLY		8
+#define PW_CALLBACK_NAS_PROMPT          9
+
+/*	FRAMED PROTOCOLS	*/
+
+#define	PW_PPP				1
+#define	PW_SLIP				2
+#define PW_ARA                          3
+#define PW_GANDALF                      4
+#define PW_XYLOGICS                     5
+
+/*	FRAMED ROUTING VALUES	*/
+
+#define	PW_NONE				0
+#define	PW_BROADCAST			1
+#define	PW_LISTEN			2
+#define	PW_BROADCAST_LISTEN		3
+
+/*	FRAMED COMPRESSION TYPES	*/
+
+#define	PW_VAN_JACOBSON_TCP_IP		1
+#define	PW_IPX_HEADER_COMPRESSION	2
+
+/*	LOGIN SERVICES	*/
+
+#define PW_TELNET                       0
+#define PW_RLOGIN                       1
+#define PW_TCP_CLEAR                    2
+#define PW_PORTMASTER                   3
+#define PW_LAT                          4
+#define PW_X25_PAD                      5
+#define PW_X25_T3POS                    6
+
+/*	TERMINATION ACTIONS	*/
+
+#define	PW_DEFAULT			0
+#define	PW_RADIUS_REQUEST		1
+
+/*	PROHIBIT PROTOCOL  */
+
+#define PW_DUMB		0	/* 1 and 2 are defined in FRAMED PROTOCOLS */
+#define PW_AUTH_ONLY	3
+#define PW_ALL		255
+
+/*	ACCOUNTING STATUS TYPES    */
+
+#define PW_STATUS_START		1
+#define PW_STATUS_STOP		2
+#define PW_STATUS_ALIVE		3
+#define PW_STATUS_MODEM_START	4
+#define PW_STATUS_MODEM_STOP	5
+#define PW_STATUS_CANCEL	6
+#define PW_ACCOUNTING_ON	7
+#define PW_ACCOUNTING_OFF	8
+
+/*      ACCOUNTING TERMINATION CAUSES   */
+
+#define PW_USER_REQUEST         1
+#define PW_LOST_CARRIER         2
+#define PW_LOST_SERVICE         3
+#define PW_ACCT_IDLE_TIMEOUT    4
+#define PW_ACCT_SESSION_TIMEOUT 5
+#define PW_ADMIN_RESET          6
+#define PW_ADMIN_REBOOT         7
+#define PW_PORT_ERROR           8
+#define PW_NAS_ERROR            9
+#define PW_NAS_REQUEST          10
+#define PW_NAS_REBOOT           11
+#define PW_PORT_UNNEEDED        12
+#define PW_PORT_PREEMPTED       13
+#define PW_PORT_SUSPENDED       14
+#define PW_SERVICE_UNAVAILABLE  15
+#define PW_CALLBACK             16
+#define PW_USER_ERROR           17
+#define PW_HOST_REQUEST         18
+
+/*     NAS PORT TYPES    */
+
+#define PW_ASYNC		0
+#define PW_SYNC			1
+#define PW_ISDN_SYNC		2
+#define PW_ISDN_SYNC_V120	3
+#define PW_ISDN_SYNC_V110	4
+#define PW_VIRTUAL		5
+
+/*	   AUTHENTIC TYPES */
+#define PW_RADIUS	1
+#define PW_LOCAL	2
+#define PW_REMOTE	3
+
+/*    Session-Octets-Limit    */
+#define PW_OCTETS_DIRECTION_SUM	0
+#define PW_OCTETS_DIRECTION_IN	1
+#define PW_OCTETS_DIRECTION_OUT	2
+#define PW_OCTETS_DIRECTION_MAX	3
+
+
+/* Vendor codes */
+#define VENDOR_NONE     (-1)
+#define VENDOR_MICROSOFT	311
+
+/* Server data structures */
+
+typedef struct dict_attr
+{
+	char              name[NAME_LENGTH + 1];	/* attribute name */
+	int               value;			/* attribute index */
+	int               type;				/* string, int, etc. */
+	int               vendorcode;                   /* vendor code */
+	struct dict_attr *next;
+} DICT_ATTR;
+
+typedef struct dict_value
+{
+	char               attrname[NAME_LENGTH +1];
+	char               name[NAME_LENGTH + 1];
+	int                value;
+	struct dict_value *next;
+} DICT_VALUE;
+
+typedef struct vendor_dict
+{
+    char vendorname[NAME_LENGTH + 1];
+    int vendorcode;
+    DICT_ATTR *attributes;
+    struct vendor_dict *next;
+} VENDOR_DICT;
+
+typedef struct value_pair
+{
+	char               name[NAME_LENGTH + 1];
+	int                attribute;
+	int                vendorcode;
+	int                type;
+	UINT4              lvalue;
+	u_char             strvalue[AUTH_STRING_LEN + 1];
+	struct value_pair *next;
+} VALUE_PAIR;
+
+/* don't change this, as it has to be the same as in the Merit radiusd code */
+#define MGMT_POLL_SECRET	"Hardlyasecret"
+
+/*	Define return codes from "SendServer" utility */
+
+#define BADRESP_RC	-2
+#define ERROR_RC	-1
+#define OK_RC		0
+#define TIMEOUT_RC	1
+
+typedef struct send_data /* Used to pass information to sendserver() function */
+{
+	u_char          code;		/* RADIUS packet code */
+	u_char          seq_nbr;	/* Packet sequence number */
+	char           *server;		/* Name/addrress of RADIUS server */
+	int             svc_port;	/* RADIUS protocol destination port */
+	int             timeout;	/* Session timeout in seconds */
+	int		retries;
+	VALUE_PAIR     *send_pairs;     /* More a/v pairs to send */
+	VALUE_PAIR     *receive_pairs;  /* Where to place received a/v pairs */
+} SEND_DATA;
+
+typedef struct request_info
+{
+	char		secret[MAX_SECRET_LENGTH + 1];
+	u_char		request_vector[AUTH_VECTOR_LEN];
+} REQUEST_INFO;
+
+#ifndef MIN
+#define MIN(a, b)     ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b)     ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX	1024
+#endif
+
+typedef struct env
+{
+	int maxsize, size;
+	char **env;
+} ENV;
+
+#define ENV_SIZE	128
+
+/*	Function prototypes	*/
+
+/*	avpair.c		*/
+
+VALUE_PAIR *rc_avpair_add __P((VALUE_PAIR **, int, void *, int, int));
+int rc_avpair_assign __P((VALUE_PAIR *, void *, int));
+VALUE_PAIR *rc_avpair_new __P((int, void *, int, int));
+VALUE_PAIR *rc_avpair_gen __P((AUTH_HDR *));
+VALUE_PAIR *rc_avpair_get __P((VALUE_PAIR *, UINT4));
+VALUE_PAIR *rc_avpair_copy __P((VALUE_PAIR *));
+void rc_avpair_insert __P((VALUE_PAIR **, VALUE_PAIR *, VALUE_PAIR *));
+void rc_avpair_free __P((VALUE_PAIR *));
+int rc_avpair_parse __P((char *, VALUE_PAIR **));
+int rc_avpair_tostr __P((VALUE_PAIR *, char *, int, char *, int));
+VALUE_PAIR *rc_avpair_readin __P((FILE *));
+
+/*	buildreq.c		*/
+
+void rc_buildreq __P((SEND_DATA *, int, char *, unsigned short, int, int));
+unsigned char rc_get_seqnbr __P((void));
+int rc_auth __P((UINT4, VALUE_PAIR *, VALUE_PAIR **, char *, REQUEST_INFO *));
+int rc_auth_using_server __P((SERVER *, UINT4, VALUE_PAIR *, VALUE_PAIR **,
+			      char *, REQUEST_INFO *));
+int rc_auth_proxy __P((VALUE_PAIR *, VALUE_PAIR **, char *));
+int rc_acct __P((UINT4, VALUE_PAIR *));
+int rc_acct_using_server __P((SERVER *, UINT4, VALUE_PAIR *));
+int rc_acct_proxy __P((VALUE_PAIR *));
+int rc_check __P((char *, unsigned short, char *));
+
+/*	clientid.c		*/
+
+int rc_read_mapfile __P((char *));
+UINT4 rc_map2id __P((char *));
+
+/*	config.c		*/
+
+int rc_read_config __P((char *));
+char *rc_conf_str __P((char *));
+int rc_conf_int __P((char *));
+SERVER *rc_conf_srv __P((char *));
+int rc_find_server __P((char *, UINT4 *, char *));
+
+/*	dict.c			*/
+
+int rc_read_dictionary __P((char *));
+DICT_ATTR *rc_dict_getattr __P((int, int));
+DICT_ATTR *rc_dict_findattr __P((char *));
+DICT_VALUE *rc_dict_findval __P((char *));
+DICT_VALUE * rc_dict_getval __P((UINT4, char *));
+VENDOR_DICT * rc_dict_findvendor __P((char *));
+VENDOR_DICT * rc_dict_getvendor __P((int));
+
+/*	ip_util.c		*/
+
+UINT4 rc_get_ipaddr __P((char *));
+int rc_good_ipaddr __P((char *));
+const char *rc_ip_hostname __P((UINT4));
+UINT4 rc_own_ipaddress __P((void));
+
+
+/*	sendserver.c		*/
+
+int rc_send_server __P((SEND_DATA *, char *, REQUEST_INFO *));
+
+/*	util.c			*/
+
+void rc_str2tm __P((char *, struct tm *));
+char *rc_mksid __P((void));
+void rc_mdelay __P((int));
+
+/* md5.c			*/
+
+void rc_md5_calc __P((unsigned char *, unsigned char *, unsigned int));
+
+#endif /* RADIUSCLIENT_H */
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/radrealms.c tomato121/release/src/router/pppd/pppd/plugins/radius/radrealms.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/radrealms.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/radrealms.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,147 @@
+/*
+*
+* radrealms.c
+*
+* A pppd plugin which is stacked on top of radius.so.  This plugin
+* allows selection of alternate set of servers based on the user's realm.
+*
+* Author: Ben McKeegan  ben@netservers.co.uk
+*
+* Copyright (C) 2002 Netservers
+*
+* This plugin may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+*/
+
+static char const RCSID[] =
+    "$Id: radrealms.c,v 1.2 2004/11/14 07:26:26 paulus Exp $";
+
+#include "pppd.h"
+#include "radiusclient.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+char pppd_version[] = VERSION;
+
+char radrealms_config[MAXPATHLEN] = "/etc/radiusclient/realms";
+
+static option_t Options[] = {
+    { "realms-config-file", o_string, &radrealms_config },
+    { NULL }
+};
+
+extern void (*radius_pre_auth_hook)(char const *user,
+				    SERVER **authserver,
+				    SERVER **acctserver);
+
+static void
+lookup_realm(char const *user,
+	     SERVER **authserver,
+	     SERVER **acctserver)
+{
+    char *realm;
+    FILE *fd;
+    SERVER *accts, *auths, *s;
+    char buffer[512], *p;
+    int line = 0;
+    
+    auths = (SERVER *) malloc(sizeof(SERVER));
+    auths->max = 0;
+    accts = (SERVER *) malloc(sizeof(SERVER));
+    accts->max = 0;
+    
+    realm = strrchr(user, '@');
+    
+    if (realm) {
+	info("Looking up servers for realm '%s'", realm);
+    } else {
+	info("Looking up servers for DEFAULT realm");
+    }
+    if (realm) {
+	if (*(++realm) == '\0') {
+	    realm = NULL;
+	}
+    }
+    
+    if ((fd = fopen(radrealms_config, "r")) == NULL) {
+	option_error("cannot open %s", radrealms_config);
+	return;
+    } 
+    info("Reading %s", radrealms_config);
+    
+    while ((fgets(buffer, sizeof(buffer), fd) != NULL)) {
+	line++;
+
+	if ((*buffer == '\n') || (*buffer == '#') || (*buffer == '\0'))
+	    continue;
+
+	buffer[strlen(buffer)-1] = '\0';
+
+	p = strtok(buffer, "\t ");
+
+	if (p == NULL || (strcmp(p, "authserver") !=0
+	    && strcmp(p, "acctserver"))) {
+	    fclose(fd);
+	    option_error("%s: invalid line %d: %s", radrealms_config,
+			 line, buffer);
+	    return;
+	}
+	info("Parsing '%s' entry:", p);
+	s = auths;
+	if (p[1] == 'c') {
+	    s = accts;
+	}
+	if (s->max >= SERVER_MAX)
+	    continue;
+
+	if ((p = strtok(NULL, "\t ")) == NULL) {
+	    fclose(fd);
+	    option_error("%s: realm name missing on line %d: %s",
+			 radrealms_config, line, buffer);
+	    return;
+	}
+
+	if ((realm != NULL && strcmp(p, realm) == 0) ||
+	    (realm == NULL && strcmp(p, "DEFAULT") == 0) ) {
+	    info(" - Matched realm %s", p);
+	    if ((p = strtok(NULL, ":")) == NULL) {
+		fclose(fd);
+		option_error("%s: server address missing on line %d: %s",
+			     radrealms_config, line, buffer);
+		return;
+	    }
+	    s->name[s->max] = strdup(p);
+	    info(" - Address is '%s'",p);
+	    if ((p = strtok(NULL, "\t ")) == NULL) {
+		fclose(fd);
+		option_error("%s: server port missing on line %d:  %s",
+			     radrealms_config, line, buffer);
+		return;
+	    }
+	    s->port[s->max] = atoi(p);
+	    info(" - Port is '%d'", s->port[s->max]);
+	    s->max++;
+	} else 
+	    info(" - Skipping realm '%s'", p);
+    }
+    fclose(fd);
+
+    if (accts->max)
+	*acctserver = accts;
+
+    if (auths->max)
+	*authserver = auths;
+
+    return;
+}
+
+void
+plugin_init(void)
+{
+    radius_pre_auth_hook = lookup_realm;
+
+    add_options(Options);
+    info("RADIUS Realms plugin initialized.");
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/sendserver.c tomato121/release/src/router/pppd/pppd/plugins/radius/sendserver.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/sendserver.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/sendserver.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,520 @@
+/*
+ * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996,1997 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+#include <pathnames.h>
+
+static void rc_random_vector (unsigned char *);
+static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char);
+
+/*
+ * Function: rc_pack_list
+ *
+ * Purpose: Packs an attribute value pair list into a buffer.
+ *
+ * Returns: Number of octets packed.
+ *
+ */
+
+static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth)
+{
+    int             length, i, pc, secretlen, padded_length;
+    int             total_length = 0;
+    UINT4           lvalue;
+    unsigned char   passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)];
+    unsigned char   md5buf[256];
+    unsigned char   *buf, *vector, *lenptr;
+
+    buf = auth->data;
+
+    while (vp != (VALUE_PAIR *) NULL)
+	{
+
+	    if (vp->vendorcode != VENDOR_NONE) {
+		*buf++ = PW_VENDOR_SPECIFIC;
+
+		/* Place-holder for where to put length */
+		lenptr = buf++;
+
+		/* Insert vendor code */
+		*buf++ = 0;
+		*buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255;
+		*buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255;
+		*buf++ = ((unsigned int) vp->vendorcode) & 255;
+
+		/* Insert vendor-type */
+		*buf++ = vp->attribute;
+
+		/* Insert value */
+		switch(vp->type) {
+		case PW_TYPE_STRING:
+		    length = vp->lvalue;
+		    *lenptr = length + 8;
+		    *buf++ = length+2;
+		    memcpy(buf, vp->strvalue, (size_t) length);
+		    buf += length;
+		    total_length += length+8;
+		    break;
+		case PW_TYPE_INTEGER:
+		case PW_TYPE_IPADDR:
+		    length = sizeof(UINT4);
+		    *lenptr = length + 8;
+		    *buf++ = length+2;
+		    lvalue = htonl(vp->lvalue);
+		    memcpy(buf, (char *) &lvalue, sizeof(UINT4));
+		    buf += length;
+		    total_length += length+8;
+		    break;
+		default:
+		    break;
+		}
+	    } else {
+		*buf++ = vp->attribute;
+		switch (vp->attribute) {
+		case PW_USER_PASSWORD:
+
+		    /* Encrypt the password */
+
+		    /* Chop off password at AUTH_PASS_LEN */
+		    length = vp->lvalue;
+		    if (length > AUTH_PASS_LEN) length = AUTH_PASS_LEN;
+
+		    /* Calculate the padded length */
+		    padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1);
+
+		    /* Record the attribute length */
+		    *buf++ = padded_length + 2;
+
+		    /* Pad the password with zeros */
+		    memset ((char *) passbuf, '\0', AUTH_PASS_LEN);
+		    memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
+
+		    secretlen = strlen (secret);
+		    vector = (char *)auth->vector;
+		    for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN) {
+			/* Calculate the MD5 digest*/
+			strcpy ((char *) md5buf, secret);
+			memcpy ((char *) md5buf + secretlen, vector,
+				AUTH_VECTOR_LEN);
+			rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
+
+			/* Remeber the start of the digest */
+			vector = buf;
+
+			/* Xor the password into the MD5 digest */
+			for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++) {
+			    *buf++ ^= passbuf[pc];
+			}
+		    }
+
+		    total_length += padded_length + 2;
+
+		    break;
+#if 0
+		case PW_CHAP_PASSWORD:
+
+		    *buf++ = CHAP_VALUE_LENGTH + 2;
+
+		    /* Encrypt the Password */
+		    length = vp->lvalue;
+		    if (length > CHAP_VALUE_LENGTH) {
+			length = CHAP_VALUE_LENGTH;
+		    }
+		    memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH);
+		    memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
+
+		    /* Calculate the MD5 Digest */
+		    secretlen = strlen (secret);
+		    strcpy ((char *) md5buf, secret);
+		    memcpy ((char *) md5buf + secretlen, (char *) auth->vector,
+			    AUTH_VECTOR_LEN);
+		    rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
+
+		    /* Xor the password into the MD5 digest */
+		    for (i = 0; i < CHAP_VALUE_LENGTH; i++) {
+			*buf++ ^= passbuf[i];
+		    }
+		    total_length += CHAP_VALUE_LENGTH + 2;
+
+		    break;
+#endif
+		default:
+		    switch (vp->type) {
+		    case PW_TYPE_STRING:
+			length = vp->lvalue;
+			*buf++ = length + 2;
+			memcpy (buf, vp->strvalue, (size_t) length);
+			buf += length;
+			total_length += length + 2;
+			break;
+
+		    case PW_TYPE_INTEGER:
+		    case PW_TYPE_IPADDR:
+			*buf++ = sizeof (UINT4) + 2;
+			lvalue = htonl (vp->lvalue);
+			memcpy (buf, (char *) &lvalue, sizeof (UINT4));
+			buf += sizeof (UINT4);
+			total_length += sizeof (UINT4) + 2;
+			break;
+
+		    default:
+			break;
+		    }
+		    break;
+		}
+	    }
+	    vp = vp->next;
+	}
+    return total_length;
+}
+
+/*
+ * Function: rc_send_server
+ *
+ * Purpose: send a request to a RADIUS server and wait for the reply
+ *
+ */
+
+int rc_send_server (SEND_DATA *data, char *msg, REQUEST_INFO *info)
+{
+	int             sockfd;
+	struct sockaddr salocal;
+	struct sockaddr saremote;
+	struct sockaddr_in *sin;
+	struct timeval  authtime;
+	fd_set          readfds;
+	AUTH_HDR       *auth, *recv_auth;
+	UINT4           auth_ipaddr;
+	char           *server_name;	/* Name of server to query */
+	int             salen;
+	int             result;
+	int             total_length;
+	int             length;
+	int             retry_max;
+	int		secretlen;
+	char            secret[MAX_SECRET_LENGTH + 1];
+	unsigned char   vector[AUTH_VECTOR_LEN];
+	char            recv_buffer[BUFFER_LEN];
+	char            send_buffer[BUFFER_LEN];
+	int		retries;
+	VALUE_PAIR	*vp;
+
+	server_name = data->server;
+	if (server_name == (char *) NULL || server_name[0] == '\0')
+		return (ERROR_RC);
+
+	if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \
+	    (vp->lvalue == PW_ADMINISTRATIVE))
+	{
+		strcpy(secret, MGMT_POLL_SECRET);
+		if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0)
+			return (ERROR_RC);
+	}
+	else
+	{
+		if (rc_find_server (server_name, &auth_ipaddr, secret) != 0)
+		{
+			return (ERROR_RC);
+		}
+	}
+
+	sockfd = socket (AF_INET, SOCK_DGRAM, 0);
+	if (sockfd < 0)
+	{
+		memset (secret, '\0', sizeof (secret));
+		error("rc_send_server: socket: %s", strerror(errno));
+		return (ERROR_RC);
+	}
+
+	length = sizeof (salocal);
+	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_port = htons ((unsigned short) 0);
+	if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
+		   getsockname (sockfd, (struct sockaddr *) sin, &length) < 0)
+	{
+		close (sockfd);
+		memset (secret, '\0', sizeof (secret));
+		error("rc_send_server: bind: %s: %m", server_name);
+		return (ERROR_RC);
+	}
+
+	retry_max = data->retries;	/* Max. numbers to try for reply */
+	retries = 0;			/* Init retry cnt for blocking call */
+
+	/* Build a request */
+	auth = (AUTH_HDR *) send_buffer;
+	auth->code = data->code;
+	auth->id = data->seq_nbr;
+
+	if (data->code == PW_ACCOUNTING_REQUEST)
+	{
+		total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
+
+		auth->length = htons ((unsigned short) total_length);
+
+		memset((char *) auth->vector, 0, AUTH_VECTOR_LEN);
+		secretlen = strlen (secret);
+		memcpy ((char *) auth + total_length, secret, secretlen);
+		rc_md5_calc (vector, (char *) auth, total_length + secretlen);
+		memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
+	}
+	else
+	{
+		rc_random_vector (vector);
+		memcpy (auth->vector, vector, AUTH_VECTOR_LEN);
+
+		total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
+
+		auth->length = htons ((unsigned short) total_length);
+	}
+
+	sin = (struct sockaddr_in *) & saremote;
+	memset ((char *) sin, '\0', sizeof (saremote));
+	sin->sin_family = AF_INET;
+	sin->sin_addr.s_addr = htonl (auth_ipaddr);
+	sin->sin_port = htons ((unsigned short) data->svc_port);
+
+	for (;;)
+	{
+		sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0,
+			(struct sockaddr *) sin, sizeof (struct sockaddr_in));
+
+		authtime.tv_usec = 0L;
+		authtime.tv_sec = (long) data->timeout;
+		FD_ZERO (&readfds);
+		FD_SET (sockfd, &readfds);
+		if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0)
+		{
+			if (errno == EINTR)
+				continue;
+			error("rc_send_server: select: %m");
+			memset (secret, '\0', sizeof (secret));
+			close (sockfd);
+			return (ERROR_RC);
+		}
+		if (FD_ISSET (sockfd, &readfds))
+			break;
+
+		/*
+		 * Timed out waiting for response.  Retry "retry_max" times
+		 * before giving up.  If retry_max = 0, don't retry at all.
+		 */
+		if (++retries >= retry_max)
+		{
+			error("rc_send_server: no reply from RADIUS server %s:%u",
+			      rc_ip_hostname (auth_ipaddr), data->svc_port);
+			close (sockfd);
+			memset (secret, '\0', sizeof (secret));
+			return (TIMEOUT_RC);
+		}
+	}
+	salen = sizeof (saremote);
+	length = recvfrom (sockfd, (char *) recv_buffer,
+			   (int) sizeof (recv_buffer),
+			   (int) 0, &saremote, &salen);
+
+	if (length <= 0)
+	{
+		error("rc_send_server: recvfrom: %s:%d: %m", server_name,\
+		      data->svc_port);
+		close (sockfd);
+		memset (secret, '\0', sizeof (secret));
+		return (ERROR_RC);
+	}
+
+	recv_auth = (AUTH_HDR *)recv_buffer;
+
+	result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr);
+
+	data->receive_pairs = rc_avpair_gen(recv_auth);
+
+	close (sockfd);
+	if (info)
+	{
+		memcpy(info->secret, secret, sizeof(info->secret));
+		memcpy(info->request_vector, vector,
+		       sizeof(info->request_vector));
+	}
+	memset (secret, '\0', sizeof (secret));
+
+	if (result != OK_RC) return (result);
+
+	*msg = '\0';
+	vp = data->receive_pairs;
+	while (vp)
+	{
+		if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE)))
+		{
+			strcat(msg, vp->strvalue);
+			strcat(msg, "\n");
+			vp = vp->next;
+		}
+	}
+
+	if ((recv_auth->code == PW_ACCESS_ACCEPT) ||
+		(recv_auth->code == PW_PASSWORD_ACK) ||
+		(recv_auth->code == PW_ACCOUNTING_RESPONSE))
+	{
+		result = OK_RC;
+	}
+	else
+	{
+		result = BADRESP_RC;
+	}
+
+	return (result);
+}
+
+/*
+ * Function: rc_check_reply
+ *
+ * Purpose: verify items in returned packet.
+ *
+ * Returns:	OK_RC       -- upon success,
+ *		BADRESP_RC  -- if anything looks funny.
+ *
+ */
+
+static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret,
+			   unsigned char *vector, unsigned char seq_nbr)
+{
+	int             secretlen;
+	int             totallen;
+	unsigned char   calc_digest[AUTH_VECTOR_LEN];
+	unsigned char   reply_digest[AUTH_VECTOR_LEN];
+
+	totallen = ntohs (auth->length);
+
+	secretlen = strlen (secret);
+
+	/* Do sanity checks on packet length */
+	if ((totallen < 20) || (totallen > 4096))
+	{
+		error("rc_check_reply: received RADIUS server response with invalid length");
+		return (BADRESP_RC);
+	}
+
+	/* Verify buffer space, should never trigger with current buffer size and check above */
+	if ((totallen + secretlen) > bufferlen)
+	{
+		error("rc_check_reply: not enough buffer space to verify RADIUS server response");
+		return (BADRESP_RC);
+	}
+	/* Verify that id (seq. number) matches what we sent */
+	if (auth->id != seq_nbr)
+	{
+		error("rc_check_reply: received non-matching id in RADIUS server response");
+		return (BADRESP_RC);
+	}
+
+	/* Verify the reply digest */
+	memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN);
+	memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
+	memcpy ((char *) auth + totallen, secret, secretlen);
+	rc_md5_calc (calc_digest, (char *) auth, totallen + secretlen);
+
+#ifdef DIGEST_DEBUG
+	{
+		int i;
+
+		fputs("reply_digest: ", stderr);
+		for (i = 0; i < AUTH_VECTOR_LEN; i++)
+		{
+			fprintf(stderr,"%.2x ", (int) reply_digest[i]);
+		}
+		fputs("\ncalc_digest:  ", stderr);
+		for (i = 0; i < AUTH_VECTOR_LEN; i++)
+		{
+			fprintf(stderr,"%.2x ", (int) calc_digest[i]);
+		}
+		fputs("\n", stderr);
+	}
+#endif
+
+	if (memcmp ((char *) reply_digest, (char *) calc_digest,
+		    AUTH_VECTOR_LEN) != 0)
+	{
+#ifdef RADIUS_116
+		/* the original Livingston radiusd v1.16 seems to have
+		   a bug in digest calculation with accounting requests,
+		   authentication request are ok. i looked at the code
+		   but couldn't find any bugs. any help to get this
+		   kludge out are welcome. preferably i want to
+		   reproduce the calculation bug here to be compatible
+		   to stock Livingston radiusd v1.16.	-lf, 03/14/96
+		 */
+		if (auth->code == PW_ACCOUNTING_RESPONSE)
+			return (OK_RC);
+#endif
+		error("rc_check_reply: received invalid reply digest from RADIUS server");
+		return (BADRESP_RC);
+	}
+
+	return (OK_RC);
+
+}
+
+/*
+ * Function: rc_random_vector
+ *
+ * Purpose: generates a random vector of AUTH_VECTOR_LEN octets.
+ *
+ * Returns: the vector (call by reference)
+ *
+ */
+
+static void rc_random_vector (unsigned char *vector)
+{
+	int             randno;
+	int             i;
+	int		fd;
+
+/* well, I added this to increase the security for user passwords.
+   we use /dev/urandom here, as /dev/random might block and we don't
+   need that much randomness. BTW, great idea, Ted!     -lf, 03/18/95	*/
+
+	if ((fd = open(_PATH_DEV_URANDOM, O_RDONLY)) >= 0)
+	{
+		unsigned char *pos;
+		int readcount;
+
+		i = AUTH_VECTOR_LEN;
+		pos = vector;
+		while (i > 0)
+		{
+			readcount = read(fd, (char *)pos, i);
+			pos += readcount;
+			i -= readcount;
+		}
+
+		close(fd);
+		return;
+	} /* else fall through */
+
+	for (i = 0; i < AUTH_VECTOR_LEN;)
+	{
+		randno = magic();
+		memcpy ((char *) vector, (char *) &randno, sizeof (int));
+		vector += sizeof (int);
+		i += sizeof (int);
+	}
+
+	return;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/radius/util.c tomato121/release/src/router/pppd/pppd/plugins/radius/util.c
--- tomato/release/src/router/pppd/pppd/plugins/radius/util.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/radius/util.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,84 @@
+/*
+ * $Id: util.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
+ *
+ * Copyright (C) 1995,1996,1997 Lars Fenneberg
+ *
+ * Copyright 1992 Livingston Enterprises, Inc.
+ *
+ * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
+ * and Merit Network, Inc. All Rights Reserved
+ *
+ * See the file COPYRIGHT for the respective terms and conditions.
+ * If the file is missing contact me at lf@elemental.net
+ * and I'll send you a copy.
+ *
+ */
+
+#include <includes.h>
+#include <radiusclient.h>
+
+/*
+ * Function: rc_str2tm
+ *
+ * Purpose: Turns printable string into correct tm struct entries.
+ *
+ */
+
+static const char * months[] =
+		{
+			"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+			"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+		};
+
+void rc_str2tm (char *valstr, struct tm *tm)
+{
+	int             i;
+
+	/* Get the month */
+	for (i = 0; i < 12; i++)
+	{
+		if (strncmp (months[i], valstr, 3) == 0)
+		{
+			tm->tm_mon = i;
+			i = 13;
+		}
+	}
+
+	/* Get the Day */
+	tm->tm_mday = atoi (&valstr[4]);
+
+	/* Now the year */
+	tm->tm_year = atoi (&valstr[7]) - 1900;
+}
+
+void rc_mdelay(int msecs)
+{
+	struct timeval tv;
+
+	tv.tv_sec = (int) msecs / 1000;
+	tv.tv_usec = (msecs % 1000) * 1000;
+
+	select(0,(fd_set *)NULL,(fd_set *)NULL,(fd_set *)NULL, &tv);
+}
+
+/*
+ * Function: rc_mksid
+ *
+ * Purpose: generate a quite unique string
+ *
+ * Remarks: not that unique at all...
+ *
+ */
+
+char *
+rc_mksid (void)
+{
+  static char buf[15];
+  static unsigned short int cnt = 0;
+  sprintf (buf, "%08lX%04X%02hX",
+	   (unsigned long int) time (NULL),
+	   (unsigned int) getpid (),
+	   cnt & 0xFF);
+  cnt++;
+  return buf;
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/common.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/common.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/common.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/common.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,504 @@
+/***********************************************************************
+*
+* common.c
+*
+* Implementation of user-space PPPoE redirector for Linux.
+*
+* Common functions used by PPPoE client and server
+*
+* Copyright (C) 2000 by 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.
+*
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: common.c,v 1.2 2004/01/13 04:03:58 paulus Exp $";
+
+#include "pppoe.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/**********************************************************************
+*%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 (packet->ver != 1) {
+	syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver);
+	return -1;
+    }
+    if (packet->type != 1) {
+	syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type);
+	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);
+	return -1;
+    }
+
+    /* 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 0;
+	}
+	if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
+	    syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen);
+	    return -1;
+	}
+	func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra);
+	curTag = curTag + TAG_HDR_SIZE + tagLen;
+    }
+    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)
+{
+    printf( "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:
+* conn -- PPPoE connection
+* msg -- if non-NULL, extra error message to include in PADT packet.
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADT packet
+***********************************************************************/
+void
+sendPADT(PPPoEConnection *conn, char const *msg)
+{
+    PPPoEPacket packet;
+    unsigned char *cursor = packet.payload;
+
+    UINT16_t plen = 0;
+
+    /* Do nothing if no session established yet */
+    if (!conn->session) return;
+
+    /* Do nothing if no discovery socket */
+    if (conn->discoverySocket < 0) return;
+
+    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
+    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
+
+    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    packet.ver = 1;
+    packet.type = 1;
+    packet.code = CODE_PADT;
+    packet.session = conn->session;
+
+    /* Reset Session to zero so there is no possibility of
+       recursive calls to this function by any signal handler */
+    conn->session = 0;
+
+    /* 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));
+	memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
+	cursor += sizeof(pid) + TAG_HDR_SIZE;
+	plen += sizeof(pid) + TAG_HDR_SIZE;
+    }
+
+    /* Copy error message */
+    if (msg) {
+	PPPoETag err;
+	size_t elen = strlen(msg);
+	err.type = htons(TAG_GENERIC_ERROR);
+	err.length = htons(elen);
+	strcpy(err.payload, msg);
+	memcpy(cursor, &err, elen + TAG_HDR_SIZE);
+	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,
+		   ntohs(conn->cookie.length) + TAG_HDR_SIZE);
+	memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
+	cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
+	plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
+    }
+
+    if (conn->relayId.type) {
+	CHECK_ROOM(cursor, packet.payload,
+		   ntohs(conn->relayId.length) + TAG_HDR_SIZE);
+	memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(conn->relayId.length) + 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);
+    }
+    syslog(LOG_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)
+{
+    switch(type) {
+    case TAG_SERVICE_NAME_ERROR:
+	syslog(LOG_ERR, "PADT: Service-Name-Error: %.*s", (int) len, data);
+	printf( "PADT: Service-Name-Error: %.*s\n", (int) len, data);
+	break;
+    case TAG_AC_SYSTEM_ERROR:
+	syslog(LOG_ERR, "PADT: System-Error: %.*s", (int) len, data);
+	printf( "PADT: System-Error: %.*s\n", (int) len, data);
+	break;
+    case TAG_GENERIC_ERROR:
+	syslog(LOG_ERR, "PADT: Generic-Error: %.*s", (int) len, data);
+	printf( "PADT: Generic-Error: %.*s\n", (int) len, data);
+	break;
+    }
+}
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/config.h tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/config.h
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/config.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/config.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,135 @@
+/* config.h.  Generated automatically by configure.  */
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+/* #undef pid_t */
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+/* Define if the setvbuf function takes the buffering type as its second
+   argument and the buffer pointer as the third, as on System V
+   before release 3.  */
+/* #undef SETVBUF_REVERSED */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if your <sys/time.h> declares struct tm.  */
+/* #undef TM_IN_SYS_TIME */
+
+#define HAVE_STRUCT_SOCKADDR_LL 1
+
+/* The number of bytes in a unsigned int.  */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The number of bytes in a unsigned long.  */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* The number of bytes in a unsigned short.  */
+#define SIZEOF_UNSIGNED_SHORT 2
+
+/* Define if you have the select function.  */
+#define HAVE_SELECT 1
+
+/* Define if you have the socket function.  */
+#define HAVE_SOCKET 1
+
+/* Define if you have the strerror function.  */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strtol function.  */
+#define HAVE_STRTOL 1
+
+/* Define if you have the <asm/types.h> header file.  */
+#define HAVE_ASM_TYPES_H 1
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <getopt.h> header file.  */
+#define HAVE_GETOPT_H 1
+
+/* Define if you have the <linux/if_ether.h> header file.  */
+#define HAVE_LINUX_IF_ETHER_H 1
+
+/* Define if you have kernel-mode PPPoE in Linux file.  */
+#define HAVE_LINUX_KERNEL_PPPOE 1
+
+/* Define if you have the <linux/if_packet.h> header file.  */
+#define HAVE_LINUX_IF_PACKET_H 1
+
+/* Define if you have the <linux/if_pppox.h> header file.  */
+#define HAVE_LINUX_IF_PPPOX_H 1
+
+/* Define if you have the <net/bpf.h> header file.  */
+#define HAVE_NET_BPF_H 1
+
+/* Define if you have the <net/if_arp.h> header file.  */
+#define HAVE_NET_IF_ARP_H 1
+
+/* Define if you have the <net/ethernet.h> header file.  */
+#define HAVE_NET_ETHERNET_H 1
+
+/* Define if you have the <net/if.h> header file.  */
+#define HAVE_NET_IF_H 1
+
+/* Define if you have the <linux/if.h> header file.  */
+#define HAVE_LINUX_IF_H 1
+
+/* Define if you have the <net/if_dl.h> header file.  */
+/* #undef HAVE_NET_IF_DL_H */
+
+/* Define if you have the <net/if_ether.h> header file.  */
+/* #undef HAVE_NET_IF_ETHER_H */
+
+/* Define if you have the <net/if_types.h> header file.  */
+/* #undef HAVE_NET_IF_TYPES_H */
+
+/* Define if you have the <netinet/if_ether.h> header file.  */
+#define HAVE_NETINET_IF_ETHER_H 1
+
+/* Define if you have the <netpacket/packet.h> header file.  */
+#define HAVE_NETPACKET_PACKET_H 1
+
+/* Define if you have the <sys/cdefs.h> header file.  */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define if you have the <sys/dlpi.h> header file.  */
+/* #undef HAVE_SYS_DLPI_H */
+
+/* Define if you have the <sys/ioctl.h> header file.  */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define if you have the <sys/param.h> header file.  */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define if you have the <sys/socket.h> header file.  */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define if you have the <sys/time.h> header file.  */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <sys/uio.h> header file.  */
+#define HAVE_SYS_UIO_H 1
+
+/* Define if you have the <syslog.h> header file.  */
+#define HAVE_SYSLOG_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+#define HAVE_UNISTD_H 1
+
+/* 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 -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/debug.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/debug.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/debug.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/debug.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,143 @@
+/***********************************************************************
+*
+* debug.c
+*
+* Implementation of user-space PPPoE redirector for Linux.
+*
+* Functions for printing debugging information
+*
+* Copyright (C) 2000 by 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.
+*
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: debug.c,v 1.1 2001/12/14 02:55:20 mostrows Exp $";
+
+#include "pppoe.h"
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/**********************************************************************
+*%FUNCTION: dumpHex
+*%ARGUMENTS:
+* fp -- file to dump to
+* buf -- buffer to dump
+* len -- length of data
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Dumps buffer to fp in an easy-to-read format
+***********************************************************************/
+void
+dumpHex(FILE *fp, unsigned char const *buf, int len)
+{
+    int i;
+    int base;
+
+    if (!fp) return;
+
+    /* do NOT dump PAP packets */
+    if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) {
+	fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n");
+	return;
+    }
+
+    for (base=0; base<len; base += 16) {
+	for (i=base; i<base+16; i++) {
+	    if (i < len) {
+		fprintf(fp, "%02x ", (unsigned) buf[i]);
+	    } else {
+		fprintf(fp, "   ");
+	    }
+	}
+	fprintf(fp, "  ");
+	for (i=base; i<base+16; i++) {
+	    if (i < len) {
+		if (isprint(buf[i])) {
+		    fprintf(fp, "%c", buf[i]);
+		} else {
+		    fprintf(fp, ".");
+		}
+	    } else {
+		break;
+	    }
+	}
+	fprintf(fp, "\n");
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: dumpPacket
+*%ARGUMENTS:
+* fp -- file to dump to
+* packet -- a PPPoE packet
+* dir -- either SENT or RCVD
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Dumps the PPPoE packet to fp in an easy-to-read format
+***********************************************************************/
+void
+dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir)
+{
+    int len = ntohs(packet->length);
+
+    /* Sheesh... printing times is a pain... */
+    struct timeval tv;
+    time_t now;
+    int millisec;
+    struct tm *lt;
+    char timebuf[256];
+
+    UINT16_t type = etherType(packet);
+    if (!fp) return;
+    gettimeofday(&tv, NULL);
+    now = (time_t) tv.tv_sec;
+    millisec = tv.tv_usec / 1000;
+    lt = localtime(&now);
+    strftime(timebuf, 256, "%H:%M:%S", lt);
+    fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir);
+    if (type == Eth_PPPOE_Discovery) {
+	fprintf(fp, "Discovery (%x) ", (unsigned) type);
+    } else if (type == Eth_PPPOE_Session) {
+	fprintf(fp, "Session (%x) ", (unsigned) type);
+    } else {
+	fprintf(fp, "Unknown (%x) ", (unsigned) type);
+    }
+
+    switch(packet->code) {
+    case CODE_PADI: fprintf(fp, "PADI "); break;
+    case CODE_PADO: fprintf(fp, "PADO "); break;
+    case CODE_PADR: fprintf(fp, "PADR "); break;
+    case CODE_PADS: fprintf(fp, "PADS "); break;
+    case CODE_PADT: fprintf(fp, "PADT "); break;
+    case CODE_SESS: fprintf(fp, "SESS "); break;
+    }
+
+    fprintf(fp, "sess-id %d length %d\n",
+	    (int) ntohs(packet->session),
+	    len);
+
+    /* Ugly... I apologize... */
+    fprintf(fp,
+	    "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x "
+	    "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n",
+	    (unsigned) packet->ethHdr.h_source[0],
+	    (unsigned) packet->ethHdr.h_source[1],
+	    (unsigned) packet->ethHdr.h_source[2],
+	    (unsigned) packet->ethHdr.h_source[3],
+	    (unsigned) packet->ethHdr.h_source[4],
+	    (unsigned) packet->ethHdr.h_source[5],
+	    (unsigned) packet->ethHdr.h_dest[0],
+	    (unsigned) packet->ethHdr.h_dest[1],
+	    (unsigned) packet->ethHdr.h_dest[2],
+	    (unsigned) packet->ethHdr.h_dest[3],
+	    (unsigned) packet->ethHdr.h_dest[4],
+	    (unsigned) packet->ethHdr.h_dest[5]);
+    dumpHex(fp, packet->payload, ntohs(packet->length));
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/discovery.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/discovery.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/discovery.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/discovery.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,645 @@
+/***********************************************************************
+*
+* discovery.c
+*
+* Perform PPPoE discovery
+*
+* Copyright (C) 1999 by Roaring Penguin Software Inc.
+*
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: discovery.c,v 1.4 2005/03/22 10:22:32 paulus Exp $";
+
+#include "pppoe.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef USE_LINUX_PACKET
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#endif
+
+#include <signal.h>
+
+/**********************************************************************
+*%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;
+	if (conn->printACNames) {
+	    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 (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);
+	}
+	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);
+	}
+	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);
+	}
+	break;
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: parsePADSTags
+*%ARGUMENTS:
+* type -- tag type
+* len -- tag length
+* data -- tag data
+* extra -- extra user data (pointer to PPPoEConnection structure)
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Picks interesting tags out of a PADS packet
+***********************************************************************/
+void
+parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
+	      void *extra)
+{
+    PPPoEConnection *conn = (PPPoEConnection *) extra;
+    switch(type) {
+    case TAG_SERVICE_NAME:
+	syslog(LOG_DEBUG, "PADS: Service-Name: '%.*s'", (int) len, data);
+	break;
+    case TAG_SERVICE_NAME_ERROR:
+	syslog(LOG_ERR, "PADS: Service-Name-Error: %.*s", (int) len, data);
+	printf( "PADS: Service-Name-Error: %.*s\n", (int) len, data);
+	exit(1);
+    case TAG_AC_SYSTEM_ERROR:
+	syslog(LOG_ERR, "PADS: System-Error: %.*s", (int) len, data);
+	printf( "PADS: System-Error: %.*s\n", (int) len, data);
+	exit(1);
+    case TAG_GENERIC_ERROR:
+	syslog(LOG_ERR, "PADS: Generic-Error: %.*s", (int) len, data);
+	printf( "PADS: Generic-Error: %.*s\n", (int) len, data);
+	exit(1);
+    case TAG_RELAY_SESSION_ID:
+	conn->relayId.type = htons(type);
+	conn->relayId.length = htons(len);
+	memcpy(conn->relayId.payload, data, len);
+	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.ver = 1;
+    packet.type = 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;
+	
+    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) {
+		fatalSys("select (waitForPADO)");
+	    }
+	    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)",
+		   (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)) {
+		printErr("Ignoring PADO packet from broadcast MAC address");
+		continue;
+	    }
+	    parsePacket(&packet, parsePADOTags, &pc);
+	    if (!pc.seenACName) {
+		printErr("Ignoring PADO packet with no AC-Name tag");
+		continue;
+	    }
+	    if (!pc.seenServiceName) {
+		printErr("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;
+	    }
+	}
+    } while (conn->discoveryState != STATE_RECEIVED_PADO);
+}
+
+/***********************************************************************
+*%FUNCTION: sendPADR
+*%ARGUMENTS:
+* conn -- PPPoE connection structur
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Sends a PADR packet
+***********************************************************************/
+void
+sendPADR(PPPoEConnection *conn)
+{
+    PPPoEPacket packet;
+    PPPoETag *svc = (PPPoETag *) packet.payload;
+    unsigned char *cursor = 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);
+
+    memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
+    memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
+
+    packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
+    packet.ver = 1;
+    packet.type = 1;
+    packet.code = CODE_PADR;
+    packet.session = 0;
+
+    svc->type = TAG_SERVICE_NAME;
+    svc->length = htons(namelen);
+    if (conn->serviceName) {
+	memcpy(svc->payload, conn->serviceName, namelen);
+    }
+    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;
+    }
+
+    /* Copy cookie and relay-ID if needed */
+    if (conn->cookie.type) {
+	CHECK_ROOM(cursor, packet.payload,
+		   ntohs(conn->cookie.length) + TAG_HDR_SIZE);
+	memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
+	cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
+	plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
+    }
+
+    if (conn->relayId.type) {
+	CHECK_ROOM(cursor, packet.payload,
+		   ntohs(conn->relayId.length) + TAG_HDR_SIZE);
+	memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
+	cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
+	plen += ntohs(conn->relayId.length) + 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: waitForPADS
+*%ARGUMENTS:
+* conn -- PPPoE connection info
+* timeout -- how long to wait (in seconds)
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Waits for a PADS packet and copies useful information
+***********************************************************************/
+void
+waitForPADS(PPPoEConnection *conn, int timeout)
+{
+    fd_set readable;
+    int r;
+    struct timeval tv;
+    PPPoEPacket packet;
+    int len;
+
+    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) {
+		fatalSys("select (waitForPADS)");
+	    }
+	    if (r == 0) return;
+	}
+
+	/* 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)",
+		   (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 from the AC, it's not for me */
+	if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;
+
+	/* If it's not for us, loop again */
+	if (!packetIsForMe(conn, &packet)) continue;
+
+	/* Is it PADS?  */
+	if (packet.code == CODE_PADS) {
+	    /* Parse for goodies */
+	    parsePacket(&packet, parsePADSTags, conn);
+	    conn->discoveryState = STATE_SESSION;
+	    break;
+	}
+    } while (conn->discoveryState != STATE_SESSION);
+
+    /* 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));
+
+    /* 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));
+    }
+}
+
+/**********************************************************************
+*%FUNCTION: discovery
+*%ARGUMENTS:
+* conn -- PPPoE connection info structure
+*%RETURNS:
+* Nothing
+*%DESCRIPTION:
+* Performs the PPPoE discovery phase
+***********************************************************************/
+void
+discovery(PPPoEConnection *conn)
+{
+    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;
+    }
+
+    do {
+	padiAttempts++;
+	if (padiAttempts > MAX_PADI_ATTEMPTS) {
+	    warn("Timeout waiting for PADO packets");
+	    close(conn->discoverySocket);
+	    conn->discoverySocket = -1;
+	    return;
+	}
+	sendPADI(conn);
+	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;
+	}
+    } 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;
+    do {
+	padrAttempts++;
+	if (padrAttempts > MAX_PADI_ATTEMPTS) {
+	    warn("Timeout waiting for PADS packets");
+	    close(conn->discoverySocket);
+	    conn->discoverySocket = -1;
+	    return;
+	}
+	sendPADR(conn);
+	conn->discoveryState = STATE_SENT_PADR;
+	waitForPADS(conn, timeout);
+	timeout *= 2;
+    } while (conn->discoveryState == STATE_SENT_PADR);
+
+    /* We're done. */
+    conn->discoveryState = STATE_SESSION;
+    return;
+}
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/if.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/if.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/if.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/if.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,1097 @@
+/***********************************************************************
+*
+* if.c
+*
+* Implementation of user-space PPPoE redirector for Linux.
+*
+* Functions for opening a raw socket and reading/writing raw Ethernet frames.
+*
+* Copyright (C) 2000 by 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.
+*
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: if.c,v 1.1 2001/12/14 02:55:20 mostrows Exp $";
+
+#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
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_NET_IF_ARP_H
+#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... */
+
+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) {
+	syslog(LOG_ERR, "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:
+* 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) {
+	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);
+    }
+
+#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;
+}
+
+#endif /* USE_LINUX */
+
+/***********************************************************************
+*%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(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
+
+#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);
+
+
+
+#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
+#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:
+* 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)
+{
+#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)");
+	return -1;
+    }
+#endif
+#endif
+    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 -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,68 @@
+# Generated automatically from Makefile.in by configure.
+#***********************************************************************
+#
+# Makefile
+#
+# Makefile for Roaring Penguin's Linux PPPoE plugin.
+#
+# 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.7 2006/06/04 05:07:46 paulus Exp $
+#***********************************************************************
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION)
+
+PPPDVERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
+
+INSTALL	= install
+
+# Version is set ONLY IN THE MAKEFILE!  Don't delete this!
+VERSION=3.3
+
+COPTS=-O2 -g
+CFLAGS=$(COPTS) -I../../../include/linux
+#CFLAGS=$(COPTS) -I$(LINUXDIR)/include/linux
+
+all: rp-pppoe.so pppoe-discovery
+
+pppoe-discovery: libplugin.a pppoe-discovery.o
+	$(CC) -o pppoe-discovery pppoe-discovery.o libplugin.a
+
+pppoe-discovery.o: pppoe-discovery.c
+	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -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
+
+install: all
+	$(INSTALL) -d -m 755 $(LIBDIR)
+	$(INSTALL) -c -m 4550 rp-pppoe.so $(LIBDIR)
+	$(INSTALL) -d -m 755 $(BINDIR)
+	$(INSTALL) -c -m 555 pppoe-discovery $(BINDIR)
+
+clean:
+	rm -f *.o *.so
+
+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 $@ $^
+
+discovery.o: discovery.c
+	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -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
+
+common.o: common.c
+	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o common.o -fPIC common.c
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile.linux tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile.linux
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile.linux	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,68 @@
+# Generated automatically from Makefile.in by configure.
+#***********************************************************************
+#
+# Makefile
+#
+# Makefile for Roaring Penguin's Linux PPPoE plugin.
+#
+# 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.7 2006/06/04 05:07:46 paulus Exp $
+#***********************************************************************
+
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION)
+
+PPPDVERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
+
+INSTALL	= install
+
+# Version is set ONLY IN THE MAKEFILE!  Don't delete this!
+VERSION=3.3
+
+COPTS=-O2 -g
+CFLAGS=$(COPTS) -I../../../include/linux
+#CFLAGS=$(COPTS) -I$(LINUXDIR)/include/linux
+
+all: rp-pppoe.so pppoe-discovery
+
+pppoe-discovery: libplugin.a pppoe-discovery.o
+	$(CC) -o pppoe-discovery pppoe-discovery.o libplugin.a
+
+pppoe-discovery.o: pppoe-discovery.c
+	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -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
+
+install: all
+	$(INSTALL) -d -m 755 $(LIBDIR)
+	$(INSTALL) -c -m 4550 rp-pppoe.so $(LIBDIR)
+	$(INSTALL) -d -m 755 $(BINDIR)
+	$(INSTALL) -c -m 555 pppoe-discovery $(BINDIR)
+
+clean:
+	rm -f *.o *.so
+
+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 $@ $^
+
+discovery.o: discovery.c
+	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -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
+
+common.o: common.c
+	$(CC) $(CFLAGS) '-DVERSION="$(VERSION)"' -c -o common.o -fPIC common.c
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,448 @@
+/***********************************************************************
+*
+* plugin.c
+*
+* pppd plugin for kernel-mode PPPoE on Linux
+*
+* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski
+* and Jamal Hadi Salim.
+*
+* Much code and many ideas derived from pppoe plugin by Michal
+* Ostrowski and Jamal Hadi Salim, which carries this copyright:
+*
+* Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+*                Jamal Hadi Salim <hadi@cyberus.ca>
+* Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
+* which is based in part on work from Jens Axboe and Paul Mackerras.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: plugin.c,v 1.15 2006/05/29 23:29:16 paulus Exp $";
+
+#define _GNU_SOURCE 1
+#include "pppoe.h"
+
+#include "pppd/pppd.h"
+#include "pppd/fsm.h"
+#include "pppd/lcp.h"
+#include "pppd/ipcp.h"
+#include "pppd/ccp.h"
+#include "pppd/pathnames.h"
+
+#include <linux/types.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include "ppp_defs.h"
+#include "if_ppp.h"
+#include "if_pppox.h"
+
+#define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
+
+char pppd_version[] = VERSION;
+
+/* From sys-linux.c in pppd -- MUST FIX THIS! */
+extern int new_style_driver;
+
+char *pppd_pppoe_service = NULL;
+static char *acName = NULL;
+static char *existingSession = NULL;
+static int printACNames = 0;
+
+static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
+static option_t Options[] = {
+    { "device name", o_wild, (void *) &PPPoEDevnameHook,
+      "PPPoE device name",
+      OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+      devnam},
+    { "rp_pppoe_service", o_string, &pppd_pppoe_service,
+      "Desired PPPoE service name" },
+    { "rp_pppoe_ac",      o_string, &acName,
+      "Desired PPPoE access concentrator name" },
+    { "rp_pppoe_sess",    o_string, &existingSession,
+      "Attach to existing session (sessid:macaddr)" },
+    { "rp_pppoe_verbose", o_int, &printACNames,
+      "Be verbose about discovered access concentrators"},
+    { NULL }
+};
+
+static PPPoEConnection *conn = NULL;
+
+/**********************************************************************
+ * %FUNCTION: PPPOEInitDevice
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ *
+ * %DESCRIPTION:
+ * Initializes PPPoE device.
+ ***********************************************************************/
+static int
+PPPOEInitDevice(void)
+{
+    conn = malloc(sizeof(PPPoEConnection));
+    if (!conn) {
+	fatal("Could not allocate memory for PPPoE session");
+    }
+    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->discoverySocket = -1;
+    conn->sessionSocket = -1;
+    conn->useHostUniq = 1;
+    conn->printACNames = printACNames;
+    return 1;
+}
+
+/**********************************************************************
+ * %FUNCTION: PPPOEConnectDevice
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ * Non-negative if all goes well; -1 otherwise
+ * %DESCRIPTION:
+ * Connects PPPoE device.
+ ***********************************************************************/
+static int
+PPPOEConnectDevice(void)
+{
+    struct sockaddr_pppox sp;
+
+    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+    if (existingSession) {
+	unsigned int mac[ETH_ALEN];
+	int i, ses;
+	if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x",
+		   &ses, &mac[0], &mac[1], &mac[2],
+		   &mac[3], &mac[4], &mac[5]) != 7) {
+	    fatal("Illegal value for rp_pppoe_sess option");
+	}
+	conn->session = htons(ses);
+	for (i=0; i<ETH_ALEN; i++) {
+	    conn->peerEth[i] = (unsigned char) mac[i];
+	}
+    } else {
+	discovery(conn);
+	if (conn->discoveryState != STATE_SESSION) {
+	    error("Unable to complete PPPoE Discovery");
+	    return -1;
+	}
+    }
+
+    /* 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;
+    memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
+    memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
+
+    /* Set remote_number for ServPoET */
+    sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X",
+	    (unsigned) conn->peerEth[0],
+	    (unsigned) conn->peerEth[1],
+	    (unsigned) conn->peerEth[2],
+	    (unsigned) conn->peerEth[3],
+	    (unsigned) conn->peerEth[4],
+	    (unsigned) conn->peerEth[5]);
+
+    if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
+		sizeof(struct sockaddr_pppox)) < 0) {
+	fatal("Failed to connect PPPoE socket: %d %m", errno);
+	return -1;
+    }
+
+    return conn->sessionSocket;
+}
+
+/*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));
+//    info("set mtu rp-pppoe to %s (%d)\n",ifname,mtu);
+    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
+}
+
+/**********************************************************************
+ * %FUNCTION: PPPOEDisconnectDevice
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ * Nothing
+ * %DESCRIPTION:
+ * Disconnects PPPoE device
+ ***********************************************************************/
+static void
+PPPOEDisconnectDevice(void)
+{
+    struct sockaddr_pppox sp;
+
+    sp.sa_family = AF_PPPOX;
+    sp.sa_protocol = PX_PROTO_OE;
+    sp.sa_addr.pppoe.sid = 0;
+    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;
+    }
+    close(conn->sessionSocket);
+    /* don't send PADT?? */
+    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))
+	exit(EXIT_OPTION_ERROR);
+
+}
+
+struct channel pppoe_channel;
+
+/**********************************************************************
+ * %FUNCTION: PPPoEDevnameHook
+ * %ARGUMENTS:
+ * cmd -- the command (actually, the device name
+ * argv -- argument vector
+ * doit -- if non-zero, set device name.  Otherwise, just check if possible
+ * %RETURNS:
+ * 1 if we will handle this device; 0 otherwise.
+ * %DESCRIPTION:
+ * Checks if name is a valid interface name; if so, returns 1.  Also
+ * sets up devnam (string representation of device).
+ ***********************************************************************/
+static int
+PPPoEDevnameHook(char *cmd, char **argv, int doit)
+{
+    int r = 1;
+    int fd;
+    struct ifreq ifr;
+
+    /*
+     * 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;
+    }
+
+    /* Open a socket */
+    if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
+	r = 0;
+    }
+
+    /* Try getting interface index */
+    if (r) {
+	strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
+	if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
+	    r = 0;
+	} else {
+	    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+		r = 0;
+	    } else {
+		if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		    if (doit)
+			error("Interface %s not Ethernet", cmd);
+		    r = 0;
+		}
+	    }
+	}
+    }
+
+    /* Close socket */
+    close(fd);
+    if (r && doit) {
+	strncpy(devnam, cmd, sizeof(devnam));
+	if (the_channel != &pppoe_channel) {
+
+	    the_channel = &pppoe_channel;
+	    modem = 0;
+
+	    PPPOEInitDevice();
+	}
+	return 1;
+    }
+
+    return r;
+}
+
+/**********************************************************************
+ * %FUNCTION: plugin_init
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ * Nothing
+ * %DESCRIPTION:
+ * Initializes hooks for pppd plugin
+ ***********************************************************************/
+void
+plugin_init(void)
+{
+    if (!ppp_available() && !new_style_driver) {
+	fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
+    }
+
+    add_options(Options);
+
+    info("RP-PPPoE plugin version %s compiled against pppd %s",
+	 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)
+{
+    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);
+}
+
+/**********************************************************************
+*%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);
+}
+
+void pppoe_check_options(void)
+{
+    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;
+
+    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: 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 -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c.orig tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c.orig
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c.orig	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/plugin.c.orig	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,421 @@
+/***********************************************************************
+*
+* plugin.c
+*
+* pppd plugin for kernel-mode PPPoE on Linux
+*
+* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski
+* and Jamal Hadi Salim.
+*
+* Much code and many ideas derived from pppoe plugin by Michal
+* Ostrowski and Jamal Hadi Salim, which carries this copyright:
+*
+* Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
+*                Jamal Hadi Salim <hadi@cyberus.ca>
+* Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr.,
+* which is based in part on work from Jens Axboe and Paul Mackerras.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+***********************************************************************/
+
+static char const RCSID[] =
+"$Id: plugin.c,v 1.14 2005/08/25 10:51:27 paulus Exp $";
+
+#define _GNU_SOURCE 1
+#include "pppoe.h"
+
+#include "pppd/pppd.h"
+#include "pppd/fsm.h"
+#include "pppd/lcp.h"
+#include "pppd/ipcp.h"
+#include "pppd/ccp.h"
+#include "pppd/pathnames.h"
+
+#include <linux/types.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include "ppp_defs.h"
+#include "if_ppp.h"
+#include "if_pppox.h"
+
+#define _PATH_ETHOPT         _ROOT_PATH "/etc/ppp/options."
+
+char pppd_version[] = VERSION;
+
+/* From sys-linux.c in pppd -- MUST FIX THIS! */
+extern int new_style_driver;
+
+char *pppd_pppoe_service = NULL;
+static char *acName = NULL;
+static char *existingSession = NULL;
+static int printACNames = 0;
+
+static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
+static option_t Options[] = {
+    { "device name", o_wild, (void *) &PPPoEDevnameHook,
+      "PPPoE device name",
+      OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+      devnam},
+    { "rp_pppoe_service", o_string, &pppd_pppoe_service,
+      "Desired PPPoE service name" },
+    { "rp_pppoe_ac",      o_string, &acName,
+      "Desired PPPoE access concentrator name" },
+    { "rp_pppoe_sess",    o_string, &existingSession,
+      "Attach to existing session (sessid:macaddr)" },
+    { "rp_pppoe_verbose", o_int, &printACNames,
+      "Be verbose about discovered access concentrators"},
+    { NULL }
+};
+
+static PPPoEConnection *conn = NULL;
+
+/**********************************************************************
+ * %FUNCTION: PPPOEInitDevice
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ *
+ * %DESCRIPTION:
+ * Initializes PPPoE device.
+ ***********************************************************************/
+static int
+PPPOEInitDevice(void)
+{
+    conn = malloc(sizeof(PPPoEConnection));
+    if (!conn) {
+	fatal("Could not allocate memory for PPPoE session");
+    }
+    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->discoverySocket = -1;
+    conn->sessionSocket = -1;
+    conn->useHostUniq = 1;
+    conn->printACNames = printACNames;
+    return 1;
+}
+
+/**********************************************************************
+ * %FUNCTION: PPPOEConnectDevice
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ * Non-negative if all goes well; -1 otherwise
+ * %DESCRIPTION:
+ * Connects PPPoE device.
+ ***********************************************************************/
+static int
+PPPOEConnectDevice(void)
+{
+    struct sockaddr_pppox sp;
+
+    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+    if (existingSession) {
+	unsigned int mac[ETH_ALEN];
+	int i, ses;
+	if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x",
+		   &ses, &mac[0], &mac[1], &mac[2],
+		   &mac[3], &mac[4], &mac[5]) != 7) {
+	    fatal("Illegal value for rp_pppoe_sess option");
+	}
+	conn->session = htons(ses);
+	for (i=0; i<ETH_ALEN; i++) {
+	    conn->peerEth[i] = (unsigned char) mac[i];
+	}
+    } else {
+	discovery(conn);
+	if (conn->discoveryState != STATE_SESSION) {
+	    error("Unable to complete PPPoE Discovery");
+	    return -1;
+	}
+    }
+
+    /* 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;
+    memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
+    memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
+
+    /* Set remote_number for ServPoET */
+    sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X",
+	    (unsigned) conn->peerEth[0],
+	    (unsigned) conn->peerEth[1],
+	    (unsigned) conn->peerEth[2],
+	    (unsigned) conn->peerEth[3],
+	    (unsigned) conn->peerEth[4],
+	    (unsigned) conn->peerEth[5]);
+
+    if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
+		sizeof(struct sockaddr_pppox)) < 0) {
+	fatal("Failed to connect PPPoE socket: %d %m", errno);
+	return -1;
+    }
+
+    return conn->sessionSocket;
+}
+
+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
+}
+
+/**********************************************************************
+ * %FUNCTION: PPPOEDisconnectDevice
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ * Nothing
+ * %DESCRIPTION:
+ * Disconnects PPPoE device
+ ***********************************************************************/
+static void
+PPPOEDisconnectDevice(void)
+{
+    struct sockaddr_pppox sp;
+
+    sp.sa_family = AF_PPPOX;
+    sp.sa_protocol = PX_PROTO_OE;
+    sp.sa_addr.pppoe.sid = 0;
+    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;
+    }
+    close(conn->sessionSocket);
+    /* don't send PADT?? */
+    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))
+	exit(EXIT_OPTION_ERROR);
+
+}
+
+struct channel pppoe_channel;
+
+/**********************************************************************
+ * %FUNCTION: PPPoEDevnameHook
+ * %ARGUMENTS:
+ * cmd -- the command (actually, the device name
+ * argv -- argument vector
+ * doit -- if non-zero, set device name.  Otherwise, just check if possible
+ * %RETURNS:
+ * 1 if we will handle this device; 0 otherwise.
+ * %DESCRIPTION:
+ * Checks if name is a valid interface name; if so, returns 1.  Also
+ * sets up devnam (string representation of device).
+ ***********************************************************************/
+static int
+PPPoEDevnameHook(char *cmd, char **argv, int doit)
+{
+    int r = 1;
+    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 */
+    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 */
+    if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) {
+	r = 0;
+    }
+
+    /* Try getting interface index */
+    if (r) {
+	strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
+	if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
+	    r = 0;
+	} else {
+	    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+		r = 0;
+	    } else {
+		if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		    error("Interface %s not Ethernet", cmd);
+		    r=0;
+		}
+	    }
+	}
+    }
+
+    /* Close socket */
+    close(fd);
+    if (r) {
+	strncpy(devnam, cmd, sizeof(devnam));
+	if (the_channel != &pppoe_channel) {
+
+	    the_channel = &pppoe_channel;
+	    modem = 0;
+
+	    PPPOEInitDevice();
+	}
+	return 1;
+    }
+
+    return r;
+}
+
+/**********************************************************************
+ * %FUNCTION: plugin_init
+ * %ARGUMENTS:
+ * None
+ * %RETURNS:
+ * Nothing
+ * %DESCRIPTION:
+ * Initializes hooks for pppd plugin
+ ***********************************************************************/
+void
+plugin_init(void)
+{
+    if (!ppp_available() && !new_style_driver) {
+	fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?");
+    }
+
+    add_options(Options);
+
+    info("RP-PPPoE plugin version %s compiled against pppd %s",
+	 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)
+{
+    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);
+}
+
+/**********************************************************************
+*%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);
+}
+
+void pppoe_check_options(void)
+{
+    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;
+
+    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: 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 -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe-discovery.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe-discovery.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe-discovery.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe-discovery.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,124 @@
+/*
+ * Perform PPPoE discovery
+ *
+ * Copyright (C) 2000-2001 by Roaring Penguin Software Inc.
+ * Copyright (C) 2004 Marco d'Itri <md@linux.it>
+ *
+ * This program may be distributed according to the terms of the GNU
+ * General Public License, version 2 or (at your option) any later version.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "pppoe.h"
+
+char *xstrdup(const char *s);
+void usage(void);
+
+void die(int status)
+{
+	exit(status);
+}
+
+int main(int argc, char *argv[])
+{
+    int opt;
+    PPPoEConnection *conn;
+
+    conn = malloc(sizeof(PPPoEConnection));
+    if (!conn)
+	fatalSys("malloc");
+
+    memset(conn, 0, sizeof(PPPoEConnection));
+
+    while ((opt = getopt(argc, argv, "I:D:VUAS:C:h")) > 0) {
+	switch(opt) {
+	case 'S':
+	    conn->serviceName = xstrdup(optarg);
+	    break;
+	case 'C':
+	    conn->acName = xstrdup(optarg);
+	    break;
+	case 'U':
+	    conn->useHostUniq = 1;
+	    break;
+	case 'D':
+	    conn->debugFile = fopen(optarg, "w");
+	    if (!conn->debugFile) {
+		printf( "Could not open %s: %s\n",
+			optarg, strerror(errno));
+		exit(1);
+	    }
+	    fprintf(conn->debugFile, "pppoe-discovery %s\n", VERSION);
+	    break;
+	case 'I':
+	    conn->ifName = xstrdup(optarg);
+	    break;
+	case 'A':
+	    /* this is the default */
+	    break;
+	case 'V':
+	case 'h':
+	    usage();
+	    exit(0);
+	default:
+	    usage();
+	    exit(1);
+	}
+    }
+
+    /* default interface name */
+    if (!conn->ifName)
+	conn->ifName = strdup("eth0");
+
+    conn->discoverySocket = -1;
+    conn->sessionSocket = -1;
+    conn->printACNames = 1;
+
+    discovery(conn);
+    exit(0);
+}
+
+void rp_fatal(char const *str)
+{
+    char buf[1024];
+
+    printErr(str);
+    sprintf(buf, "pppoe-discovery: %.256s", 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));
+    exit(1);
+}
+
+void sysErr(char const *str)
+{
+    rp_fatal(str);
+}
+
+char *xstrdup(const char *s)
+{
+    register char *ret = strdup(s);
+    if (!ret)
+	sysErr("strdup");
+    return ret;
+}
+
+void usage(void)
+{
+    printf( "Usage: pppoe-discovery [options]\n");
+    printf( "\nVersion " VERSION "\n");
+}
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe.h tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe.h
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/pppoe.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,323 @@
+/***********************************************************************
+*
+* pppoe.h
+*
+* Declaration of various PPPoE constants
+*
+* Copyright (C) 2000 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: pppoe.h,v 1.2 2004/11/04 10:07:37 paulus Exp $
+*
+***********************************************************************/
+
+#ifdef __sun__
+#define __EXTENSIONS__
+#endif
+
+#include "config.h"
+
+#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H)
+#define _POSIX_SOURCE 1 /* For sigaction defines */
+#endif
+
+#include <stdio.h>		/* For FILE */
+#include <sys/types.h>		/* For pid_t */
+
+/* How do we access raw Ethernet devices? */
+#undef USE_LINUX_PACKET
+#undef USE_BPF
+
+#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H)
+#define USE_LINUX_PACKET 1
+#elif defined(HAVE_SYS_DLPI_H)
+#define USE_DLPI
+#elif defined(HAVE_NET_BPF_H)
+#define USE_BPF 1
+#endif
+
+/* Sanity check */
+#if !defined(USE_BPF) && !defined(USE_LINUX_PACKET) && !defined(USE_DLPI)
+#error Unknown method for accessing raw Ethernet frames
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+/* Ugly header files on some Linux boxes... */
+#if defined(HAVE_LINUX_IF_H)
+#include <linux/if.h>
+#elif defined(HAVE_NET_IF_H)
+#include <net/if.h>
+#endif
+
+#ifdef HAVE_NET_IF_TYPES_H
+#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
+typedef unsigned short UINT16_t;
+#elif SIZEOF_UNSIGNED_INT == 2
+typedef unsigned int UINT16_t;
+#else
+#error Could not find a 16-bit integer type
+#endif
+
+#if SIZEOF_UNSIGNED_SHORT == 4
+typedef unsigned short UINT32_t;
+#elif SIZEOF_UNSIGNED_INT == 4
+typedef unsigned int UINT32_t;
+#elif SIZEOF_UNSIGNED_LONG == 4
+typedef unsigned long UINT32_t;
+#else
+#error Could not find a 16-bit integer type
+#endif
+
+#ifdef HAVE_LINUX_IF_ETHER_H
+#include <linux/if_ether.h>
+#endif
+
+#include <netinet/in.h>
+
+#ifdef HAVE_NETINET_IF_ETHER_H
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifndef HAVE_SYS_DLPI_H
+#include <netinet/if_ether.h>
+#endif
+#endif
+
+
+
+/* Ethernet frame types according to RFC 2516 */
+#define ETH_PPPOE_DISCOVERY 0x8863
+#define ETH_PPPOE_SESSION   0x8864
+
+/* But some brain-dead peers disobey the RFC, so frame types are variables */
+extern UINT16_t Eth_PPPOE_Discovery;
+extern UINT16_t Eth_PPPOE_Session;
+
+/* PPPoE codes */
+#define CODE_PADI           0x09
+#define CODE_PADO           0x07
+#define CODE_PADR           0x19
+#define CODE_PADS           0x65
+#define CODE_PADT           0xA7
+#define CODE_SESS           0x00
+
+/* PPPoE Tags */
+#define TAG_END_OF_LIST        0x0000
+#define TAG_SERVICE_NAME       0x0101
+#define TAG_AC_NAME            0x0102
+#define TAG_HOST_UNIQ          0x0103
+#define TAG_AC_COOKIE          0x0104
+#define TAG_VENDOR_SPECIFIC    0x0105
+#define TAG_RELAY_SESSION_ID   0x0110
+#define TAG_SERVICE_NAME_ERROR 0x0201
+#define TAG_AC_SYSTEM_ERROR    0x0202
+#define TAG_GENERIC_ERROR      0x0203
+
+/* Discovery phase states */
+#define STATE_SENT_PADI     0
+#define STATE_RECEIVED_PADO 1
+#define STATE_SENT_PADR     2
+#define STATE_SESSION       3
+#define STATE_TERMINATED    4
+
+/* How many PADI/PADS attempts? */
+#define MAX_PADI_ATTEMPTS 3
+
+/* Initial timeout for PADO/PADS */
+#define PADI_TIMEOUT 5
+
+/* States for scanning PPP frames */
+#define STATE_WAITFOR_FRAME_ADDR 0
+#define STATE_DROP_PROTO         1
+#define STATE_BUILDING_PACKET    2
+
+/* Special PPP frame characters */
+#define FRAME_ESC    0x7D
+#define FRAME_FLAG   0x7E
+#define FRAME_ADDR   0xFF
+#define FRAME_CTRL   0x03
+#define FRAME_ENC    0x20
+
+#define IPV4ALEN     4
+#define SMALLBUF   256
+
+/* 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 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 */
+} PPPoEPacket;
+
+/* 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)
+
+/* 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 */
+} PPPoETag;
+/* Header size of a PPPoE tag */
+#define TAG_HDR_SIZE 4
+
+/* Chunk to read from stdin */
+#define READ_CHUNK 4096
+
+/* Function passed to parsePacket */
+typedef void ParseFunc(UINT16_t type,
+		       UINT16_t len,
+		       unsigned char *data,
+		       void *extra);
+
+#define PPPINITFCS16    0xffff  /* Initial FCS value */
+
+/* Keep track of the state of a connection -- collect everything in
+   one spot */
+
+typedef struct PPPoEConnectionStruct {
+    int discoveryState;		/* Where we are in discovery */
+    int discoverySocket;	/* Raw socket for discovery frames */
+    int sessionSocket;		/* Raw socket for session frames */
+    unsigned char myEth[ETH_ALEN]; /* My MAC address */
+    unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */
+    UINT16_t session;		/* Session ID */
+    char *ifName;		/* Interface name */
+    char *serviceName;		/* Desired service name, if any */
+    char *acName;		/* Desired AC name, if any */
+    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 */
+} PPPoEConnection;
+
+/* Structure used to determine acceptable PADO or PADS packet */
+struct PacketCriteria {
+    PPPoEConnection *conn;
+    int acNameOK;
+    int serviceNameOK;
+    int seenACName;
+    int seenServiceName;
+};
+
+/* Function Prototypes */
+UINT16_t etherType(PPPoEPacket *packet);
+int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr);
+int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size);
+int receivePacket(int sock, PPPoEPacket *pkt, int *size);
+void fatalSys(char const *str);
+void rp_fatal(char const *str);
+void printErr(char const *str);
+void sysErr(char const *str);
+void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir);
+void dumpHex(FILE *fp, unsigned char const *buf, int len);
+int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra);
+void parseLogErrs(UINT16_t typ, UINT16_t len, unsigned char *data, void *xtra);
+void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet);
+void asyncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet);
+void asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss);
+void syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss);
+char *strDup(char const *str);
+void sendPADT(PPPoEConnection *conn, char const *msg);
+void sendSessionPacket(PPPoEConnection *conn,
+		       PPPoEPacket *packet, int len);
+void initPPP(void);
+void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss);
+UINT16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr);
+UINT16_t pppFCS16(UINT16_t fcs, unsigned char *cp, int len);
+void discovery(PPPoEConnection *conn);
+unsigned char *findTag(PPPoEPacket *packet, UINT16_t tagType,
+		       PPPoETag *tag);
+
+#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"); \
+        return; \
+    } \
+} while(0)
+
+/* True if Ethernet address is broadcast or multicast */
+#define NOT_UNICAST(e) ((e[0] & 0x01) != 0)
+#define BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) == 0xFF)
+#define NOT_BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) != 0xFF)
diff -Naur tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/utils.c tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/utils.c
--- tomato/release/src/router/pppd/pppd/plugins/rp-pppoe/utils.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/rp-pppoe/utils.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+void dbglog(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsyslog(LOG_DEBUG, fmt, ap);
+    vfprintf(stderr, fmt, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+}
+
+void info(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsyslog(LOG_INFO, fmt, ap);
+    vfprintf(stderr, fmt, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+}
+
+void warn(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsyslog(LOG_WARNING, fmt, ap);
+    vfprintf(stderr, fmt, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+}
+
+void error(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsyslog(LOG_ERR, fmt, ap);
+    vfprintf(stderr, fmt, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+}
+
+void fatal(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsyslog(LOG_ERR, fmt, ap);
+    vfprintf(stderr, fmt, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+    exit(1);
+}
+
diff -Naur tomato/release/src/router/pppd/pppd/plugins/winbind.c tomato121/release/src/router/pppd/pppd/plugins/winbind.c
--- tomato/release/src/router/pppd/pppd/plugins/winbind.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/plugins/winbind.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,669 @@
+/***********************************************************************
+*
+* winbind.c
+*
+* WINBIND plugin for pppd.  Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
+* authentication using WINBIND to contact a NT-style PDC.
+* 
+* Based on the structure of the radius module.
+*
+* Copyright (C) 2003 Andrew Bartlet <abartlet@samba.org>
+*
+* Copyright 1999 Paul Mackerras, Alan Curry. 
+* (pipe read code from passpromt.c)
+*
+* Copyright (C) 2002 Roaring Penguin Software Inc.
+*
+* Based on a patch for ipppd, which is:
+*    Copyright (C) 1996, Matjaz Godec <gody@elgo.si>
+*    Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de>
+*    Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net>
+*
+* Uses radiusclient library, which is:
+*    Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
+*    Copyright (C) 2002 Roaring Penguin Software Inc.
+*
+* MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with
+* modification from Frank Cusack, <frank@google.com>.
+*
+* Updated on 2003-12-12 to support updated PPP plugin API from latest CVS
+*    Copyright (C) 2003, Sean E. Millichamp <sean at bruenor dot org>
+*
+* This plugin may be distributed according to the terms of the GNU
+* General Public License, version 2 or (at your option) any later version.
+*
+***********************************************************************/
+
+#include "pppd.h"
+#include "chap-new.h"
+#include "chap_ms.h"
+#ifdef MPPE
+#include "md5.h"
+#endif
+#include "fsm.h"
+#include "ipcp.h"
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define BUF_LEN 1024
+
+#define NOT_AUTHENTICATED 0
+#define AUTHENTICATED 1
+
+static char *ntlm_auth = NULL;
+
+static int set_ntlm_auth(char **argv)
+{
+	char *p;
+
+	p = argv[0];
+	if (p[0] != '/') {
+		option_error("ntlm_auth-helper argument must be full path");
+		return 0;
+	}
+	p = strdup(p);
+	if (p == NULL) {
+		novm("ntlm_auth-helper argument");
+		return 0;
+	}
+	if (ntlm_auth != NULL)
+		free(ntlm_auth);
+	ntlm_auth = p;
+	return 1;
+}
+
+static option_t Options[] = {
+	{ "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth,
+	  "Path to ntlm_auth executable", OPT_PRIV },
+	{ NULL }
+};
+
+static int
+winbind_secret_check(void);
+
+static int winbind_pap_auth(char *user,
+			   char *passwd,
+			   char **msgp,
+			   struct wordlist **paddrs,
+			   struct wordlist **popts);
+static int winbind_chap_verify(char *user, char *ourname, int id,
+			       struct chap_digest_type *digest,
+			       unsigned char *challenge,
+			       unsigned char *response,
+			       char *message, int message_space);
+static int winbind_allowed_address(u_int32_t addr); 
+
+char pppd_version[] = VERSION;
+
+/**********************************************************************
+* %FUNCTION: plugin_init
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Initializes WINBIND plugin.
+***********************************************************************/
+void
+plugin_init(void)
+{
+    pap_check_hook = winbind_secret_check;
+    pap_auth_hook = winbind_pap_auth;
+
+    chap_check_hook = winbind_secret_check;
+    chap_verify_hook = winbind_chap_verify;
+
+    allowed_address_hook = winbind_allowed_address;
+
+    /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */
+    chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT);
+    
+    add_options(Options);
+
+    info("WINBIND plugin initialized.");
+}
+
+/**
+ Routine to get hex characters and turn them into a 16 byte array.
+ the array can be variable length, and any non-hex-numeric
+ characters are skipped.  "0xnn" or "0Xnn" is specially catered
+ for.
+
+ valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
+
+**/
+
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   
+   Copyright (C) Andrew Tridgell 1992-2001
+   Copyright (C) Simo Sorce      2001-2002
+   Copyright (C) Martin Pool     2003
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+size_t strhex_to_str(char *p, size_t len, const char *strhex)
+{
+	size_t i;
+	size_t num_chars = 0;
+	unsigned char   lonybble, hinybble;
+	const char     *hexchars = "0123456789ABCDEF";
+	char           *p1 = NULL, *p2 = NULL;
+
+	for (i = 0; i < len && strhex[i] != 0; i++) {
+		if (strncmp(hexchars, "0x", 2) == 0) {
+			i++; /* skip two chars */
+			continue;
+		}
+
+		if (!(p1 = strchr(hexchars, toupper(strhex[i]))))
+			break;
+
+		i++; /* next hex digit */
+
+		if (!(p2 = strchr(hexchars, toupper(strhex[i]))))
+			break;
+
+		/* get the two nybbles */
+		hinybble = (p1 - hexchars);
+		lonybble = (p2 - hexchars);
+
+		p[num_chars] = (hinybble << 4) | lonybble;
+		num_chars++;
+
+		p1 = NULL;
+		p2 = NULL;
+	}
+	return num_chars;
+}
+
+static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * Encode a base64 string into a malloc()ed string caller to free.
+ *
+ *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
+ **/
+char * base64_encode(const char *data)
+{
+	size_t out_cnt = 0;
+	size_t len = strlen(data);
+	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;
+
+	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 (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;
+}
+
+unsigned int run_ntlm_auth(const char *username, 
+			   const char *domain, 
+			   const char *full_username,
+			   const char *plaintext_password,
+			   const u_char *challenge,
+			   size_t challenge_length,
+			   const u_char *lm_response, 
+			   size_t lm_response_length,
+			   const u_char *nt_response, 
+			   size_t nt_response_length,
+			   u_char nt_key[16], 
+			   char **error_string) 
+{
+	
+	pid_t forkret;
+        int child_in[2];
+        int child_out[2];
+	int status;
+
+	int authenticated = NOT_AUTHENTICATED; /* not auth */
+	int got_user_session_key = 0; /* not got key */
+
+	char buffer[1024];
+
+	FILE *pipe_in;
+	FILE *pipe_out;
+	
+	int i;
+	char *challenge_hex;
+	char *lm_hex_hash;
+	char *nt_hex_hash;
+
+	/* First see if we have a program to run... */
+	if (ntlm_auth == NULL)
+		return NOT_AUTHENTICATED;
+
+        /* Make first child */
+        if (pipe(child_out) == -1) {
+                error("pipe creation failed for child OUT!");
+		return NOT_AUTHENTICATED;
+        }
+
+        if (pipe(child_in) == -1) {
+                error("pipe creation failed for child IN!");
+		return NOT_AUTHENTICATED;
+        }
+
+        forkret = safe_fork(child_in[0], child_out[1], 2);
+        if (forkret == -1) {
+		if (error_string) {
+			*error_string = strdup("fork failed!");
+		}
+
+                return NOT_AUTHENTICATED;
+        }
+
+	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());
+		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);  
+		fatal("pppd/winbind: could not exec /bin/sh: %m");
+	}
+
+        /* parent */
+	close(child_out[1]);
+	close(child_in[0]);
+
+	/* Need to write the User's info onto the pipe */
+
+	pipe_in = fdopen(child_in[1], "w");
+
+	pipe_out = fdopen(child_out[0], "r");
+
+	/* look for session key coming back */
+
+	if (username) {
+		char *b64_username = base64_encode(username);
+		fprintf(pipe_in, "Username:: %s\n", b64_username);
+		free(b64_username);
+	}
+
+	if (domain) {
+		char *b64_domain = base64_encode(domain);
+		fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
+		free(b64_domain);
+	}
+
+	if (full_username) {
+		char *b64_full_username = base64_encode(full_username);
+		fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
+		free(b64_full_username);
+	}
+
+	if (plaintext_password) {
+		char *b64_plaintext_password = base64_encode(plaintext_password);
+		fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
+		free(b64_plaintext_password);
+	}
+
+	if (challenge_length) {
+		fprintf(pipe_in, "Request-User-Session-Key: yes\n");
+
+		challenge_hex = malloc(challenge_length*2+1);
+		
+		for (i = 0; i < challenge_length; i++)
+			sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
+		
+		fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
+		free(challenge_hex);
+	}
+	
+	if (lm_response_length) {
+		lm_hex_hash = malloc(lm_response_length*2+1);
+		
+		for (i = 0; i < lm_response_length; i++)
+			sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
+		
+		fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
+		free(lm_hex_hash);
+	}
+	
+	if (nt_response_length) {
+		nt_hex_hash = malloc(nt_response_length*2+1);
+		
+		for (i = 0; i < nt_response_length; i++)
+			sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
+		
+		fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
+		free(nt_hex_hash);
+	}
+	
+	fprintf(pipe_in, ".\n");
+	fflush(pipe_in);
+	
+	while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
+		char *message, *parameter;
+		if (buffer[strlen(buffer)-1] != '\n') {
+			break;
+		}
+		buffer[strlen(buffer)-1] = '\0';
+		message = buffer;
+
+		if (!(parameter = strstr(buffer, ": "))) {
+			break;
+		}
+		
+		parameter[0] = '\0';
+		parameter++;
+		parameter[0] = '\0';
+		parameter++;
+		
+		if (strcmp(message, ".") == 0) {
+			/* end of sequence */
+			break;
+		} else if (strcasecmp(message, "Authenticated") == 0) {
+			if (strcasecmp(parameter, "Yes") == 0) {
+				authenticated = AUTHENTICATED;
+			} else {
+				notice("Winbind has declined authentication for user!");
+				authenticated = NOT_AUTHENTICATED;
+			}
+		} else if (strcasecmp(message, "User-session-key") == 0) {
+			/* length is the number of characters to parse */
+			if (nt_key) { 
+				if (strhex_to_str(nt_key, 32, parameter) == 16) {
+					got_user_session_key = 1;
+				} else {
+					notice("NT session key for user was not 16 bytes!");
+				}
+			}
+		} else if (strcasecmp(message, "Error") == 0) {
+			authenticated = NOT_AUTHENTICATED;
+			if (error_string)
+				*error_string = strdup(parameter);
+		} else if (strcasecmp(message, "Authentication-Error") == 0) {
+			authenticated = NOT_AUTHENTICATED;
+			if (error_string)
+				*error_string = strdup(parameter);
+		} else {
+			notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter); 
+		}
+	}
+
+        /* parent */
+        if (close(child_out[0]) == -1) {
+                notice("error closing pipe?!? for child OUT[0]");
+                return NOT_AUTHENTICATED;
+        }
+
+       /* parent */
+        if (close(child_in[1]) == -1) {
+                notice("error closing pipe?!? for child IN[1]");
+                return NOT_AUTHENTICATED;
+        }
+
+	while ((wait(&status) == -1) && errno == EINTR)
+                ;
+
+	if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
+		notice("Did not get user session key, despite being authenticated!");
+		return NOT_AUTHENTICATED;
+	}
+	return authenticated;
+}
+
+/**********************************************************************
+* %FUNCTION: winbind_secret_check
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  0 if we don't have an ntlm_auth program to run, otherwise 1.
+* %DESCRIPTION:
+* Tells pppd that we will try to authenticate the peer, and not to
+* worry about looking in /etc/ppp/ *-secrets
+***********************************************************************/
+static int
+winbind_secret_check(void)
+{
+	return ntlm_auth != NULL;
+}
+
+/**********************************************************************
+* %FUNCTION: winbind_pap_auth
+* %ARGUMENTS:
+*  user -- user-name of peer
+*  passwd -- password supplied by peer
+*  msgp -- Message which will be sent in PAP response
+*  paddrs -- set to a list of possible peer IP addresses
+*  popts -- set to a list of additional pppd options
+* %RETURNS:
+*  1 if we can authenticate, -1 if we cannot.
+* %DESCRIPTION:
+* Performs PAP authentication using WINBIND
+***********************************************************************/
+static int
+winbind_pap_auth(char *user,
+		char *password,
+		char **msgp,
+		struct wordlist **paddrs,
+		struct wordlist **popts)
+{
+	if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
+		return 1;
+	} 
+	return -1;
+}
+
+/**********************************************************************
+* %FUNCTION: winbind_chap_auth
+* %ARGUMENTS:
+*  user -- user-name of peer
+*  remmd -- hash received from peer
+*  remmd_len -- length of remmd
+*  cstate -- pppd's chap_state structure
+* %RETURNS:
+*  AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
+* %DESCRIPTION:
+* Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
+***********************************************************************/
+
+static int 
+winbind_chap_verify(char *user, char *ourname, int id,
+		    struct chap_digest_type *digest,
+		    unsigned char *challenge,
+		    unsigned char *response,
+		    char *message, int message_space)
+{
+	int challenge_len, response_len;
+	char domainname[256];
+	char *domain;
+	char *username;
+	char *p;
+	char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
+
+	/* The first byte of each of these strings contains their length */
+	challenge_len = *challenge++;
+	response_len = *response++;
+	
+	/* remove domain from "domain\username" */
+	if ((username = strrchr(user, '\\')) != NULL)
+		++username;
+	else
+		username = user;
+	
+	strlcpy(domainname, user, sizeof(domainname));
+	
+	/* remove domain from "domain\username" */
+	if ((p = strrchr(domainname, '\\')) != NULL) {
+		*p = '\0';
+		domain = domainname;
+	} else {
+		domain = NULL;
+	}
+	
+	/*  generate MD based on negotiated type */
+	switch (digest->code) {
+		
+	case CHAP_MICROSOFT:
+	{
+		char *error_string = NULL;
+		u_char *nt_response = NULL;
+		u_char *lm_response = NULL;
+		int nt_response_size = 0;
+		int lm_response_size = 0;
+		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 (response[MS_CHAP_USENT]) {
+			nt_response = &response[MS_CHAP_NTRESP];
+			nt_response_size = MS_CHAP_NTRESP_LEN;
+		} else {
+#ifdef MSLANMAN
+			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");
+			return NOT_AUTHENTICATED;
+#endif /* MSLANMAN */
+		}
+		
+		/* ship off to winbind, and check */
+		
+		if (run_ntlm_auth(username, 
+				  domain,
+				  NULL,
+				  NULL,
+				  challenge, challenge_len,
+				  lm_response, lm_response_size,
+				  nt_response, nt_response_size,
+				  session_key,
+				  &error_string) == AUTHENTICATED) {
+			mppe_set_keys(challenge, session_key);
+			slprintf(message, message_space, "Access granted");
+			return AUTHENTICATED;
+			
+		} else {
+			if (error_string) {
+				notice(error_string);
+				free(error_string);
+			}
+			slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
+				 challenge_len, challenge);
+			return NOT_AUTHENTICATED;
+		}
+		break;
+	}
+	
+	case CHAP_MICROSOFT_V2:
+	{
+		u_char Challenge[8];
+		u_char session_key[MD4_SIGNATURE_SIZE];
+		char *error_string = NULL;
+		
+		if (response_len != MS_CHAP2_RESPONSE_LEN)
+			break;			/* not even the right length */
+		
+		ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge,
+			      user, Challenge);
+		
+		/* ship off to winbind, and check */
+		
+		if (run_ntlm_auth(username, 
+				  domain, 
+				  NULL,
+				  NULL,
+				  Challenge, 8,
+				  NULL, 0,
+				  &response[MS_CHAP2_NTRESP],
+				  MS_CHAP2_NTRESP_LEN,
+				  session_key,
+				  &error_string) == AUTHENTICATED) {
+			
+			GenerateAuthenticatorResponse(session_key,
+				&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",
+					 saresponse, "Access granted");
+			}
+			return AUTHENTICATED;
+			
+		} else {
+			if (error_string) {
+				notice(error_string);
+				slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+					 challenge_len, challenge, error_string);
+				free(error_string);
+			} else {
+				slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+					 challenge_len, challenge, "Access denied");
+			}
+			return NOT_AUTHENTICATED;
+		}
+		break;
+	}
+	
+	default:
+		error("WINBIND: Challenge type %u unsupported", digest->code);
+	}
+	return NOT_AUTHENTICATED;
+}
+
+static int 
+winbind_allowed_address(u_int32_t addr) 
+{
+	ipcp_options *wo = &ipcp_wantoptions[0];
+	if (wo->hisaddr !=0 && wo->hisaddr == addr) {
+		return 1;
+	}
+	return -1;
+}
diff -Naur tomato/release/src/router/pppd/pppd/pppcrypt.c tomato121/release/src/router/pppd/pppd/pppcrypt.c
--- tomato/release/src/router/pppd/pppd/pppcrypt.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/pppcrypt.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,193 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  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.
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include "pppd.h"
+#include "pppcrypt.h"
+
+static u_char
+Get7Bits(input, startBit)
+u_char *input;
+int startBit;
+{
+	unsigned int word;
+
+	word  = (unsigned)input[startBit / 8] << 8;
+	word |= (unsigned)input[startBit / 8 + 1];
+
+	word >>= 15 - (startBit % 8 + 7);
+
+	return word & 0xFE;
+}
+
+static void
+MakeKey(key, des_key)
+u_char *key;		/* IN  56 bit DES key missing parity bits */
+u_char *des_key;	/* OUT 64 bit DES key with parity bits added */
+{
+	des_key[0] = Get7Bits(key,  0);
+	des_key[1] = Get7Bits(key,  7);
+	des_key[2] = Get7Bits(key, 14);
+	des_key[3] = Get7Bits(key, 21);
+	des_key[4] = Get7Bits(key, 28);
+	des_key[5] = Get7Bits(key, 35);
+	des_key[6] = Get7Bits(key, 42);
+	des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+	des_set_odd_parity((des_cblock *)des_key);
+#endif
+}
+
+#ifdef USE_CRYPT
+/*
+ * in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void
+Expand(in, out)
+u_char *in;
+u_char *out;
+{
+        int j, c;
+        int i;
+
+        for (i = 0; i < 64; in++){
+		c = *in;
+                for (j = 7; j >= 0; j--)
+                        *out++ = (c >> j) & 01;
+                i += 8;
+        }
+}
+
+/* The inverse of Expand
+ */
+static void
+Collapse(in, out)
+u_char *in;
+u_char *out;
+{
+        int j;
+        int i;
+	unsigned int c;
+
+	for (i = 0; i < 64; i += 8, out++) {
+	    c = 0;
+	    for (j = 7; j >= 0; j--, in++)
+		c |= *in << j;
+	    *out = c & 0xff;
+	}
+}
+
+bool
+DesSetkey(key)
+u_char *key;
+{
+	u_char des_key[8];
+	u_char crypt_key[66];
+
+	MakeKey(key, des_key);
+	Expand(des_key, crypt_key);
+	errno = 0;
+	setkey((const char *)crypt_key);
+	if (errno != 0)
+		return (0);
+	return (1);
+}
+
+bool
+DesEncrypt(clear, cipher)
+u_char *clear;	/* IN  8 octets */
+u_char *cipher;	/* OUT 8 octets */
+{
+	u_char des_input[66];
+
+	Expand(clear, des_input);
+	errno = 0;
+	encrypt((char *)des_input, 0);
+	if (errno != 0)
+		return (0);
+	Collapse(des_input, cipher);
+	return (1);
+}
+
+bool
+DesDecrypt(cipher, clear)
+u_char *cipher;	/* IN  8 octets */
+u_char *clear;	/* OUT 8 octets */
+{
+	u_char des_input[66];
+
+	Expand(cipher, des_input);
+	errno = 0;
+	encrypt((char *)des_input, 1);
+	if (errno != 0)
+		return (0);
+	Collapse(des_input, clear);
+	return (1);
+}
+
+#else /* USE_CRYPT */
+static des_key_schedule	key_schedule;
+
+bool
+DesSetkey(key)
+u_char *key;
+{
+	des_cblock des_key;
+	MakeKey(key, des_key);
+	des_set_key(&des_key, key_schedule);
+	return (1);
+}
+
+bool
+DesEncrypt(clear, key, cipher)
+u_char *clear;	/* IN  8 octets */
+u_char *cipher;	/* OUT 8 octets */
+{
+	des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher,
+	    key_schedule, 1);
+	return (1);
+}
+
+bool
+DesDecrypt(cipher, clear)
+u_char *cipher;	/* IN  8 octets */
+u_char *clear;	/* OUT 8 octets */
+{
+	des_ecb_encrypt((des_cblock *)cipher, (des_cblock *)clear,
+	    key_schedule, 0);
+	return (1);
+}
+
+#endif /* USE_CRYPT */
diff -Naur tomato/release/src/router/pppd/pppd/pppcrypt.h tomato121/release/src/router/pppd/pppd/pppcrypt.h
--- tomato/release/src/router/pppd/pppd/pppcrypt.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/pppcrypt.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,48 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  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.
+ *
+ * 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 PPPCRYPT_H
+#define	PPPCRYPT_H
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+
+extern bool	DesSetkey __P((u_char *));
+extern bool	DesEncrypt __P((u_char *, u_char *));
+extern bool	DesDecrypt __P((u_char *, u_char *));
+
+#endif /* PPPCRYPT_H */
diff -Naur tomato/release/src/router/pppd/pppd/pppd.8 tomato121/release/src/router/pppd/pppd/pppd.8
--- tomato/release/src/router/pppd/pppd/pppd.8	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/pppd.8	2008-04-22 21:44:05.000000000 +1000
@@ -1,62 +1,78 @@
 .\" manual page [] for pppd 2.4
-.\" $Id: pppd.8,v 1.1 2003/07/10 07:43:04 honor Exp $
+.\" $Id: pppd.8,v 1.88 2006/06/16 00:01:23 paulus Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
 .\" IP indented paragraph
 .\" TP hanging label
+.\" 
+.\" Copyright (c) 1993-2003 Paul Mackerras <paulus@samba.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, 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.
+.\"
 .TH PPPD 8
 .SH NAME
-pppd \- Point to Point Protocol daemon
+pppd \- Point-to-Point Protocol Daemon
 .SH SYNOPSIS
 .B pppd
 [
-.I tty_name
-] [
-.I speed
-] [
 .I options
 ]
 .SH DESCRIPTION
 .LP
-The Point-to-Point Protocol (PPP) provides a method for transmitting
-datagrams over serial point-to-point links.  PPP
-is composed of three parts: a method for encapsulating datagrams over
-serial links, an extensible Link Control Protocol (LCP), and
-a family of Network Control Protocols (NCP) for establishing
-and configuring different network-layer protocols.
-.LP
-The encapsulation scheme is provided by driver code in the kernel.
-Pppd provides the basic LCP, authentication support, and an NCP for
-establishing and configuring the Internet Protocol (IP) (called the IP
-Control Protocol, IPCP).
+PPP is the protocol used for establishing internet links over dial-up
+modems, DSL connections, and many other types of point-to-point
+links.  The \fIpppd\fR daemon works together with the kernel PPP
+driver to establish and maintain a PPP link with another system
+(called the \fIpeer\fR) and to negotiate Internet Protocol (IP)
+addresses for each end of the link.  Pppd can also authenticate the
+peer and/or supply authentication information to the peer.  PPP can be
+used with other network protocols besides IP, but such use is becoming
+increasingly rare.
 .SH FREQUENTLY USED OPTIONS
 .TP
-.I <tty_name>
-Communicate over the named device.  The string "/dev/" is prepended if
-necessary.  If no device name is given, or if the name of the terminal
+.I ttyname
+Use the serial port called \fIttyname\fR to communicate with 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,
 and will not fork to put itself in the background.  A value for this
 option from a privileged source cannot be overridden by a
 non-privileged user.
 .TP
-.I <speed>
-Set the baud rate to <speed> (a decimal number).  On systems such as
+.I speed
+An option that is a decimal number is taken as the desired baud rate
+for the serial device.  On systems such as
 4.4BSD and NetBSD, any speed can be specified.  Other systems
-(e.g. SunOS) allow only a limited set of speeds.
+(e.g. Linux, SunOS) only support the commonly-used baud rates.
 .TP
-.B asyncmap \fI<map>
-Set the async character map to <map>.  This map describes which
-control characters cannot be successfully received over the serial
-line.  Pppd will ask the peer to send these characters as a 2-byte
-escape sequence.  The argument is a 32 bit hex number with each bit
-representing a character to escape.  Bit 0 (00000001) represents the
-character 0x00; bit 31 (80000000) represents the character 0x1f or ^_.
+.B asyncmap \fImap
+This option sets the Async-Control-Character-Map (ACCM) for this end
+of the link.  The ACCM is a set of 32 bits, one for each of the
+ASCII control characters with values from 0 to 31, where a 1 bit
+indicates that the corresponding control character should not be used
+in PPP packets sent to this system.  The map is encoded as a
+hexadecimal number (without a leading 0x) where the least significant
+bit (00000001) represents character 0 and the most significant bit
+(80000000) represents character 31.
+Pppd will ask the peer to send these characters as a 2-byte
+escape sequence.
 If multiple \fIasyncmap\fR options are given, the values are ORed
-together.  If no \fIasyncmap\fR option is given, no async character
-map will be negotiated for the receive direction; the peer should then
-escape \fIall\fR control characters.  To escape transmitted
-characters, use the \fIescape\fR option.
+together.  If no \fIasyncmap\fR option is given, the default is zero,
+so pppd will ask the peer not to escape any control characters.
+To escape transmitted characters, use the \fIescape\fR option.
 .TP
 .B auth
 Require the peer to authenticate itself before allowing network
@@ -66,22 +82,28 @@
 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.
 .TP
 .B connect \fIscript
-Use the executable or shell command specified by \fIscript\fR to set
-up the serial line.  This script would typically use the chat(8)
-program to dial the modem and start the remote ppp session.  A value
+Usually there is something which needs to be done to prepare the link
+before the PPP protocol can be started; for instance, with a dial-up
+modem, commands need to be sent to the modem to dial the appropriate
+phone number.  This option specifies an command for pppd to execute
+(by passing it to a shell) before attempting to start PPP negotiation.
+The chat (8) program is often useful here, as it provides a way to
+send arbitrary strings to a modem and respond to received characters.
+A value
 for this option from a privileged source cannot be overridden by a
 non-privileged user.
 .TP
 .B crtscts
-Use hardware flow control (i.e. RTS/CTS) to control the flow of
-data on the serial port.  If neither the \fIcrtscts\fR, the
+Specifies that pppd should set the serial port to use hardware flow
+control using the RTS and CTS signals in the RS-232 interface.
+If neither the \fIcrtscts\fR, the
 \fInocrtscts\fR, the \fIcdtrcts\fR nor the \fInocdtrcts\fR option
 is given, the hardware flow control setting for the serial port is
 left unchanged.
@@ -89,7 +111,7 @@
 RTS output. Such serial ports use this mode to implement
 unidirectional flow control. The serial port will
 suspend transmission when requested by the modem (via CTS)
-but will be unable to request the modem stop sending to the
+but will be unable to request the modem to stop sending to the
 computer. This mode retains the ability to use DTR as
 a modem control line.
 .TP
@@ -100,8 +122,9 @@
 is privileged if the \fInodefaultroute\fR option has been specified.
 .TP
 .B disconnect \fIscript
-Run the executable or shell command specified by \fIscript\fR after
-pppd has terminated the link.  This script could, for example, issue
+Execute the command specified by \fIscript\fR, by passing it to a
+shell, after
+pppd has terminated the link.  This command could, for example, issue
 commands to the modem to cause it to hang up if hardware modem control
 signals were not available.  The disconnect script is not run if the
 modem has already hung up.  A value for this option from a privileged
@@ -122,7 +145,7 @@
 The file must be readable by the user who has invoked pppd.
 .TP
 .B init \fIscript
-Run the executable or shell command specified by \fIscript\fR to
+Execute the command specified by \fIscript\fR, by passing it to a shell, to
 initialize the serial line.  This script would typically use the
 chat(8) program to configure the modem to enable auto answer.  A value
 for this option from a privileged source cannot be overridden by a
@@ -130,21 +153,24 @@
 .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
-will ask the peer to send packets of no more than \fIn\fR bytes.  The
-minimum MRU value is 128.  The default MRU value is 1500.  A value of
-296 is recommended for slow links (40 bytes for TCP/IP header + 256
-bytes of data).  (Note that for IPv6 MRU must be at least 1280)
+will ask the peer to send packets of no more than \fIn\fR bytes.
+The value of \fIn\fR must be between 128 and 16384; the default is 1500.
+A value of
+296 works well on very slow links (40 bytes for TCP/IP header + 256
+bytes of data).
+Note that for the IPv6 protocol, the MRU must be at least 1280.
 .TP
 .B mtu \fIn
 Set the MTU [Maximum Transmit Unit] value to \fIn\fR.  Unless the
 peer requests a smaller value via MRU negotiation, pppd will
 request that the kernel networking code send data packets of no more
-than \fIn\fR bytes through the PPP network interface.  (Note that for 
-IPv6 MTU must be at least 1280)
+than \fIn\fR bytes through the PPP network interface.  Note that for 
+the IPv6 protocol, the MTU must be at least 1280.
 .TP
 .B passive
 Enables the "passive" option in the LCP.  With this option, pppd will
@@ -164,21 +190,21 @@
 not required.  If a local and/or remote IP address is specified with
 this option, pppd
 will not accept a different value from the peer in the IPCP
-negotiation, unless the \fIipcp-accept-local\fR and/or
-\fIipcp-accept-remote\fR options are given, respectively.
+negotiation, unless the \fIipcp\-accept\-local\fR and/or
+\fIipcp\-accept\-remote\fR options are given, respectively.
 .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
 IPv6 addresses (e.g. ::dead:beef). If the
-\fIipv6cp-use-ipaddr\fR
+\fIipv6cp\-use\-ipaddr\fR
 option is given, the local identifier is the local IPv4 address (see above).
-On systems which supports a unique persistent id, such as EUI-48 derived
-from the Ethernet MAC address, \fIipv6cp-use-persistent\fR option can be
+On systems which supports a unique persistent id, such as EUI\-48 derived
+from the Ethernet MAC address, \fIipv6cp\-use\-persistent\fR option can be
 used to replace the \fIipv6 <local>,<remote>\fR option. Otherwise the 
 identifier is randomized.
 .TP
-.B active-filter \fIfilter-expression
+.B active\-filter \fIfilter\-expression
 Specifies a packet filter to be applied to data packets to determine
 which packets are to be regarded as link activity, and therefore reset
 the idle timer, or cause the link to be brought up in demand-dialling
@@ -186,20 +212,27 @@
 \fBidle\fR option if there are packets being sent or received
 regularly over the link (for example, routing information packets)
 which would otherwise prevent the link from ever appearing to be idle.
-The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+The \fIfilter\-expression\fR syntax is as described for tcpdump(1),
 except that qualifiers which are inappropriate for a PPP link, such as
 \fBether\fR and \fBarp\fR, are not permitted.  Generally the filter
 expression should be enclosed in single-quotes to prevent whitespace
 in the expression from being interpreted by the shell. This option
-is currently only available under NetBSD, and then only
-if both the kernel and pppd were compiled with PPP_FILTER defined.
+is currently only available under Linux, and requires that the kernel
+was configured to include PPP filtering support (CONFIG_PPP_FILTER).
+Note that it
+is possible to apply different constraints to incoming and outgoing
+packets using the \fBinbound\fR and \fBoutbound\fR qualifiers.
 .TP
-.B allow-ip \fIaddress(es)
+.B allow\-ip \fIaddress(es)
 Allow peers to use the given IP address or subnet without
 authenticating themselves.  The parameter is parsed as for each
 element of the list of allowed IP addresses in the secrets files (see
 the AUTHENTICATION section below).
 .TP
+.B allow\-number \fInumber
+Allow peers to connect from the given telephone number.  A trailing
+`*' character will match all numbers beginning with the leading part.
+.TP
 .B bsdcomp \fInr,nt
 Request that the peer compress packets that it sends, using the
 BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and
@@ -223,20 +256,28 @@
 bi-directional flow control. The sacrifice is that this flow
 control mode does not permit using DTR as a modem control line.
 .TP
-.B chap-interval \fIn
+.B chap\-interval \fIn
 If this option is given, pppd will rechallenge the peer every \fIn\fR
 seconds.
 .TP
-.B chap-max-challenge \fIn
+.B chap\-max\-challenge \fIn
 Set the maximum number of CHAP challenge transmissions to \fIn\fR
 (default 10).
 .TP
-.B chap-restart \fIn
+.B chap\-restart \fIn
 Set the CHAP restart interval (retransmission timeout for challenges)
 to \fIn\fR seconds (default 3).
 .TP
-.B connect-delay \fIn
-Wait for up \fIn\fR milliseconds after the connect script finishes for
+.B child\-timeout \fIn
+When exiting, wait for up to \fIn\fR seconds for any child processes
+(such as the command specified with the \fBpty\fR command) to exit
+before exiting.  At the end of the timeout, pppd will send a SIGTERM
+signal to any remaining child processes and exit.  A value of 0 means
+no timeout, that is, pppd will wait until all child processes have
+exited.
+.TP
+.B connect\-delay \fIn
+Wait for up to \fIn\fR milliseconds after the connect script finishes for
 a valid PPP packet from the peer.  At the end of this time, or when a
 valid PPP packet is received from the peer, pppd will commence
 negotiation by sending its first LCP packet.  The default value is
@@ -251,11 +292,11 @@
 \fIdebug\fR.  This information can be directed to a file by setting up
 /etc/syslog.conf appropriately (see syslog.conf(5)).
 .TP
-.B default-asyncmap
+.B default\-asyncmap
 Disable asyncmap negotiation, forcing all control characters to be
 escaped for both the transmit and the receive direction.
 .TP
-.B default-mru
+.B default\-mru
 Disable MRU [Maximum Receive Unit] negotiation.  With this option,
 pppd will use the default MRU value of 1500 bytes for both the
 transmit and receive direction.
@@ -327,7 +368,29 @@
 network interface.  This option is currently only available under
 Linux.
 .TP
-.B hide-password
+.B eap\-interval \fIn
+If this option is given and pppd authenticates the peer with EAP
+(i.e., is the server), pppd will restart EAP authentication every
+\fIn\fR seconds.  For EAP SRP\-SHA1, see also the \fBsrp\-interval\fR
+option, which enables lightweight rechallenge.
+.TP
+.B eap\-max\-rreq \fIn
+Set the maximum number of EAP Requests to which pppd will respond (as
+a client) without hearing EAP Success or Failure.  (Default is 20.)
+.TP
+.B eap\-max\-sreq \fIn
+Set the maximum number of EAP Requests that pppd will issue (as a
+server) while attempting authentication.  (Default is 10.)
+.TP
+.B eap\-restart \fIn
+Set the retransmit timeout for EAP Requests when acting as a server
+(authenticator).  (Default is 3 seconds.)
+.TP
+.B eap\-timeout \fIn
+Set the maximum time to wait for the peer to send an EAP Request when
+acting as a client (authenticatee).  (Default is 20 seconds.)
+.TP
+.B hide\-password
 When logging the contents of PAP packets, this option causes pppd to
 exclude the password string from the log.  This is the default.
 .TP
@@ -342,52 +405,53 @@
 seconds.  The link is idle when no data packets (i.e. IP packets) are
 being sent or received.  Note: it is not advisable to use this option
 with the \fIpersist\fR option without the \fIdemand\fR option.
-If the \fBactive-filter\fR
+If the \fBactive\-filter\fR
 option is given, data packets which are rejected by the specified
 activity filter also count as the link being idle.
 .TP
-.B ipcp-accept-local
+.B ipcp\-accept\-local
 With this option, pppd will accept the peer's idea of our local IP
 address, even if the local IP address was specified in an option.
 .TP
-.B ipcp-accept-remote
+.B ipcp\-accept\-remote
 With this option, pppd will accept the peer's idea of its (remote) IP
 address, even if the remote IP address was specified in an option.
 .TP
-.B ipcp-max-configure \fIn
+.B ipcp\-max\-configure \fIn
 Set the maximum number of IPCP configure-request transmissions to
 \fIn\fR (default 10).
 .TP
-.B ipcp-max-failure \fIn
+.B ipcp\-max\-failure \fIn
 Set the maximum number of IPCP configure-NAKs returned before starting
 to send configure-Rejects instead to \fIn\fR (default 10).
 .TP
-.B ipcp-max-terminate \fIn
+.B ipcp\-max\-terminate \fIn
 Set the maximum number of IPCP terminate-request transmissions to
 \fIn\fR (default 3).
 .TP
-.B ipcp-restart \fIn
+.B ipcp\-restart \fIn
 Set the IPCP restart interval (retransmission timeout) to \fIn\fR
 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-max-configure \fIn
+.B ipv6cp\-max\-configure \fIn
 Set the maximum number of IPv6CP configure-request transmissions to
 \fIn\fR (default 10).
 .TP
-.B ipv6cp-max-failure \fIn
+.B ipv6cp\-max\-failure \fIn
 Set the maximum number of IPv6CP configure-NAKs returned before starting
 to send configure-Rejects instead to \fIn\fR (default 10).
 .TP
-.B ipv6cp-max-terminate \fIn
+.B ipv6cp\-max\-terminate \fIn
 Set the maximum number of IPv6CP terminate-request transmissions to
 \fIn\fR (default 3).
 .TP
-.B ipv6cp-restart \fIn
+.B ipv6cp\-restart \fIn
 Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR
 seconds (default 3).
 .TP
@@ -396,61 +460,61 @@
 supported under Linux, and only if your kernel has been configured to
 include IPX support.
 .TP
-.B ipx-network \fIn
+.B ipx\-network \fIn
 Set the IPX network number in the IPXCP configure request frame to
 \fIn\fR, a hexadecimal number (without a leading 0x).  There is no
 valid default.  If this option is not specified, the network number is
 obtained from the peer.  If the peer does not have the network number,
 the IPX protocol will not be started.
 .TP
-.B ipx-node \fIn\fB:\fIm
+.B ipx\-node \fIn\fB:\fIm
 Set the IPX node numbers. The two node numbers are separated from each
 other with a colon character. The first number \fIn\fR is the local
 node number. The second number \fIm\fR is the peer's node number. Each
 node number is a hexadecimal number, at most 10 digits long. The node
-numbers on the ipx-network must be unique. There is no valid
+numbers on the ipx\-network must be unique. There is no valid
 default. If this option is not specified then the node numbers are
 obtained from the peer.
 .TP
-.B ipx-router-name \fI<string>
+.B ipx\-router\-name \fI<string>
 Set the name of the router. This is a string and is sent to the peer
 as information data.
 .TP
-.B ipx-routing \fIn
+.B ipx\-routing \fIn
 Set the routing protocol to be received by this option. More than one
-instance of \fIipx-routing\fR may be specified. The '\fInone\fR'
-option (0) may be specified as the only instance of ipx-routing. The
+instance of \fIipx\-routing\fR may be specified. The '\fInone\fR'
+option (0) may be specified as the only instance of ipx\-routing. The
 values may be \fI0\fR for \fINONE\fR, \fI2\fR for \fIRIP/SAP\fR, and
 \fI4\fR for \fINLSP\fR.
 .TP
-.B ipxcp-accept-local
-Accept the peer's NAK for the node number specified in the ipx-node
+.B ipxcp\-accept\-local
+Accept the peer's NAK for the node number specified in the ipx\-node
 option. If a node number was specified, and non-zero, the default is
 to insist that the value be used. If you include this option then you
 will permit the peer to override the entry of the node number.
 .TP
-.B ipxcp-accept-network
+.B ipxcp\-accept\-network
 Accept the peer's NAK for the network number specified in the
-ipx-network option. If a network number was specified, and non-zero, the
+ipx\-network option. If a network number was specified, and non-zero, the
 default is to insist that the value be used. If you include this
 option then you will permit the peer to override the entry of the node
 number.
 .TP
-.B ipxcp-accept-remote
+.B ipxcp\-accept\-remote
 Use the peer's network number specified in the configure request
 frame. If a node number was specified for the peer and this option was
 not specified, the peer will be forced to use the value which you have
 specified.
 .TP
-.B ipxcp-max-configure \fIn
+.B ipxcp\-max\-configure \fIn
 Set the maximum number of IPXCP configure request frames which the
 system will send to \fIn\fR. The default is 10.
 .TP
-.B ipxcp-max-failure \fIn
+.B ipxcp\-max\-failure \fIn
 Set the maximum number of IPXCP NAK frames which the local system will
 send before it rejects the options. The default value is 3.
 .TP
-.B ipxcp-max-terminate \fIn
+.B ipxcp\-max\-terminate \fIn
 Set the maximum nuber of IPXCP terminate request frames before the
 local system considers that the peer is not listening to them. The
 default value is 3.
@@ -475,42 +539,42 @@
 dynamic IP address option (i.e. set /proc/sys/net/ipv4/ip_dynaddr to
 1) in demand mode if the local address changes.
 .TP
-.B lcp-echo-failure \fIn
+.B lcp\-echo\-failure \fIn
 If this option is given, pppd will presume the peer to be dead
-if \fIn\fR LCP echo-requests are sent without receiving a valid LCP
-echo-reply.  If this happens, pppd will terminate the
+if \fIn\fR LCP echo\-requests are sent without receiving a valid LCP
+echo\-reply.  If this happens, pppd will terminate the
 connection.  Use of this option requires a non-zero value for the
-\fIlcp-echo-interval\fR parameter.  This option can be used to enable
+\fIlcp\-echo\-interval\fR parameter.  This option can be used to enable
 pppd to terminate after the physical connection has been broken
 (e.g., the modem has hung up) in situations where no hardware modem
 control lines are available.
 .TP
-.B lcp-echo-interval \fIn
-If this option is given, pppd will send an LCP echo-request frame to
+.B lcp\-echo\-interval \fIn
+If this option is given, pppd will send an LCP echo\-request frame to
 the peer every \fIn\fR seconds.  Normally the peer should respond to
-the echo-request by sending an echo-reply.  This option can be used
-with the \fIlcp-echo-failure\fR option to detect that the peer is no
+the echo\-request by sending an echo\-reply.  This option can be used
+with the \fIlcp\-echo\-failure\fR option to detect that the peer is no
 longer connected.
 .TP
-.B lcp-max-configure \fIn
+.B lcp\-max\-configure \fIn
 Set the maximum number of LCP configure-request transmissions to
 \fIn\fR (default 10).
 .TP
-.B lcp-max-failure \fIn
+.B lcp\-max\-failure \fIn
 Set the maximum number of LCP configure-NAKs returned before starting
 to send configure-Rejects instead to \fIn\fR (default 10).
 .TP
-.B lcp-max-terminate \fIn
+.B lcp\-max\-terminate \fIn
 Set the maximum number of LCP terminate-request transmissions to
 \fIn\fR (default 3).
 .TP
-.B lcp-restart \fIn
+.B lcp\-restart \fIn
 Set the LCP restart interval (retransmission timeout) to \fIn\fR
 seconds (default 3).
 .TP
 .B linkname \fIname\fR
 Sets the logical name of the link to \fIname\fR.  Pppd will create a
-file named \fBppp-\fIname\fB.pid\fR in /var/run (or /etc/ppp on some
+file named \fBppp\-\fIname\fB.pid\fR in /var/run (or /etc/ppp on some
 systems) containing its process ID.  This can be useful in determining
 which instance of pppd is responsible for the link to a given peer
 system.  This is a privileged option.
@@ -518,7 +582,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
@@ -536,7 +601,7 @@
 .B login
 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
+must have an entry in the /etc/ppp/pap\-secrets file as well as the
 system password database to be allowed access.
 .TP
 .B maxconnect \fIn
@@ -561,6 +626,30 @@
 Enables the use of PPP multilink; this is an alias for the `multilink'
 option.  This option is currently only available under Linux.
 .TP
+.B mppc
+Enables MPPC (Microsoft Point to Point Compression).  This is the default.
+.TP
+.B mppe \fIsubopt1[,subopt2[,subopt3[..]]]
+Modify MPPE (Microsoft Point to Point Encryption) parameters. In order
+for MPPE to successfully come up, you must have authenticated with either
+MS-CHAP or MS-CHAPv2. By default MPPE is optional, it means that pppd will
+not propose MPPE to the peer, but will negotiate MPPE if peer wants that.
+You can change this using \fIrequired\fR suboption.
+This option is presently only supported under Linux, and only if your
+kernel has been configured to include MPPE support.
+.IP
+MPPE suboptions:
+.br
+\fIrequired\fR - require MPPE; disconnect if peer doesn't support it,
+.br
+\fIstateless\fR - try to negotiate stateless mode; default is stateful,
+.br
+\fIno40\fR - disable 40 bit keys,
+.br
+\fIno56\fR - disable 56 bit keys,
+.br
+\fIno128\fR - disable 128 bit keys
+.TP
 .B mpshortseq
 Enables the use of short (12-bit) sequence numbers in multilink
 headers, as opposed to 24-bit sequence numbers.  This option is only
@@ -574,15 +663,15 @@
 currently only available under Linux, and only has any effect if
 multilink is enabled (see the multilink option).
 .TP
-.B ms-dns \fI<addr>
+.B ms\-dns \fI<addr>
 If pppd is acting as a server for Microsoft Windows clients, this
 option allows pppd to supply one or two DNS (Domain Name Server)
 addresses to the clients.  The first instance of this option specifies
 the primary DNS address; the second instance (if given) specifies the
 secondary DNS address.  (This option was present in some older
-versions of pppd under the name \fBdns-addr\fR.)
+versions of pppd under the name \fBdns\-addr\fR.)
 .TP
-.B ms-wins \fI<addr>
+.B ms\-wins \fI<addr>
 If pppd is acting as a server for Microsoft Windows or "Samba"
 clients, this option allows pppd to supply one or two WINS (Windows
 Internet Name Services) server addresses to the clients.  The first
@@ -607,17 +696,6 @@
 local system to the peer.  (Note that pppd does not append the domain
 name to \fIname\fR.)
 .TP
-.B netmask \fIn
-Set the interface netmask to \fIn\fR, a 32 bit netmask in "decimal dot"
-notation (e.g. 255.255.255.0).  If this option is given, the value
-specified is ORed with the default netmask.  The default netmask is
-chosen based on the negotiated remote IP address; it is the
-appropriate network mask for the class of the remote IP address, ORed
-with the netmasks for any non point-to-point network interfaces in the
-system which are on the same network.  (Note: on some platforms, pppd
-will always use 255.255.255.255 for the netmask, if that is the only
-appropriate value for a point-to-point interface.)
-.TP
 .B noaccomp
 Disable Address/Control compression in both directions (send and
 receive).
@@ -690,6 +768,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.
@@ -703,6 +786,12 @@
 Disables the use of PPP multilink.  This option is currently only
 available under Linux.
 .TP
+.B nomppc
+Disables MPPC (Microsoft Point to Point Compression).
+.TP
+.B nomppe
+Disables MPPE (Microsoft Point to Point Encryption).
+.TP
 .B nompshortseq
 Disables the use of short (12-bit) sequence numbers in the PPP
 multilink protocol, forcing the use of 24-bit sequence numbers.  This
@@ -723,7 +812,7 @@
 specified.
 .TP
 .B nopredictor1
-Do not accept or agree to Predictor-1 compression.
+Do not accept or agree to Predictor\-1 compression.
 .TP
 .B noproxyarp
 Disable the \fIproxyarp\fR option.  The system administrator who
@@ -754,47 +843,56 @@
 ask the peer to do so.
 .TP
 .B papcrypt
-Indicates that all secrets in the /etc/ppp/pap-secrets file which are
+Indicates that all secrets in the /etc/ppp/pap\-secrets file which are
 used for checking the identity of the peer are encrypted, and thus
 pppd should not accept a password which, before encryption, is
-identical to the secret from the /etc/ppp/pap-secrets file.
+identical to the secret from the /etc/ppp/pap\-secrets file.
 .TP
-.B pap-max-authreq \fIn
+.B pap\-max\-authreq \fIn
 Set the maximum number of PAP authenticate-request transmissions to
 \fIn\fR (default 10).
 .TP
-.B pap-restart \fIn
+.B pap\-restart \fIn
 Set the PAP restart interval (retransmission timeout) to \fIn\fR
 seconds (default 3).
 .TP
-.B pap-timeout \fIn
+.B pap\-timeout \fIn
 Set the maximum time that pppd will wait for the peer to authenticate
 itself with PAP to \fIn\fR seconds (0 means no limit).
 .TP
-.B pass-filter \fIfilter-expression
+.B pass\-filter \fIfilter\-expression
 Specifies a packet filter to applied to data packets being sent or
 received to determine which packets should be allowed to pass.
 Packets which are rejected by the filter are silently discarded.  This
 option can be used to prevent specific network daemons (such as
-routed) using up link bandwidth, or to provide a basic firewall
+routed) using up link bandwidth, or to provide a very basic firewall
 capability.
-The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+The \fIfilter\-expression\fR syntax is as described for tcpdump(1),
 except that qualifiers which are inappropriate for a PPP link, such as
 \fBether\fR and \fBarp\fR, are not permitted.  Generally the filter
 expression should be enclosed in single-quotes to prevent whitespace
 in the expression from being interpreted by the shell.  Note that it
 is possible to apply different constraints to incoming and outgoing
 packets using the \fBinbound\fR and \fBoutbound\fR qualifiers. This
-option is currently only available under NetBSD, and then only if both
-the kernel and pppd were compiled with PPP_FILTER defined.
+option is currently only available under Linux, and requires that the
+kernel was configured to include PPP filtering support (CONFIG_PPP_FILTER).
+.TP
+.B password \fIpassword\-string
+Specifies the password to use for authenticating to the peer.  Use
+of this option is discouraged, as the password is likely to be visible
+to other users on the system (for example, by using ps(1)).
 .TP
 .B persist
 Do not exit after a connection is terminated; instead try to reopen
-the connection.
+the connection. The \fBmaxfail\fR option still has an effect on
+persistent connections.
 .TP
 .B plugin \fIfilename
 Load the shared library object file \fIfilename\fR as a plugin.  This
-is a privileged option.
+is a privileged option.  If \fIfilename\fR does not contain a slash
+(/), pppd will look in the \fB/usr/lib/pppd/\fIversion\fR directory
+for the plugin, where
+\fIversion\fR is the version number of pppd (for example, 2.4.2).
 .TP
 .B predictor1
 Request that the peer compress frames that it sends using Predictor-1
@@ -802,12 +900,12 @@
 if requested.  This option has no effect unless the kernel driver
 supports Predictor-1 compression.
 .TP
-.B privgroup \fIgroup-name
-Allows members of group \fIgroup-name\fR to use privileged options.
+.B privgroup \fIgroup\-name
+Allows members of group \fIgroup\-name\fR to use privileged options.
 This is a privileged option.  Use of this option requires care as
-there is no guarantee that members of \fIgroup-name\fR cannot use pppd
+there is no guarantee that members of \fIgroup\-name\fR cannot use pppd
 to become root themselves.  Consider it equivalent to putting the
-members of \fIgroup-name\fR in the kmem or disk group.
+members of \fIgroup\-name\fR in the kmem or disk group.
 .TP
 .B proxyarp
 Add an entry to this system's ARP [Address Resolution Protocol] table
@@ -825,7 +923,7 @@
 \fIrecord\fR option is used in conjuction with the \fIpty\fR option,
 the child process will have pipes on its standard input and output.)
 .TP
-.B receive-all
+.B receive\-all
 With this option, pppd will accept all control characters from the
 peer, including those marked in the receive asyncmap.  Without this
 option, pppd will discard those characters as specified in RFC1662.
@@ -845,23 +943,52 @@
 Set the assumed name of the remote system for authentication purposes
 to \fIname\fR.
 .TP
-.B refuse-chap
+.B remotenumber \fInumber
+Set the assumed telephone number of the remote system for authentication
+purposes to \fInumber\fR.
+.TP
+.B refuse\-chap
 With this option, pppd will not agree to authenticate itself to the
 peer using CHAP.
 .TP
-.B refuse-pap
+.B refuse\-mschap
+With this option, pppd will not agree to authenticate itself to the
+peer using MS\-CHAP.
+.TP
+.B refuse\-mschap\-v2
+With this option, pppd will not agree to authenticate itself to the
+peer using MS\-CHAPv2.
+.TP
+.B refuse\-eap
+With this option, pppd will not agree to authenticate itself to the
+peer using EAP.
+.TP
+.B refuse\-pap
 With this option, pppd will not agree to authenticate itself to the
 peer using PAP.
 .TP
-.B require-chap
+.B require\-chap
 Require the peer to authenticate itself using CHAP [Challenge
 Handshake Authentication Protocol] authentication.
 .TP
-.B require-pap
+.TP
+.B require\-mschap
+Require the peer to authenticate itself using MS\-CHAP [Microsoft Challenge
+Handshake Authentication Protocol] authentication.
+.TP
+.B require\-mschap\-v2
+Require the peer to authenticate itself using MS\-CHAPv2 [Microsoft Challenge
+Handshake Authentication Protocol, Version 2] authentication.
+.TP
+.B require\-eap
+Require the peer to authenticate itself using EAP [Extensible
+Authentication Protocol] authentication.
+.TP
+.B require\-pap
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
 .TP
-.B show-password
+.B show\-password
 When logging the contents of PAP packets, this option causes pppd to
 show the password string in the log message.
 .TP
@@ -870,12 +997,37 @@
 connection until a valid LCP packet is received from the peer (as for
 the `passive' option with ancient versions of pppd).
 .TP
+.B srp\-interval \fIn
+If this parameter is given and pppd uses EAP SRP\-SHA1 to authenticate
+the peer (i.e., is the server), then pppd will use the optional
+lightweight SRP rechallenge mechanism at intervals of \fIn\fR
+seconds.  This option is faster than \fBeap\-interval\fR
+reauthentication because it uses a hash\-based mechanism and does not
+derive a new session key.
+.TP
+.B srp\-pn\-secret \fIstring
+Set the long-term pseudonym-generating secret for the server.  This
+value is optional and if set, needs to be known at the server
+(authenticator) side only, and should be different for each server (or
+poll of identical servers).  It is used along with the current date to
+generate a key to encrypt and decrypt the client's identity contained
+in the pseudonym.
+.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
+file any pseudonym offered by the peer during authentication.
+.TP
 .B sync
 Use synchronous HDLC serial encoding instead of asynchronous.
 The device used by pppd with this option must have sync support.
 Currently supports Microgate SyncLink adapters
 under Linux and FreeBSD 2.2.8 and later.
 .TP
+.B unit \fInum
+Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
+connections.
+.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
@@ -890,8 +1042,9 @@
 .TP
 .B usepeerdns
 Ask the peer for up to 2 DNS server addresses.  The addresses supplied
-by the peer (if any) are passed to the /etc/ppp/ip-up script in the
-environment variables DNS1 and DNS2.  In addition, pppd will create an
+by the peer (if any) are passed to the /etc/ppp/ip\-up script in the
+environment variables DNS1 and DNS2, and the environment variable
+USEPEERDNS will be set to 1.  In addition, pppd will create an
 /etc/ppp/resolv.conf file containing one or two nameserver lines with
 the address(es) supplied by the peer.
 .TP
@@ -899,7 +1052,7 @@
 Sets the name used for authenticating the local system to the peer to
 \fIname\fR.
 .TP
-.B vj-max-slots \fIn
+.B vj\-max\-slots \fIn
 Sets the number of connection slots to be used by the Van Jacobson
 TCP/IP header compression and decompression code to \fIn\fR, which
 must be between 2 and 16 (inclusive).
@@ -986,15 +1139,18 @@
 correspond to the internet hostnames of the peers, but this is not
 essential.
 .LP
-At present, pppd supports two authentication protocols: the Password
-Authentication Protocol (PAP) and the Challenge Handshake
-Authentication Protocol (CHAP).  PAP involves the client sending its
-name and a cleartext password to the server to authenticate itself.
-In contrast, the server initiates the CHAP authentication exchange by
-sending a challenge to the client (the challenge packet includes the
-server's name).  The client must respond with a response which
-includes its name plus a hash value derived from the shared secret and
-the challenge, in order to prove that it knows the secret.
+At present, pppd supports three authentication protocols: the Password
+Authentication Protocol (PAP), Challenge Handshake Authentication
+Protocol (CHAP), and Extensible Authentication Protocol (EAP).  PAP
+involves the client sending its name and a cleartext password to the
+server to authenticate itself.  In contrast, the server initiates the
+CHAP authentication exchange by sending a challenge to the client (the
+challenge packet includes the server's name).  The client must respond
+with a response which includes its name plus a hash value derived from
+the shared secret and the challenge, in order to prove that it knows
+the secret.  EAP supports CHAP-style authentication, and also includes
+the SRP\-SHA1 mechanism, which is resistant to dictionary-based attacks
+and does not require a cleartext password on the server side.
 .LP
 The PPP protocol, being symmetrical, allows both peers to require the
 other to authenticate itself.  In that case, two separate and
@@ -1008,8 +1164,10 @@
 if it has no secrets which could be used to do so.
 .LP
 Pppd stores secrets for use in authentication in secrets
-files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP).
-Both secrets files have the same format.  The secrets files can
+files (/etc/ppp/pap\-secrets for PAP, /etc/ppp/chap\-secrets for CHAP,
+MS\-CHAP, MS\-CHAPv2, and EAP MD5-Challenge, and /etc/ppp/srp\-secrets
+for EAP SRP\-SHA1).
+All secrets files have the same format.  The secrets files can
 contain secrets for pppd to use in authenticating itself to other
 systems, as well as secrets for pppd to use when authenticating other
 systems to itself.
@@ -1035,7 +1193,7 @@
 .LP
 Any following words on the same line are taken to be a list of
 acceptable IP addresses for that client.  If there are only 3 words on
-the line, or if the first word is "-", then all IP addresses are
+the line, or if the first word is "\-", then all IP addresses are
 disallowed.  To allow any address, use "*".  A word starting with "!"
 indicates that the specified address is \fInot\fR acceptable.  An
 address may be followed by "/" and a number \fIn\fR, to indicate a
@@ -1054,7 +1212,9 @@
 name of the local system defaults to the hostname, with the domain
 name appended if the \fIdomain\fR option is used.  This default can be
 overridden with the \fIname\fR option, except when the
-\fIusehostname\fR option is used.
+\fIusehostname\fR option is used.  (For EAP SRP\-SHA1, see the
+srp\-entry(8) utility for generating proper validator entries to be
+used in the "secret" field.)
 .LP
 When pppd is choosing a secret to use in authenticating itself to the
 peer, it first determines what name it is going to use to identify
@@ -1063,14 +1223,14 @@
 the name of the local system, determined as described in the previous
 paragraph.  Then pppd looks for a secret with this name in the first
 field and the peer's name in the second field.  Pppd will know the
-name of the peer if CHAP authentication is being used, because the
-peer will have sent it in the challenge packet.  However, if PAP is being
-used, pppd will have to determine the peer's name from the options
-specified by the user.  The user can specify the peer's name directly
-with the \fIremotename\fR option.  Otherwise, if the remote IP address
-was specified by a name (rather than in numeric form), that name will
-be used as the peer's name.  Failing that, pppd will use the null
-string as the peer's name.
+name of the peer if CHAP or EAP authentication is being used, because
+the peer will have sent it in the challenge packet.  However, if PAP
+is being used, pppd will have to determine the peer's name from the
+options specified by the user.  The user can specify the peer's name
+directly with the \fIremotename\fR option.  Otherwise, if the remote
+IP address was specified by a name (rather than in numeric form), that
+name will be used as the peer's name.  Failing that, pppd will use the
+null string as the peer's name.
 .LP
 When authenticating the peer with PAP, the supplied password is first
 compared with the secret from the secrets file.  If the password
@@ -1082,10 +1242,10 @@
 .LP
 Furthermore, if the \fIlogin\fR option was specified, the username and
 password are also checked against the system password database.  Thus,
-the system administrator can set up the pap-secrets file to allow PPP
+the system administrator can set up the pap\-secrets file to allow PPP
 access only to certain users, and to restrict the set of IP addresses
 that each user can use.  Typically, when using the \fIlogin\fR option,
-the secret in /etc/ppp/pap-secrets would be "", which will match any
+the secret in /etc/ppp/pap\-secrets would be "", which will match any
 password supplied by the peer.  This avoids the need to have the same
 secret in two places.
 .LP
@@ -1102,7 +1262,7 @@
 authentication.  If the peer refuses to authenticate itself when
 requested, pppd takes that as equivalent to authenticating with PAP
 using the empty string for the username and password.  Thus, by adding
-a line to the pap-secrets file which specifies the empty string for
+a line to the pap\-secrets file which specifies the empty string for
 the client and password, it is possible to allow restricted access to
 hosts which refuse to authenticate themselves.
 .SH ROUTING
@@ -1115,7 +1275,7 @@
 modification to routing tables and/or ARP (Address Resolution
 Protocol) tables.  In most cases the \fIdefaultroute\fR and/or
 \fIproxyarp\fR options are sufficient for this, but in some cases
-further intervention is required.  The /etc/ppp/ip-up script can be
+further intervention is required.  The /etc/ppp/ip\-up script can be
 used for this.
 .LP
 Sometimes it is desirable to add a default route through the remote
@@ -1154,17 +1314,17 @@
 endpoint discriminator is a block of data which is hopefully unique
 for each peer.  Several types of data can be used, including
 locally-assigned strings of bytes, IP addresses, MAC addresses,
-randomly strings of bytes, or E-164 phone numbers.  The endpoint
+randomly strings of bytes, or E\-164 phone numbers.  The endpoint
 discriminator sent to the peer by pppd can be set using the endpoint
 option.
 .LP
-In circumstances the peer may send no endpoint discriminator or a
-non-unique value.  The optional bundle option adds an extra string
-which is added to the peer's endpoint discriminator and authenticated
-identity when matching up links to be joined together in a bundle.
-The bundle option can also be used to allow the establishment of
-multiple bundles between the local system and the peer.  Pppd uses a
-TDB database in /var/run/pppd.tdb to match up links.
+In some circumstances the peer may send no endpoint discriminator or a
+non-unique value.  The bundle option adds an extra string which is
+added to the peer's endpoint discriminator and authenticated identity
+when matching up links to be joined together in a bundle.  The bundle
+option can also be used to allow the establishment of multiple bundles
+between the local system and the peer.  Pppd uses a TDB database in
+/var/run/pppd2.tdb to match up links.
 .LP
 Assuming that multilink is enabled and the peer is willing to
 negotiate multilink, then when pppd is invoked to bring up the first
@@ -1172,8 +1332,19 @@
 the peer and create a new bundle, that is, another ppp network
 interface unit.  When another pppd is invoked to bring up another link
 to the peer, it will detect the existing bundle and join its link to
-it.  Currently, if the first pppd terminates (for example, because of
-a hangup or a received signal) the bundle is destroyed.
+it.
+.LP
+If the first link terminates (for example, because of a hangup or a
+received LCP terminate-request) the bundle is not destroyed unless
+there are no other links remaining in the bundle.  Rather than
+exiting, the first pppd keeps running after its link terminates, until
+all the links in the bundle have terminated.  If the first pppd
+receives a SIGTERM or SIGINT signal, it will destroy the bundle and
+send a SIGHUP to the pppd processes for each of the links in the
+bundle.  If the first pppd receives a SIGHUP signal, it will terminate
+its link but not the bundle.
+.LP
+Note: demand mode is not currently supported with multilink.
 .SH EXAMPLES
 .LP
 The following examples assume that the /etc/ppp/options file contains
@@ -1190,12 +1361,12 @@
 .IP
 ttyS0 19200 crtscts
 .br
-connect '/usr/sbin/chat -v -f /etc/ppp/chat-isp'
+connect '/usr/sbin/chat \-v \-f /etc/ppp/chat\-isp'
 .br
 noauth
 .LP
 In this example, we are using chat to dial the ISP's modem and go
-through any logon sequence required.  The /etc/ppp/chat-isp file
+through any logon sequence required.  The /etc/ppp/chat\-isp file
 contains the script used by chat; it could for example contain
 something like this:
 .IP
@@ -1223,7 +1394,7 @@
 .br
 "ispts" "\\q^Uppp"
 .br
-"~-^Uppp-~"
+"~\-^Uppp\-~"
 .LP
 See the chat(8) man page for details of chat scripts.
 .LP
@@ -1236,16 +1407,18 @@
 .LP
 To allow a user to use the PPP facilities, you need to allocate an IP
 address for that user's machine and create an entry in
-/etc/ppp/pap-secrets or /etc/ppp/chap-secrets (depending on which
-authentication method the PPP implementation on the user's machine
-supports), so that the user's
-machine can authenticate itself.  For example, if Joe has a machine
-called "joespc" which is to be allowed to dial in to the machine
-called "server" and use the IP address joespc.my.net, you would add an
-entry like this to /etc/ppp/pap-secrets or /etc/ppp/chap-secrets:
+/etc/ppp/pap\-secrets, /etc/ppp/chap\-secrets, or /etc/ppp/srp\-secrets
+(depending on which authentication method the PPP implementation on
+the user's machine supports), so that the user's machine can
+authenticate itself.  For example, if Joe has a machine called
+"joespc" that is to be allowed to dial in to the machine called
+"server" and use the IP address joespc.my.net, you would add an entry
+like this to /etc/ppp/pap\-secrets or /etc/ppp/chap\-secrets:
 .IP
 joespc	server	"joe's secret"	joespc.my.net
 .LP
+(See srp\-entry(8) for a means to generate the server's entry when
+SRP\-SHA1 is in use.)
 Alternatively, you can create a username called (for example) "ppp",
 whose login shell is pppd and whose home directory is /etc/ppp.
 Options to be used when pppd is run this way can be put in
@@ -1264,13 +1437,15 @@
 .SH DIAGNOSTICS
 .LP
 Messages are sent to the syslog daemon using facility LOG_DAEMON.
-(This can be overriden by recompiling pppd with the macro
-LOG_PPP defined as the desired facility.)  In order to see the error
-and debug messages, you will need to edit your /etc/syslog.conf file
-to direct the messages to the desired output device or file.
+(This can be overridden by recompiling pppd with the macro
+LOG_PPP defined as the desired facility.)  See the syslog(8)
+documentation for details of where the syslog daemon will write the
+messages.  On most systems, the syslog daemon uses the
+/etc/syslog.conf file to specify the destination(s) for syslog
+messages.  You may need to edit that file to suit.
 .LP
 The \fIdebug\fR option causes the contents of all control packets sent
-or received to be logged, that is, all LCP, PAP, CHAP or IPCP packets.
+or received to be logged, that is, all LCP, PAP, CHAP, EAP, or IPCP packets.
 This can be useful if the PPP negotiation does not succeed or if
 authentication fails.
 If debugging is enabled at compile time, the \fIdebug\fR option also
@@ -1356,7 +1531,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
@@ -1410,61 +1586,79 @@
 .TP
 .B LINKNAME
 The logical name of the link, set with the \fIlinkname\fR option.
+.TP
+.B DNS1
+If the peer supplies DNS server addresses, this variable is set to the
+first DNS server address supplied.
+.TP
+.B DNS2
+If the peer supplies DNS server addresses, this variable is set to the
+second DNS server address supplied.
 .P
 Pppd invokes the following scripts, if they exist.  It is not an error
 if they don't exist.
 .TP
-.B /etc/ppp/auth-up
+.B /etc/ppp/auth\-up
 A program or script which is executed after the remote system
 successfully authenticates itself.  It is executed with the parameters
 .IP
-\fIinterface-name peer-name user-name tty-device speed\fR
+\fIinterface\-name peer\-name user\-name tty\-device speed\fR
 .IP
 Note that this script is not executed if the peer doesn't authenticate
 itself, for example when the \fInoauth\fR option is used.
 .TP
-.B /etc/ppp/auth-down
+.B /etc/ppp/auth\-down
 A program or script which is executed when the link goes down, if
-/etc/ppp/auth-up was previously executed.  It is executed in the same
-manner with the same parameters as /etc/ppp/auth-up.
+/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-up
+.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
 executed with the parameters
 .IP
-\fIinterface-name tty-device speed local-IP-address
-remote-IP-address ipparam\fR
+\fIinterface\-name tty\-device speed local\-IP\-address
+remote\-IP\-address ipparam\fR
 .TP
-.B /etc/ppp/ip-down
+.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
-invoked in the same manner and with the same parameters as the ip-up
+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
-.B /etc/ppp/ipv6-up
-Like /etc/ppp/ip-up, except that it is executed when the link is available 
+.B /etc/ppp/ipv6\-up
+Like /etc/ppp/ip\-up, except that it is executed when the link is available 
 for sending and receiving IPv6 packets. It is executed with the parameters
 .IP
-\fIinterface-name tty-device speed local-link-local-address
-remote-link-local-address ipparam\fR
+\fIinterface\-name tty\-device speed local\-link\-local\-address
+remote\-link\-local\-address ipparam\fR
 .TP
-.B /etc/ppp/ipv6-down
-Similar to /etc/ppp/ip-down, but it is executed when IPv6 packets can no
+.B /etc/ppp/ipv6\-down
+Similar to /etc/ppp/ip\-down, but it is executed when IPv6 packets can no
 longer be transmitted on the link. It is executed with the same parameters 
-as the ipv6-up script.
+as the ipv6\-up script.
 .TP
-.B /etc/ppp/ipx-up
+.B /etc/ppp/ipx\-up
 A program or script which is executed when the link is available for
 sending and receiving IPX packets (that is, IPXCP has come up).  It is
 executed with the parameters
 .IP
-\fIinterface-name tty-device speed network-number local-IPX-node-address
-remote-IPX-node-address local-IPX-routing-protocol remote-IPX-routing-protocol
-local-IPX-router-name remote-IPX-router-name ipparam pppd-pid\fR 
+\fIinterface\-name tty\-device speed network\-number local\-IPX\-node\-address
+remote\-IPX\-node\-address local\-IPX\-routing\-protocol remote\-IPX\-routing\-protocol
+local\-IPX\-router\-name remote\-IPX\-router\-name ipparam pppd\-pid\fR 
 .IP
-The local-IPX-routing-protocol and remote-IPX-routing-protocol field
+The local\-IPX\-routing\-protocol and remote\-IPX\-routing\-protocol field
 may be one of the following:
 .IP
 NONE      to indicate that there is no routing protocol
@@ -1475,32 +1669,49 @@
 .br
 RIP NLSP  to indicate that both RIP/SAP and NLSP should be used
 .TP
-.B /etc/ppp/ipx-down
+.B /etc/ppp/ipx\-down
 A program or script which is executed when the link is no longer
 available for sending and receiving IPX packets.  This script can be
-used for undoing the effects of the /etc/ppp/ipx-up script.  It is
-invoked in the same manner and with the same parameters as the ipx-up
+used for undoing the effects of the /etc/ppp/ipx\-up script.  It is
+invoked in the same manner and with the same parameters as the ipx\-up
 script.
 .SH FILES
 .TP
 .B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others)
 Process-ID for pppd process on ppp interface unit \fIn\fR.
 .TP
-.B /var/run/ppp-\fIname\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp-\fIname\fB.pid \fR(others)
+.B /var/run/ppp\-\fIname\fB.pid \fR(BSD or Linux),
+\fB/etc/ppp/ppp\-\fIname\fB.pid \fR(others)
 Process-ID for pppd process for logical link \fIname\fR (see the
 \fIlinkname\fR option).
 .TP
-.B /etc/ppp/pap-secrets
+.B /var/run/pppd2.tdb
+Database containing information about pppd processes, interfaces and
+links, used for matching links to bundles in multilink operation.  May
+be examined by external programs to obtain information about running
+pppd instances, the interfaces and devices they are using, IP address
+assignments, etc.
+.B /etc/ppp/pap\-secrets
 Usernames, passwords and IP addresses for PAP authentication.  This
 file should be owned by root and not readable or writable by any other
 user.  Pppd will log a warning if this is not the case.
 .TP
-.B /etc/ppp/chap-secrets
-Names, secrets and IP addresses for CHAP authentication.  As for
-/etc/ppp/pap-secrets, this file should be owned by root and not
+.B /etc/ppp/chap\-secrets
+Names, secrets and IP addresses for CHAP/MS\-CHAP/MS\-CHAPv2 authentication.
+As for /etc/ppp/pap\-secrets, this file should be owned by root and not
 readable or writable by any other user.  Pppd will log a warning if
 this is not the case.
 .TP
+.B /etc/ppp/srp\-secrets
+Names, secrets, and IP addresses for EAP authentication.  As for
+/etc/ppp/pap\-secrets, this file should be owned by root and not
+readable or writable by any other user.  Pppd will log a warning if
+this is not the case.
+.TP
+.B ~/.ppp_pseudonym
+Saved client-side SRP\-SHA1 pseudonym.  See the \fIsrp\-use\-pseudonym\fR
+option for details.
+.TP
 .B /etc/ppp/options
 System default options for pppd, read before user default options or
 command-line options.
@@ -1522,6 +1733,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.
@@ -1545,7 +1758,7 @@
 .TP
 .B RFC1661
 Simpson, W.A.
-.I The Point\-to\-Point Protocol (PPP).
+.I The Point-to-Point Protocol (PPP).
 July 1994.
 .TP
 .B RFC1662
@@ -1553,16 +1766,35 @@
 .I PPP in HDLC-like Framing.
 July 1994.
 .TP
+.B RFC2284
+Blunk, L.; Vollbrecht, J.,
+.I PPP Extensible Authentication Protocol (EAP).
+March 1998.
+.TP
 .B RFC2472
 Haskin, D.
 .I IP Version 6 over PPP
 December 1998.
+.TP
+.B RFC2945
+Wu, T.,
+.I The SRP Authentication and Key Exchange System
+September 2000.
+.TP
+.B draft\-ietf\-pppext\-eap\-srp\-03.txt
+Carlson, J.; et al.,
+.I EAP SRP\-SHA1 Authentication Protocol.
+July 2001.
 .SH NOTES
-The following signals have the specified effect when sent to pppd.
+Some limited degree of control can be exercised over a running pppd
+process by sending it a signal from the list below.
 .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
@@ -1571,6 +1803,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.
@@ -1582,10 +1816,101 @@
 indicate a bug in one or other implementation.)
 
 .SH AUTHORS
-Paul Mackerras (Paul.Mackerras@cs.anu.edu.au), based on earlier work by
+Paul Mackerras (paulus@samba.org), based on earlier work by
 Drew Perkins,
 Brad Clements,
 Karl Fox,
 Greg Christy,
 and
 Brad Parker.
+
+.SH COPYRIGHT
+Pppd is copyrighted and made available under conditions which provide
+that it may be copied and used in source or binary forms provided that
+the conditions listed below are met.  Portions of pppd are covered by
+the following copyright notices:
+.LP
+Copyright (c) 1984-2000 Carnegie Mellon University. All rights
+reserved.
+.br
+Copyright (c) 1993-2004 Paul Mackerras. All rights reserved.
+.br
+Copyright (c) 1995 Pedro Roque Marques.  All rights reserved.
+.br
+Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+.br
+Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+.br
+Copyright (C) Andrew Tridgell 1999
+.br
+Copyright (c) 2000 by Sun Microsystems, Inc.  All rights reserved.
+.br
+Copyright (c) 2001 by Sun Microsystems, Inc.  All rights reserved.
+.br
+Copyright (c) 2002 Google, Inc.  All rights reserved.
+.LP
+The copyright notices contain the following statements.
+.LP
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+.LP
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+.LP
+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.
+.LP
+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
+.br
+     Office of Technology Transfer
+.br
+     Carnegie Mellon University
+.br
+     5000 Forbes Avenue
+.br
+     Pittsburgh, PA  15213-3890
+.br
+     (412) 268-4387, fax: (412) 268-7395
+.br
+     tech-transfer@andrew.cmu.edu
+.LP
+3b. 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.
+.LP
+4. Redistributions of any form whatsoever must retain the following
+   acknowledgments:
+.br
+   "This product includes software developed by Computing Services
+    at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+.br
+   "This product includes software developed by Paul Mackerras
+    <paulus@samba.org>".
+.br
+   "This product includes software developed by Pedro Roque Marques
+    <pedro_m@yahoo.com>".
+.br
+   "This product includes software developed by Tommi Komulainen
+    <Tommi.Komulainen@iki.fi>".
+.LP
+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.
+.LP
+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.
diff -Naur tomato/release/src/router/pppd/pppd/pppd.h tomato121/release/src/router/pppd/pppd/pppd.h
--- tomato/release/src/router/pppd/pppd/pppd.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/pppd.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,45 @@
 /*
  * pppd.h - PPP daemon global declarations.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: pppd.h,v 1.3 2004/06/30 10:31:51 honor Exp $
+ * 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.
+ *
+ * $Id: pppd.h,v 1.91 2005/08/25 23:59:34 paulus Exp $
  */
 
 /*
@@ -71,7 +94,7 @@
 	o_int,
 	o_uint32,
 	o_string,
-	o_wild,
+	o_wild
 };
 
 typedef struct {
@@ -79,7 +102,7 @@
 	enum opt_type type;
 	void	*addr;
 	char	*description;
-	int	flags;
+	unsigned int flags;
 	void	*addr2;
 	int	upper_limit;
 	int	lower_limit;
@@ -94,6 +117,7 @@
 #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_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_LLIMIT	0x4000	/* check value against lower limit */
@@ -102,6 +126,7 @@
 #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_ZEROINF	0x40000	/* with OPT_NOINCR, 0 == infinity */
 #define OPT_PRIO	0x80000	/* process option priorities for this option */
@@ -147,6 +172,8 @@
 struct pppd_stats {
     unsigned int	bytes_in;
     unsigned int	bytes_out;
+    unsigned int	pkts_in;
+    unsigned int	pkts_out;
 };
 
 /* Used for storing a sequence of words.  Usually malloced. */
@@ -188,11 +215,14 @@
 extern char	ifname[];	/* Interface name */
 extern char	hostname[];	/* Our hostname */
 extern u_char	outpacket_buf[]; /* Buffer for outgoing packets */
+extern int	devfd;		/* fd of underlying device */
+extern int	fd_ppp;		/* fd for talking PPP */
 extern int	phase;		/* Current state of link - see values below */
 extern int	baud_rate;	/* Current link speed in bits/sec */
 extern char	*progname;	/* Name of this program */
 extern int	redirect_stderr;/* Connector's stderr should go to file */
 extern char	peer_authname[];/* Authenticated name of peer */
+extern int	auth_done[NUM_PPP]; /* Methods actually used for auth */
 extern int	privileged;	/* We were run by real-uid root */
 extern int	need_holdoff;	/* Need holdoff period after link terminates */
 extern char	**script_env;	/* Environment variables for scripts */
@@ -201,7 +231,7 @@
 extern int	ngroups;	/* How many groups valid in groups */
 extern struct pppd_stats link_stats; /* byte/packet counts etc. for link */
 extern int	link_stats_valid; /* set if link_stats is valid */
-extern int	link_connect_time; /* time the link was up for */
+extern unsigned	link_connect_time; /* time the link was up for */
 extern int	using_pty;	/* using pty as device (notty or pty opt.) */
 extern int	log_to_fd;	/* logging to this fd as well as syslog */
 extern bool	log_default;	/* log_to_fd is default (stdout) */
@@ -211,12 +241,27 @@
 extern int	unsuccess;	/* # unsuccessful connection attempts */
 extern int	do_callback;	/* set if we want to do callback next */
 extern int	doing_callback;	/* set if this is a callback */
+extern int	error_count;	/* # of times error() has been called */
 extern char	ppp_devnam[MAXPATHLEN];
+extern char     remote_number[MAXNAMELEN]; /* Remote telephone number, if avail. */
+extern int      ppp_session_number; /* Session number (eg PPPoE session) */
+extern int	fd_devnull;	/* fd open to /dev/null */
+
+extern int	listen_time;	/* time to listen first (ms) */
+extern bool	doing_multilink;
+extern bool	multilink_master;
+extern bool	bundle_eof;
+extern bool	bundle_terminating;
+
 extern struct notifier *pidchange;   /* for notifications of pid changing */
 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 int	listen_time;	/* time to listen first (ms) */
+extern struct notifier *ip_up_notifier; /* IPCP has come up */
+extern struct notifier *ip_down_notifier; /* IPCP 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 */
 
 /* Values for do_callback and doing_callback */
 #define CALLBACK_DIALIN		1	/* we are expecting the call back */
@@ -253,6 +298,11 @@
 extern bool	explicit_remote;/* remote_name specified with remotename opt */
 extern bool	demand;		/* Do dial-on-demand */
 extern char	*ipparam;	/* Extra parameter for ip up/down scripts */
+extern char	*ipupcustom;	/* Custom ip up script */
+extern char	*ipdowncustom;	/* Custom ip down script */
+extern char	*chapseccustom;	/* Custom chap-secrets file */
+extern char	*papseccustom;	/* Custom pap-secrets file */
+extern char	*srpseccustom;	/* Custom srp-secrets file */
 extern bool	cryptpap;	/* Others' PAP passwords are encrypted */
 extern int	idle_time_limit;/* Shut down link if idle for this long */
 extern int	holdoff;	/* Dead time before restarting */
@@ -272,6 +322,27 @@
 extern char	*bundle_name;	/* bundle name for multilink */
 extern bool	dump_options;	/* print out option values */
 extern bool	dryrun;		/* check everything, print options, exit */
+extern int	child_wait;	/* # seconds to wait for children at end */
+
+#ifdef CHAPMS
+extern bool	ms_ignore_domain; /* Ignore any MS domain prefix */
+#endif
+
+#ifdef MAXOCTETS
+extern unsigned int maxoctets;	     /* Maximum octetes per session (in bytes) */
+extern int       maxoctets_dir;      /* Direction :
+				      0 - in+out (default)
+				      1 - in 
+				      2 - out
+				      3 - max(in,out) */
+extern int       maxoctets_timeout;  /* Timeout for check of octets limit */
+#define PPP_OCTETS_DIRECTION_SUM        0
+#define PPP_OCTETS_DIRECTION_IN         1
+#define PPP_OCTETS_DIRECTION_OUT        2
+#define PPP_OCTETS_DIRECTION_MAXOVERAL  3
+/* same as previos, but little different on RADIUS side */
+#define PPP_OCTETS_DIRECTION_MAXSESSION 4	
+#endif
 
 #ifdef PPP_FILTER
 extern struct	bpf_program pass_filter;   /* Filter for pkts to pass */
@@ -283,6 +354,23 @@
 				/* Has meaning only with MS-CHAP challenges */
 #endif
 
+/* Values for auth_pending, auth_done */
+#define PAP_WITHPEER	0x1
+#define PAP_PEER	0x2
+#define CHAP_WITHPEER	0x4
+#define CHAP_PEER	0x8
+#define EAP_WITHPEER	0x10
+#define EAP_PEER	0x20
+
+/* Values for auth_done only */
+#define CHAP_MD5_WITHPEER	0x40
+#define CHAP_MD5_PEER		0x80
+#define CHAP_MS_SHIFT		8	/* LSB position for MS auths */
+#define CHAP_MS_WITHPEER	0x100
+#define CHAP_MS_PEER		0x200
+#define CHAP_MS2_WITHPEER	0x400
+#define CHAP_MS2_PEER		0x800
+
 extern char *current_option;	/* the name of the option being parsed */
 extern int  privileged_option;	/* set iff the current option came from root */
 extern char *option_source;	/* string saying where the option came from */
@@ -303,6 +391,7 @@
 #define PHASE_TERMINATE		9
 #define PHASE_DISCONNECT	10
 #define PHASE_HOLDOFF		11
+#define PHASE_MASTER		12
 
 /*
  * The following struct gives the addresses of procedures to call
@@ -379,18 +468,6 @@
 
 extern struct channel *the_channel;
 
-#define ppp_send_config(unit, mtu, accm, pc, acc)			 \
-do {									 \
-	if (the_channel->send_config)					 \
-		(*the_channel->send_config)((mtu), (accm), (pc), (acc)); \
-} while (0)
-
-#define ppp_recv_config(unit, mtu, accm, pc, acc)			 \
-do {									 \
-	if (the_channel->send_config)					 \
-		(*the_channel->recv_config)((mtu), (accm), (pc), (acc)); \
-} while (0)
-
 /*
  * Prototypes.
  */
@@ -406,12 +483,15 @@
 void untimeout __P((void (*func)(void *), void *arg));
 				/* Cancel call to func(arg) */
 void record_child __P((int, char *, void (*) (void *), void *));
+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 */
+void reset_link_stats __P((int)); /* Reset (init) stats when link goes up */
 void update_link_stats __P((int)); /* Get stats at link termination */
 void script_setenv __P((char *, char *, int));	/* set script env var */
 void script_unsetenv __P((char *));		/* unset script env var */
@@ -419,6 +499,12 @@
 void add_notifier __P((struct notifier **, notify_func, void *));
 void remove_notifier __P((struct notifier **, notify_func, void *));
 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));
 
 /* Procedures exported from tty.c. */
 void tty_init __P((void));
@@ -432,32 +518,53 @@
 int vslprintf __P((char *, int, char *, va_list));	/* vsprintf++ */
 size_t strlcpy __P((char *, const char *, size_t));	/* safe strcpy */
 size_t strlcat __P((char *, const char *, size_t));	/* safe strncpy */
-void dbglog __P((char *, ...));	/* log a debug message */
-void info __P((char *, ...));	/* log an informational message */
-void notice __P((char *, ...));	/* log a notice-level message */
-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) */
+
+#define option_error(fmt,args...) { }
+//void option_error __P((char *fmt, ...));
+
+//void dbglog __P((char *, ...));	/* log a debug message */
+//void info __P((char *, ...));	/* log an informational message */
+//void notice __P((char *, ...));	/* log a notice-level message */
+
+
+#define notice(fmt,...) { }
+#define info(fmt,...) { }
+#define dbglog(fmt,...) { }
+#define warn(fmt,...) { }
+#define error(fmt,...) { ++error_count; }
+#define fatal(fmt,...) { die(1); }
+//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 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));
+				/* dump packet to debug log if interesting */
+ssize_t complete_read __P((int, void *, size_t));
+				/* read a complete buffer */
 
 /* 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 */
 void link_established __P((int)); /* the link is up; authenticate now */
-void start_networks __P((void));  /* start all the network control protos */
+void start_networks __P((int));   /* start all the network control protos */
+void continue_networks __P((int)); /* start network [ip, etc] control protos */
 void np_up __P((int, int));	  /* a network protocol has come up */
 void np_down __P((int, int));	  /* a network protocol has gone down */
 void np_finished __P((int, int)); /* a network protocol no longer needs link */
 void auth_peer_fail __P((int, int));
 				/* peer failed to authenticate itself */
-void auth_peer_success __P((int, int, char *, int));
+void auth_peer_success __P((int, int, int, char *, int));
 				/* peer successfully authenticated itself */
 void auth_withpeer_fail __P((int, int));
 				/* we failed to authenticate ourselves */
-void auth_withpeer_success __P((int, int));
+void auth_withpeer_success __P((int, int, int));
 				/* we successfully authenticated ourselves */
 void auth_check_options __P((void));
 				/* check authentication options supplied */
@@ -466,8 +573,11 @@
 				/* Check peer-supplied username/password */
 int  get_secret __P((int, char *, char *, char *, int *, int));
 				/* get "secret" for chap */
+int  get_srp_secret __P((int unit, char *client, char *server, char *secret,
+    int am_server));
 int  auth_ip_addr __P((int, u_int32_t));
 				/* check if IP address is authorized */
+int  auth_number __P((void));	/* check if remote number is authorized */
 int  bad_ip_adrs __P((u_int32_t));
 				/* check if IP address is unreasonable */
 
@@ -481,10 +591,19 @@
 int  loop_frame __P((unsigned char *, int)); /* should we bring link up? */
 
 /* Procedures exported from multilink.c */
+#ifdef HAVE_MULTILINK
 void mp_check_options __P((void)); /* Check multilink-related options */
 int  mp_join_bundle __P((void));  /* join our link to an appropriate bundle */
+void mp_exit_bundle __P((void));  /* have disconnected our link from bundle */
+void mp_bundle_terminated __P((void));
 char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
 int  str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+#else
+#define mp_bundle_terminated()	/* nothing */
+#define mp_exit_bundle()	/* nothing */
+#define doing_multilink		0
+#define multilink_master	0
+#endif
 
 /* Procedures exported from sys-*.c */
 void sys_init __P((void));	/* Do system-dependent initialization */
@@ -496,9 +615,12 @@
 int  open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
 int  tty_establish_ppp __P((int));  /* Turn serial port into a ppp interface */
 void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */
+void generic_disestablish_ppp __P((int dev_fd)); /* Restore device setting */
+int  generic_establish_ppp __P((int dev_fd)); /* Make a ppp interface */
 void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
 int  bundle_attach __P((int));	/* Attach link to existing bundle */
 void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
+void destroy_bundle __P((void)); /* Tell driver to destroy bundle */
 void clean_check __P((void));	/* Check if line was 8-bit clean */
 void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
 void restore_tty __P((int));	/* Restore port's original parameters */
@@ -526,6 +648,7 @@
 int  get_ppp_stats __P((int, struct pppd_stats *));
 				/* Return link statistics */
 void netif_set_mtu __P((int, int)); /* Set PPP interface MTU */
+int  netif_get_mtu __P((int));      /* Get PPP interface MTU */
 int  sifvjcomp __P((int, int, int, int));
 				/* Configure VJ TCP header compression */
 int  sifup __P((int));		/* Configure i/f up for one protocol */
@@ -570,6 +693,7 @@
 char *get_first_ethernet __P((void));
 
 /* Procedures exported from options.c */
+int setipaddr __P((char *, char **, int)); /* Set local/remote ip addresses */
 int  parse_args __P((int argc, char **argv));
 				/* Parse options from arguments given */
 int  options_from_file __P((char *filename, int must_exist, int check_prot,
@@ -581,7 +705,8 @@
 				/* Parse options from a wordlist */
 int  getword __P((FILE *f, char *word, int *newlinep, char *filename));
 				/* Read a word from a file */
-void option_error __P((char *fmt, ...));
+
+
 				/* Print an error message about an option */
 int int_option __P((char *, int *));
 				/* Simplified number_option for decimal ints */
@@ -606,10 +731,18 @@
 				 struct wordlist **popts));
 extern void (*pap_logout_hook) __P((void));
 extern int (*pap_passwd_hook) __P((char *user, char *passwd));
+extern int (*allowed_address_hook) __P((u_int32_t addr));
 extern void (*ip_up_hook) __P((void));
 extern void (*ip_down_hook) __P((void));
 extern void (*ip_choose_hook) __P((u_int32_t *));
 
+extern int (*chap_check_hook) __P((void));
+extern int (*chap_passwd_hook) __P((char *user, char *passwd));
+
+/* Let a plugin snoop sent and received packets.  Useful for L2TP */
+extern void (*snoop_recv_hook) __P((unsigned char *p, int len));
+extern void (*snoop_send_hook) __P((unsigned char *p, int len));
+
 /*
  * Inline versions of get/put char/short/long.
  * Pointer is advanced; we assume that both arguments
@@ -658,6 +791,7 @@
 
 #define BCOPY(s, d, l)		memcpy(d, s, l)
 #define BZERO(s, n)		memset(s, 0, n)
+#define	BCMP(s1, s2, l)		memcmp(s1, s2, l)
 
 #define PRINTMSG(m, l)		{ info("Remote message: %0.*v", l, m); }
 
@@ -692,6 +826,10 @@
 #define EXIT_LOOPBACK		17
 #define EXIT_INIT_FAILED	18
 #define EXIT_AUTH_TOPEER_FAILED	19
+#ifdef MAXOCTETS
+#define EXIT_TRAFFIC_LIMIT	20
+#endif
+#define EXIT_CNID_AUTH_FAILED	21
 
 /*
  * Debug macros.  Slightly useful for finding bugs in pppd, not particularly
@@ -786,7 +924,8 @@
 #define MAX(a, b)	((a) > (b)? (a): (b))
 #endif
 
-#endif /* __PPP_H__ */
+#ifndef offsetof
+#define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
 
-extern int log_to_file(char *buf);
-extern int my_gettimeofday(struct timeval *timenow, struct timezone *tz);
+#endif /* __PPP_H__ */
diff -Naur tomato/release/src/router/pppd/pppd/ppp.pam tomato121/release/src/router/pppd/pppd/ppp.pam
--- tomato/release/src/router/pppd/pppd/ppp.pam	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/ppp.pam	2008-04-22 21:44:05.000000000 +1000
@@ -3,4 +3,4 @@
 auth	required	pam_nologin.so
 auth	required	pam_unix.so
 account	required	pam_unix.so
-session	required	pam_unix.so
\ No newline at end of file
+session	required	pam_unix.so
diff -Naur tomato/release/src/router/pppd/pppd/sha1.c tomato121/release/src/router/pppd/pppd/sha1.c
--- tomato/release/src/router/pppd/pppd/sha1.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/sha1.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,169 @@
+/*
+ * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
+ * 
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ * 
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+#include <netinet/in.h>	/* htonl() */
+#include "sha1.h"
+
+static void
+SHA1_Transform(u_int32_t[5], const unsigned char[64]);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#define blk0(i) (block->l[i] = htonl(block->l[i]))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void
+SHA1_Transform(u_int32_t state[5], const unsigned char buffer[64])
+{
+    u_int32_t a, b, c, d, e;
+    typedef union {
+	unsigned char c[64];
+	u_int32_t l[16];
+    } CHAR64LONG16;
+    CHAR64LONG16 *block;
+
+#ifdef SHA1HANDSOFF
+    static unsigned char workspace[64];
+    block = (CHAR64LONG16 *) workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16 *) buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void
+SHA1_Init(SHA1_CTX *context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void
+SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len)
+{
+    unsigned int i, j;
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    i = 64 - j;
+    while (len >= i) {
+	memcpy(&context->buffer[j], data, i);
+	SHA1_Transform(context->state, context->buffer);
+	data += i;
+	len -= i;
+	i = 64;
+	j = 0;
+    }
+
+    memcpy(&context->buffer[j], data, len);
+}
+
+
+/* Add padding and return the message digest. */
+
+void
+SHA1_Final(unsigned char digest[20], SHA1_CTX *context)
+{
+    u_int32_t i, j;
+    unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *) "\200", 1);
+    while ((context->count[0] & 504) != 448) {
+	SHA1_Update(context, (unsigned char *) "\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+    for (i = 0; i < 20; i++) {
+	digest[i] = (unsigned char)
+		     ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = j = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
+    SHA1Transform(context->state, context->buffer);
+#endif
+}
+
diff -Naur tomato/release/src/router/pppd/pppd/sha1.h tomato121/release/src/router/pppd/pppd/sha1.h
--- tomato/release/src/router/pppd/pppd/sha1.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/sha1.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,31 @@
+/* sha1.h */
+
+/* If OpenSSL is in use, then use that version of SHA-1 */
+#ifdef OPENSSL
+#include <t_sha.h>
+#define __SHA1_INCLUDE_
+#endif
+
+#ifndef __SHA1_INCLUDE_
+
+#ifndef SHA1_SIGNATURE_SIZE
+#ifdef SHA_DIGESTSIZE
+#define SHA1_SIGNATURE_SIZE SHA_DIGESTSIZE
+#else
+#define SHA1_SIGNATURE_SIZE 20
+#endif
+#endif
+
+typedef struct {
+    u_int32_t state[5];
+    u_int32_t count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+extern void SHA1_Init(SHA1_CTX *);
+extern void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
+extern void SHA1_Final(unsigned char[SHA1_SIGNATURE_SIZE], SHA1_CTX *);
+
+#define __SHA1_INCLUDE_
+#endif /* __SHA1_INCLUDE_ */
+
diff -Naur tomato/release/src/router/pppd/pppd/spinlock.c tomato121/release/src/router/pppd/pppd/spinlock.c
--- tomato/release/src/router/pppd/pppd/spinlock.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/spinlock.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,473 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   trivial database library 
+
+   Copyright (C) Anton Blanchard                   2001
+   
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define DEBUG
+
+#ifdef USE_SPINLOCKS
+
+/*
+ * ARCH SPECIFIC
+ */
+
+#if defined(SPARC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	unsigned int result;
+
+	asm volatile("ldstub    [%1], %0"
+		: "=r" (result)
+		: "r" (lock)
+		: "memory");
+
+	return (result == 0) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("":::"memory");
+	*lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 0);
+}
+
+#elif defined(POWERPC_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	unsigned int result;
+
+	__asm__ __volatile__(
+"1:	lwarx		%0,0,%1\n\
+	cmpwi		0,%0,0\n\
+	li		%0,0\n\
+	bne-		2f\n\
+	li		%0,1\n\
+	stwcx.		%0,0,%1\n\
+	bne-		1b\n\
+	isync\n\
+2:"	: "=&r"(result)
+	: "r"(lock)
+	: "cr0", "memory");
+
+	return (result == 1) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("eieio":::"memory");
+	*lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 0);
+}
+
+#elif defined(INTEL_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	int oldval;
+
+	asm volatile("xchgl %0,%1"
+		: "=r" (oldval), "=m" (*lock)
+		: "0" (0)
+		: "memory");
+
+	return oldval > 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("":::"memory");
+	*lock = 1;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 1;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 1);
+}
+
+#elif defined(MIPS_SPINLOCKS) && defined(sgi) && (_COMPILER_VERSION >= 730)
+
+/* Implement spinlocks on IRIX using the MIPSPro atomic fetch operations. See
+ * sync(3) for the details of the intrinsic operations.
+ *
+ * "sgi" and "_COMPILER_VERSION" are always defined by MIPSPro.
+ */
+
+#ifdef STANDALONE
+
+/* MIPSPro 7.3 has "__inline" as an extension, but not "inline. */
+#define inline __inline
+
+#endif /* STANDALONE */
+
+/* Returns 0 if the lock is acquired, EBUSY otherwise. */
+static inline int __spin_trylock(spinlock_t *lock)
+{
+        unsigned int val;
+        val = __lock_test_and_set(lock, 1);
+        return val == 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+        __lock_release(lock);
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+        __lock_release(lock);
+}
+
+/* Returns 1 if the lock is held, 0 otherwise. */
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+        unsigned int val;
+        val = __add_and_fetch(lock, 0);
+	return val;
+}
+
+#elif defined(MIPS_SPINLOCKS) 
+
+static inline unsigned int load_linked(unsigned long addr)
+{
+	unsigned int res;
+
+	__asm__ __volatile__("ll\t%0,(%1)"
+		: "=r" (res)
+		: "r" (addr));
+
+	return res;
+}
+
+static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
+{
+	unsigned int res;
+
+	__asm__ __volatile__("sc\t%0,(%2)"
+		: "=r" (res)
+		: "0" (value), "r" (addr));
+	return res;
+}
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+	unsigned int mw;
+
+	do {
+		mw = load_linked(lock);
+		if (mw) 
+			return EBUSY;
+	} while (!store_conditional(lock, 1));
+
+	asm volatile("":::"memory");
+
+	return 0;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+	asm volatile("":::"memory");
+	*lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+	*lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+	return (*lock != 0);
+}
+
+#else
+#error Need to implement spinlock code in spinlock.c
+#endif
+
+/*
+ * OS SPECIFIC
+ */
+
+static void yield_cpu(void)
+{
+	struct timespec tm;
+
+#ifdef USE_SCHED_YIELD
+	sched_yield();
+#else
+	/* Linux will busy loop for delays < 2ms on real time tasks */
+	tm.tv_sec = 0;
+	tm.tv_nsec = 2000000L + 1;
+	nanosleep(&tm, NULL);
+#endif
+}
+
+static int this_is_smp(void)
+{
+#if defined(HAVE_SYSCONF) && defined(SYSCONF_SC_NPROC_ONLN)
+        return (sysconf(_SC_NPROC_ONLN) > 1) ? 1 : 0;
+#else
+	return 0;
+#endif
+}
+
+/*
+ * GENERIC
+ */
+
+static int smp_machine = 0;
+
+static inline void __spin_lock(spinlock_t *lock)
+{
+	int ntries = 0;
+
+	while(__spin_trylock(lock)) {
+		while(__spin_is_locked(lock)) {
+			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+				continue;
+			yield_cpu();
+		}
+	}
+}
+
+static void __read_lock(tdb_rwlock_t *rwlock)
+{
+	int ntries = 0;
+
+	while(1) {
+		__spin_lock(&rwlock->lock);
+
+		if (!(rwlock->count & RWLOCK_BIAS)) {
+			rwlock->count++;
+			__spin_unlock(&rwlock->lock);
+			return;
+		}
+	
+		__spin_unlock(&rwlock->lock);
+
+		while(rwlock->count & RWLOCK_BIAS) {
+			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+				continue;
+			yield_cpu();
+		}
+	}
+}
+
+static void __write_lock(tdb_rwlock_t *rwlock)
+{
+	int ntries = 0;
+
+	while(1) {
+		__spin_lock(&rwlock->lock);
+
+		if (rwlock->count == 0) {
+			rwlock->count |= RWLOCK_BIAS;
+			__spin_unlock(&rwlock->lock);
+			return;
+		}
+
+		__spin_unlock(&rwlock->lock);
+
+		while(rwlock->count != 0) {
+			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+				continue;
+			yield_cpu();
+		}
+	}
+}
+
+static void __write_unlock(tdb_rwlock_t *rwlock)
+{
+	__spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+	if (!(rwlock->count & RWLOCK_BIAS))
+		fprintf(stderr, "bug: write_unlock\n");
+#endif
+
+	rwlock->count &= ~RWLOCK_BIAS;
+	__spin_unlock(&rwlock->lock);
+}
+
+static void __read_unlock(tdb_rwlock_t *rwlock)
+{
+	__spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+	if (!rwlock->count)
+		fprintf(stderr, "bug: read_unlock\n");
+
+	if (rwlock->count & RWLOCK_BIAS)
+		fprintf(stderr, "bug: read_unlock\n");
+#endif
+
+	rwlock->count--;
+	__spin_unlock(&rwlock->lock);
+}
+
+/* TDB SPECIFIC */
+
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+	tdb_rwlock_t *rwlocks;
+
+	if (!tdb->map_ptr) return -1;
+	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+	switch(rw_type) {
+	case F_RDLCK:
+		__read_lock(&rwlocks[list+1]);
+		break;
+
+	case F_WRLCK:
+		__write_lock(&rwlocks[list+1]);
+		break;
+
+	default:
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	}
+	return 0;
+}
+
+/* unlock the database. */
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+	tdb_rwlock_t *rwlocks;
+
+	if (!tdb->map_ptr) return -1;
+	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+	switch(rw_type) {
+	case F_RDLCK:
+		__read_unlock(&rwlocks[list+1]);
+		break;
+
+	case F_WRLCK:
+		__write_unlock(&rwlocks[list+1]);
+		break;
+
+	default:
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	}
+
+	return 0;
+}
+
+int tdb_create_rwlocks(int fd, unsigned int hash_size)
+{
+	unsigned size, i;
+	tdb_rwlock_t *rwlocks;
+
+	size = TDB_SPINLOCK_SIZE(hash_size);
+	rwlocks = malloc(size);
+	if (!rwlocks)
+		return -1;
+
+	for(i = 0; i < hash_size+1; i++) {
+		__spin_lock_init(&rwlocks[i].lock);
+		rwlocks[i].count = 0;
+	}
+
+	/* Write it out (appending to end) */
+	if (write(fd, rwlocks, size) != size) {
+		free(rwlocks);
+		return -1;
+	}
+	smp_machine = this_is_smp();
+	free(rwlocks);
+	return 0;
+}
+
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+	tdb_rwlock_t *rwlocks;
+	unsigned i;
+
+	if (tdb->header.rwlocks == 0) return 0;
+	if (!tdb->map_ptr) return -1;
+
+	/* We're mmapped here */
+	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+	for(i = 0; i < tdb->header.hash_size+1; i++) {
+		__spin_lock_init(&rwlocks[i].lock);
+		rwlocks[i].count = 0;
+	}
+	return 0;
+}
+#else
+int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+
+/* Non-spinlock version: remove spinlock pointer */
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+	tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
+				- (char *)&tdb->header);
+
+	tdb->header.rwlocks = 0;
+	if (lseek(tdb->fd, off, SEEK_SET) != off
+	    || write(tdb->fd, (void *)&tdb->header.rwlocks,
+		     sizeof(tdb->header.rwlocks)) 
+	    != sizeof(tdb->header.rwlocks))
+		return -1;
+	return 0;
+}
+#endif
diff -Naur tomato/release/src/router/pppd/pppd/spinlock.h tomato121/release/src/router/pppd/pppd/spinlock.h
--- tomato/release/src/router/pppd/pppd/spinlock.h	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/spinlock.h	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,59 @@
+#ifndef __SPINLOCK_H__
+#define __SPINLOCK_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "tdb.h"
+
+#ifdef USE_SPINLOCKS
+
+#define RWLOCK_BIAS 0x1000UL
+
+/* OS SPECIFIC */
+#define MAX_BUSY_LOOPS 1000
+#undef USE_SCHED_YIELD
+
+/* ARCH SPECIFIC */
+/* We should make sure these are padded to a cache line */
+#if defined(SPARC_SPINLOCKS)
+typedef volatile char spinlock_t;
+#elif defined(POWERPC_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#elif defined(INTEL_SPINLOCKS)
+typedef volatile int spinlock_t;
+#elif defined(MIPS_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#else
+#error Need to implement spinlock code in spinlock.h
+#endif
+
+typedef struct {
+	spinlock_t lock;
+	volatile int count;
+} tdb_rwlock_t;
+
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+
+#define TDB_SPINLOCK_SIZE(hash_size) (((hash_size) + 1) * sizeof(tdb_rwlock_t))
+
+#else /* !USE_SPINLOCKS */
+#if 0
+#define tdb_create_rwlocks(fd, hash_size) 0
+#define tdb_spinlock(tdb, list, rw_type) (-1)
+#define tdb_spinunlock(tdb, list, rw_type) (-1)
+#else
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+#endif
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+#define TDB_SPINLOCK_SIZE(hash_size) 0
+
+#endif
+
+#endif
diff -Naur tomato/release/src/router/pppd/pppd/srp-entry.8 tomato121/release/src/router/pppd/pppd/srp-entry.8
--- tomato/release/src/router/pppd/pppd/srp-entry.8	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/srp-entry.8	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,83 @@
+.\" manual page [] for srp-entry
+.\" $Id: srp-entry.8,v 1.2 2004/11/13 12:22:49 paulus Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH SRP-ENTRY 8
+.SH NAME
+srp\-entry \- Generate a SRP\-SHA1 Server Entry
+.SH SYNOPSIS
+.B srp\-entry
+[
+.I \-i index
+] [
+.I clientname
+]
+.SH DESCRIPTION
+.LP
+This utility generates an entry suitable for use in the
+/etc/ppp/srp\-secrets file on a PPP EAP SRP\-SHA1 authenticator
+("server").  This file has the same basic layout as the other pppd(8)
+authentication files, /etc/ppp/pap\-secrets and /etc/ppp/chap\-secrets.
+Thus, the entry generated has at least four main fields separated by
+spaces.  The first field is the authenticatee ("client") name.  The
+second is the server name.  The third is the secret.  The fourth is
+the allowed (or assigned) IP address for the client, and defaults to
+"*".  Additional fields can contain additional IP addresses or pppd
+options; see pppd(8) for details.
+.LP
+The third field has three subfields, separated by colons.  The first
+subfield is the index of the modulus and generator from SRP's
+/etc/tpasswd.conf.  The special value 0 is used to represent the
+well-known modulus and generator specified in the EAP SRP\-SHA1 draft.
+The second subfield is the password validator.  The third is the
+password salt.  These latter two values are encoded in base64 notation.
+.SH OPTIONS
+.TP
+.I \-i <index>
+Specifies the modulus/generator index in /etc/tpasswd.conf.  In order
+to use this option, you will need to run the "tconf" utility from the
+SRP package to generate local entries for this file.  Note that if
+these values are not known to the client, the client will be forced to
+run time-consuming safety tests on the values used.  For this reason,
+using the well-known values is recommended.
+.TP
+.I <clientname>
+Specifies the client name.  The password validator is a hashed
+combination of the client's name and password, and both are required.
+If the client name is not supplied on the command line, srp\-entry will
+prompt for the client name first.
+.SH FILES
+.TP
+.B /etc/ppp/srp\-secrets
+Usernames, passwords and IP addresses for SRP authentication.  This
+file should be owned by root and not readable or writable by any other
+user.  Pppd will log a warning if this is not the case.  Note that
+srp\-entry does not write to this file.  The user is responsible for
+copying the output of srp\-entry into this file.
+.TP
+.B /etc/tpasswd.conf
+Indexed copies of tested modulus/generator combinations; part of the
+SRP package.
+.SH SEE ALSO
+.TP
+pppd(8)
+.TP
+.B RFC2284
+Blunk, L., Vollbrecht, J.,
+.I PPP Extensible Authentication Protocol (EAP).
+March 1998.
+.TP
+.B draft\-ietf\-pppext\-eap\-srp\-03.txt
+Carlson, J., et al.,
+.I EAP SRP\-SHA1 Authentication Protocol.
+July 2001.
+.TP
+.B RFC2945
+Wu, T.,
+.I The SRP Authentication and Key Exchange System
+September 2000.
+.SH AUTHOR
+James Carlson (james.d.carlson@sun.com)
diff -Naur tomato/release/src/router/pppd/pppd/srp-entry.c tomato121/release/src/router/pppd/pppd/srp-entry.c
--- tomato/release/src/router/pppd/pppd/srp-entry.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/srp-entry.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,190 @@
+/*
+ * Utility program for generating entries in /etc/ppp/srp-secrets
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * Usage:
+ *	srp-entry [-i index] [clientname]
+ *
+ * Index, if supplied, is the modulus/generator index from
+ * /etc/tpasswd.conf.  If not supplied, then the last (highest
+ * numbered) entry from that file is used.  If the file doesn't exist,
+ * then the default "well known" EAP SRP-SHA1 modulus/generator is
+ * used.
+ *
+ * The default modulus/generator can be requested as index 0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <t_pwd.h>
+
+#ifndef	SOL2
+#define	getpassphrase	getpass
+#endif
+
+#define	HAS_SPACE	1
+#define	HAS_DQUOTE	2
+#define	HAS_SQUOTE	4
+#define	HAS_BACKSLASH	8
+
+static const u_char wkmodulus[] = {
+	0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+	0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+	0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+	0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+	0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+	0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+	0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+	0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+	0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+	0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+	0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+	0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+	0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+	0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+	0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+	0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+	0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+	0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+	0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+	0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+	0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+	0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+	0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+	0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+	0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+	0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+	0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+	0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+	0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+	0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+	0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+	0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+
+static const char *myname;
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, "Usage:\n\t%s [-i index] [clientname]\n",
+	    myname);
+	exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+	struct t_conf *tc;
+	struct t_confent *tcent, mytce;
+	struct t_pw pwval;
+	char *name;
+	char pname[256];
+	char *pass1, *pass2;
+	int flags, idx;
+	char *cp;
+	char delimit;
+	char strbuf[MAXB64PARAMLEN];
+	char saltbuf[MAXB64SALTLEN];
+
+	if ((myname = *argv) == NULL)
+		myname = "srp-entry";
+	else
+		argv++;
+
+	idx = -1;
+	if (*argv != NULL && strcmp(*argv, "-i") == 0) {
+		if (*++argv == NULL)
+			usage();
+		idx = atoi(*argv++);
+	}
+
+	tcent = NULL;
+	if (idx != 0 && (tc = t_openconf(NULL)) != NULL) {
+		if (idx == -1)
+			tcent = t_getconflast(tc);
+		else
+			tcent = t_getconfbyindex(tc, idx);
+	}
+	if (idx <= 0 && tcent == NULL) {
+		mytce.index = 0;
+		mytce.modulus.data = (u_char *)wkmodulus;
+		mytce.modulus.len = sizeof (wkmodulus);
+		mytce.generator.data = (u_char *)"\002";
+		mytce.generator.len = 1;
+		tcent = &mytce;
+	}
+	if (tcent == NULL) {
+		(void) fprintf(stderr, "SRP modulus/generator %d not found\n",
+		    idx);
+		exit(1);
+	}
+
+	if ((name = *argv) == NULL) {
+		(void) printf("Client name: ");
+		if (fgets(pname, sizeof (pname), stdin) == NULL)
+			exit(1);
+		if ((cp = strchr(pname, '\n')) != NULL)
+			*cp = '\0';
+		name = pname;
+	}
+
+	for (;;) {
+		if ((pass1 = getpassphrase("Pass phrase: ")) == NULL)
+			exit(1);
+		pass1 = strdup(pass1);
+		if ((pass2 = getpassphrase("Re-enter phrase: ")) == NULL)
+			exit(1);
+		if (strcmp(pass1, pass2) == 0)
+			break;
+		free(pass1);
+		(void) printf("Phrases don't match; try again.\n");
+	}
+
+	memset(&pwval, 0, sizeof (pwval));
+	t_makepwent(&pwval, name, pass1, NULL, tcent);
+	flags = 0;
+	for (cp = name; *cp != '\0'; cp++)
+		if (isspace(*cp))
+			flags |= HAS_SPACE;
+		else if (*cp == '"')
+			flags |= HAS_DQUOTE;
+		else if (*cp == '\'')
+			flags |= HAS_SQUOTE;
+		else if (*cp == '\\')
+			flags |= HAS_BACKSLASH;
+	delimit = flags == 0 ? '\0' : (flags & HAS_DQUOTE) ? '\'' : '"';
+	if (delimit != '\0')
+		(void) putchar(delimit);
+	for (cp = name; *cp != '\0'; cp++) {
+		if (*cp == delimit || *cp == '\\')
+			(void) putchar('\\');
+		(void) putchar(*cp);
+	}
+	if (delimit != '\0')
+		(void) putchar(delimit);
+	(void) printf(" * %d:%s:%s *\n",
+	    pwval.pebuf.index, t_tob64(strbuf,
+		(char *)pwval.pebuf.password.data, pwval.pebuf.password.len),
+	    t_tob64(saltbuf, (char *)pwval.pebuf.salt.data,
+		pwval.pebuf.salt.len));
+	return 0;
+}
diff -Naur tomato/release/src/router/pppd/pppd/sys-linux.c tomato121/release/src/router/pppd/pppd/sys-linux.c
--- tomato/release/src/router/pppd/pppd/sys-linux.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/sys-linux.c	2008-04-22 21:44:05.000000000 +1000
@@ -2,20 +2,71 @@
  * sys-linux.c - System-dependent procedures for setting up
  * PPP interfaces on Linux systems
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1994-2004 Paul Mackerras. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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 main.c and pppd.h, which are:
+ *
+ * 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 <sys/ioctl.h>
@@ -85,7 +136,7 @@
 #endif /* IPX_CHANGE */
 
 #ifdef PPP_FILTER
-#include <net/bpf.h>
+#include <pcap-bpf.h>
 #include <linux/filter.h>
 #endif /* PPP_FILTER */
 
@@ -108,7 +159,7 @@
 
 #define IN6_LLADDR_FROM_EUI64(sin6, eui64) do {			\
 	memset(&sin6.s6_addr, 0, sizeof(struct in6_addr));	\
-	sin6.s6_addr16[0] = htons(0xfe80); 			\
+	sin6.s6_addr16[0] = htons(0xfe80);			\
 	eui64_copy(eui64, sin6.s6_addr32[2]);			\
 	} while (0)
 
@@ -122,12 +173,19 @@
 static int initfdflags = -1;	/* Initial file descriptor flags for fd */
 static int ppp_fd = -1;		/* fd which is set to PPP discipline */
 static int sock_fd = -1;	/* socket for doing interface ioctls */
-static int slave_fd = -1;
-static int master_fd = -1;
+static int slave_fd = -1;	/* pty for old-style demand mode, slave */
+static int master_fd = -1;	/* pty for old-style demand mode, master */
 #ifdef INET6
 static int sock6_fd = -1;
 #endif /* INET6 */
-static int ppp_dev_fd = -1;	/* fd for /dev/ppp (new style driver) */
+
+/*
+ * For the old-style kernel driver, this is the same as ppp_fd.
+ * For the new-style driver, it is the fd of an instance of /dev/ppp
+ * which is attached to the ppp unit and is used for controlling it.
+ */
+int ppp_dev_fd = -1;		/* fd for /dev/ppp (new style driver) */
+
 static int chindex;		/* channel index (new style driver) */
 
 static fd_set in_fds;		/* set of fds that wait_input waits for */
@@ -141,13 +199,13 @@
 static int restore_term        = 0;	/* 1 => we've munged the terminal */
 static struct termios inittermios;	/* Initial TTY termios */
 
-static int new_style_driver = 0;
+int new_style_driver = 0;
 
 static char loop_name[20];
 static unsigned char inbuf[512]; /* buffer for chars read from loopback */
 
 static int	if_is_up;	/* Interface has been marked up */
-static u_int32_t default_route_gateway;	/* Gateway for default route added */
+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 */
@@ -168,8 +226,7 @@
 #define SIN_ADDR(x)	(((struct sockaddr_in *) (&(x)))->sin_addr.s_addr)
 
 /* Prototypes for procedures local to this file. */
-static int get_flags (int fd);
-static void set_flags (int fd, int flags);
+static int modify_flags(int fd, int clear_bits, int set_bits);
 static int translate_speed (int bps);
 static int baud_rate_of (int speed);
 static void close_route_table (void);
@@ -182,7 +239,6 @@
 static int set_kdebugflag(int level);
 static int ppp_registered(void);
 static int make_ppp_unit(void);
-static void restore_loop(void);	/* Transfer ppp unit back to loopback */
 
 extern u_char	inpacket_buf[];	/* borrowed from main.c */
 
@@ -204,7 +260,6 @@
 /* new_fd is the fd of a tty */
 static void set_ppp_fd (int new_fd)
 {
-	SYSDEBUG ((LOG_DEBUG, "setting ppp_fd to %d\n", new_fd));
 	ppp_fd = new_fd;
 	if (!new_style_driver)
 		ppp_dev_fd = new_fd;
@@ -223,36 +278,26 @@
 	return 0;
 }
 
-/********************************************************************
- *
- * Functions to read and set the flags value in the device driver
+/*
+ * modify_flags - set and clear flag bits controlling the kernel
+ * PPP driver.
  */
+static int modify_flags(int fd, int clear_bits, int set_bits)
+{
+	int flags;
 
-static int get_flags (int fd)
-{    
-    int flags;
-
-    if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
-	if ( ok_error (errno) )
-	    flags = 0;
-	else
-	    fatal("ioctl(PPPIOCGFLAGS): %m");
-    }
-
-    SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags));
-    return flags;
-}
-
-/********************************************************************/
+	if (ioctl(fd, PPPIOCGFLAGS, &flags) == -1)
+		goto err;
+	flags = (flags & ~clear_bits) | set_bits;
+	if (ioctl(fd, PPPIOCSFLAGS, &flags) == -1)
+		goto err;
 
-static void set_flags (int fd, int flags)
-{    
-    SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags));
+	return 0;
 
-    if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) {
-	if (! ok_error (errno) )
-	    fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno);
-    }
+ err:
+	if (errno != EIO)
+		error("Failed to set PPP kernel option flags: %m");
+	return -1;
 }
 
 /********************************************************************
@@ -262,18 +307,6 @@
 
 void sys_init(void)
 {
-    int flags;
-
-    if (new_style_driver) {
-	ppp_dev_fd = open("/dev/ppp", O_RDWR);
-	if (ppp_dev_fd < 0)
-	    fatal("Couldn't open /dev/ppp: %m");
-	flags = fcntl(ppp_dev_fd, F_GETFL);
-	if (flags == -1
-	    || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-	    warn("Couldn't set /dev/ppp to nonblock: %m");
-    }
-
     /* Get an internet socket for doing socket ioctls. */
     sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock_fd < 0)
@@ -308,8 +341,8 @@
 /*
  * 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);
@@ -322,15 +355,18 @@
 void
 sys_close(void)
 {
-    if (new_style_driver)
+    if (new_style_driver && ppp_dev_fd >= 0)
 	close(ppp_dev_fd);
     if (sock_fd >= 0)
 	close(sock_fd);
+#ifdef INET6
+    if (sock6_fd >= 0)
+	close(sock6_fd);
+#endif
     if (slave_fd >= 0)
 	close(slave_fd);
     if (master_fd >= 0)
 	close(master_fd);
-    closelog();
 }
 
 /********************************************************************
@@ -340,15 +376,13 @@
 
 static int set_kdebugflag (int requested_level)
 {
-    if (new_style_driver && ifunit < 0)
+    if (ppp_dev_fd < 0)
 	return 1;
     if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) {
 	if ( ! ok_error (errno) )
-	    error("ioctl(PPPIOCSDEBUG): %m");
+	    error("ioctl(PPPIOCSDEBUG): %m (line %d)", __LINE__);
 	return (0);
     }
-    SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d",
-		requested_level));
     return (1);
 }
 
@@ -359,22 +393,23 @@
 
 int tty_establish_ppp (int tty_fd)
 {
-    int x;
-    int fd = -1;
+    int ret_fd;
 
 /*
  * Ensure that the tty device is in exclusive mode.
  */
     if (ioctl(tty_fd, TIOCEXCL, 0) < 0) {
 	if ( ! ok_error ( errno ))
+	{
 	    warn("Couldn't make tty exclusive: %m");
+	}
     }
 /*
  * Demand mode - prime the old ppp device to relinquish the unit.
  */
     if (!new_style_driver && looped
 	&& ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
-	error("ioctl(transfer ppp unit): %m");
+	error("ioctl(transfer ppp unit): %m, line %d", __LINE__);
 	return -1;
     }
 /*
@@ -392,11 +427,38 @@
 	}
     }
 
+    ret_fd = generic_establish_ppp(tty_fd);
+
+#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+		 | SC_LOG_FLUSH)
+
+    if (ret_fd >= 0) {
+	modify_flags(ppp_fd, SC_RCVB | SC_LOGB,
+		     (kdebugflag * SC_DEBUG) & SC_LOGB);
+    } else {
+	if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+	{
+	    warn("Couldn't reset tty to normal line discipline: %m");
+	}
+    }
+
+    return ret_fd;
+}
+
+/********************************************************************
+ *
+ * generic_establish_ppp - Turn the fd into a ppp interface.
+ */
+int generic_establish_ppp (int fd)
+{
+    int x;
+
     if (new_style_driver) {
-	/* Open another instance of /dev/ppp and connect the channel to it */
 	int flags;
 
-	if (ioctl(tty_fd, PPPIOCGCHAN, &chindex) == -1) {
+	/* Open an instance of /dev/ppp and connect the channel to it */
+	if (ioctl(fd, PPPIOCGCHAN, &chindex) == -1) {
 	    error("Couldn't get channel number: %m");
 	    goto err;
 	}
@@ -406,6 +468,7 @@
 	    error("Couldn't reopen /dev/ppp: %m");
 	    goto err;
 	}
+	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
 	if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) {
 	    error("Couldn't attach to channel %d: %m", chindex);
 	    goto err_close;
@@ -426,7 +489,7 @@
 	}
 
 	if (looped)
-	    set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC);
+	    modify_flags(ppp_dev_fd, SC_LOOP_TRAFFIC, 0);
 
 	if (!multilink) {
 	    add_fd(ppp_dev_fd);
@@ -440,11 +503,11 @@
 	/*
 	 * Old-style driver: find out which interface we were given.
 	 */
-	set_ppp_fd (tty_fd);
-	if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {	
+	set_ppp_fd (fd);
+	if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
 	    if (ok_error (errno))
 		goto err;
-	    fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+	    fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
 	}
 	/* Check that we got the same unit again. */
 	if (looped && x != ifunit)
@@ -454,87 +517,105 @@
 	/*
 	 * Fetch the initial file flags and reset blocking mode on the file.
 	 */
-	initfdflags = fcntl(tty_fd, F_GETFL);
+	initfdflags = fcntl(fd, F_GETFL);
 	if (initfdflags == -1 ||
-	    fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+	    fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
 	    if ( ! ok_error (errno))
+	    {
 		warn("Couldn't set device to non-blocking mode: %m");
+	    }
 	}
     }
 
-    looped = 0;
-
     /*
      * Enable debug in the driver if requested.
      */
     if (!looped)
 	set_kdebugflag (kdebugflag);
 
-#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
-#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
-		 | SC_LOG_FLUSH)
-
-    set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
-		       | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
-
-    SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
-	    driver_version, driver_modification, driver_patch));
+    looped = 0;
 
     return ppp_fd;
 
  err_close:
     close(fd);
  err:
-    if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
-	warn("Couldn't reset tty to normal line discipline: %m");
     return -1;
 }
 
 /********************************************************************
  *
- * tty_disestablish_ppp - Restore the serial port to normal operation,
- * and reconnect the ppp unit to the loopback if in demand mode.
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
  * This shouldn't call die() because it's called from die().
  */
 
 void tty_disestablish_ppp(int tty_fd)
 {
-    if (demand)
-	restore_loop();
     if (!hungup) {
 /*
  * Flush the tty output buffer so that the TIOCSETD doesn't hang.
  */
 	if (tcflush(tty_fd, TCIOFLUSH) < 0)
+	{
 	    warn("tcflush failed: %m");
+	    goto flushfailed;
+	}
 /*
  * Restore the previous line discipline
  */
 	if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) {
 	    if ( ! ok_error (errno))
-		error("ioctl(TIOCSETD, N_TTY): %m");
+	    {
+		error("ioctl(TIOCSETD, N_TTY): %m (line %d)", __LINE__);
+	    }
 	}
-	
+
 	if (ioctl(tty_fd, TIOCNXCL, 0) < 0) {
 	    if ( ! ok_error (errno))
-		warn("ioctl(TIOCNXCL): %m(%d)", errno);
+	    {
+		warn("ioctl(TIOCNXCL): %m (line %d)", __LINE__);
+	    }
 	}
 
 	/* Reset non-blocking mode on fd. */
 	if (initfdflags != -1 && fcntl(tty_fd, F_SETFL, initfdflags) < 0) {
 	    if ( ! ok_error (errno))
+	    {
 		warn("Couldn't restore device fd flags: %m");
+	    }
 	}
     }
+flushfailed:
     initfdflags = -1;
 
+    generic_disestablish_ppp(tty_fd);
+}
+
+/********************************************************************
+ *
+ * generic_disestablish_ppp - Restore device components to normal
+ * operation, and reconnect the ppp unit to the loopback if in demand
+ * mode.  This shouldn't call die() because it's called from die().
+ */
+void generic_disestablish_ppp(int dev_fd)
+{
     if (new_style_driver) {
 	close(ppp_fd);
 	ppp_fd = -1;
-	if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
-	    error("Couldn't release PPP unit: %m");
-	if (!multilink)
+	if (demand) {
+	    modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
+	    looped = 1;
+	} else if (!doing_multilink && ppp_dev_fd >= 0) {
+	    close(ppp_dev_fd);
 	    remove_fd(ppp_dev_fd);
+	    ppp_dev_fd = -1;
+	}
+    } else {
+	/* old-style driver */
+	if (demand)
+	    set_ppp_fd(slave_fd);
+	else
+	    ppp_dev_fd = -1;
     }
 }
 
@@ -544,17 +625,35 @@
  */
 static int make_ppp_unit()
 {
-	int x;
+	int x, flags;
+
+	if (ppp_dev_fd >= 0) {
+		dbglog("in make_ppp_unit, already had /dev/ppp open?");
+		close(ppp_dev_fd);
+	}
+	ppp_dev_fd = open("/dev/ppp", O_RDWR);
+	if (ppp_dev_fd < 0)
+	 {
+		fatal("Couldn't open /dev/ppp: %m");
+	 }
+	flags = fcntl(ppp_dev_fd, F_GETFL);
+	if (flags == -1
+	    || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+	    {
+		warn("Couldn't set /dev/ppp to nonblock: %m");
+	    }
 
 	ifunit = req_unit;
 	x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
 	if (x < 0 && req_unit >= 0 && errno == EEXIST) {
-		warn("Couldn't allocate PPP unit %d as it is already in use");
+		warn("Couldn't allocate PPP unit %d as it is already in use", req_unit);
 		ifunit = -1;
 		x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
 	}
 	if (x < 0)
+	{
 		error("Couldn't create new ppp unit: %m");
+	}
 	return x;
 }
 
@@ -564,20 +663,16 @@
  */
 void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
 {
-	int flags;
-
 	if (!new_style_driver)
 		return;
 
 	/* set the mrru, mtu and flags */
 	if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
 		error("Couldn't set MRRU: %m");
-	flags = get_flags(ppp_dev_fd);
-	flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
-	flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
-		| (mrru? SC_MULTILINK: 0);
 
-	set_flags(ppp_dev_fd, flags);
+	modify_flags(ppp_dev_fd, SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ|SC_MULTILINK,
+		     ((rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
+		      | (mrru? SC_MULTILINK: 0)));
 
 	/* connect up the channel */
 	if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
@@ -611,22 +706,42 @@
  */
 int bundle_attach(int ifnum)
 {
+	int master_fd;
+
 	if (!new_style_driver)
 		return -1;
 
-	if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
-		if (errno == ENXIO)
+	master_fd = open("/dev/ppp", O_RDWR);
+	if (master_fd < 0)
+		fatal("Couldn't open /dev/ppp: %m");
+	if (ioctl(master_fd, PPPIOCATTACH, &ifnum) < 0) {
+		if (errno == ENXIO) {
+			close(master_fd);
 			return 0;	/* doesn't still exist */
+		}
 		fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
 	}
 	if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
 		fatal("Couldn't connect to interface unit %d: %m", ifnum);
-	set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+	modify_flags(master_fd, 0, SC_MULTILINK);
+	close(master_fd);
 
 	ifunit = ifnum;
 	return 1;
 }
 
+/*
+ * destroy_bundle - tell the driver to destroy our bundle.
+ */
+void destroy_bundle(void)
+{
+	if (ppp_dev_fd >= 0) {
+		close(ppp_dev_fd);
+		remove_fd(ppp_dev_fd);
+		ppp_dev_fd = -1;
+	}
+}
+
 /********************************************************************
  *
  * clean_check - Fetch the flags for the device and generate
@@ -644,20 +759,20 @@
 	    case SC_RCV_B7_0:
 		s = "all had bit 7 set to 1";
 		break;
-		
+
 	    case SC_RCV_B7_1:
 		s = "all had bit 7 set to 0";
 		break;
-		
+
 	    case SC_RCV_EVNP:
 		s = "all had odd parity";
 		break;
-		
+
 	    case SC_RCV_ODDP:
 		s = "all had even parity";
 		break;
 	    }
-	    
+
 	    if (s != NULL) {
 		warn("Receive serial link is not 8-bit clean:");
 		warn("Problem: %s", s);
@@ -665,7 +780,7 @@
 	}
     }
 }
-	
+
 
 /*
  * List of valid speeds.
@@ -782,7 +897,7 @@
 static int baud_rate_of (int speed)
 {
     struct speed *speedp;
-    
+
     if (speed != 0) {
 	for (speedp = speeds; speedp->speed_int; speedp++) {
 	    if (speed == speedp->speed_val)
@@ -807,13 +922,13 @@
     setdtr(tty_fd, 1);
     if (tcgetattr(tty_fd, &tios) < 0) {
 	if (!ok_error(errno))
-	    fatal("tcgetattr: %m(%d)", errno);
+	    fatal("tcgetattr: %m (line %d)", __LINE__);
 	return;
     }
-    
+
     if (!restore_term)
 	inittermios = tios;
-    
+
     tios.c_cflag     &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
     tios.c_cflag     |= CS8 | CREAD | HUPCL;
 
@@ -822,7 +937,7 @@
     tios.c_lflag      = 0;
     tios.c_cc[VMIN]   = 1;
     tios.c_cc[VTIME]  = 0;
-    
+
     if (local || !modem)
 	tios.c_cflag ^= (CLOCAL | HUPCL);
 
@@ -844,7 +959,7 @@
     default:
 	break;
     }
-    
+
     speed = translate_speed(inspeed);
     if (speed) {
 	cfsetospeed (&tios, speed);
@@ -860,10 +975,10 @@
 	    fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
     }
 
-    if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0)
-	if (!ok_error(errno))
-	    fatal("tcsetattr: %m");
-    
+    while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
+	if (errno != EINTR)
+	    fatal("tcsetattr: %m (line %d)", __LINE__);
+
     baud_rate    = baud_rate_of(speed);
     restore_term = 1;
 }
@@ -898,10 +1013,12 @@
  */
 	if (!default_device)
 	    inittermios.c_lflag &= ~(ECHO | ECHONL);
-	
+
 	if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) {
 	    if (! ok_error (errno))
-		warn("tcsetattr: %m");
+	    {
+		warn("tcsetattr: %m (line %d)", __LINE__);
+	    }
 	}
     }
 }
@@ -916,8 +1033,8 @@
     int fd = ppp_fd;
     int proto;
 
-    if (debug)
-	dbglog("sent %P", p, len);
+    dump_packet("sent", p, len);
+    if (snoop_send_hook) snoop_send_hook(p, len);
 
     if (len < PPP_HDRLEN)
 	return;
@@ -925,15 +1042,18 @@
 	p += 2;
 	len -= 2;
 	proto = (p[0] << 8) + p[1];
-	if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
+	if (ppp_dev_fd >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
 	    fd = ppp_dev_fd;
     }
     if (write(fd, p, len) < 0) {
-	if (errno == EWOULDBLOCK || errno == ENOBUFS
+	if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS
 	    || errno == ENXIO || errno == EIO || errno == EINTR)
+	    {
 	    warn("write: warning: %m (%d)", errno);
-	else
+	    }
+	else{
 	    error("write: %m (%d)", errno);
+	    }
     }
 }
 
@@ -953,7 +1073,7 @@
     exc = in_fds;
     n = select(max_in_fd + 1, &ready, NULL, &exc, timo);
     if (n < 0 && errno != EINTR)
-	fatal("select: %m(%d)", errno);
+	fatal("select: %m");
 }
 
 /*
@@ -961,6 +1081,8 @@
  */
 void add_fd(int fd)
 {
+    if (fd >= FD_SETSIZE)
+	fatal("internal error: file descriptor too large (%d)", fd);
     FD_SET(fd, &in_fds);
     if (fd > max_in_fd)
 	max_in_fd = fd;
@@ -993,19 +1115,27 @@
     nr = -1;
     if (ppp_fd >= 0) {
 	nr = read(ppp_fd, buf, len);
-	if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+	if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+	    && errno != EIO && errno != EINTR)
 	    error("read: %m");
 	if (nr < 0 && errno == ENXIO)
 	    return 0;
     }
-    if (nr < 0 && new_style_driver && ifunit >= 0) {
+    if (nr < 0 && new_style_driver && ppp_dev_fd >= 0 && !bundle_eof) {
 	/* N.B. we read ppp_fd first since LCP packets come in there. */
 	nr = read(ppp_dev_fd, buf, len);
-	if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+	if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+	    && errno != EIO && errno != EINTR)
 	    error("read /dev/ppp: %m");
 	if (nr < 0 && errno == ENXIO)
-	    return 0;
+	    nr = 0;
+	if (nr == 0 && doing_multilink) {
+	    remove_fd(ppp_dev_fd);
+	    bundle_eof = 1;
+	}
     }
+    if (new_style_driver && ppp_fd < 0 && ppp_dev_fd < 0)
+	nr = 0;
     return (new_style_driver && nr > 0)? nr+2: nr;
 }
 
@@ -1035,7 +1165,7 @@
     if (n == 0)
 	fatal("eof on loopback");
 
-    if (errno != EWOULDBLOCK)
+    if (errno != EWOULDBLOCK && errno != EAGAIN)
 	fatal("read from loopback: %m(%d)", errno);
 
     return rv;
@@ -1049,14 +1179,30 @@
 {
     struct ifreq ifr;
 
-    SYSDEBUG ((LOG_DEBUG, "netif_set_mtu: mtu = %d\n", mtu));
-
     memset (&ifr, '\0', sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
     ifr.ifr_mtu = mtu;
-	
+//    info("set mtu ppp to %s (%d)\n",ifname,mtu);
     if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
-	fatal("ioctl(SIOCSIFMTU): %m");
+	error("ioctl(SIOCSIFMTU): %m (line %d)", __LINE__);
+}
+
+/*
+ * netif_get_mtu - get the MTU on the PPP network interface.
+ */
+int
+netif_get_mtu(int unit)
+{
+    struct ifreq ifr;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+
+    if (ifunit >= 0 && ioctl(sock_fd, SIOCGIFMTU, (caddr_t) &ifr) < 0) {
+	error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__);
+	return 0;
+    }
+    return ifr.ifr_mtu;
 }
 
 /********************************************************************
@@ -1065,28 +1211,25 @@
  * the ppp interface.
  */
 
-void tty_send_config (int mtu,u_int32_t asyncmap,int pcomp,int accomp)
+void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp)
 {
-    u_int x;
+	int x;
 
-/*
- * Set the asyncmap and other parameters for the ppp device
- */
-    if (!still_ppp())
-	return;
-    link_mtu = mtu;
-    SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
-    if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
-	if (!ok_error(errno))
-	    fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno);
-	return;
-    }
-    
-    x = get_flags(ppp_fd);
-    x = pcomp  ? x | SC_COMP_PROT : x & ~SC_COMP_PROT;
-    x = accomp ? x | SC_COMP_AC   : x & ~SC_COMP_AC;
-    x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC;
-    set_flags(ppp_fd, x);
+	if (!still_ppp())
+		return;
+	link_mtu = mtu;
+	if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+		if (errno != EIO && errno != ENOTTY)
+		{
+			error("Couldn't set transmit async character map: %m");
+		}
+		++error_count;
+		return;
+	}
+
+	x = (pcomp? SC_COMP_PROT: 0) | (accomp? SC_COMP_AC: 0)
+	    | (sync_serial? SC_SYNC: 0);
+	modify_flags(ppp_fd, SC_COMP_PROT|SC_COMP_AC|SC_SYNC, x);
 }
 
 /********************************************************************
@@ -1096,14 +1239,11 @@
 
 void tty_set_xaccm (ext_accm accm)
 {
-    SYSDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n",
-		accm[0], accm[1], accm[2], accm[3]));
-
     if (!still_ppp())
 	return;
     if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) {
 	if ( ! ok_error (errno))
-	    warn("ioctl(set extended ACCM): %m(%d)", errno);
+	    warn("ioctl(set extended ACCM): %m (line %d)", __LINE__);
     }
 }
 
@@ -1113,31 +1253,29 @@
  * the ppp interface.
  */
 
-void tty_recv_config (int mru,u_int32_t asyncmap,int pcomp,int accomp)
+void tty_recv_config(int mru, u_int32_t asyncmap, int pcomp, int accomp)
 {
-    SYSDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru));
 /*
  * If we were called because the link has gone down then there is nothing
  * which may be done. Just return without incident.
  */
-    if (!still_ppp())
-	return;
+	if (!still_ppp())
+		return;
 /*
  * Set the receiver parameters
  */
-    if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
-	if ( ! ok_error (errno))
-	    error("ioctl(PPPIOCSMRU): %m(%d)", errno);
-    }
-    if (new_style_driver && ifunit >= 0
-	&& ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
-	error("Couldn't set MRU in generic PPP layer: %m");
-
-    SYSDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap));
-    if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
-	if (!ok_error(errno))
-	    error("ioctl(PPPIOCSRASYNCMAP): %m(%d)", errno);
-    }
+	if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+		if (errno != EIO && errno != ENOTTY)
+			error("Couldn't set channel receive MRU: %m");
+	}
+	if (new_style_driver && ppp_dev_fd >= 0
+	    && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
+		error("Couldn't set MRU in generic PPP layer: %m");
+
+	if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+		if (errno != EIO && errno != ENOTTY)
+			error("Couldn't set channel receive asyncmap: %m");
+	}
 }
 
 /********************************************************************
@@ -1146,7 +1284,8 @@
  * is acceptable for use.
  */
 
-int ccp_test (int unit, u_char *opt_ptr, int opt_len, int for_transmit)
+int
+ccp_test(int unit, u_char *opt_ptr, int opt_len, int for_transmit)
 {
     struct ppp_option_data data;
 
@@ -1168,12 +1307,11 @@
 
 void ccp_flags_set (int unit, int isopen, int isup)
 {
-    if (still_ppp()) {
-	int x = get_flags(ppp_dev_fd);
-	x = isopen? x | SC_CCP_OPEN : x &~ SC_CCP_OPEN;
-	x = isup?   x | SC_CCP_UP   : x &~ SC_CCP_UP;
-	set_flags (ppp_dev_fd, x);
-    }
+	int x;
+
+	x = (isopen? SC_CCP_OPEN: 0) | (isup? SC_CCP_UP: 0);
+	if (still_ppp() && ppp_dev_fd >= 0)
+		modify_flags(ppp_dev_fd, SC_CCP_OPEN|SC_CCP_UP, x);
 }
 
 #ifdef PPP_FILTER
@@ -1188,9 +1326,13 @@
 	fp.filter = (struct sock_filter *) pass->bf_insns;
 	if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) {
 		if (errno == ENOTTY)
+		{
 			warn("kernel does not support PPP filtering");
+		}
 		else
+		{
 			error("Couldn't set pass-filter in kernel: %m");
+		}
 		return 0;
 	}
 	fp.len = active->bf_len;
@@ -1213,7 +1355,7 @@
     struct ppp_idle *ip;
 {
     return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
-} 
+}
 
 /********************************************************************
  *
@@ -1236,6 +1378,8 @@
     }
     stats->bytes_in = req.stats.p.ppp_ibytes;
     stats->bytes_out = req.stats.p.ppp_obytes;
+    stats->pkts_in = req.stats.p.ppp_ipackets;
+    stats->pkts_out = req.stats.p.ppp_opackets;
     return 1;
 }
 
@@ -1248,9 +1392,13 @@
 
 int ccp_fatal_error (int unit)
 {
-    int x = get_flags(ppp_dev_fd);
+	int flags;
 
-    return x & SC_DC_FERROR;
+	if (ioctl(ppp_dev_fd, PPPIOCGFLAGS, &flags) < 0) {
+		error("Couldn't read compression error flags: %m");
+		flags = 0;
+	}
+	return flags & SC_DC_FERROR;
 }
 
 /********************************************************************
@@ -1311,8 +1459,8 @@
 static void close_route_table (void)
 {
     if (route_fd != (FILE *) 0) {
-        fclose (route_fd);
-        route_fd = (FILE *) 0;
+	fclose (route_fd);
+	route_fd = (FILE *) 0;
     }
 }
 
@@ -1331,8 +1479,8 @@
     path = path_to_procfs("/net/route");
     route_fd = fopen (path, "r");
     if (route_fd == NULL) {
-        error("can't open routing table %s: %m", path);
-        return 0;
+	error("can't open routing table %s: %m", path);
+	return 0;
     }
 
     route_dev_col = 0;		/* default to usual columns */
@@ -1380,7 +1528,7 @@
 {
     char *cols[ROUTE_MAX_COLS], *p;
     int col;
-	
+
     memset (rt, '\0', sizeof (struct rtentry));
 
     if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
@@ -1414,15 +1562,15 @@
     int result = 0;
 
     if (!open_route_table())
-        return 0;
+	return 0;
 
     while (read_route_table(rt) != 0) {
-        if ((rt->rt_flags & RTF_UP) == 0)
+	if ((rt->rt_flags & RTF_UP) == 0)
 	    continue;
 
 	if (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0)
 	    continue;
-        if (SIN_ADDR(rt->rt_dst) == 0L) {
+	if (SIN_ADDR(rt->rt_dst) == 0L) {
 	    result = 1;
 	    break;
 	}
@@ -1470,33 +1618,37 @@
     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;
 
     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, SIOCADDRT, &rt) < 0) {
 	if ( ! ok_error ( errno ))
-	    error("default route ioctl(SIOCADDRT): %m(%d)", errno);
+	    error("default route ioctl(SIOCADDRT): %m");
 	return 0;
     }
 
-    default_route_gateway = gateway;
+    have_default_route = 1;
     return 1;
 }
 
@@ -1509,7 +1661,7 @@
 {
     struct rtentry rt;
 
-    default_route_gateway = 0;
+    have_default_route = 0;
 
     memset (&rt, '\0', sizeof (rt));
     SET_SA_FAMILY (rt.rt_dst,     AF_INET);
@@ -1520,13 +1672,11 @@
 	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 ))
-		error("default route ioctl(SIOCDELRT): %m (%d)", errno);
+		error("default route ioctl(SIOCDELRT): %m");
 	    return 0;
 	}
     }
@@ -1546,7 +1696,7 @@
 
     if (has_proxy_arp == 0) {
 	memset (&arpreq, '\0', sizeof(arpreq));
-    
+
 	SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
 	SIN_ADDR(arpreq.arp_pa) = his_adr;
 	arpreq.arp_flags = ATF_PERM | ATF_PUBL;
@@ -1563,7 +1713,7 @@
 
 	if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) {
 	    if ( ! ok_error ( errno ))
-		error("ioctl(SIOCSARP): %m(%d)", errno);
+		error("ioctl(SIOCSARP): %m");
 	    return 0;
 	}
 	proxy_arp_addr = his_adr;
@@ -1575,7 +1725,9 @@
 		int fd = open(forw_path, O_WRONLY);
 		if (fd >= 0) {
 		    if (write(fd, "1", 1) != 1)
+		    {
 			error("Couldn't enable IP forwarding: %m");
+		    }
 		    close(fd);
 		}
 	    }
@@ -1604,13 +1756,15 @@
 
 	if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) {
 	    if ( ! ok_error ( errno ))
-		warn("ioctl(SIOCDARP): %m(%d)", errno);
+	    {
+		warn("ioctl(SIOCDARP): %m");
+	    }
 	    return 0;
 	}
     }
     return 1;
 }
-     
+
 /********************************************************************
  *
  * get_ether_addr - get the hardware address of an interface on the
@@ -1624,20 +1778,21 @@
     struct ifreq *ifr, *ifend;
     u_int32_t ina, mask;
     char *aliasp;
-    struct ifreq ifreq;
+    struct ifreq ifreq, bestifreq;
     struct ifconf ifc;
     struct ifreq ifs[MAX_IFS];
-    
+
+    u_int32_t bestmask=0;
+    int found_interface = 0;
+
     ifc.ifc_len = sizeof(ifs);
     ifc.ifc_req = ifs;
     if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
 	if ( ! ok_error ( errno ))
-	    error("ioctl(SIOCGIFCONF): %m(%d)", errno);
+	    error("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__);
 	return 0;
     }
 
-    SYSDEBUG ((LOG_DEBUG, "proxy arp: scanning %d interfaces for IP %s",
-		ifc.ifc_len / sizeof(struct ifreq), ip_ntoa(ipaddr)));
 /*
  * Scan through looking for an interface with an Internet
  * address on the same subnet as `ipaddr'.
@@ -1647,8 +1802,6 @@
 	if (ifr->ifr_addr.sa_family == AF_INET) {
 	    ina = SIN_ADDR(ifr->ifr_addr);
 	    strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
-            SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s",
-			ifreq.ifr_name));
 /*
  * Check that the interface is up, and not point-to-point
  * nor loopback.
@@ -1662,22 +1815,26 @@
  * Get its netmask and check that it's on the right subnet.
  */
 	    if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
-	        continue;
+		continue;
 
 	    mask = SIN_ADDR(ifreq.ifr_addr);
-	    SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx",
-			ip_ntoa(ina), ntohl(mask)));
 
 	    if (((ipaddr ^ ina) & mask) != 0)
-	        continue;
-	    break;
+		continue; /* no match */
+	    /* matched */
+	    if (mask >= bestmask) {
+		/* Compare using >= instead of > -- it is possible for
+		   an interface to have a netmask of 0.0.0.0 */
+		found_interface = 1;
+		bestifreq = ifreq;
+		bestmask = mask;
+	    }
 	}
     }
-    
-    if (ifr >= ifend)
-        return 0;
 
-    strlcpy(name, ifreq.ifr_name, namelen);
+    if (!found_interface) return 0;
+
+    strlcpy(name, bestifreq.ifr_name, namelen);
 
     /* trim off the :1 in eth0:1 */
     aliasp = strchr(name, ':');
@@ -1688,26 +1845,16 @@
 /*
  * Now get the hardware address.
  */
-    memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
-    if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) {
-        error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno);
-        return 0;
+    memset (&bestifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+    if (ioctl (sock_fd, SIOCGIFHWADDR, &bestifreq) < 0) {
+	error("SIOCGIFHWADDR(%s): %m", bestifreq.ifr_name);
+	return 0;
     }
 
     memcpy (hwaddr,
-	    &ifreq.ifr_hwaddr,
+	    &bestifreq.ifr_hwaddr,
 	    sizeof (struct sockaddr));
 
-    SYSDEBUG ((LOG_DEBUG,
-	   "proxy arp: found hwaddr %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-		(int) ((unsigned char *) &hwaddr->sa_data)[0],
-		(int) ((unsigned char *) &hwaddr->sa_data)[1],
-		(int) ((unsigned char *) &hwaddr->sa_data)[2],
-		(int) ((unsigned char *) &hwaddr->sa_data)[3],
-		(int) ((unsigned char *) &hwaddr->sa_data)[4],
-		(int) ((unsigned char *) &hwaddr->sa_data)[5],
-		(int) ((unsigned char *) &hwaddr->sa_data)[6],
-		(int) ((unsigned char *) &hwaddr->sa_data)[7]));
     return 1;
 }
 
@@ -1761,14 +1908,14 @@
     struct ifreq ifs[MAX_IFS];
 
     addr = ntohl(addr);
-    
+
     if (IN_CLASSA(addr))	/* determine network mask for address class */
 	nmask = IN_CLASSA_NET;
     else if (IN_CLASSB(addr))
 	    nmask = IN_CLASSB_NET;
     else
 	    nmask = IN_CLASSC_NET;
-    
+
     /* class D nets are disallowed by bad_ip_adrs */
     mask = netmask | htonl(nmask);
 /*
@@ -1778,10 +1925,12 @@
     ifc.ifc_req = ifs;
     if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
 	if ( ! ok_error ( errno ))
-	    warn("ioctl(SIOCGIFCONF): %m(%d)", errno);
+	{
+	    warn("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__);
+	}
 	return mask;
     }
-    
+
     ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
     for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
 /*
@@ -1798,7 +1947,7 @@
 	strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
 	if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
 	    continue;
-	
+
 	if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
 	    continue;
 /*
@@ -1825,7 +1974,7 @@
     *version      = (int) strtoul (buf, &endp, 10);
     *modification = 0;
     *patch        = 0;
-    
+
     if (endp != buf && *endp == '.') {
 	buf = endp + 1;
 	*modification = (int) strtoul (buf, &endp, 10);
@@ -1865,10 +2014,10 @@
      * Try to put the device into the PPP discipline.
      */
     if (ioctl(local_fd, TIOCSETD, &ppp_disc) < 0) {
-	error("ioctl(TIOCSETD(PPP)): %m(%d)", errno);
+	error("ioctl(TIOCSETD(PPP)): %m (line %d)", __LINE__);
     } else
 	ret = 1;
-    
+
     close(local_fd);
     close(mfd);
     return ret;
@@ -1888,7 +2037,7 @@
     int    my_version, my_modification, my_patch;
     int osmaj, osmin, ospatch;
 
-    no_ppp_msg = 
+    no_ppp_msg =
 	"This system lacks kernel support for PPP.  This could be because\n"
 	"the PPP kernel module could not be loaded, or because PPP was not\n"
 	"included in the kernel configuration.  If PPP was included as a\n"
@@ -1940,11 +2089,11 @@
 
 /*
  * Open a socket for doing the ioctl operations.
- */    
+ */
     s = socket(AF_INET, SOCK_DGRAM, 0);
     if (s < 0)
 	return 0;
-    
+
     strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
     ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
 /*
@@ -1962,10 +2111,10 @@
  * Ensure that the hardware address is for PPP and not something else
  */
     if (ok)
-        ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
+	ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
 
     if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP))
-        ok = 0;
+	ok = 0;
 
 /*
  *  This is the PPP device. Validate the version of the driver at this
@@ -1997,7 +2146,7 @@
 	    /* The version numbers must match */
 	    if (driver_version != my_version)
 		ok = 0;
-      
+
 	    /* The modification levels must be legal */
 	    if (driver_modification < 3) {
 		if (driver_modification >= 2) {
@@ -2041,11 +2190,8 @@
     utmpname(_PATH_UTMP);
     setutent();
     while ((utp = getutent()) && (utp->ut_pid != mypid))
-        /* nothing */;
+	/* nothing */;
 
-    /* Is this call really necessary? There is another one after the 'put' */
-    endutent();
-    
     if (utp)
 	memcpy(&ut, utp, sizeof(ut));
     else
@@ -2054,7 +2200,7 @@
 
     if (ut.ut_id[0] == 0)
 	strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
-	
+
     strncpy(ut.ut_user, name, sizeof(ut.ut_user));
     strncpy(ut.ut_line, line, sizeof(ut.ut_line));
 
@@ -2071,7 +2217,7 @@
     if (ipcp_protent.enabled_flag && ipcp_hisoptions[0].neg_addr)
 	memcpy(&ut.ut_addr, (char *) &ipcp_hisoptions[0].hisaddr,
 		 sizeof(ut.ut_addr));
-	
+
     /* CL: Makes sure that the logout works */
     if (*host == 0 && *name==0)
 	ut.ut_host[0]=0;
@@ -2089,7 +2235,9 @@
 	flock(wtmp, LOCK_EX);
 
 	if (write (wtmp, (char *)&ut, sizeof(ut)) != sizeof(ut))
+	{
 	    warn("error writing %s: %m", _PATH_WTMP);
+	}
 
 	flock(wtmp, LOCK_UN);
 
@@ -2106,21 +2254,18 @@
 
 int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
 {
-    u_int x = get_flags(ppp_dev_fd);
+	u_int x;
 
-    if (vjcomp) {
-        if (ioctl (ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
-	    if (! ok_error (errno))
-		error("ioctl(PPPIOCSMAXCID): %m(%d)", errno);
-	    vjcomp = 0;
+	if (vjcomp) {
+		if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0)
+			error("Couldn't set up TCP header compression: %m");
+		vjcomp = 0;
 	}
-    }
 
-    x = vjcomp  ? x | SC_COMP_TCP     : x &~ SC_COMP_TCP;
-    x = cidcomp ? x & ~SC_NO_TCP_CCID : x | SC_NO_TCP_CCID;
-    set_flags (ppp_dev_fd, x);
+	x = (vjcomp? SC_COMP_TCP: 0) | (cidcomp? 0: SC_NO_TCP_CCID);
+	modify_flags(ppp_dev_fd, SC_COMP_TCP|SC_NO_TCP_CCID, x);
 
-    return 1;
+	return 1;
 }
 
 /********************************************************************
@@ -2136,14 +2281,14 @@
     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(%d)", errno);
+	    error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
 	return 0;
     }
 
     ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
     if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno))
-	    error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+	    error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
 	return 0;
     }
     if_is_up++;
@@ -2168,7 +2313,7 @@
     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(%d)", errno);
+	    error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
 	return 0;
     }
 
@@ -2176,7 +2321,7 @@
     ifr.ifr_flags |= IFF_POINTOPOINT;
     if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno))
-	    error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+	    error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
 	return 0;
     }
     return 1;
@@ -2190,15 +2335,15 @@
 int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
 	     u_int32_t net_mask)
 {
-    struct ifreq   ifr; 
+    struct ifreq   ifr;
     struct rtentry rt;
-    
+
     memset (&ifr, '\0', sizeof (ifr));
     memset (&rt,  '\0', sizeof (rt));
-    
-    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET); 
-    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET); 
-    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET); 
+
+    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET);
+    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
 
     strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
 /*
@@ -2208,12 +2353,12 @@
     if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
 	if (errno != EEXIST) {
 	    if (! ok_error (errno))
-		error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+		error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
 	}
-        else {
+	else {
 	    warn("ioctl(SIOCSIFADDR): Address already exists");
 	}
-        return (0);
+	return (0);
     }
 /*
  *  Set the gateway address
@@ -2221,9 +2366,9 @@
     SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
     if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno))
-	    error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno); 
+	    error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__);
 	return (0);
-    } 
+    }
 /*
  *  Set the netmask.
  *  For recent kernels, force the netmask to 255.255.255.255.
@@ -2234,9 +2379,9 @@
 	SIN_ADDR(ifr.ifr_netmask) = net_mask;
 	if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
 	    if (! ok_error (errno))
-		error("ioctl(SIOCSIFNETMASK): %m(%d)", errno); 
+		error("ioctl(SIOCSIFNETMASK): %m (line %d)", __LINE__);
 	    return (0);
-	} 
+	}
     }
 /*
  *  Add the device route
@@ -2257,7 +2402,7 @@
 
 	if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
 	    if (! ok_error (errno))
-		error("ioctl(SIOCADDRT) device route: %m(%d)", errno);
+		error("ioctl(SIOCADDRT) device route: %m (line %d)", __LINE__);
 	    return (0);
 	}
     }
@@ -2314,7 +2459,7 @@
 
 	if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
 	    if (still_ppp() && ! ok_error (errno))
-		error("ioctl(SIOCDELRT) device route: %m(%d)", errno);
+		error("ioctl(SIOCDELRT) device route: %m (line %d)", __LINE__);
 	    return (0);
 	}
     }
@@ -2323,10 +2468,10 @@
     memset(&ifr, 0, sizeof(ifr));
     SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    
+
     if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
 	if (! ok_error (errno)) {
-	    error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+	    error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
 	    return 0;
 	}
     }
@@ -2338,7 +2483,7 @@
 
 #ifdef INET6
 /********************************************************************
- * 
+ *
  * sif6addr - Config the interface with an IPv6 link-local address
  */
 int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
@@ -2355,10 +2500,10 @@
     memset(&ifr, 0, sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
-	error("sif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+	error("sif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__);
 	return 0;
     }
-    
+
     /* Local interface */
     memset(&ifr6, 0, sizeof(ifr6));
     IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
@@ -2366,10 +2511,10 @@
     ifr6.ifr6_prefixlen = 10;
 
     if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) {
-	error("sif6addr: ioctl(SIOCSIFADDR): %m (%d)", errno);
+	error("sif6addr: ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
 	return 0;
     }
-    
+
     /* Route to remote host */
     memset(&rt6, 0, sizeof(rt6));
     IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
@@ -2377,9 +2522,9 @@
     rt6.rtmsg_dst_len = 10;
     rt6.rtmsg_ifindex = ifr.ifr_ifindex;
     rt6.rtmsg_metric = 1;
-    
+
     if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
-	error("sif6addr: ioctl(SIOCADDRT): %m (%d)", errno);
+	error("sif6addr: ioctl(SIOCADDRT): %m (line %d)", __LINE__);
 	return 0;
     }
 
@@ -2404,10 +2549,10 @@
     memset(&ifr, 0, sizeof(ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
-	error("cif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+	error("cif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__);
 	return 0;
     }
-    
+
     memset(&ifr6, 0, sizeof(ifr6));
     IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
     ifr6.ifr6_ifindex = ifr.ifr_ifindex;
@@ -2416,12 +2561,12 @@
     if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) {
 	if (errno != EADDRNOTAVAIL) {
 	    if (! ok_error (errno))
-		error("cif6addr: ioctl(SIOCDIFADDR): %m (%d)", errno);
+		error("cif6addr: ioctl(SIOCDIFADDR): %m (line %d)", __LINE__);
 	}
-        else {
+	else {
 	    warn("cif6addr: ioctl(SIOCDIFADDR): No such address");
 	}
-        return (0);
+	return (0);
     }
     return 1;
 }
@@ -2455,10 +2600,14 @@
 #ifdef TIOCSPTLCK
 	    ptn = 0;
 	    if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0)
+	    {
 		warn("Couldn't unlock pty slave %s: %m", pty_name);
+	    }
 #endif
 	    if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0)
+	    {
 		warn("Couldn't open pty slave %s: %m", pty_name);
+	    }
 	}
     }
 #endif /* TIOCGPTN */
@@ -2495,9 +2644,13 @@
 	tios.c_oflag  = 0;
 	tios.c_lflag  = 0;
 	if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
+	{
 	    warn("couldn't set attributes on pty: %m");
+	}
     } else
+    {
 	warn("couldn't get attributes on pty: %m");
+    }
 
     return 1;
 }
@@ -2517,7 +2670,7 @@
 	/* allocate ourselves a ppp unit */
 	if (make_ppp_unit() < 0)
 	    die(1);
-	set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
+	modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
 	set_kdebugflag(kdebugflag);
 	ppp_fd = -1;
 	return ppp_dev_fd;
@@ -2525,27 +2678,30 @@
 
     if (!get_pty(&master_fd, &slave_fd, loop_name, 0))
 	fatal("No free pty for loopback");
-    SYSDEBUG(("using %s for loopback", loop_name));
 
     set_ppp_fd(slave_fd);
 
     flags = fcntl(master_fd, F_GETFL);
     if (flags == -1 ||
 	fcntl(master_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-	warn("couldn't set master loopback to nonblock: %m(%d)", errno);
-
+	{
+	warn("couldn't set master loopback to nonblock: %m");
+	}
     flags = fcntl(ppp_fd, F_GETFL);
     if (flags == -1 ||
 	fcntl(ppp_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-	warn("couldn't set slave loopback to nonblock: %m(%d)", errno);
-
+	{
+	warn("couldn't set slave loopback to nonblock: %m");
+	}
     if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0)
-	fatal("ioctl(TIOCSETD): %m(%d)", errno);
+    {
+	fatal("ioctl(TIOCSETD): %m (line %d)", __LINE__);
+    }
 /*
  * Find out which interface we were given.
  */
     if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0)
-	fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+	fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
 /*
  * Enable debug in the driver if requested.
  */
@@ -2556,33 +2712,6 @@
 
 /********************************************************************
  *
- * restore_loop - reattach the ppp unit to the loopback.
- *
- * The kernel ppp driver automatically reattaches the ppp unit to
- * the loopback if the serial port is set to a line discipline other
- * than ppp, or if it detects a modem hangup.  The former will happen
- * in disestablish_ppp if the latter hasn't already happened, so we
- * shouldn't need to do anything.
- *
- * Just to be sure, set the real serial port to the normal discipline.
- */
-
-static void
-restore_loop(void)
-{
-    looped = 1;
-    if (new_style_driver) {
-	set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC);
-	return;
-    }
-    if (ppp_fd != slave_fd) {
-	(void) ioctl(ppp_fd, TIOCSETD, &tty_disc);
-	set_ppp_fd(slave_fd);
-    }
-}
-
-/********************************************************************
- *
  * sifnpmode - Set the mode for handling packets for a given NP.
  */
 
@@ -2598,8 +2727,7 @@
     npi.mode     = mode;
     if (ioctl(ppp_dev_fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) {
 	if (! ok_error (errno))
-	    error("ioctl(PPPIOCSNPMODE, %d, %d): %m (%d)",
-		   proto, mode, errno);
+	    error("ioctl(PPPIOCSNPMODE, %d, %d): %m", proto, mode);
 	return 0;
     }
     return 1;
@@ -2616,14 +2744,14 @@
     int    result = 1;
 
 #ifdef IPX_CHANGE
-    int    skfd; 
+    int    skfd;
     struct ifreq         ifr;
     struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
 
     skfd = socket (AF_IPX, SOCK_DGRAM, 0);
-    if (skfd < 0) { 
+    if (skfd < 0) {
 	if (! ok_error (errno))
-	    dbglog("socket(AF_IPX): %m (%d)", errno);
+	    dbglog("socket(AF_IPX): %m (line %d)", __LINE__);
 	result = 0;
     }
     else {
@@ -2643,7 +2771,7 @@
 	    result = 0;
 	    if (errno != EEXIST) {
 		if (! ok_error (errno))
-		    dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (%d)", errno);
+		    dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (line %d)", __LINE__);
 	    }
 	    else {
 		warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists");
@@ -2667,14 +2795,14 @@
     int    result = 1;
 
 #ifdef IPX_CHANGE
-    int    skfd; 
+    int    skfd;
     struct ifreq         ifr;
     struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
 
     skfd = socket (AF_IPX, SOCK_DGRAM, 0);
-    if (skfd < 0) { 
+    if (skfd < 0) {
 	if (! ok_error (errno))
-	    dbglog("socket(AF_IPX): %m (%d)", errno);
+	    dbglog("socket(AF_IPX): %m (line %d)", __LINE__);
 	result = 0;
     }
     else {
@@ -2689,7 +2817,7 @@
  */
 	if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
 	    if (! ok_error (errno))
-		info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (%d)", errno);
+		info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (line %d)", __LINE__);
 	    result = 0;
 	}
 	close (skfd);
@@ -2729,7 +2857,8 @@
 
     if (ipxcp_protent.enabled_flag) {
 	struct stat stat_buf;
-        if ((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;
@@ -2748,3 +2877,51 @@
     }
     return 1;
 }
+
+#ifdef INET6
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * convert the 48-bit MAC address of eth0 into EUI 64. caller also assumes
+ * that the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+int
+ether_to_eui64(eui64_t *p_eui64)
+{
+    struct ifreq ifr;
+    int skfd;
+    const unsigned char *ptr;
+
+    skfd = socket(PF_INET6, SOCK_DGRAM, 0);
+    if(skfd == -1)
+    {
+        warn("could not open IPv6 socket");
+        return 0;
+    }
+
+    strcpy(ifr.ifr_name, "eth0");
+    if(ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
+    {
+        close(skfd);
+        warn("could not obtain hardware address for eth0");
+        return 0;
+    }
+    close(skfd);
+
+    /*
+     * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+     */
+    ptr = ifr.ifr_hwaddr.sa_data;
+    p_eui64->e8[0] = ptr[0] | 0x02;
+    p_eui64->e8[1] = ptr[1];
+    p_eui64->e8[2] = ptr[2];
+    p_eui64->e8[3] = 0xFF;
+    p_eui64->e8[4] = 0xFE;
+    p_eui64->e8[5] = ptr[3];
+    p_eui64->e8[6] = ptr[4];
+    p_eui64->e8[7] = ptr[5];
+
+    return 1;
+}
+#endif
diff -Naur tomato/release/src/router/pppd/pppd/sys-solaris.c tomato121/release/src/router/pppd/pppd/sys-solaris.c
--- tomato/release/src/router/pppd/pppd/sys-solaris.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/sys-solaris.c	2008-04-22 21:44:05.000000000 +1000
@@ -18,36 +18,80 @@
  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1995-2002 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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 main.c and pppd.h, which are:
+ *
+ * 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.
  */
 
-#define RCSID	"$Id: sys-solaris.c,v 1.1 2003/07/10 07:43:04 honor Exp $"
+#define RCSID	"$Id: sys-solaris.c,v 1.14 2005/05/04 21:31:20 carlsonj Exp $"
 
 #include <limits.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
+#include <string.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -58,6 +102,7 @@
 #endif
 #include <signal.h>
 #include <utmpx.h>
+#include <stropts.h>
 #include <sys/types.h>
 #include <sys/ioccom.h>
 #include <sys/stream.h>
@@ -149,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)
 
@@ -1292,8 +1337,8 @@
     int retries;
     struct pollfd pfd;
 
-    if (debug)
-	dbglog("sent %P", p, len);
+    dump_packet("sent", p, len);
+    if (snoop_send_hook) snoop_send_hook(p, len);
 
     data.len = len;
     data.buf = (caddr_t) p;
@@ -1500,27 +1545,27 @@
 
     link_mtu = mtu;
     if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
-	if (hungup && errno == ENXIO)
+	if (hungup && errno == ENXIO) {
+	    ++error_count;
 	    return;
+	}
 	error("Couldn't set MTU: %m");
     }
     if (fdmuxid >= 0) {
 	if (!sync_serial) {
-	    if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	    if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0)
 		error("Couldn't set transmit ACCM: %m");
-	    }
 	}
 	cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
 	cf[1] = COMP_PROT | COMP_AC;
 	if (any_compressions() &&
-	    strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	    strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0)
 	    error("Couldn't set prot/AC compression: %m");
-	}
     }
 }
 
 /*
- * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ * tty_set_xaccm - set the extended transmit ACCM for the interface.
  */
 void
 tty_set_xaccm(accm)
@@ -1537,7 +1582,7 @@
 }
 
 /*
- * ppp_recv_config - configure the receive-side characteristics of
+ * tty_recv_config - configure the receive-side characteristics of
  * the ppp interface.
  */
 void
@@ -1550,22 +1595,22 @@
 
     link_mru = mru;
     if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
-	if (hungup && errno == ENXIO)
+	if (hungup && errno == ENXIO) {
+	    ++error_count;
 	    return;
+	}
 	error("Couldn't set MRU: %m");
     }
     if (fdmuxid >= 0) {
 	if (!sync_serial) {
-	    if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	    if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0)
 		error("Couldn't set receive ACCM: %m");
-	    }
 	}
 	cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
 	cf[1] = DECOMP_PROT | DECOMP_AC;
 	if (any_compressions() &&
-	    strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	    strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0)
 	    error("Couldn't set prot/AC decompression: %m");
-	}
     }
 }
 
@@ -1629,6 +1674,8 @@
     }
     stats->bytes_in = s.p.ppp_ibytes;
     stats->bytes_out = s.p.ppp_obytes;
+    stats->pkts_in = s.p.ppp_ipackets;
+    stats->pkts_out = s.p.ppp_opackets;
     return 1;
 }
 
@@ -1956,12 +2003,6 @@
 	error("Couldn't set remote IP address: %m");
 	ret = 0;
     }
-#if 0	/* now done in ppp_send_config */
-    ifr.ifr_metric = link_mtu;
-    if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
-	error("Couldn't set IP MTU: %m");
-    }
-#endif
 
     remote_addr = h;
     return ret;
diff -Naur tomato/release/src/router/pppd/pppd/sys-sunos4.c tomato121/release/src/router/pppd/pppd/sys-sunos4.c
--- tomato/release/src/router/pppd/pppd/sys-sunos4.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/sys-sunos4.c	1970-01-01 10:00:00.000000000 +1000
@@ -1,1559 +0,0 @@
-/*
- * System-dependent procedures for pppd under SunOS 4.
- *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
- */
-
-#define RCSID	"$Id: sys-sunos4.c,v 1.1 2003/07/10 07:43:05 honor Exp $"
-
-#include <stdio.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <termios.h>
-#include <signal.h>
-#include <malloc.h>
-#include <utmp.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/stream.h>
-#include <sys/stropts.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/poll.h>
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/nit_if.h>
-#include <net/route.h>
-#include <net/ppp_defs.h>
-#include <net/pppio.h>
-#include <netinet/in.h>
-
-#include "pppd.h"
-
-#if defined(sun) && defined(sparc)
-#include <alloca.h>
-#ifndef __GNUC__
-extern void *alloca();
-#endif
-#endif /*sparc*/
-
-static const char rcsid[] = RCSID;
-
-static int	pppfd;
-static int	fdmuxid = -1;
-static int	iffd;
-static int	sockfd;
-
-static int	restore_term;
-static struct termios inittermios;
-static struct winsize wsinfo;	/* Initial window size info */
-static pid_t	parent_pid;	/* PID of our parent */
-
-extern u_char	inpacket_buf[];	/* borrowed from main.c */
-
-#define MAX_POLLFDS	32
-static struct pollfd pollfds[MAX_POLLFDS];
-static int n_pollfds;
-
-static int	link_mtu, link_mru;
-
-#define NMODULES	32
-static int	tty_nmodules;
-static char	tty_modules[NMODULES][FMNAMESZ+1];
-
-static int	if_is_up;	/* Interface has been marked up */
-static u_int32_t ifaddrs[2];	/* local and remote addresses */
-static u_int32_t default_route_gateway;	/* Gateway for default route added */
-static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
-
-/* Prototypes for procedures local to this file. */
-static int translate_speed __P((int));
-static int baud_rate_of __P((int));
-static int get_ether_addr __P((u_int32_t, struct sockaddr *));
-static int strioctl __P((int, int, void *, int, int));
-
-
-/*
- * sys_init - System-dependent initialization.
- */
-void
-sys_init()
-{
-    int x;
-
-    /* Get an internet socket for doing socket ioctl's on. */
-    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
-	fatal("Couldn't create IP socket: %m");
-
-    /*
-     * We may want to send a SIGHUP to the session leader associated
-     * with our controlling terminal later.  Because SunOS doesn't
-     * have getsid(), we make do with sending the signal to our
-     * parent process.
-     */
-    parent_pid = getppid();
-
-    /*
-     * Open the ppp device.
-     */
-    pppfd = open("/dev/ppp", O_RDWR | O_NONBLOCK, 0);
-    if (pppfd < 0)
-	fatal("Can't open /dev/ppp: %m");
-    if (kdebugflag) {
-	x = PPPDBG_LOG + PPPDBG_DRIVER;
-	strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
-    }
-
-    /* Assign a new PPA and get its unit number. */
-    if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0)
-	fatal("Can't create new PPP interface: %m");
-
-    /*
-     * Open the ppp device again and push the if_ppp module on it.
-     */
-    iffd = open("/dev/ppp", O_RDWR, 0);
-    if (iffd < 0)
-	fatal("Can't open /dev/ppp (2): %m");
-    if (kdebugflag) {
-	x = PPPDBG_LOG + PPPDBG_DRIVER;
-	strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
-    }
-    if (strioctl(iffd, PPPIO_ATTACH, &ifunit, sizeof(int), 0) < 0)
-	fatal("Couldn't attach ppp interface to device: %m");
-    if (ioctl(iffd, I_PUSH, "if_ppp") < 0)
-	fatal("Can't push ppp interface module: %m");
-    if (kdebugflag) {
-	x = PPPDBG_LOG + PPPDBG_IF;
-	strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
-    }
-    if (strioctl(iffd, PPPIO_NEWPPA, &ifunit, sizeof(int), 0) < 0)
-	fatal("Couldn't create ppp interface unit: %m");
-    x = PPP_IP;
-    if (strioctl(iffd, PPPIO_BIND, &x, sizeof(int), 0) < 0)
-	fatal("Couldn't bind ppp interface to IP SAP: %m");
-
-    n_pollfds = 0;
-}
-
-/*
- * sys_cleanup - restore any system state we modified before exiting:
- * mark the interface down, delete default route and/or proxy arp entry.
- * This shouldn't call die() because it's called from die().
- */
-void
-sys_cleanup()
-{
-    if (if_is_up)
-	sifdown(0);
-    if (ifaddrs[0])
-	cifaddr(0, ifaddrs[0], ifaddrs[1]);
-    if (default_route_gateway)
-	cifdefaultroute(0, 0, default_route_gateway);
-    if (proxy_arp_addr)
-	cifproxyarp(0, proxy_arp_addr);
-}
-
-/*
- * sys_close - Clean up in a child process before execing.
- */
-void
-sys_close()
-{
-    close(iffd);
-    close(pppfd);
-    close(sockfd);
-}
-
-/*
- * sys_check_options - check the options that the user specified
- */
-int
-sys_check_options()
-{
-    return 1;
-}
-
-#if 0
-/*
- * daemon - Detach us from controlling terminal session.
- */
-int
-daemon(nochdir, noclose)
-    int nochdir, noclose;
-{
-    int pid;
-
-    if ((pid = fork()) < 0)
-	return -1;
-    if (pid != 0)
-	exit(0);		/* parent dies */
-    setsid();
-    if (!nochdir)
-	chdir("/");
-    if (!noclose) {
-	fclose(stdin);		/* don't need stdin, stdout, stderr */
-	fclose(stdout);
-	fclose(stderr);
-    }
-    return 0;
-}
-#endif
-
-/*
- * ppp_available - check whether the system has any ppp interfaces
- */
-int
-ppp_available()
-{
-    struct stat buf;
-
-    return stat("/dev/ppp", &buf) >= 0;
-}
-
-/*
- * tty_establish_ppp - Turn the serial port into a ppp interface.
- */
-int
-tty_establish_ppp(fd)
-    int fd;
-{
-    int i;
-
-    /* Pop any existing modules off the tty stream. */
-    for (i = 0;; ++i)
-	if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
-	    || ioctl(fd, I_POP, 0) < 0)
-	    break;
-    tty_nmodules = i;
-
-    /* Push the async hdlc module and the compressor module. */
-    if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0)
-	fatal("Couldn't push PPP Async HDLC module: %m");
-    if (ioctl(fd, I_PUSH, "ppp_comp") < 0)
-	error("Couldn't push PPP compression module: %m");
-
-    /* Link the serial port under the PPP multiplexor. */
-    if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0)
-	fatal("Can't link tty to PPP mux: %m");
-
-    return pppfd;
-}
-
-/*
- * disestablish_ppp - Restore the serial port to normal operation.
- * It attempts to reconstruct the stream with the previously popped
- * modules.  This shouldn't call die() because it's called from die().
- */
-void
-tty_disestablish_ppp(fd)
-    int fd;
-{
-    int i;
-
-    if (fdmuxid >= 0) {
-	if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
-	    if (!hungup)
-		error("Can't unlink tty from PPP mux: %m");
-	}
-	fdmuxid = -1;
-
-	if (!hungup) {
-	    while (ioctl(fd, I_POP, 0) >= 0)
-		;
-	    for (i = tty_nmodules - 1; i >= 0; --i)
-		if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
-		    error("Couldn't restore tty module %s: %m",
-			   tty_modules[i]);
-	}
-	if (hungup && default_device && parent_pid > 0) {
-	    /*
-	     * If we have received a hangup, we need to send a SIGHUP
-	     * to the terminal's controlling process.  The reason is
-	     * that the original stream head for the terminal hasn't
-	     * seen the M_HANGUP message (it went up through the ppp
-	     * driver to the stream head for our fd to /dev/ppp).
-	     * Actually we send the signal to the process that invoked
-	     * pppd, since SunOS doesn't have getsid().
-	     */
-	    kill(parent_pid, SIGHUP);
-	}
-    }
-}
-
-/*
- * Check whether the link seems not to be 8-bit clean.
- */
-void
-clean_check()
-{
-    int x;
-    char *s;
-
-    if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
-	return;
-    s = NULL;
-    switch (~x) {
-    case RCV_B7_0:
-	s = "bit 7 set to 1";
-	break;
-    case RCV_B7_1:
-	s = "bit 7 set to 0";
-	break;
-    case RCV_EVNP:
-	s = "odd parity";
-	break;
-    case RCV_ODDP:
-	s = "even parity";
-	break;
-    }
-    if (s != NULL) {
-	warn("Serial link is not 8-bit clean:");
-	warn("All received characters had %s", s);
-    }
-}
-
-/*
- * List of valid speeds.
- */
-struct speed {
-    int speed_int, speed_val;
-} speeds[] = {
-#ifdef B50
-    { 50, B50 },
-#endif
-#ifdef B75
-    { 75, B75 },
-#endif
-#ifdef B110
-    { 110, B110 },
-#endif
-#ifdef B134
-    { 134, B134 },
-#endif
-#ifdef B150
-    { 150, B150 },
-#endif
-#ifdef B200
-    { 200, B200 },
-#endif
-#ifdef B300
-    { 300, B300 },
-#endif
-#ifdef B600
-    { 600, B600 },
-#endif
-#ifdef B1200
-    { 1200, B1200 },
-#endif
-#ifdef B1800
-    { 1800, B1800 },
-#endif
-#ifdef B2000
-    { 2000, B2000 },
-#endif
-#ifdef B2400
-    { 2400, B2400 },
-#endif
-#ifdef B3600
-    { 3600, B3600 },
-#endif
-#ifdef B4800
-    { 4800, B4800 },
-#endif
-#ifdef B7200
-    { 7200, B7200 },
-#endif
-#ifdef B9600
-    { 9600, B9600 },
-#endif
-#ifdef B19200
-    { 19200, B19200 },
-#endif
-#ifdef B38400
-    { 38400, B38400 },
-#endif
-#ifdef EXTA
-    { 19200, EXTA },
-#endif
-#ifdef EXTB
-    { 38400, EXTB },
-#endif
-#ifdef B57600
-    { 57600, B57600 },
-#endif
-#ifdef B115200
-    { 115200, B115200 },
-#endif
-    { 0, 0 }
-};
-
-/*
- * Translate from bits/second to a speed_t.
- */
-static int
-translate_speed(bps)
-    int bps;
-{
-    struct speed *speedp;
-
-    if (bps == 0)
-	return 0;
-    for (speedp = speeds; speedp->speed_int; speedp++)
-	if (bps == speedp->speed_int)
-	    return speedp->speed_val;
-    warn("speed %d not supported", bps);
-    return 0;
-}
-
-/*
- * Translate from a speed_t to bits/second.
- */
-static int
-baud_rate_of(speed)
-    int speed;
-{
-    struct speed *speedp;
-
-    if (speed == 0)
-	return 0;
-    for (speedp = speeds; speedp->speed_int; speedp++)
-	if (speed == speedp->speed_val)
-	    return speedp->speed_int;
-    return 0;
-}
-
-/*
- * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
- * at the requested speed, etc.  If `local' is true, set CLOCAL
- * regardless of whether the modem option was specified.
- */
-void
-set_up_tty(fd, local)
-    int fd, local;
-{
-    int speed;
-    struct termios tios;
-
-    if (tcgetattr(fd, &tios) < 0)
-	fatal("tcgetattr: %m");
-
-    if (!restore_term) {
-	inittermios = tios;
-	ioctl(fd, TIOCGWINSZ, &wsinfo);
-    }
-
-    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
-    if (crtscts > 0)
-	tios.c_cflag |= CRTSCTS;
-    else if (crtscts < 0)
-	tios.c_cflag &= ~CRTSCTS;
-
-    tios.c_cflag |= CS8 | CREAD | HUPCL;
-    if (local || !modem)
-	tios.c_cflag |= CLOCAL;
-    tios.c_iflag = IGNBRK | IGNPAR;
-    tios.c_oflag = 0;
-    tios.c_lflag = 0;
-    tios.c_cc[VMIN] = 1;
-    tios.c_cc[VTIME] = 0;
-
-    if (crtscts == -2) {
-	tios.c_iflag |= IXON | IXOFF;
-	tios.c_cc[VSTOP] = 0x13;	/* DC3 = XOFF = ^S */
-	tios.c_cc[VSTART] = 0x11;	/* DC1 = XON  = ^Q */
-    }
-
-    speed = translate_speed(inspeed);
-    if (speed) {
-	cfsetospeed(&tios, speed);
-	cfsetispeed(&tios, speed);
-    } else {
-	speed = cfgetospeed(&tios);
-	/*
-	 * We can't proceed if the serial port speed is 0,
-	 * since that implies that the serial port is disabled.
-	 */
-	if (speed == B0)
-	    fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
-    }
-
-    if (tcsetattr(fd, TCSAFLUSH, &tios) < 0)
-	fatal("tcsetattr: %m");
-
-    baud_rate = inspeed = baud_rate_of(speed);
-    restore_term = 1;
-}
-
-/*
- * restore_tty - restore the terminal to the saved settings.
- */
-void
-restore_tty(fd)
-    int fd;
-{
-    if (restore_term) {
-	if (!default_device) {
-	    /*
-	     * Turn off echoing, because otherwise we can get into
-	     * a loop with the tty and the modem echoing to each other.
-	     * We presume we are the sole user of this tty device, so
-	     * when we close it, it will revert to its defaults anyway.
-	     */
-	    inittermios.c_lflag &= ~(ECHO | ECHONL);
-	}
-	if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
-	    if (!hungup && errno != ENXIO)
-		warn("tcsetattr: %m");
-	ioctl(fd, TIOCSWINSZ, &wsinfo);
-	restore_term = 0;
-    }
-}
-
-/*
- * setdtr - control the DTR line on the serial port.
- * This is called from die(), so it shouldn't call die().
- */
-void
-setdtr(fd, on)
-int fd, on;
-{
-    int modembits = TIOCM_DTR;
-
-    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
-}
-
-/*
- * open_loopback - open the device we use for getting packets
- * in demand mode.  Under SunOS, we use our existing fd
- * to the ppp driver.
- */
-int
-open_ppp_loopback()
-{
-    return pppfd;
-}
-
-/*
- * output - Output PPP packet.
- */
-void
-output(unit, p, len)
-    int unit;
-    u_char *p;
-    int len;
-{
-    struct strbuf data;
-    int retries;
-    struct pollfd pfd;
-
-    if (debug)
-	dbglog("sent %P", p, len);
-
-    data.len = len;
-    data.buf = (caddr_t) p;
-    retries = 4;
-    while (putmsg(pppfd, NULL, &data, 0) < 0) {
-	if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
-	    if (errno != ENXIO)
-		error("Couldn't send packet: %m");
-	    break;
-	}
-	pfd.fd = pppfd;
-	pfd.events = POLLOUT;
-	poll(&pfd, 1, 250);	/* wait for up to 0.25 seconds */
-    }
-}
-
-
-/*
- * wait_input - wait until there is data available,
- * for the length of time specified by *timo (indefinite
- * if timo is NULL).
- */
-void
-wait_input(timo)
-    struct timeval *timo;
-{
-    int t;
-
-    t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
-    if (poll(pollfds, n_pollfds, t) < 0 && errno != EINTR) {
-	if (errno != EAGAIN)
-	    fatal("poll: %m");
-	/* we can get EAGAIN on a heavily loaded system,
-	 * just wait a short time and try again. */
-	usleep(50000);
-    }
-}
-
-/*
- * add_fd - add an fd to the set that wait_input waits for.
- */
-void add_fd(fd)
-    int fd;
-{
-    int n;
-
-    for (n = 0; n < n_pollfds; ++n)
-	if (pollfds[n].fd == fd)
-	    return;
-    if (n_pollfds < MAX_POLLFDS) {
-	pollfds[n_pollfds].fd = fd;
-	pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP;
-	++n_pollfds;
-    } else
-	error("Too many inputs!");
-}
-
-/*
- * remove_fd - remove an fd from the set that wait_input waits for.
- */
-void remove_fd(fd)
-    int fd;
-{
-    int n;
-
-    for (n = 0; n < n_pollfds; ++n) {
-	if (pollfds[n].fd == fd) {
-	    while (++n < n_pollfds)
-		pollfds[n-1] = pollfds[n];
-	    --n_pollfds;
-	    break;
-	}
-    }
-}
-
-#if 0
-/*
- * wait_loop_output - wait until there is data available on the
- * loopback, for the length of time specified by *timo (indefinite
- * if timo is NULL).
- */
-void
-wait_loop_output(timo)
-    struct timeval *timo;
-{
-    wait_input(timo);
-}
-
-/*
- * wait_time - wait for a given length of time or until a
- * signal is received.
- */
-void
-wait_time(timo)
-    struct timeval *timo;
-{
-    int n;
-
-    n = select(0, NULL, NULL, NULL, timo);
-    if (n < 0 && errno != EINTR)
-	fatal("select: %m");
-}
-#endif
-
-/*
- * read_packet - get a PPP packet from the serial device.
- */
-int
-read_packet(buf)
-    u_char *buf;
-{
-    struct strbuf ctrl, data;
-    int flags, len;
-    unsigned char ctrlbuf[64];
-
-    for (;;) {
-	data.maxlen = PPP_MRU + PPP_HDRLEN;
-	data.buf = (caddr_t) buf;
-	ctrl.maxlen = sizeof(ctrlbuf);
-	ctrl.buf = (caddr_t) ctrlbuf;
-	flags = 0;
-	len = getmsg(pppfd, &ctrl, &data, &flags);
-	if (len < 0) {
-	    if (errno == EAGAIN || errno == EINTR)
-		return -1;
-	    fatal("Error reading packet: %m");
-	}
-
-	if (ctrl.len <= 0)
-	    return data.len;
-
-	/*
-	 * Got a M_PROTO or M_PCPROTO message.  Huh?
-	 */
-	if (debug)
-	    dbglog("got ctrl msg len=%d", ctrl.len);
-
-    }
-}
-
-/*
- * get_loop_output - get outgoing packets from the ppp device,
- * and detect when we want to bring the real link up.
- * Return value is 1 if we need to bring up the link, 0 otherwise.
- */
-int
-get_loop_output()
-{
-    int len;
-    int rv = 0;
-
-    while ((len = read_packet(inpacket_buf)) > 0) {
-	if (loop_frame(inpacket_buf, len))
-	    rv = 1;
-    }
-    return rv;
-}
-
-/*
- * ppp_send_config - configure the transmit characteristics of
- * the ppp interface.
- */
-void
-ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
-    int unit, mtu;
-    u_int32_t asyncmap;
-    int pcomp, accomp;
-{
-    int cf[2];
-    struct ifreq ifr;
-
-    link_mtu = mtu;
-    if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
-	if (hungup && errno == ENXIO)
-	    return;
-	error("Couldn't set MTU: %m");
-    }
-    if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
-	error("Couldn't set transmit ACCM: %m");
-    }
-    cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
-    cf[1] = COMP_PROT | COMP_AC;
-    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
-	error("Couldn't set prot/AC compression: %m");
-    }
-
-    /* set mtu for ip as well */
-    memset(&ifr, 0, sizeof(ifr));
-    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    ifr.ifr_metric = link_mtu;
-    if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
-	error("Couldn't set IP MTU: %m");
-    }
-}
-
-/*
- * ppp_set_xaccm - set the extended transmit ACCM for the interface.
- */
-void
-ppp_set_xaccm(unit, accm)
-    int unit;
-    ext_accm accm;
-{
-    if (strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
-	if (!hungup || errno != ENXIO)
-	    warn("Couldn't set extended ACCM: %m");
-    }
-}
-
-/*
- * ppp_recv_config - configure the receive-side characteristics of
- * the ppp interface.
- */
-void
-ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
-    int unit, mru;
-    u_int32_t asyncmap;
-    int pcomp, accomp;
-{
-    int cf[2];
-
-    link_mru = mru;
-    if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
-	if (hungup && errno == ENXIO)
-	    return;
-	error("Couldn't set MRU: %m");
-    }
-    if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
-	error("Couldn't set receive ACCM: %m");
-    }
-    cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
-    cf[1] = DECOMP_PROT | DECOMP_AC;
-    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
-	error("Couldn't set prot/AC decompression: %m");
-    }
-}
-
-/*
- * ccp_test - ask kernel whether a given compression method
- * is acceptable for use.
- */
-int
-ccp_test(unit, opt_ptr, opt_len, for_transmit)
-    int unit, opt_len, for_transmit;
-    u_char *opt_ptr;
-{
-    if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
-		 opt_ptr, opt_len, 0) >= 0)
-	return 1;
-    return (errno == ENOSR)? 0: -1;
-}
-
-/*
- * ccp_flags_set - inform kernel about the current state of CCP.
- */
-void
-ccp_flags_set(unit, isopen, isup)
-    int unit, isopen, isup;
-{
-    int cf[2];
-
-    cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
-    cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
-    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
-	if (!hungup || errno != ENXIO)
-	    error("Couldn't set kernel CCP state: %m");
-    }
-}
-
-/*
- * get_idle_time - return how long the link has been idle.
- */
-int
-get_idle_time(u, ip)
-    int u;
-    struct ppp_idle *ip;
-{
-    return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
-}
-
-/*
- * get_ppp_stats - return statistics for the link.
- */
-int
-get_ppp_stats(u, stats)
-    int u;
-    struct pppd_stats *stats;
-{
-    struct ppp_stats s;
-
-    if (strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) {
-	error("Couldn't get link statistics: %m");
-	return 0;
-    }
-    stats->bytes_in = s.p.ppp_ibytes;
-    stats->bytes_out = s.p.ppp_obytes;
-    return 1;
-}
-
-
-/*
- * ccp_fatal_error - returns 1 if decompression was disabled as a
- * result of an error detected after decompression of a packet,
- * 0 otherwise.  This is necessary because of patent nonsense.
- */
-int
-ccp_fatal_error(unit)
-    int unit;
-{
-    int cf[2];
-
-    cf[0] = cf[1] = 0;
-    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
-	if (errno != ENXIO && errno != EINVAL)
-	    error("Couldn't get compression flags: %m");
-	return 0;
-    }
-    return cf[0] & CCP_FATALERROR;
-}
-
-/*
- * sifvjcomp - config tcp header compression
- */
-int
-sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
-    int u, vjcomp, xcidcomp, xmaxcid;
-{
-    int cf[2];
-    char maxcid[2];
-
-    if (vjcomp) {
-	maxcid[0] = xcidcomp;
-	maxcid[1] = 15;		/* XXX should be rmaxcid */
-	if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
-	    error("Couldn't initialize VJ compression: %m");
-	}
-    }
-
-    cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0)	/* XXX this is wrong */
-	+ (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
-    cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
-    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
-	if (vjcomp)
-	    error("Couldn't enable VJ compression: %m");
-    }
-
-    return 1;
-}
-
-/*
- * sifup - Config the interface up and enable IP packets to pass.
- */
-int
-sifup(u)
-    int u;
-{
-    struct ifreq ifr;
-
-    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
-	error("Couldn't mark interface up (get): %m");
-	return 0;
-    }
-    ifr.ifr_flags |= IFF_UP;
-    if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
-	error("Couldn't mark interface up (set): %m");
-	return 0;
-    }
-    if_is_up = 1;
-    return 1;
-}
-
-/*
- * sifdown - Config the interface down and disable IP.
- */
-int
-sifdown(u)
-    int u;
-{
-    struct ifreq ifr;
-
-    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
-	error("Couldn't mark interface down (get): %m");
-	return 0;
-    }
-    if ((ifr.ifr_flags & IFF_UP) != 0) {
-	ifr.ifr_flags &= ~IFF_UP;
-	if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
-	    error("Couldn't mark interface down (set): %m");
-	    return 0;
-	}
-    }
-    if_is_up = 0;
-    return 1;
-}
-
-/*
- * sifnpmode - Set the mode for handling packets for a given NP.
- */
-int
-sifnpmode(u, proto, mode)
-    int u;
-    int proto;
-    enum NPmode mode;
-{
-    int npi[2];
-
-    npi[0] = proto;
-    npi[1] = (int) mode;
-    if (strioctl(pppfd, PPPIO_NPMODE, npi, 2 * sizeof(int), 0) < 0) {
-	error("ioctl(set NP %d mode to %d): %m", proto, mode);
-	return 0;
-    }
-    return 1;
-}
-
-#define INET_ADDR(x)	(((struct sockaddr_in *) &(x))->sin_addr.s_addr)
-
-/*
- * sifaddr - Config the interface IP addresses and netmask.
- */
-int
-sifaddr(u, o, h, m)
-    int u;
-    u_int32_t o, h, m;
-{
-    struct ifreq ifr;
-
-    memset(&ifr, 0, sizeof(ifr));
-    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    ifr.ifr_addr.sa_family = AF_INET;
-    INET_ADDR(ifr.ifr_addr) = m;
-    if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
-	error("Couldn't set IP netmask: %m");
-    }
-    ifr.ifr_addr.sa_family = AF_INET;
-    INET_ADDR(ifr.ifr_addr) = o;
-    if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
-	error("Couldn't set local IP address: %m");
-    }
-    ifr.ifr_dstaddr.sa_family = AF_INET;
-    INET_ADDR(ifr.ifr_dstaddr) = h;
-    if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) {
-	error("Couldn't set remote IP address: %m");
-    }
-#if 0	/* now done in ppp_send_config */
-    ifr.ifr_metric = link_mtu;
-    if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
-	error("Couldn't set IP MTU: %m");
-    }
-#endif
-    ifaddrs[0] = o;
-    ifaddrs[1] = h;
-
-    return 1;
-}
-
-/*
- * cifaddr - Clear the interface IP addresses, and delete routes
- * through the interface if possible.
- */
-int
-cifaddr(u, o, h)
-    int u;
-    u_int32_t o, h;
-{
-    struct rtentry rt;
-
-    bzero(&rt, sizeof(rt));
-    rt.rt_dst.sa_family = AF_INET;
-    INET_ADDR(rt.rt_dst) = h;
-    rt.rt_gateway.sa_family = AF_INET;
-    INET_ADDR(rt.rt_gateway) = o;
-    rt.rt_flags = RTF_HOST;
-    if (ioctl(sockfd, SIOCDELRT, &rt) < 0)
-	error("Couldn't delete route through interface: %m");
-    ifaddrs[0] = 0;
-    return 1;
-}
-
-/*
- * sifdefaultroute - assign a default route through the address given.
- */
-int
-sifdefaultroute(u, l, g)
-    int u;
-    u_int32_t l, g;
-{
-    struct rtentry rt;
-
-    bzero(&rt, sizeof(rt));
-    rt.rt_dst.sa_family = AF_INET;
-    INET_ADDR(rt.rt_dst) = 0;
-    rt.rt_gateway.sa_family = AF_INET;
-    INET_ADDR(rt.rt_gateway) = g;
-    rt.rt_flags = RTF_GATEWAY;
-
-    if (ioctl(sockfd, SIOCADDRT, &rt) < 0) {
-	error("Can't add default route: %m");
-	return 0;
-    }
-
-    default_route_gateway = g;
-    return 1;
-}
-
-/*
- * cifdefaultroute - delete a default route through the address given.
- */
-int
-cifdefaultroute(u, l, g)
-    int u;
-    u_int32_t l, g;
-{
-    struct rtentry rt;
-
-    bzero(&rt, sizeof(rt));
-    rt.rt_dst.sa_family = AF_INET;
-    INET_ADDR(rt.rt_dst) = 0;
-    rt.rt_gateway.sa_family = AF_INET;
-    INET_ADDR(rt.rt_gateway) = g;
-    rt.rt_flags = RTF_GATEWAY;
-
-    if (ioctl(sockfd, SIOCDELRT, &rt) < 0) {
-	error("Can't delete default route: %m");
-	return 0;
-    }
-
-    default_route_gateway = 0;
-    return 1;
-}
-
-/*
- * sifproxyarp - Make a proxy ARP entry for the peer.
- */
-int
-sifproxyarp(unit, hisaddr)
-    int unit;
-    u_int32_t hisaddr;
-{
-    struct arpreq arpreq;
-
-    bzero(&arpreq, sizeof(arpreq));
-    if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
-	return 0;
-
-    arpreq.arp_pa.sa_family = AF_INET;
-    INET_ADDR(arpreq.arp_pa) = hisaddr;
-    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
-    if (ioctl(sockfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
-	error("Couldn't set proxy ARP entry: %m");
-	return 0;
-    }
-
-    proxy_arp_addr = hisaddr;
-    return 1;
-}
-
-/*
- * cifproxyarp - Delete the proxy ARP entry for the peer.
- */
-int
-cifproxyarp(unit, hisaddr)
-    int unit;
-    u_int32_t hisaddr;
-{
-    struct arpreq arpreq;
-
-    bzero(&arpreq, sizeof(arpreq));
-    arpreq.arp_pa.sa_family = AF_INET;
-    INET_ADDR(arpreq.arp_pa) = hisaddr;
-    if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
-	error("Couldn't delete proxy ARP entry: %m");
-	return 0;
-    }
-
-    proxy_arp_addr = 0;
-    return 1;
-}
-
-/*
- * get_ether_addr - get the hardware address of an interface on the
- * the same subnet as ipaddr.
- */
-#define MAX_IFS		32
-
-static int
-get_ether_addr(ipaddr, hwaddr)
-    u_int32_t ipaddr;
-    struct sockaddr *hwaddr;
-{
-    struct ifreq *ifr, *ifend;
-    u_int32_t ina, mask;
-    struct ifreq ifreq;
-    struct ifconf ifc;
-    struct ifreq ifs[MAX_IFS];
-    int nit_fd;
-
-    ifc.ifc_len = sizeof(ifs);
-    ifc.ifc_req = ifs;
-    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
-	error("ioctl(SIOCGIFCONF): %m");
-	return 0;
-    }
-
-    /*
-     * Scan through looking for an interface with an Internet
-     * address on the same subnet as `ipaddr'.
-     */
-    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
-    for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
-	    ((char *)&ifr->ifr_addr + sizeof(struct sockaddr))) {
-        if (ifr->ifr_addr.sa_family == AF_INET) {
-
-            /*
-             * Check that the interface is up, and not point-to-point
-             * or loopback.
-             */
-            strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
-            if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
-                continue;
-            if ((ifreq.ifr_flags &
-                 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
-                 != (IFF_UP|IFF_BROADCAST))
-                continue;
-
-            /*
-             * Get its netmask and check that it's on the right subnet.
-             */
-            if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
-                continue;
-            ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
-            mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr;
-            if ((ipaddr & mask) != (ina & mask))
-                continue;
-
-            break;
-        }
-    }
-
-    if (ifr >= ifend)
-	return 0;
-    info("found interface %s for proxy arp", ifr->ifr_name);
-
-    /*
-     * Grab the physical address for this interface.
-     */
-    if ((nit_fd = open("/dev/nit", O_RDONLY)) < 0) {
-	error("Couldn't open /dev/nit: %m");
-	return 0;
-    }
-    strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
-    if (ioctl(nit_fd, NIOCBIND, &ifreq) < 0
-	|| ioctl(nit_fd, SIOCGIFADDR, &ifreq) < 0) {
-	error("Couldn't get hardware address for %s: %m",
-	       ifreq.ifr_name);
-	close(nit_fd);
-	return 0;
-    }
-
-    hwaddr->sa_family = AF_UNSPEC;
-    memcpy(hwaddr->sa_data, ifreq.ifr_addr.sa_data, 6);
-    close(nit_fd);
-    return 1;
-}
-
-/*
- * have_route_to - determine if the system has any route to
- * a given IP address.
- * For demand mode to work properly, we have to ignore routes
- * through our own interface.
- */
-int have_route_to(addr)
-    u_int32_t addr;
-{
-    return -1;
-}
-
-#define	WTMPFILE	"/usr/adm/wtmp"
-
-void
-logwtmp(line, name, host)
-    const char *line, *name, *host;
-{
-    int fd;
-    struct stat buf;
-    struct utmp ut;
-
-    if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
-	return;
-    if (!fstat(fd, &buf)) {
-	strncpy(ut.ut_line, line, sizeof(ut.ut_line));
-	strncpy(ut.ut_name, name, sizeof(ut.ut_name));
-	strncpy(ut.ut_host, host, sizeof(ut.ut_host));
-	(void)time(&ut.ut_time);
-	if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp))
-	    (void)ftruncate(fd, buf.st_size);
-    }
-    close(fd);
-}
-
-/*
- * Return user specified netmask, modified by any mask we might determine
- * for address `addr' (in network byte order).
- * Here we scan through the system's list of interfaces, looking for
- * any non-point-to-point interfaces which might appear to be on the same
- * network as `addr'.  If we find any, we OR in their netmask to the
- * user-specified netmask.
- */
-u_int32_t
-GetMask(addr)
-    u_int32_t addr;
-{
-    u_int32_t mask, nmask, ina;
-    struct ifreq *ifr, *ifend, ifreq;
-    struct ifconf ifc;
-
-    addr = ntohl(addr);
-    if (IN_CLASSA(addr))	/* determine network mask for address class */
-	nmask = IN_CLASSA_NET;
-    else if (IN_CLASSB(addr))
-	nmask = IN_CLASSB_NET;
-    else
-	nmask = IN_CLASSC_NET;
-    /* class D nets are disallowed by bad_ip_adrs */
-    mask = netmask | htonl(nmask);
-
-    /*
-     * Scan through the system's network interfaces.
-     */
-    ifc.ifc_len = MAX_IFS * sizeof(struct ifreq);
-    ifc.ifc_req = alloca(ifc.ifc_len);
-    if (ifc.ifc_req == 0)
-	return mask;
-    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
-	warn("Couldn't get system interface list: %m");
-	return mask;
-    }
-    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
-    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
-	/*
-	 * Check the interface's internet address.
-	 */
-	if (ifr->ifr_addr.sa_family != AF_INET)
-	    continue;
-	ina = INET_ADDR(ifr->ifr_addr);
-	if ((ntohl(ina) & nmask) != (addr & nmask))
-	    continue;
-	/*
-	 * Check that the interface is up, and not point-to-point or loopback.
-	 */
-	strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
-	if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
-	    continue;
-	if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
-	    != IFF_UP)
-	    continue;
-	/*
-	 * Get its netmask and OR it into our mask.
-	 */
-	if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
-	    continue;
-	mask |= INET_ADDR(ifreq.ifr_addr);
-    }
-
-    return mask;
-}
-
-static int
-strioctl(fd, cmd, ptr, ilen, olen)
-    int fd, cmd, ilen, olen;
-    void *ptr;
-{
-    struct strioctl str;
-
-    str.ic_cmd = cmd;
-    str.ic_timout = 0;
-    str.ic_len = ilen;
-    str.ic_dp = ptr;
-    if (ioctl(fd, I_STR, &str) == -1)
-	return -1;
-    if (str.ic_len != olen)
-	dbglog("strioctl: expected %d bytes, got %d for cmd %x\n",
-	       olen, str.ic_len, cmd);
-    return 0;
-}
-
-/*
- * Use the hostid as part of the random number seed.
- */
-int
-get_host_seed()
-{
-    return gethostid();
-}
-
-#if 0
-/*
- * Code for locking/unlocking the serial device.
- * This code is derived from chat.c.
- */
-
-#if !defined(HDB) && !defined(SUNOS3)
-#define	HDB	1		/* ascii lock files are the default */
-#endif
-
-#ifndef LOCK_DIR
-# if HDB
-#  define	PIDSTRING
-#  define	LOCK_PREFIX	"/usr/spool/locks/LCK.."
-# else /* HDB */
-#  define	LOCK_PREFIX	"/usr/spool/uucp/LCK.."
-# endif /* HDB */
-#endif /* LOCK_DIR */
-
-static char *lock_file;		/* name of lock file created */
-
-/*
- * lock - create a lock file for the named device.
- */
-int
-lock(dev)
-    char *dev;
-{
-    char hdb_lock_buffer[12];
-    int fd, pid, n;
-    char *p;
-    size_t l;
-
-    if ((p = strrchr(dev, '/')) != NULL)
-	dev = p + 1;
-    l = strlen(LOCK_PREFIX) + strlen(dev) + 1;
-    lock_file = malloc(l);
-    if (lock_file == NULL)
-	novm("lock file name");
-    slprintf(lock_file, l, "%s%s", LOCK_PREFIX, dev);
-
-    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
-	if (errno == EEXIST
-	    && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
-	    /* Read the lock file to find out who has the device locked */
-#ifdef PIDSTRING
-	    n = read(fd, hdb_lock_buffer, 11);
-	    if (n > 0) {
-		hdb_lock_buffer[n] = 0;
-		pid = atoi(hdb_lock_buffer);
-	    }
-#else
-	    n = read(fd, &pid, sizeof(pid));
-#endif
-	    if (n <= 0) {
-		error("Can't read pid from lock file %s", lock_file);
-		close(fd);
-	    } else {
-		if (kill(pid, 0) == -1 && errno == ESRCH) {
-		    /* pid no longer exists - remove the lock file */
-		    if (unlink(lock_file) == 0) {
-			close(fd);
-			notice("Removed stale lock on %s (pid %d)",
-			       dev, pid);
-			continue;
-		    } else
-			warn("Couldn't remove stale lock on %s",
-			       dev);
-		} else
-		    notice("Device %s is locked by pid %d",
-			   dev, pid);
-	    }
-	    close(fd);
-	} else
-	    error("Can't create lock file %s: %m", lock_file);
-	free(lock_file);
-	lock_file = NULL;
-	return -1;
-    }
-
-#ifdef PIDSTRING
-    slprintf(hdb_lock_buffer, sizeof(hdb_lock_buffer), "%10d\n", getpid());
-    write(fd, hdb_lock_buffer, 11);
-#else
-    pid = getpid();
-    write(fd, &pid, sizeof pid);
-#endif
-
-    close(fd);
-    return 0;
-}
-
-/*
- * unlock - remove our lockfile
- */
-void
-unlock()
-{
-    if (lock_file) {
-	unlink(lock_file);
-	free(lock_file);
-	lock_file = NULL;
-    }
-}
-#endif /* lock stuff removed */
-
-/*
- * get_pty - get a pty master/slave pair and chown the slave side
- * to the uid given.  Assumes slave_name points to >= 12 bytes of space.
- */
-int
-get_pty(master_fdp, slave_fdp, slave_name, uid)
-    int *master_fdp;
-    int *slave_fdp;
-    char *slave_name;
-    int uid;
-{
-    int i, mfd, sfd;
-    char pty_name[12];
-    struct termios tios;
-
-    sfd = -1;
-    for (i = 0; i < 64; ++i) {
-	slprintf(pty_name, sizeof(pty_name), "/dev/pty%c%x",
-		 'p' + i / 16, i % 16);
-	mfd = open(pty_name, O_RDWR, 0);
-	if (mfd >= 0) {
-	    pty_name[5] = 't';
-	    sfd = open(pty_name, O_RDWR | O_NOCTTY, 0);
-	    if (sfd >= 0)
-		break;
-	    close(mfd);
-	}
-    }
-    if (sfd < 0)
-	return 0;
-
-    strlcpy(slave_name, pty_name, 12);
-    *master_fdp = mfd;
-    *slave_fdp = sfd;
-    fchown(sfd, uid, -1);
-    fchmod(sfd, S_IRUSR | S_IWUSR);
-    if (tcgetattr(sfd, &tios) == 0) {
-	tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
-	tios.c_cflag |= CS8 | CREAD;
-	tios.c_iflag  = IGNPAR | CLOCAL;
-	tios.c_oflag  = 0;
-	tios.c_lflag  = 0;
-	if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
-	    warn("couldn't set attributes on pty: %m");
-    } else
-	warn("couldn't get attributes on pty: %m");
-
-    return 1;
-}
-
-/*
- * SunOS doesn't have strtoul :-(
- */
-unsigned long
-strtoul(str, ptr, base)
-    char *str, **ptr;
-    int base;
-{
-    return (unsigned long) strtol(str, ptr, base);
-}
-
-/*
- * Or strerror :-(
- */
-extern char *sys_errlist[];
-extern int sys_nerr;
-
-char *
-strerror(n)
-    int n;
-{
-    static char unknown[32];
-
-    if (n > 0 && n < sys_nerr)
-	return sys_errlist[n];
-    slprintf(unknown, sizeof(unknown), "Error %d", n);
-    return unknown;
-}
diff -Naur tomato/release/src/router/pppd/pppd/tdb.c tomato121/release/src/router/pppd/pppd/tdb.c
--- tomato/release/src/router/pppd/pppd/tdb.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/tdb.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,21 +1,50 @@
-/* 
- * Database functions
- * Copyright (C) Andrew Tridgell 1999
- * 
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms AND provided that this software or
- * any derived work is only used as part of the PPP daemon (pppd)
- * and related utilities.
- * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ /* 
+   Unix SMB/CIFS implementation.
+
+   trivial database library
+
+   Copyright (C) Andrew Tridgell              1999-2004
+   Copyright (C) Paul `Rusty' Russell		   2000
+   Copyright (C) Jeremy Allison			   2000-2003
+   
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+/* NOTE: If you use tdbs under valgrind, and in particular if you run
+ * tdbtorture, you may get spurious "uninitialized value" warnings.  I
+ * think this is because valgrind doesn't understand that the mmap'd
+ * area may be written to by other processes.  Memory can, from the
+ * point of view of the grinded process, spontaneously become
+ * initialized.
  *
- * Note: this software is also available under the Gnu Public License
- * version 2 or later.
- */
+ * I can think of a few solutions.  [mbp 20030311]
+ *
+ * 1 - Write suppressions for Valgrind so that it doesn't complain
+ * about this.  Probably the most reasonable but people need to
+ * remember to use them.
+ *
+ * 2 - Use IO not mmap when running under valgrind.  Not so nice.
+ *
+ * 3 - Use the special valgrind macros to mark memory as valid at the
+ * right time.  Probably too hard -- the process just doesn't know.
+ */ 
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -26,662 +55,1045 @@
 #include <errno.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <signal.h>
 #include "tdb.h"
+#include "spinlock.h"
 
-#define TDB_VERSION (0x26011967 + 1)
+#define TDB_MAGIC_FOOD "TDB file\n"
+#define TDB_VERSION (0x26011967 + 6)
 #define TDB_MAGIC (0x26011999U)
 #define TDB_FREE_MAGIC (~TDB_MAGIC)
-#define TDB_ALIGN 4
-#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN)
-#define DEFAULT_HASH_SIZE 128
+#define TDB_DEAD_MAGIC (0xFEE1DEAD)
+#define TDB_ALIGNMENT 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
+#define DEFAULT_HASH_SIZE 131
 #define TDB_PAGE_SIZE 0x2000
-#define TDB_LEN_MULTIPLIER 10
 #define FREELIST_TOP (sizeof(struct tdb_header))
+#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
+#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
+#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
+#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
+#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
+#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + TDB_SPINLOCK_SIZE(hash_size))
+
 
-#define LOCK_SET 1
-#define LOCK_CLEAR 0
+/* NB assumes there is a local variable called "tdb" that is the
+ * current context, also takes doubly-parenthesized print-style
+ * argument. */
+#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
 
 /* lock offsets */
 #define GLOBAL_LOCK 0
 #define ACTIVE_LOCK 4
-#define LIST_LOCK_BASE 1024
-
-#define BUCKET(hash) ((hash) % tdb->header.hash_size)
 
 #ifndef MAP_FILE
 #define MAP_FILE 0
 #endif
 
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+TDB_DATA tdb_null;
+
+/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
+static TDB_CONTEXT *tdbs = NULL;
+
+static int tdb_munmap(TDB_CONTEXT *tdb)
+{
+	if (tdb->flags & TDB_INTERNAL)
+		return 0;
+
+#ifdef HAVE_MMAP
+	if (tdb->map_ptr) {
+		int ret = munmap(tdb->map_ptr, tdb->map_size);
+		if (ret != 0)
+			return ret;
+	}
+#endif
+	tdb->map_ptr = NULL;
+	return 0;
+}
+
+static void tdb_mmap(TDB_CONTEXT *tdb)
+{
+	if (tdb->flags & TDB_INTERNAL)
+		return;
+
+#ifdef HAVE_MMAP
+	if (!(tdb->flags & TDB_NOMMAP)) {
+		tdb->map_ptr = mmap(NULL, tdb->map_size, 
+				    PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
+				    MAP_SHARED|MAP_FILE, tdb->fd, 0);
+
+		/*
+		 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+		 */
+
+		if (tdb->map_ptr == MAP_FAILED) {
+			tdb->map_ptr = NULL;
+			TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", 
+				 tdb->map_size, strerror(errno)));
+		}
+	} else {
+		tdb->map_ptr = NULL;
+	}
+#else
+	tdb->map_ptr = NULL;
+#endif
+}
+
+/* Endian conversion: we only ever deal with 4 byte quantities */
+static void *convert(void *buf, u32 size)
+{
+	u32 i, *p = buf;
+	for (i = 0; i < size / 4; i++)
+		p[i] = TDB_BYTEREV(p[i]);
+	return buf;
+}
+#define DOCONV() (tdb->flags & TDB_CONVERT)
+#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
+
 /* the body of the database is made of one list_struct for the free space
    plus a separate data list for each hash value */
 struct list_struct {
-	tdb_len rec_len; /* total byte length of record */
 	tdb_off next; /* offset of the next record in the list */
+	tdb_len rec_len; /* total byte length of record */
 	tdb_len key_len; /* byte length of key */
 	tdb_len data_len; /* byte length of data */
-	unsigned full_hash; /* the full 32 bit hash of the key */
-	unsigned magic;   /* try to catch errors */
-	/*
-	   the following union is implied 
-	   union {
-              char record[rec_len];
-	      struct {
-	        char key[key_len];
-		char data[data_len];
-	      }
-           }
+	u32 full_hash; /* the full 32 bit hash of the key */
+	u32 magic;   /* try to catch errors */
+	/* the following union is implied:
+		union {
+			char record[rec_len];
+			struct {
+				char key[key_len];
+				char data[data_len];
+			}
+			u32 totalsize; (tailer)
+		}
 	*/
 };
 
-/* a null data record - useful for error returns */
-static TDB_DATA null_data;
+/***************************************************************
+ Allow a caller to set a "alarm" flag that tdb can check to abort
+ a blocking lock on SIGALRM.
+***************************************************************/
+
+static sig_atomic_t *palarm_fired;
+
+void tdb_set_lock_alarm(sig_atomic_t *palarm)
+{
+	palarm_fired = palarm;
+}
 
 /* a byte range locking function - return 0 on success
-   this functions locks/unlocks 1 byte at the specified offset */
+   this functions locks/unlocks 1 byte at the specified offset.
+
+   On error, errno is also set so that errors are passed back properly
+   through tdb_open(). */
 static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, 
-		      int set, int rw_type, int lck_type)
+		      int rw_type, int lck_type, int probe)
 {
-#if NOLOCK
-	return 0;
-#else
 	struct flock fl;
+	int ret;
 
-        if (tdb->fd == -1) return 0;   /* for in memory tdb */
-
-	if (tdb->read_only) return -1;
+	if (tdb->flags & TDB_NOLOCK)
+		return 0;
+	if ((rw_type == F_WRLCK) && (tdb->read_only)) {
+		errno = EACCES;
+		return -1;
+	}
 
-	fl.l_type = set==LOCK_SET?rw_type:F_UNLCK;
+	fl.l_type = rw_type;
 	fl.l_whence = SEEK_SET;
 	fl.l_start = offset;
 	fl.l_len = 1;
 	fl.l_pid = 0;
 
-	if (fcntl(tdb->fd, lck_type, &fl) != 0) {
-#if TDB_DEBUG
-		if (lck_type == F_SETLKW) {
-			printf("lock %d failed at %d (%s)\n", 
-			       set, offset, strerror(errno));
+	do {
+		ret = fcntl(tdb->fd,lck_type,&fl);
+		if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired)
+			break;
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret == -1) {
+		if (!probe && lck_type != F_SETLK) {
+			/* Ensure error code is set for log fun to examine. */
+			if (errno == EINTR && palarm_fired && *palarm_fired)
+				tdb->ecode = TDB_ERR_LOCK_TIMEOUT;
+			else
+				tdb->ecode = TDB_ERR_LOCK;
+			TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
+				 tdb->fd, offset, rw_type, lck_type));
 		}
-#endif
-		tdb->ecode = TDB_ERR_LOCK;
-		return -1;
+		/* Was it an alarm timeout ? */
+		if (errno == EINTR && palarm_fired && *palarm_fired) {
+			TDB_LOG((tdb, 5, "tdb_brlock timed out (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
+				 tdb->fd, offset, rw_type, lck_type));
+			return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1);
+		}
+		/* Otherwise - generic lock error. errno set by fcntl.
+		 * EAGAIN is an expected return from non-blocking
+		 * locks. */
+		if (errno != EAGAIN) {
+			TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n", 
+				 tdb->fd, offset, rw_type, lck_type, 
+				 strerror(errno)));
+		}
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
 	}
 	return 0;
-#endif
 }
 
 /* lock a list in the database. list -1 is the alloc list */
-static int tdb_lock(TDB_CONTEXT *tdb, int list)
+static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype)
 {
 	if (list < -1 || list >= (int)tdb->header.hash_size) {
-#if TDB_DEBUG
-		printf("bad list %d\n", list);
-#endif
+		TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", 
+			   list, ltype));
 		return -1;
 	}
-	if (tdb->locked[list+1] == 0) {
-		if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET, 
-			       F_WRLCK, F_SETLKW) != 0) {
+	if (tdb->flags & TDB_NOLOCK)
+		return 0;
+
+	/* Since fcntl locks don't nest, we do a lock for the first one,
+	   and simply bump the count for future ones */
+	if (tdb->locked[list+1].count == 0) {
+		if (!tdb->read_only && tdb->header.rwlocks) {
+			if (tdb_spinlock(tdb, list, ltype)) {
+				TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list %d ltype=%d\n", 
+					   list, ltype));
+				return -1;
+			}
+		} else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
+			TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", 
+					   list, ltype, strerror(errno)));
 			return -1;
 		}
+		tdb->locked[list+1].ltype = ltype;
 	}
-	tdb->locked[list+1]++;
+	tdb->locked[list+1].count++;
 	return 0;
 }
 
-/* unlock the database. */
-static int tdb_unlock(TDB_CONTEXT *tdb, int list)
+/* unlock the database: returns void because it's too late for errors. */
+	/* changed to return int it may be interesting to know there
+	   has been an error  --simo */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype)
 {
+	int ret = -1;
+
+	if (tdb->flags & TDB_NOLOCK)
+		return 0;
+
+	/* Sanity checks */
 	if (list < -1 || list >= (int)tdb->header.hash_size) {
-#if TDB_DEBUG
-		printf("bad unlock list %d\n", list);
-#endif
-		return -1;
+		TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+		return ret;
 	}
 
-	if (tdb->locked[list+1] == 0) {
-#if TDB_DEBUG
-		printf("not locked %d\n", list);
-#endif
-		tdb->ecode = TDB_ERR_LOCK;
-		return -1;
+	if (tdb->locked[list+1].count==0) {
+		TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+		return ret;
 	}
-	if (tdb->locked[list+1] == 1) {
-		if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR, 
-			       F_WRLCK, F_SETLKW) != 0) {
-			return -1;
-		}
-	}
-	tdb->locked[list+1]--;
-	return 0;
-}
-
-/* the hash algorithm - turn a key into an integer
-   This is based on the hash agorithm from gdbm */
-static unsigned tdb_hash(TDB_DATA *key)
-{
-	unsigned value;	/* Used to compute the hash value.  */
-	unsigned   i;	/* Used to cycle through random values. */
 
-	/* Set the initial value from the key size. */
-	value = 0x238F13AF * key->dsize;
-	for (i=0; i < key->dsize; i++) {
-		value = (value + (key->dptr[i] << (i*5 % 24)));
+	if (tdb->locked[list+1].count == 1) {
+		/* Down to last nested lock: unlock underneath */
+		if (!tdb->read_only && tdb->header.rwlocks) {
+			ret = tdb_spinunlock(tdb, list, ltype);
+		} else {
+			ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
+		}
+	} else {
+		ret = 0;
 	}
+	tdb->locked[list+1].count--;
 
-	value = (1103515243 * value + 12345);  
-
-	return value;
-}
-
-/* find the top of the hash chain for an open database */
-static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash)
-{
-	tdb_off ret;
-	hash = BUCKET(hash);
-	ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off);
+	if (ret)
+		TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); 
 	return ret;
 }
 
-
 /* check for an out of bounds access - if it is out of bounds then
    see if the database has been expanded by someone else and expand
-   if necessary */
-static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset)
+   if necessary 
+   note that "len" is the minimum length needed for the db
+*/
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe)
 {
 	struct stat st;
-	if ((offset <= tdb->map_size) || (tdb->fd == -1)) return 0;
-
-	fstat(tdb->fd, &st);
-	if (st.st_size <= (ssize_t)offset) {
-		tdb->ecode = TDB_ERR_IO;
-		return -1;
+	if (len <= tdb->map_size)
+		return 0;
+	if (tdb->flags & TDB_INTERNAL) {
+		if (!probe) {
+			/* Ensure ecode is set for log fn. */
+			tdb->ecode = TDB_ERR_IO;
+			TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n",
+				 (int)len, (int)tdb->map_size));
+		}
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
 	}
 
-#if HAVE_MMAP
-	if (tdb->map_ptr) {
-		munmap(tdb->map_ptr, tdb->map_size);
-		tdb->map_ptr = NULL;
+	if (fstat(tdb->fd, &st) == -1)
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+
+	if (st.st_size < (size_t)len) {
+		if (!probe) {
+			/* Ensure ecode is set for log fn. */
+			tdb->ecode = TDB_ERR_IO;
+			TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n",
+				 (int)len, (int)st.st_size));
+		}
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
 	}
-#endif
 
+	/* Unmap, update size, remap */
+	if (tdb_munmap(tdb) == -1)
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
 	tdb->map_size = st.st_size;
-#if HAVE_MMAP
-	tdb->map_ptr = (void *)mmap(NULL, tdb->map_size, 
-				    tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE,
-				    MAP_SHARED | MAP_FILE, tdb->fd, 0);
-#endif	
+	tdb_mmap(tdb);
 	return 0;
 }
 
-
 /* write a lump of data at a specified offset */
-static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, const char *buf, tdb_len len)
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len)
 {
-	if (tdb_oob(tdb, offset + len) != 0) {
-		/* oops - trying to write beyond the end of the database! */
+	if (tdb_oob(tdb, off + len, 0) != 0)
 		return -1;
-	}
 
-	if (tdb->map_ptr) {
-		memcpy(offset + (char *)tdb->map_ptr, buf, len);
-	} else {
-		if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
-		    write(tdb->fd, buf, len) != (ssize_t)len) {
-			tdb->ecode = TDB_ERR_IO;
-			return -1;
-		}
+	if (tdb->map_ptr)
+		memcpy(off + (char *)tdb->map_ptr, buf, len);
+#ifdef HAVE_PWRITE
+	else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+	else if (lseek(tdb->fd, off, SEEK_SET) != off
+		 || write(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_IO;
+		TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n",
+			   off, len, strerror(errno)));
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
 	}
 	return 0;
 }
 
-/* read a lump of data at a specified offset */
-static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
+/* read a lump of data at a specified offset, maybe convert */
+static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv)
 {
-	if (tdb_oob(tdb, offset + len) != 0) {
-		/* oops - trying to read beyond the end of the database! */
+	if (tdb_oob(tdb, off + len, 0) != 0)
 		return -1;
-	}
 
-	if (tdb->map_ptr) {
-		memcpy(buf, offset + (char *)tdb->map_ptr, len);
-	} else {
-		if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
-		    read(tdb->fd, buf, len) != (ssize_t)len) {
-			tdb->ecode = TDB_ERR_IO;
-			return -1;
-		}
+	if (tdb->map_ptr)
+		memcpy(buf, off + (char *)tdb->map_ptr, len);
+#ifdef HAVE_PREAD
+	else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+	else if (lseek(tdb->fd, off, SEEK_SET) != off
+		 || read(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_IO;
+		TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n",
+			   off, len, strerror(errno)));
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
 	}
+	if (cv)
+		convert(buf, len);
 	return 0;
 }
 
-
 /* read a lump of data, allocating the space for it */
 static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
 {
 	char *buf;
 
-	buf = (char *)malloc(len);
-
-	if (!buf) {
+	if (!(buf = malloc(len))) {
+		/* Ensure ecode is set for log fn. */
 		tdb->ecode = TDB_ERR_OOM;
-		return NULL;
+		TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n",
+			   len, strerror(errno)));
+		return TDB_ERRCODE(TDB_ERR_OOM, buf);
 	}
-
-	if (tdb_read(tdb, offset, buf, len) == -1) {
-		free(buf);
+	if (tdb_read(tdb, offset, buf, len, 0) == -1) {
+		SAFE_FREE(buf);
 		return NULL;
 	}
-	
 	return buf;
 }
 
-/* convenience routine for writing a record */
-static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+/* read/write a tdb_off */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
 {
-	return tdb_write(tdb, offset, (char *)rec, sizeof(*rec));
+	return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
 }
-
-/* convenience routine for writing a tdb_off */
 static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
 {
-	return tdb_write(tdb, offset, (char *)d, sizeof(*d));
+	tdb_off off = *d;
+	return tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
 }
 
-/* read a tdb_off from the store */
-static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+/* read/write a record */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+	if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
+		return -1;
+	if (TDB_BAD_MAGIC(rec)) {
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_CORRUPT;
+		TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+		return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+	}
+	return tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+}
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
 {
-	return tdb_read(tdb, offset, (char *)d, sizeof(*d));
+	struct list_struct r = *rec;
+	return tdb_write(tdb, offset, CONVERT(r), sizeof(r));
 }
 
-/* read a record and check for simple errors */
-static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+/* read a freelist record and check for simple errors */
+static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec)
 {
-	if (tdb_read(tdb, offset, (char *)rec, sizeof(*rec)) == -1) return -1;
-	if (rec->magic != TDB_MAGIC) {
-#if TDB_DEBUG
-		printf("bad magic 0x%08x at offset %d\n",
-		       rec->magic, offset);
-#endif
-		tdb->ecode = TDB_ERR_CORRUPT;
+	if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
 		return -1;
+
+	if (rec->magic == TDB_MAGIC) {
+		/* this happens when a app is showdown while deleting a record - we should
+		   not completely fail when this happens */
+		TDB_LOG((tdb, 0,"rec_free_read non-free magic 0x%x at offset=%d - fixing\n", 
+			 rec->magic, off));
+		rec->magic = TDB_FREE_MAGIC;
+		if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+			return -1;
 	}
-	if (tdb_oob(tdb, rec->next) != 0) {
-		return -1;
+
+	if (rec->magic != TDB_FREE_MAGIC) {
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_CORRUPT;
+		TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", 
+			   rec->magic, off));
+		return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
 	}
+	if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+		return -1;
 	return 0;
 }
 
-/* expand the database at least length bytes by expanding the
-   underlying file and doing the mmap again if necessary */
-static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length)
+/* update a record tailer (must hold allocation lock) */
+static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset,
+			 const struct list_struct *rec)
 {
-	struct list_struct rec;
-	tdb_off offset, ptr;
-	char b = 0;
+	tdb_off totalsize;
 
-	tdb_lock(tdb,-1);
-
-	/* make sure we know about any previous expansions by another
-           process */
-	tdb_oob(tdb,tdb->map_size + 1);
+	/* Offset of tailer from record header */
+	totalsize = sizeof(*rec) + rec->rec_len;
+	return ofs_write(tdb, offset + totalsize - sizeof(tdb_off),
+			 &totalsize);
+}
 
-	/* always make room for at least 10 more records */
-	length *= TDB_LEN_MULTIPLIER;
+static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset)
+{
+	struct list_struct rec;
+	tdb_off tailer_ofs, tailer;
 
-	/* and round the database up to a multiple of TDB_PAGE_SIZE */
-	length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size;
+	if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+		printf("ERROR: failed to read record at %u\n", offset);
+		return 0;
+	}
 
-	/* expand the file itself */
-        if (tdb->fd != -1) {
-            lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET);
-            if (write(tdb->fd, &b, 1) != 1) goto fail;
-        }
+	printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+	       offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
 
-	/* form a new freelist record */
-	offset = FREELIST_TOP;
-	rec.rec_len = length - sizeof(rec);
-	rec.magic = TDB_FREE_MAGIC;
-	if (ofs_read(tdb, offset, &rec.next) == -1) {
-		goto fail;
+	tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off);
+	if (ofs_read(tdb, tailer_ofs, &tailer) == -1) {
+		printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
+		return rec.next;
 	}
 
-#if HAVE_MMAP
-	if (tdb->fd != -1 && tdb->map_ptr) {
-		munmap(tdb->map_ptr, tdb->map_size);
-		tdb->map_ptr = NULL;
+	if (tailer != rec.rec_len + sizeof(rec)) {
+		printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
+				(unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec)));
 	}
-#endif
+	return rec.next;
+}
 
-	tdb->map_size += length;
+static int tdb_dump_chain(TDB_CONTEXT *tdb, int i)
+{
+	tdb_off rec_ptr, top;
 
-        if (tdb->fd == -1) {
-            tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
-        }
+	top = TDB_HASH_TOP(i);
 
-	/* write it out */
-	if (rec_write(tdb, tdb->map_size - length, &rec) == -1) {
-		goto fail;
-	}
+	if (tdb_lock(tdb, i, F_WRLCK) != 0)
+		return -1;
 
-	/* link it into the free list */
-	ptr = tdb->map_size - length;
-	if (ofs_write(tdb, offset, &ptr) == -1) goto fail;
+	if (ofs_read(tdb, top, &rec_ptr) == -1)
+		return tdb_unlock(tdb, i, F_WRLCK);
 
-#if HAVE_MMAP
-        if (tdb->fd != -1) {
-            tdb->map_ptr = (void *)mmap(NULL, tdb->map_size, 
-                                        PROT_READ|PROT_WRITE,
-                                        MAP_SHARED | MAP_FILE, tdb->fd, 0);
-        }
-#endif
+	if (rec_ptr)
+		printf("hash=%d\n", i);
 
-	tdb_unlock(tdb, -1);
-	return 0;
+	while (rec_ptr) {
+		rec_ptr = tdb_dump_record(tdb, rec_ptr);
+	}
 
- fail:
-	tdb_unlock(tdb,-1);
-	return -1;
+	return tdb_unlock(tdb, i, F_WRLCK);
 }
 
-/* allocate some space from the free list. The offset returned points
-   to a unconnected list_struct within the database with room for at
-   least length bytes of total data
+void tdb_dump_all(TDB_CONTEXT *tdb)
+{
+	int i;
+	for (i=0;i<tdb->header.hash_size;i++) {
+		tdb_dump_chain(tdb, i);
+	}
+	printf("freelist:\n");
+	tdb_dump_chain(tdb, -1);
+}
 
-   0 is returned if the space could not be allocated
- */
-static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length)
+int tdb_printfreelist(TDB_CONTEXT *tdb)
 {
-	tdb_off offset, rec_ptr, last_ptr;
-	struct list_struct rec, lastrec, newrec;
+	int ret;
+	long total_free = 0;
+	tdb_off offset, rec_ptr;
+	struct list_struct rec;
 
-	tdb_lock(tdb, -1);
+	if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
+		return ret;
 
- again:
-	last_ptr = 0;
 	offset = FREELIST_TOP;
 
 	/* read in the freelist top */
 	if (ofs_read(tdb, offset, &rec_ptr) == -1) {
-		goto fail;
+		tdb_unlock(tdb, -1, F_WRLCK);
+		return 0;
 	}
 
-	/* keep looking until we find a freelist record that is big
-           enough */
+	printf("freelist top=[0x%08x]\n", rec_ptr );
 	while (rec_ptr) {
-		if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
-			goto fail;
+		if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return -1;
 		}
 
 		if (rec.magic != TDB_FREE_MAGIC) {
-#if TDB_DEBUG
 			printf("bad magic 0x%08x in free list\n", rec.magic);
-#endif
-			goto fail;
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return -1;
 		}
 
-		if (rec.rec_len >= length) {
-			/* found it - now possibly split it up  */
-			if (rec.rec_len > length + MIN_REC_SIZE) {
-				length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1);
-
-				newrec.rec_len = rec.rec_len - (sizeof(rec) + length);
-				newrec.next = rec.next;
-				newrec.magic = TDB_FREE_MAGIC;
-
-				rec.rec_len = length;
-				rec.next = rec_ptr + sizeof(rec) + rec.rec_len;
-				
-				if (rec_write(tdb, rec.next, &newrec) == -1) {
-					goto fail;
-				}
-
-				if (rec_write(tdb, rec_ptr, &rec) == -1) {
-					goto fail;
-				}
-			}
-
-			/* remove it from the list */
-			if (last_ptr == 0) {
-				offset = FREELIST_TOP;
-
-				if (ofs_write(tdb, offset, &rec.next) == -1) {
-					goto fail;
-				}				
-			} else {
-				lastrec.next = rec.next;
-				if (rec_write(tdb, last_ptr, &lastrec) == -1) {
-					goto fail;
-				}
-			}
-
-			/* all done - return the new record offset */
-			tdb_unlock(tdb, -1);
-			return rec_ptr;
-		}
+		printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len );
+		total_free += rec.rec_len;
 
 		/* move to the next record */
-		lastrec = rec;
-		last_ptr = rec_ptr;
 		rec_ptr = rec.next;
 	}
+	printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, 
+               (int)total_free);
 
-	/* we didn't find enough space. See if we can expand the
-	   database and if we can then try again */
-	if (tdb_expand(tdb, length + sizeof(rec)) == 0) goto again;
-
- fail:
-#if TDB_DEBUG
-	printf("tdb_allocate failed for size %u\n", length);
-#endif
-	tdb_unlock(tdb, -1);
-	return 0;
+	return tdb_unlock(tdb, -1, F_WRLCK);
 }
 
-/* initialise a new database with a specified hash size */
-static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+/* Remove an element from the freelist.  Must have alloc lock. */
+static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next)
 {
-	struct tdb_header header;
-	tdb_off offset;
-	int i, size = 0;
-	tdb_off buf[16];
+	tdb_off last_ptr, i;
 
-        /* create the header */
-        memset(&header, 0, sizeof(header));
-        memcpy(header.magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
-        header.version = TDB_VERSION;
-        header.hash_size = hash_size;
-        lseek(tdb->fd, 0, SEEK_SET);
-        ftruncate(tdb->fd, 0);
-        
-        if (tdb->fd != -1 && write(tdb->fd, &header, sizeof(header)) != 
-            sizeof(header)) {
-            tdb->ecode = TDB_ERR_IO;
-            return -1;
-        } else size += sizeof(header);
-	
-        /* the freelist and hash pointers */
-        offset = 0;
-        memset(buf, 0, sizeof(buf));
-
-        for (i=0;(hash_size+1)-i >= 16; i += 16) {
-            if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(buf)) != 
-                sizeof(buf)) {
-                tdb->ecode = TDB_ERR_IO;
-                return -1;
-            } else size += sizeof(buf);
-        }
-
-        for (;i<hash_size+1; i++) {
-            if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(tdb_off)) != 
-                sizeof(tdb_off)) {
-                tdb->ecode = TDB_ERR_IO;
-                return -1;
-            } else size += sizeof(tdb_off);
-        }
-
-        if (tdb->fd == -1) {
-            tdb->map_ptr = calloc(size, 1);
-            tdb->map_size = size;
-            if (tdb->map_ptr == NULL) {
-                tdb->ecode = TDB_ERR_IO;
-                return -1;
-            }
-            memcpy(&tdb->header, &header, sizeof(header));
-        }
-
-#if TDB_DEBUG
-	printf("initialised database of hash_size %u\n", 
-	       hash_size);
-#endif
-	return 0;
+	/* read in the freelist top */
+	last_ptr = FREELIST_TOP;
+	while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
+		if (i == off) {
+			/* We've found it! */
+			return ofs_write(tdb, last_ptr, &next);
+		}
+		/* Follow chain (next offset is at start of record) */
+		last_ptr = i;
+	}
+	TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off));
+	return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
 }
 
-/* Returns 0 on fail.  On success, return offset of record, and fills
-   in rec */
-static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, unsigned int hash,
-			struct list_struct *rec)
+/* Add an element into the freelist. Merge adjacent records if
+   neccessary. */
+static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
 {
-	tdb_off offset, rec_ptr;
-	
-	/* find the top of the hash chain */
-	offset = tdb_hash_top(tdb, hash);
-
-	/* read in the hash top */
-	if (ofs_read(tdb, offset, &rec_ptr) == -1)
-		return 0;
+	tdb_off right, left;
 
-	/* keep looking until we find the right record */
-	while (rec_ptr) {
-		if (rec_read(tdb, rec_ptr, rec) == -1)
-			return 0;
+	/* Allocation and tailer lock */
+	if (tdb_lock(tdb, -1, F_WRLCK) != 0)
+		return -1;
 
-		if (hash == rec->full_hash && key.dsize == rec->key_len) {
-			char *k;
-			/* a very likely hit - read the key */
-			k = tdb_alloc_read(tdb, rec_ptr + sizeof(*rec), 
-					   rec->key_len);
+	/* set an initial tailer, so if we fail we don't leave a bogus record */
+	if (update_tailer(tdb, offset, rec) != 0) {
+		TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n"));
+		goto fail;
+	}
 
-			if (!k)
-				return 0;
+	/* Look right first (I'm an Australian, dammit) */
+	right = offset + sizeof(*rec) + rec->rec_len;
+	if (right + sizeof(*rec) <= tdb->map_size) {
+		struct list_struct r;
+
+		if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
+			TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right));
+			goto left;
+		}
 
-			if (memcmp(key.dptr, k, key.dsize) == 0) {
-				free(k);
-				return rec_ptr;
+		/* If it's free, expand to include it. */
+		if (r.magic == TDB_FREE_MAGIC) {
+			if (remove_from_freelist(tdb, right, r.next) == -1) {
+				TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right));
+				goto left;
 			}
-			free(k);
+			rec->rec_len += sizeof(r) + r.rec_len;
 		}
-
-		/* move to the next record */
-		rec_ptr = rec->next;
 	}
-	return 0;
-}
 
-/* 
-   return an error string for the last tdb error
-*/
-char *tdb_error(TDB_CONTEXT *tdb)
-{
-	int i;
-	static struct {
-		enum TDB_ERROR ecode;
-		char *estring;
-	} emap[] = {
-		{TDB_SUCCESS, "Success"},
-		{TDB_ERR_CORRUPT, "Corrupt database"},
-		{TDB_ERR_IO, "IO Error"},
-		{TDB_ERR_LOCK, "Locking error"},
-		{TDB_ERR_OOM, "Out of memory"},
-		{TDB_ERR_EXISTS, "Record exists"},
-		{-1, NULL}};
-        if (tdb != NULL) {
-            for (i=0;emap[i].estring;i++) {
-		if (tdb->ecode == emap[i].ecode) return emap[i].estring;
-            }
-        } else {
-            return "Invalid tdb context";
-        }
-	return "Invalid error code";
+left:
+	/* Look left */
+	left = offset - sizeof(tdb_off);
+	if (left > TDB_DATA_START(tdb->header.hash_size)) {
+		struct list_struct l;
+		tdb_off leftsize;
+		
+		/* Read in tailer and jump back to header */
+		if (ofs_read(tdb, left, &leftsize) == -1) {
+			TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left));
+			goto update;
+		}
+		left = offset - leftsize;
+
+		/* Now read in record */
+		if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
+			TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
+			goto update;
+		}
+
+		/* If it's free, expand to include it. */
+		if (l.magic == TDB_FREE_MAGIC) {
+			if (remove_from_freelist(tdb, left, l.next) == -1) {
+				TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left));
+				goto update;
+			} else {
+				offset = left;
+				rec->rec_len += leftsize;
+			}
+		}
+	}
+
+update:
+	if (update_tailer(tdb, offset, rec) == -1) {
+		TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset));
+		goto fail;
+	}
+
+	/* Now, prepend to free list */
+	rec->magic = TDB_FREE_MAGIC;
+
+	if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
+	    rec_write(tdb, offset, rec) == -1 ||
+	    ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+		TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset));
+		goto fail;
+	}
+
+	/* And we're done. */
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return 0;
+
+ fail:
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return -1;
 }
 
 
-/* update an entry in place - this only works if the new data size
-   is <= the old data size and the key exists.
-   on failure return -1
-*/
-int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
+/* expand a file.  we prefer to use ftruncate, as that is what posix
+  says to use for mmap expansion */
+static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition)
+{
+	char buf[1024];
+#if HAVE_FTRUNCATE_EXTEND
+	if (ftruncate(tdb->fd, size+addition) != 0) {
+		TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", 
+			   size+addition, strerror(errno)));
+		return -1;
+	}
+#else
+	char b = 0;
+
+#ifdef HAVE_PWRITE
+	if (pwrite(tdb->fd,  &b, 1, (size+addition) - 1) != 1) {
+#else
+	if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 || 
+	    write(tdb->fd, &b, 1) != 1) {
+#endif
+		TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", 
+			   size+addition, strerror(errno)));
+		return -1;
+	}
+#endif
+
+	/* now fill the file with something. This ensures that the file isn't sparse, which would be
+	   very bad if we ran out of disk. This must be done with write, not via mmap */
+	memset(buf, 0x42, sizeof(buf));
+	while (addition) {
+		int n = addition>sizeof(buf)?sizeof(buf):addition;
+#ifdef HAVE_PWRITE
+		int ret = pwrite(tdb->fd, buf, n, size);
+#else
+		int ret;
+		if (lseek(tdb->fd, size, SEEK_SET) != size)
+			return -1;
+		ret = write(tdb->fd, buf, n);
+#endif
+		if (ret != n) {
+			TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", 
+				   n, strerror(errno)));
+			return -1;
+		}
+		addition -= n;
+		size += n;
+	}
+	return 0;
+}
+
+
+/* expand the database at least size bytes by expanding the underlying
+   file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size)
 {
-	unsigned hash;
 	struct list_struct rec;
+	tdb_off offset;
+
+	if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
+		TDB_LOG((tdb, 0, "lock failed in tdb_expand\n"));
+		return -1;
+	}
+
+	/* must know about any previous expansions by another process */
+	tdb_oob(tdb, tdb->map_size + 1, 1);
+
+	/* always make room for at least 10 more records, and round
+           the database up to a multiple of TDB_PAGE_SIZE */
+	size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size;
+
+	if (!(tdb->flags & TDB_INTERNAL))
+		tdb_munmap(tdb);
+
+	/*
+	 * We must ensure the file is unmapped before doing this
+	 * to ensure consistency with systems like OpenBSD where
+	 * writes and mmaps are not consistent.
+	 */
+
+	/* expand the file itself */
+	if (!(tdb->flags & TDB_INTERNAL)) {
+		if (expand_file(tdb, tdb->map_size, size) != 0)
+			goto fail;
+	}
+
+	tdb->map_size += size;
+
+	if (tdb->flags & TDB_INTERNAL)
+		tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+	else {
+		/*
+		 * We must ensure the file is remapped before adding the space
+		 * to ensure consistency with systems like OpenBSD where
+		 * writes and mmaps are not consistent.
+		 */
+
+		/* We're ok if the mmap fails as we'll fallback to read/write */
+		tdb_mmap(tdb);
+	}
+
+	/* form a new freelist record */
+	memset(&rec,'\0',sizeof(rec));
+	rec.rec_len = size - sizeof(rec);
+
+	/* link it into the free list */
+	offset = tdb->map_size - size;
+	if (tdb_free(tdb, offset, &rec) == -1)
+		goto fail;
+
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return 0;
+ fail:
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+   to a unconnected list_struct within the database with room for at
+   least length bytes of total data
+
+   0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length,
+			    struct list_struct *rec)
+{
+	tdb_off rec_ptr, last_ptr, newrec_ptr;
+	struct list_struct newrec;
+
+	memset(&newrec, '\0', sizeof(newrec));
+
+	if (tdb_lock(tdb, -1, F_WRLCK) == -1)
+		return 0;
+
+	/* Extra bytes required for tailer */
+	length += sizeof(tdb_off);
+
+ again:
+	last_ptr = FREELIST_TOP;
+
+	/* read in the freelist top */
+	if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
+		goto fail;
+
+	/* keep looking until we find a freelist record big enough */
+	while (rec_ptr) {
+		if (rec_free_read(tdb, rec_ptr, rec) == -1)
+			goto fail;
+
+		if (rec->rec_len >= length) {
+			/* found it - now possibly split it up  */
+			if (rec->rec_len > length + MIN_REC_SIZE) {
+				/* Length of left piece */
+				length = TDB_ALIGN(length, TDB_ALIGNMENT);
+
+				/* Right piece to go on free list */
+				newrec.rec_len = rec->rec_len
+					- (sizeof(*rec) + length);
+				newrec_ptr = rec_ptr + sizeof(*rec) + length;
+
+				/* And left record is shortened */
+				rec->rec_len = length;
+			} else
+				newrec_ptr = 0;
+
+			/* Remove allocated record from the free list */
+			if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+				goto fail;
+
+			/* Update header: do this before we drop alloc
+                           lock, otherwise tdb_free() might try to
+                           merge with us, thinking we're free.
+                           (Thanks Jeremy Allison). */
+			rec->magic = TDB_MAGIC;
+			if (rec_write(tdb, rec_ptr, rec) == -1)
+				goto fail;
+
+			/* Did we create new block? */
+			if (newrec_ptr) {
+				/* Update allocated record tailer (we
+                                   shortened it). */
+				if (update_tailer(tdb, rec_ptr, rec) == -1)
+					goto fail;
+
+				/* Free new record */
+				if (tdb_free(tdb, newrec_ptr, &newrec) == -1)
+					goto fail;
+			}
+
+			/* all done - return the new record offset */
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return rec_ptr;
+		}
+		/* move to the next record */
+		last_ptr = rec_ptr;
+		rec_ptr = rec->next;
+	}
+	/* we didn't find enough space. See if we can expand the
+	   database and if we can then try again */
+	if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
+		goto again;
+ fail:
+	tdb_unlock(tdb, -1, F_WRLCK);
+	return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+	struct tdb_header *newdb;
+	int size, ret = -1;
+
+	/* We make it up in memory, then write it out if not internal */
+	size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off);
+	if (!(newdb = calloc(size, 1)))
+		return TDB_ERRCODE(TDB_ERR_OOM, -1);
+
+	/* Fill in the header */
+	newdb->version = TDB_VERSION;
+	newdb->hash_size = hash_size;
+	if (tdb->flags & TDB_INTERNAL) {
+		tdb->map_size = size;
+		tdb->map_ptr = (char *)newdb;
+		memcpy(&tdb->header, newdb, sizeof(tdb->header));
+		/* Convert the `ondisk' version if asked. */
+		CONVERT(*newdb);
+		return 0;
+	}
+	if (lseek(tdb->fd, 0, SEEK_SET) == -1)
+		goto fail;
+
+	if (ftruncate(tdb->fd, 0) == -1)
+		goto fail;
+
+	/* This creates an endian-converted header, as if read from disk */
+	CONVERT(*newdb);
+	memcpy(&tdb->header, newdb, sizeof(tdb->header));
+	/* Don't endian-convert the magic food! */
+	memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+	if (write(tdb->fd, newdb, size) != size)
+		ret = -1;
+	else
+		ret = tdb_create_rwlocks(tdb->fd, hash_size);
+
+  fail:
+	SAFE_FREE(newdb);
+	return ret;
+}
+
+/* Returns 0 on fail.  On success, return offset of record, and fills
+   in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash,
+			struct list_struct *r)
+{
 	tdb_off rec_ptr;
-	int ret = -1;
+	
+	/* read in the hash top */
+	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
+		return 0;
 
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_update() called with null context\n");
-#endif
-            return -1;
-        }
+	/* keep looking until we find the right record */
+	while (rec_ptr) {
+		if (rec_read(tdb, rec_ptr, r) == -1)
+			return 0;
 
-	/* find which hash bucket it is in */
-	hash = tdb_hash(&key);
+		if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
+			char *k;
+			/* a very likely hit - read the key */
+			k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r), 
+					   r->key_len);
+			if (!k)
+				return 0;
 
-	tdb_lock(tdb, BUCKET(hash));
-	rec_ptr = tdb_find(tdb, key, hash, &rec);
+			if (memcmp(key.dptr, k, key.dsize) == 0) {
+				SAFE_FREE(k);
+				return rec_ptr;
+			}
+			SAFE_FREE(k);
+		}
+		rec_ptr = r->next;
+	}
+	return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
+}
 
-	if (!rec_ptr)
-		goto out;
+/* As tdb_find, but if you succeed, keep the lock */
+static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, int locktype,
+			     struct list_struct *rec)
+{
+	u32 rec_ptr;
 
-	/* must be long enough */
-	if (rec.rec_len < key.dsize + dbuf.dsize)
-		goto out;
+	if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
+		return 0;
+	if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
+		tdb_unlock(tdb, BUCKET(hash), locktype);
+	return rec_ptr;
+}
+
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb)
+{
+	return tdb->ecode;
+}
+
+static struct tdb_errname {
+	enum TDB_ERROR ecode; const char *estring;
+} emap[] = { {TDB_SUCCESS, "Success"},
+	     {TDB_ERR_CORRUPT, "Corrupt database"},
+	     {TDB_ERR_IO, "IO Error"},
+	     {TDB_ERR_LOCK, "Locking error"},
+	     {TDB_ERR_OOM, "Out of memory"},
+	     {TDB_ERR_EXISTS, "Record exists"},
+	     {TDB_ERR_NOLOCK, "Lock exists on other keys"},
+	     {TDB_ERR_NOEXIST, "Record does not exist"} };
+
+/* Error string for the last tdb error */
+const char *tdb_errorstr(TDB_CONTEXT *tdb)
+{
+	u32 i;
+	for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
+		if (tdb->ecode == emap[i].ecode)
+			return emap[i].estring;
+	return "Invalid error code";
+}
+
+/* update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1.
+*/
+
+static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf)
+{
+	struct list_struct rec;
+	tdb_off rec_ptr;
+
+	/* find entry */
+	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+		return -1;
+
+	/* must be long enough key, data and tailer */
+	if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
+		tdb->ecode = TDB_SUCCESS; /* Not really an error */
+		return -1;
+	}
 
 	if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
 		      dbuf.dptr, dbuf.dsize) == -1)
-		goto out;
+		return -1;
 
 	if (dbuf.dsize != rec.data_len) {
 		/* update size */
 		rec.data_len = dbuf.dsize;
-		ret = rec_write(tdb, rec_ptr, &rec);
-	} else
-		ret = 0;
-
- out:
-	tdb_unlock(tdb, BUCKET(hash));
-	return ret;
+		return rec_write(tdb, rec_ptr, &rec);
+	}
+ 
+	return 0;
 }
 
 /* find an entry in the database given a key */
+/* If an entry doesn't exist tdb_err will be set to
+ * TDB_ERR_NOEXIST. If a key has no data attached
+ * tdb_err will not be set. Both will return a
+ * zero pptr and zero dsize.
+ */
+
 TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
 {
-	unsigned hash;
 	tdb_off rec_ptr;
 	struct list_struct rec;
-	TDB_DATA ret = null_data;
-
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_fetch() called with null context\n");
-#endif
-            return null_data;
-        }
+	TDB_DATA ret;
+	u32 hash;
 
 	/* find which hash bucket it is in */
-	hash = tdb_hash(&key);
+	hash = tdb->hash_fn(&key);
+	if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
+		return tdb_null;
 
-	tdb_lock(tdb, BUCKET(hash));
-	rec_ptr = tdb_find(tdb, key, hash, &rec);
-
-	if (rec_ptr) {
-		ret.dptr = tdb_alloc_read(tdb,
-					  rec_ptr + sizeof(rec) + rec.key_len,
+	if (rec.data_len)
+		ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
 					  rec.data_len);
-		ret.dsize = rec.data_len;
-	}
-	
-	tdb_unlock(tdb, BUCKET(hash));
+	else
+		ret.dptr = NULL;
+	ret.dsize = rec.data_len;
+	tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
 	return ret;
 }
 
@@ -691,592 +1103,907 @@
    this doesn't match the conventions in the rest of this module, but is
    compatible with gdbm
 */
-int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
 {
-	unsigned hash;
-	tdb_off rec_ptr;
 	struct list_struct rec;
 	
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_exists() called with null context\n");
-#endif
-            return 0;
-        }
+	if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
+		return 0;
+	tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+	return 1;
+}
 
-	/* find which hash bucket it is in */
-	hash = tdb_hash(&key);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	u32 hash = tdb->hash_fn(&key);
+	return tdb_exists_hash(tdb, key, hash);
+}
+
+/* record lock stops delete underneath */
+static int lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+}
+/*
+  Write locks override our own fcntl readlocks, so check it here.
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+ 
+static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	struct tdb_traverse_lock *i;
+	for (i = &tdb->travlocks; i; i = i->next)
+		if (i->off == off)
+			return -1;
+	return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+}
 
-	tdb_lock(tdb, BUCKET(hash));
-	rec_ptr = tdb_find(tdb, key, hash, &rec);
-	tdb_unlock(tdb, BUCKET(hash));
+/*
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
 
-	return rec_ptr != 0;
+static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
 }
+/* fcntl locks don't stack: avoid unlocking someone else's */
+static int unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+	struct tdb_traverse_lock *i;
+	u32 count = 0;
 
-/* traverse the entire database - calling fn(tdb, key, data) on each element.
-   return -1 on error or the record count traversed
-   if fn is NULL then it is not called
-   a non-zero return value from fn() indicates that the traversal should stop
-  */
-int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state)
+	if (off == 0)
+		return 0;
+	for (i = &tdb->travlocks; i; i = i->next)
+		if (i->off == off)
+			count++;
+	return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+}
+
+/* actually delete an entry in the database given the offset */
+static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec)
 {
-	int count = 0;
-	unsigned h;
-	tdb_off offset, rec_ptr;
-	struct list_struct rec;
-	char *data;
-	TDB_DATA key, dbuf;
+	tdb_off last_ptr, i;
+	struct list_struct lastrec;
 
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_traverse() called with null context\n");
-#endif
-            return -1;
-        }
+	if (tdb->read_only) return -1;
 
-	/* loop over all hash chains */
-	for (h = 0; h < tdb->header.hash_size; h++) {
-		tdb_lock(tdb, BUCKET(h));
-
-		/* read in the hash top */
-		offset = tdb_hash_top(tdb, h);
-		if (ofs_read(tdb, offset, &rec_ptr) == -1) {
-			goto fail;
-		}
+	if (write_lock_record(tdb, rec_ptr) == -1) {
+		/* Someone traversing here: mark it as dead */
+		rec->magic = TDB_DEAD_MAGIC;
+		return rec_write(tdb, rec_ptr, rec);
+	}
+	if (write_unlock_record(tdb, rec_ptr) != 0)
+		return -1;
 
-		/* traverse all records for this hash */
-		while (rec_ptr) {
-			if (rec_read(tdb, rec_ptr, &rec) == -1) {
-				goto fail;
-			}
+	/* find previous record in hash chain */
+	if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
+		return -1;
+	for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
+		if (rec_read(tdb, i, &lastrec) == -1)
+			return -1;
 
-			/* now read the full record */
-			data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), 
-					     rec.key_len + rec.data_len);
-			if (!data) {
-				goto fail;
-			}
+	/* unlink it: next ptr is at start of record. */
+	if (last_ptr == 0)
+		last_ptr = TDB_HASH_TOP(rec->full_hash);
+	if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+		return -1;
 
-			key.dptr = data;
-			key.dsize = rec.key_len;
-			dbuf.dptr = data + rec.key_len;
-			dbuf.dsize = rec.data_len;
-			count++;
+	/* recover the space */
+	if (tdb_free(tdb, rec_ptr, rec) == -1)
+		return -1;
+	return 0;
+}
 
-			if (fn && fn(tdb, key, dbuf, state) != 0) {
-				/* they want us to stop traversing */
-				free(data);
-				tdb_unlock(tdb, BUCKET(h));
-				return count;
-			}
+/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
+static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,
+			 struct list_struct *rec)
+{
+	int want_next = (tlock->off != 0);
+
+	/* Lock each chain from the start one. */
+	for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+		if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)
+			return -1;
 
-			/* a miss - drat */
-			free(data);
+		/* No previous record?  Start at top of chain. */
+		if (!tlock->off) {
+			if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
+				     &tlock->off) == -1)
+				goto fail;
+		} else {
+			/* Otherwise unlock the previous record. */
+			if (unlock_record(tdb, tlock->off) != 0)
+				goto fail;
+		}
 
-			/* move to the next record */
-			rec_ptr = rec.next;
+		if (want_next) {
+			/* We have offset of old record: grab next */
+			if (rec_read(tdb, tlock->off, rec) == -1)
+				goto fail;
+			tlock->off = rec->next;
 		}
-		tdb_unlock(tdb, BUCKET(h));
-	}
 
-	/* return the number traversed */
-	return count;
+		/* Iterate through chain */
+		while( tlock->off) {
+			tdb_off current;
+			if (rec_read(tdb, tlock->off, rec) == -1)
+				goto fail;
+			if (!TDB_DEAD(rec)) {
+				/* Woohoo: we found one! */
+				if (lock_record(tdb, tlock->off) != 0)
+					goto fail;
+				return tlock->off;
+			}
+			/* Try to clean dead ones from old traverses */
+			current = tlock->off;
+			tlock->off = rec->next;
+			if (!tdb->read_only && 
+			    do_delete(tdb, current, rec) != 0)
+				goto fail;
+		}
+		tdb_unlock(tdb, tlock->hash, F_WRLCK);
+		want_next = 0;
+	}
+	/* We finished iteration without finding anything */
+	return TDB_ERRCODE(TDB_SUCCESS, 0);
 
  fail:
-	tdb_unlock(tdb, BUCKET(h));
+	tlock->off = 0;
+	if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));
 	return -1;
 }
 
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+   return -1 on error or the record count traversed
+   if fn is NULL then it is not called
+   a non-zero return value from fn() indicates that the traversal should stop
+  */
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *private)
+{
+	TDB_DATA key, dbuf;
+	struct list_struct rec;
+	struct tdb_traverse_lock tl = { NULL, 0, 0 };
+	int ret, count = 0;
+
+	/* This was in the initializaton, above, but the IRIX compiler
+	 * did not like it.  crh
+	 */
+	tl.next = tdb->travlocks.next;
+
+	/* fcntl locks don't stack: beware traverse inside traverse */
+	tdb->travlocks.next = &tl;
+
+	/* tdb_next_lock places locks on the record returned, and its chain */
+	while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {
+		count++;
+		/* now read the full record */
+		key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), 
+					  rec.key_len + rec.data_len);
+		if (!key.dptr) {
+			ret = -1;
+			if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)
+				goto out;
+			if (unlock_record(tdb, tl.off) != 0)
+				TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
+			goto out;
+		}
+		key.dsize = rec.key_len;
+		dbuf.dptr = key.dptr + rec.key_len;
+		dbuf.dsize = rec.data_len;
+
+		/* Drop chain lock, call out */
+		if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {
+			ret = -1;
+			goto out;
+		}
+		if (fn && fn(tdb, key, dbuf, private)) {
+			/* They want us to terminate traversal */
+			ret = count;
+			if (unlock_record(tdb, tl.off) != 0) {
+				TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;
+				ret = -1;
+			}
+			tdb->travlocks.next = tl.next;
+			SAFE_FREE(key.dptr);
+			return count;
+		}
+		SAFE_FREE(key.dptr);
+	}
+out:
+	tdb->travlocks.next = tl.next;
+	if (ret < 0)
+		return -1;
+	else
+		return count;
+}
 
 /* find the first entry in the database and return its key */
 TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
 {
-	tdb_off offset, rec_ptr;
+	TDB_DATA key;
 	struct list_struct rec;
-	unsigned hash;
-	TDB_DATA ret;
-
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_firstkey() called with null context\n");
-#endif
-            return null_data;
-        }
 
-	/* look for a non-empty hash chain */
-	for (hash = 0, rec_ptr = 0; 
-	     hash < tdb->header.hash_size;
-	     hash++) {
-		/* find the top of the hash chain */
-		offset = tdb_hash_top(tdb, hash);
+	/* release any old lock */
+	if (unlock_record(tdb, tdb->travlocks.off) != 0)
+		return tdb_null;
+	tdb->travlocks.off = tdb->travlocks.hash = 0;
+
+	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
+		return tdb_null;
+	/* now read the key */
+	key.dsize = rec.key_len;
+	key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
+	if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
+	return key;
+}
 
-		tdb_lock(tdb, BUCKET(hash));
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey)
+{
+	u32 oldhash;
+	TDB_DATA key = tdb_null;
+	struct list_struct rec;
+	char *k = NULL;
 
-		/* read in the hash top */
-		if (ofs_read(tdb, offset, &rec_ptr) == -1) {
-			goto fail;
+	/* Is locked key the old key?  If so, traverse will be reliable. */
+	if (tdb->travlocks.off) {
+		if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+			return tdb_null;
+		if (rec_read(tdb, tdb->travlocks.off, &rec) == -1
+		    || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
+					    rec.key_len))
+		    || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
+			/* No, it wasn't: unlock it and start from scratch */
+			if (unlock_record(tdb, tdb->travlocks.off) != 0)
+				return tdb_null;
+			if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+				return tdb_null;
+			tdb->travlocks.off = 0;
 		}
 
-		if (rec_ptr) break;
+		SAFE_FREE(k);
+	}
 
-		tdb_unlock(tdb, BUCKET(hash));
+	if (!tdb->travlocks.off) {
+		/* No previous element: do normal find, and lock record */
+		tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), F_WRLCK, &rec);
+		if (!tdb->travlocks.off)
+			return tdb_null;
+		tdb->travlocks.hash = BUCKET(rec.full_hash);
+		if (lock_record(tdb, tdb->travlocks.off) != 0) {
+			TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
+			return tdb_null;
+		}
 	}
+	oldhash = tdb->travlocks.hash;
 
-	if (rec_ptr == 0) return null_data;
+	/* Grab next record: locks chain and returned record,
+	   unlocks old record */
+	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
+		key.dsize = rec.key_len;
+		key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
+					  key.dsize);
+		/* Unlock the chain of this new record */
+		if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+			TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+	}
+	/* Unlock the chain of old record */
+	if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+	return key;
+}
 
-	/* we've found a non-empty chain, now read the record */
-	if (rec_read(tdb, rec_ptr, &rec) == -1) {
-		goto fail;
-	}
+/* delete an entry in the database given a key */
+static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+	tdb_off rec_ptr;
+	struct list_struct rec;
+	int ret;
 
-	/* allocate and read the key space */
-	ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
-	ret.dsize = rec.key_len;
-	tdb_unlock(tdb, BUCKET(hash));
+	if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))
+		return -1;
+	ret = do_delete(tdb, rec_ptr, &rec);
+	if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
+		TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
 	return ret;
+}
 
- fail:
-	tdb_unlock(tdb, BUCKET(hash));
-	return null_data;
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	u32 hash = tdb->hash_fn(&key);
+	return tdb_delete_hash(tdb, key, hash);
 }
 
-/* find the next entry in the database, returning its key */
-TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key)
+/* store an element in the database, replacing any existing element
+   with the same key 
+
+   return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
 {
-	unsigned hash, hbucket;
-	tdb_off rec_ptr, offset;
 	struct list_struct rec;
-	TDB_DATA ret;
-
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_nextkey() called with null context\n");
-#endif
-            return null_data;
-        }
+	u32 hash;
+	tdb_off rec_ptr;
+	char *p = NULL;
+	int ret = 0;
 
 	/* find which hash bucket it is in */
-	hash = tdb_hash(&key);
-	hbucket = BUCKET(hash);
-	
-	tdb_lock(tdb, hbucket);
-	rec_ptr = tdb_find(tdb, key, hash, &rec);
-	if (rec_ptr) {
-		/* we want the next record after this one */
-		rec_ptr = rec.next;
-	}
+	hash = tdb->hash_fn(&key);
+	if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+		return -1;
 
-	/* not found or last in hash: look for next non-empty hash chain */
-	while (rec_ptr == 0) {
-		tdb_unlock(tdb, hbucket);
-
-		if (++hbucket >= tdb->header.hash_size - 1)
-			return null_data;
-
-		offset = tdb_hash_top(tdb, hbucket);
-		tdb_lock(tdb, hbucket);
-		/* read in the hash top */
-		if (ofs_read(tdb, offset, &rec_ptr) == -1) {
-			tdb_unlock(tdb, hbucket);
-			return null_data;
+	/* check for it existing, on insert. */
+	if (flag == TDB_INSERT) {
+		if (tdb_exists_hash(tdb, key, hash)) {
+			tdb->ecode = TDB_ERR_EXISTS;
+			goto fail;
 		}
+	} else {
+		/* first try in-place update, on modify or replace. */
+		if (tdb_update_hash(tdb, key, hash, dbuf) == 0)
+			goto out;
+		if (tdb->ecode == TDB_ERR_NOEXIST &&
+		    flag == TDB_MODIFY) {
+			/* if the record doesn't exist and we are in TDB_MODIFY mode then
+			 we should fail the store */
+			goto fail;
 	}
+	}
+	/* reset the error code potentially set by the tdb_update() */
+	tdb->ecode = TDB_SUCCESS;
 
-	/* Read the record. */
-	if (rec_read(tdb, rec_ptr, &rec) == -1) {
-		tdb_unlock(tdb, hbucket);
-		return null_data;
-	}
-	/* allocate and read the key */
-	ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
-	ret.dsize = rec.key_len;
-	tdb_unlock(tdb, hbucket);
-
-	return ret;
-}
+	/* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+	if (flag != TDB_INSERT)
+		tdb_delete_hash(tdb, key, hash);
 
-/* delete an entry in the database given a key */
-int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
-{
-	unsigned hash;
-	tdb_off offset, rec_ptr, last_ptr;
-	struct list_struct rec, lastrec;
-	char *data = NULL;
-
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_delete() called with null context\n");
-#endif
-            return -1;
-        }
+	/* Copy key+value *before* allocating free space in case malloc
+	   fails and we are left with a dead spot in the tdb. */
 
-	/* find which hash bucket it is in */
-	hash = tdb_hash(&key);
+	if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
+		tdb->ecode = TDB_ERR_OOM;
+		goto fail;
+	}
 
-	tdb_lock(tdb, BUCKET(hash));
+	memcpy(p, key.dptr, key.dsize);
+	if (dbuf.dsize)
+		memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
 
-	/* find the top of the hash chain */
-	offset = tdb_hash_top(tdb, hash);
+	/* we have to allocate some space */
+	if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
+		goto fail;
 
-	/* read in the hash top */
-	if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+	/* Read hash top into next ptr */
+	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
 		goto fail;
-	}
 
-	last_ptr = 0;
+	rec.key_len = key.dsize;
+	rec.data_len = dbuf.dsize;
+	rec.full_hash = hash;
+	rec.magic = TDB_MAGIC;
 
-	/* keep looking until we find the right record */
-	while (rec_ptr) {
-		if (rec_read(tdb, rec_ptr, &rec) == -1) {
-			goto fail;
-		}
+	/* write out and point the top of the hash chain at it */
+	if (rec_write(tdb, rec_ptr, &rec) == -1
+	    || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+	    || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+		/* Need to tdb_unallocate() here */
+		goto fail;
+	}
+ out:
+	SAFE_FREE(p); 
+	tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+	return ret;
+fail:
+	ret = -1;
+	goto out;
+}
 
-		if (hash == rec.full_hash && key.dsize == rec.key_len) {
-			/* a very likely hit - read the record and full key */
-			data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), 
-					     rec.key_len);
-			if (!data) {
-				goto fail;
-			}
+/* Attempt to append data to an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1. Record must be locked before calling.
+*/
+static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA new_dbuf)
+{
+	struct list_struct rec;
+	tdb_off rec_ptr;
 
-			if (memcmp(key.dptr, data, key.dsize) == 0) {
-				/* a definite match - delete it */
-				if (last_ptr == 0) {
-					offset = tdb_hash_top(tdb, hash);
-					if (ofs_write(tdb, offset, &rec.next) == -1) {
-						goto fail;
-					}
-				} else {
-					lastrec.next = rec.next;
-					if (rec_write(tdb, last_ptr, &lastrec) == -1) {
-						goto fail;
-					}					
-				}
-				tdb_unlock(tdb, BUCKET(hash));
-				tdb_lock(tdb, -1);
-				/* and recover the space */
-				offset = FREELIST_TOP;
-				if (ofs_read(tdb, offset, &rec.next) == -1) {
-					goto fail2;
-				}
-				rec.magic = TDB_FREE_MAGIC;
-				if (rec_write(tdb, rec_ptr, &rec) == -1) {
-					goto fail2;
-				}
-				if (ofs_write(tdb, offset, &rec_ptr) == -1) {
-					goto fail2;
-				}
-
-				/* yipee - all done */
-				free(data);
-				tdb_unlock(tdb, -1);
-				return 0;
-			}
+	/* find entry */
+	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+		return -1;
 
-			/* a miss - drat */
-			free(data);
-			data = NULL;
-		}
+	/* Append of 0 is always ok. */
+	if (new_dbuf.dsize == 0)
+		return 0;
 
-		/* move to the next record */
-		last_ptr = rec_ptr;
-		lastrec = rec;
-		rec_ptr = rec.next;
+	/* must be long enough for key, old data + new data and tailer */
+	if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
+		/* No room. */
+		tdb->ecode = TDB_SUCCESS; /* Not really an error */
+		return -1;
 	}
 
- fail:
-	if (data) free(data);
-	tdb_unlock(tdb, BUCKET(hash));
-	return -1;
+	if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
+		      new_dbuf.dptr, new_dbuf.dsize) == -1)
+		return -1;
 
- fail2:
-	if (data) free(data);
-	tdb_unlock(tdb, -1);
-	return -1;
+	/* update size */
+	rec.data_len += new_dbuf.dsize;
+	return rec_write(tdb, rec_ptr, &rec);
 }
 
+/* Append to an entry. Create if not exist. */
 
-/* store an element in the database, replacing any existing element
-   with the same key 
-
-   return 0 on success, -1 on failure
-*/
-int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
 {
 	struct list_struct rec;
-	unsigned hash;
-	tdb_off rec_ptr, offset;
+	u32 hash;
+	tdb_off rec_ptr;
 	char *p = NULL;
-
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_store() called with null context\n");
-#endif
-            return -1;
-        }
+	int ret = 0;
+	size_t new_data_size = 0;
 
 	/* find which hash bucket it is in */
-	hash = tdb_hash(&key);
-
-	/* check for it existing */
-	if (flag == TDB_INSERT && tdb_exists(tdb, key)) {
-		tdb->ecode = TDB_ERR_EXISTS;
+	hash = tdb->hash_fn(&key);
+	if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
 		return -1;
-	}
 
-	/* first try in-place update */
-	if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) {
-		return 0;
-	}
+	/* first try in-place. */
+	if (tdb_append_inplace(tdb, key, hash, new_dbuf) == 0)
+		goto out;
 
-	rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize);
-	if (rec_ptr == 0) {
-		return -1;
-	}
+	/* reset the error code potentially set by the tdb_append_inplace() */
+	tdb->ecode = TDB_SUCCESS;
+
+	/* find entry */
+	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
+		if (tdb->ecode != TDB_ERR_NOEXIST)
+			goto fail;
 
-	tdb_lock(tdb, BUCKET(hash));
+		/* Not found - create. */
 
-	/* delete any existing record - if it doesn't exist we don't care */
-	if (flag != TDB_INSERT) {
-		tdb_delete(tdb, key);
+		ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
+		goto out;
 	}
 
-	/* read the newly created record */
-	if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+	new_data_size = rec.data_len + new_dbuf.dsize;
+
+	/* Copy key+old_value+value *before* allocating free space in case malloc
+	   fails and we are left with a dead spot in the tdb. */
+
+	if (!(p = (char *)malloc(key.dsize + new_data_size))) {
+		tdb->ecode = TDB_ERR_OOM;
 		goto fail;
 	}
 
-	if (rec.magic != TDB_FREE_MAGIC) goto fail;
+	/* Copy the key in place. */
+	memcpy(p, key.dptr, key.dsize);
+
+	/* Now read the old data into place. */
+	if (rec.data_len &&
+		tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
+			goto fail;
+
+	/* Finally append the new data. */
+	if (new_dbuf.dsize)
+		memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
 
-	/* find the top of the hash chain */
-	offset = tdb_hash_top(tdb, hash);
+	/* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
 
-	/* read in the hash top diretcly into our next pointer */
-	if (ofs_read(tdb, offset, &rec.next) == -1) {
+	tdb_delete_hash(tdb, key, hash);
+
+	if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
+		goto fail;
+
+	/* Read hash top into next ptr */
+	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
 		goto fail;
-	}
 
 	rec.key_len = key.dsize;
-	rec.data_len = dbuf.dsize;
+	rec.data_len = new_data_size;
 	rec.full_hash = hash;
 	rec.magic = TDB_MAGIC;
 
-	p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize);
-	if (!p) {
-		tdb->ecode = TDB_ERR_OOM;
+	/* write out and point the top of the hash chain at it */
+	if (rec_write(tdb, rec_ptr, &rec) == -1
+	    || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
+	    || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+		/* Need to tdb_unallocate() here */
 		goto fail;
 	}
 
-	memcpy(p, &rec, sizeof(rec));
-	memcpy(p+sizeof(rec), key.dptr, key.dsize);
-	memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize);
-
-	if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1)
-		goto fail;
+ out:
+	SAFE_FREE(p); 
+	tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+	return ret;
 
-	free(p); 
-	p = NULL;
+fail:
+	ret = -1;
+	goto out;
+}
 
-	/* and point the top of the hash chain at it */
-	if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail;
+static int tdb_already_open(dev_t device,
+			    ino_t ino)
+{
+	TDB_CONTEXT *i;
+	
+	for (i = tdbs; i; i = i->next) {
+		if (i->device == device && i->inode == ino) {
+			return 1;
+		}
+	}
 
-	tdb_unlock(tdb, BUCKET(hash));
 	return 0;
-
- fail:
-#if TDB_DEBUG
-	printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash));
-#endif
-	if (p) free(p);
-	tdb_unlock(tdb, BUCKET(hash));
-	return -1;
 }
 
+/* This is based on the hash algorithm from gdbm */
+static u32 default_tdb_hash(TDB_DATA *key)
+{
+	u32 value;	/* Used to compute the hash value.  */
+	u32   i;	/* Used to cycle through random values. */
+
+	/* Set the initial value from the key size. */
+	for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
+		value = (value + (key->dptr[i] << (i*5 % 24)));
+
+	return (1103515243 * value + 12345);  
+}
 
 /* open the database, creating it if necessary 
 
-   The open_flags and mode are passed straight to the open call on the database
-   file. A flags value of O_WRONLY is invalid
+   The open_flags and mode are passed straight to the open call on the
+   database file. A flags value of O_WRONLY is invalid. The hash size
+   is advisory, use zero for a default value.
 
-   The hash size is advisory, use zero for a default value. 
+   Return is NULL on error, in which case errno is also set.  Don't 
+   try to call tdb_error or tdb_errname, just do strerror(errno).
 
-   return is NULL on error
-*/
-TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+   @param name may be NULL for internal databases. */
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
 		      int open_flags, mode_t mode)
 {
-	TDB_CONTEXT tdb, *ret;
-	struct stat st;
+	return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
+}
 
-	memset(&tdb, 0, sizeof(tdb));
 
-	tdb.fd = -1;
-	tdb.name = NULL;
-	tdb.map_ptr = NULL;
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+			 int open_flags, mode_t mode,
+			 tdb_log_func log_fn,
+			 tdb_hash_func hash_fn)
+{
+	TDB_CONTEXT *tdb;
+	struct stat st;
+	int rev = 0, locked = 0;
+	unsigned char *vp;
+	u32 vertest;
+
+	if (!(tdb = calloc(1, sizeof *tdb))) {
+		/* Can't log this */
+		errno = ENOMEM;
+		goto fail;
+	}
+	tdb->fd = -1;
+	tdb->name = NULL;
+	tdb->map_ptr = NULL;
+	tdb->flags = tdb_flags;
+	tdb->open_flags = open_flags;
+	tdb->log_fn = log_fn;
+	tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash;
 
 	if ((open_flags & O_ACCMODE) == O_WRONLY) {
+		TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n",
+			 name));
+		errno = EINVAL;
 		goto fail;
 	}
+	
+	if (hash_size == 0)
+		hash_size = DEFAULT_HASH_SIZE;
+	if ((open_flags & O_ACCMODE) == O_RDONLY) {
+		tdb->read_only = 1;
+		/* read only databases don't do locking or clear if first */
+		tdb->flags |= TDB_NOLOCK;
+		tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+	}
 
-	if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
-
-	tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY);
+	/* internal databases don't mmap or lock, and start off cleared */
+	if (tdb->flags & TDB_INTERNAL) {
+		tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
+		tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+		if (tdb_new_database(tdb, hash_size) != 0) {
+			TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!"));
+			goto fail;
+		}
+		goto internal;
+	}
 
-        if (name != NULL) {
-            tdb.fd = open(name, open_flags, mode);
-            if (tdb.fd == -1) {
-		goto fail;
-            }
-        }
+	if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+		TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
+			 name, strerror(errno)));
+		goto fail;	/* errno set by open(2) */
+	}
 
 	/* ensure there is only one process initialising at once */
-	tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW);
-	
-	if (tdb_flags & TDB_CLEAR_IF_FIRST) {
-		/* we need to zero the database if we are the only
-		   one with it open */
-		if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) {
-			ftruncate(tdb.fd, 0);
-			tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK);
-		}
+	if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) {
+		TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n",
+			 name, strerror(errno)));
+		goto fail;	/* errno set by tdb_brlock */
 	}
 
-	/* leave this lock in place */
-	tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW);
+	/* we need to zero database if we are the only one with it open */
+	if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
+		(locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) {
+		open_flags |= O_CREAT;
+		if (ftruncate(tdb->fd, 0) == -1) {
+			TDB_LOG((tdb, 0, "tdb_open_ex: "
+				 "failed to truncate %s: %s\n",
+				 name, strerror(errno)));
+			goto fail; /* errno set by ftruncate */
+		}
+	}
 
-	if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) ||
-	    strcmp(tdb.header.magic_food, TDB_MAGIC_FOOD) != 0 ||
-	    tdb.header.version != TDB_VERSION) {
+	if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+	    || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
+	    || (tdb->header.version != TDB_VERSION
+		&& !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
 		/* its not a valid database - possibly initialise it */
-		if (!(open_flags & O_CREAT)) {
+		if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+			errno = EIO; /* ie bad format or something */
 			goto fail;
 		}
-		if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
+		rev = (tdb->flags & TDB_CONVERT);
+	}
+	vp = (unsigned char *)&tdb->header.version;
+	vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
+		  (((u32)vp[2]) << 8) | (u32)vp[3];
+	tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
+	if (!rev)
+		tdb->flags &= ~TDB_CONVERT;
+	else {
+		tdb->flags |= TDB_CONVERT;
+		convert(&tdb->header, sizeof(tdb->header));
+	}
+	if (fstat(tdb->fd, &st) == -1)
+		goto fail;
 
-		lseek(tdb.fd, 0, SEEK_SET);
-		if (tdb.fd != -1 && read(tdb.fd, &tdb.header, 
-                                         sizeof(tdb.header)) != 
-                                         sizeof(tdb.header)) 
-                    goto fail;
-	}
-
-        if (tdb.fd != -1) {
-            fstat(tdb.fd, &st);
-
-            /* map the database and fill in the return structure */
-            tdb.name = (char *)strdup(name);
-            tdb.map_size = st.st_size;
-        }
-
-        tdb.locked = (int *)calloc(tdb.header.hash_size+1, 
-                                   sizeof(tdb.locked[0]));
-        if (!tdb.locked) {
-            goto fail;
-        }
-
-#if HAVE_MMAP
-        if (tdb.fd != -1) {
-            tdb.map_ptr = (void *)mmap(NULL, st.st_size, 
-                                       tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
-                                       MAP_SHARED | MAP_FILE, tdb.fd, 0);
-        }
-#endif
+	/* Is it already in the open list?  If so, fail. */
+	if (tdb_already_open(st.st_dev, st.st_ino)) {
+		TDB_LOG((tdb, 2, "tdb_open_ex: "
+			 "%s (%d,%d) is already open in this process\n",
+			 name, (int)st.st_dev, (int)st.st_ino));
+		errno = EBUSY;
+		goto fail;
+	}
 
-	ret = (TDB_CONTEXT *)malloc(sizeof(tdb));
-	if (!ret) goto fail;
+	if (!(tdb->name = (char *)strdup(name))) {
+		errno = ENOMEM;
+		goto fail;
+	}
 
-	*ret = tdb;
+	tdb->map_size = st.st_size;
+	tdb->device = st.st_dev;
+	tdb->inode = st.st_ino;
+	tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0]));
+	if (!tdb->locked) {
+		TDB_LOG((tdb, 2, "tdb_open_ex: "
+			 "failed to allocate lock structure for %s\n",
+			 name));
+		errno = ENOMEM;
+		goto fail;
+	}
+	tdb_mmap(tdb);
+	if (locked) {
+		if (!tdb->read_only)
+			if (tdb_clear_spinlocks(tdb) != 0) {
+				TDB_LOG((tdb, 0, "tdb_open_ex: "
+				"failed to clear spinlock\n"));
+				goto fail;
+			}
+		if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) {
+			TDB_LOG((tdb, 0, "tdb_open_ex: "
+				 "failed to take ACTIVE_LOCK on %s: %s\n",
+				 name, strerror(errno)));
+			goto fail;
+		}
 
-#if TDB_DEBUG
-	printf("mapped database of hash_size %u map_size=%u\n", 
-	       hash_size, tdb.map_size);
-#endif
+	}
 
-	tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW);
-	return ret;
+	/* We always need to do this if the CLEAR_IF_FIRST flag is set, even if
+	   we didn't get the initial exclusive lock as we need to let all other
+	   users know we're using it. */
+
+	if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+		/* leave this lock in place to indicate it's in use */
+		if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)
+			goto fail;
+	}
+
+
+ internal:
+	/* Internal (memory-only) databases skip all the code above to
+	 * do with disk files, and resume here by releasing their
+	 * global lock and hooking into the active list. */
+	if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1)
+		goto fail;
+	tdb->next = tdbs;
+	tdbs = tdb;
+	return tdb;
 
  fail:
-        if (tdb.name) free(tdb.name);
-	if (tdb.fd != -1) close(tdb.fd);
-	if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
+	{ int save_errno = errno;
 
+	if (!tdb)
+		return NULL;
+	
+	if (tdb->map_ptr) {
+		if (tdb->flags & TDB_INTERNAL)
+			SAFE_FREE(tdb->map_ptr);
+		else
+			tdb_munmap(tdb);
+	}
+	SAFE_FREE(tdb->name);
+	if (tdb->fd != -1)
+		if (close(tdb->fd) != 0)
+			TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n"));
+	SAFE_FREE(tdb->locked);
+	SAFE_FREE(tdb);
+	errno = save_errno;
 	return NULL;
+	}
 }
 
-/* close a database */
+/**
+ * Close a database.
+ *
+ * @returns -1 for error; 0 for success.
+ **/
 int tdb_close(TDB_CONTEXT *tdb)
 {
-	if (!tdb) return -1;
-
-	if (tdb->name) free(tdb->name);
-	if (tdb->fd != -1) close(tdb->fd);
-	if (tdb->locked) free(tdb->locked);
+	TDB_CONTEXT **i;
+	int ret = 0;
 
 	if (tdb->map_ptr) {
-            if (tdb->fd != -1) {
-                munmap(tdb->map_ptr, tdb->map_size);
-            } else {
-                free(tdb->map_ptr);
-            }
-        }
+		if (tdb->flags & TDB_INTERNAL)
+			SAFE_FREE(tdb->map_ptr);
+		else
+			tdb_munmap(tdb);
+	}
+	SAFE_FREE(tdb->name);
+	if (tdb->fd != -1)
+		ret = close(tdb->fd);
+	SAFE_FREE(tdb->locked);
+
+	/* Remove from contexts list */
+	for (i = &tdbs; *i; i = &(*i)->next) {
+		if (*i == tdb) {
+			*i = tdb->next;
+			break;
+		}
+	}
 
 	memset(tdb, 0, sizeof(*tdb));
-	free(tdb);
+	SAFE_FREE(tdb);
 
-	return 0;
+	return ret;
 }
 
-/* lock the database. If we already have it locked then don't do anything */
-int tdb_writelock(TDB_CONTEXT *tdb)
+/* lock/unlock entire database */
+int tdb_lockall(TDB_CONTEXT *tdb)
 {
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_writelock() called with null context\n");
-#endif
-            return -1;
-        }
+	u32 i;
+
+	/* There are no locks on read-only dbs */
+	if (tdb->read_only)
+		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+	for (i = 0; i < tdb->header.hash_size; i++) 
+		if (tdb_lock(tdb, i, F_WRLCK))
+			break;
+
+	/* If error, release locks we have... */
+	if (i < tdb->header.hash_size) {
+		u32 j;
+
+		for ( j = 0; j < i; j++)
+			tdb_unlock(tdb, j, F_WRLCK);
+		return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+	}
 
-	return tdb_lock(tdb, -1);
+	return 0;
+}
+void tdb_unlockall(TDB_CONTEXT *tdb)
+{
+	u32 i;
+	for (i=0; i < tdb->header.hash_size; i++)
+		tdb_unlock(tdb, i, F_WRLCK);
 }
 
-/* unlock the database. */
-int tdb_writeunlock(TDB_CONTEXT *tdb)
+/* lock/unlock one hash chain. This is meant to be used to reduce
+   contention - it cannot guarantee how many records will be locked */
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key)
 {
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_writeunlock() called with null context\n");
-#endif
-            return -1;
-        }
+	return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
+}
+
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
+}
 
-	return tdb_unlock(tdb, -1);
+int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+	return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
 }
 
-/* lock one hash chain. This is meant to be used to reduce locking
-   contention - it cannot guarantee how many records will be locked */
-int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
 {
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_lockchain() called with null context\n");
-#endif
-            return -1;
-        }
+	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
+}
 
-	return tdb_lock(tdb, BUCKET(tdb_hash(&key)));
+
+/* register a loging function */
+void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...))
+{
+	tdb->log_fn = fn;
 }
 
+/* reopen a tdb - this can be used after a fork to ensure that we have an independent
+   seek pointer from our parent and to re-establish locks */
+int tdb_reopen(TDB_CONTEXT *tdb)
+{
+	struct stat st;
 
-/* unlock one hash chain */
-int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+	if (tdb->flags & TDB_INTERNAL)
+		return 0; /* Nothing to do. */
+	if (tdb_munmap(tdb) != 0) {
+		TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
+		goto fail;
+	}
+	if (close(tdb->fd) != 0)
+		TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
+	tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
+	if (tdb->fd == -1) {
+		TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno)));
+		goto fail;
+	}
+	if (fstat(tdb->fd, &st) != 0) {
+		TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
+		goto fail;
+	}
+	if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
+		TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n"));
+		goto fail;
+	}
+	tdb_mmap(tdb);
+	if ((tdb->flags & TDB_CLEAR_IF_FIRST) && (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)) {
+		TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n"));
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	tdb_close(tdb);
+	return -1;
+}
+
+/* reopen all tdb's */
+int tdb_reopen_all(void)
 {
-        if (tdb == NULL) {
-#ifdef TDB_DEBUG
-            printf("tdb_unlockchain() called with null context\n");
-#endif
-            return -1;
-        }
+	TDB_CONTEXT *tdb;
 
-	return tdb_unlock(tdb, BUCKET(tdb_hash(&key)));
+	for (tdb=tdbs; tdb; tdb = tdb->next) {
+		/* Ensure no clear-if-first. */
+		tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+		if (tdb_reopen(tdb) != 0)
+			return -1;
+	}
+
+	return 0;
 }
diff -Naur tomato/release/src/router/pppd/pppd/tdb.h tomato121/release/src/router/pppd/pppd/tdb.h
--- tomato/release/src/router/pppd/pppd/tdb.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/tdb.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,77 +1,164 @@
-#define STANDALONE	1
+#ifndef __TDB_H__
+#define __TDB_H__
+
 /* 
- * Database functions
- * Copyright (C) Andrew Tridgell 1999
- * 
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms AND provided that this software or
- * any derived work is only used as part of the PPP daemon (pppd)
- * and related utilities.
- * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Note: this software is also available under the Gnu Public License
- * version 2 or later.
- */
+   Unix SMB/CIFS implementation.
+   
+   trivial database library
+   
+   Copyright (C) Andrew Tridgell 1999-2004
+   
+     ** NOTE! The following LGPL license applies to the tdb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
 
-typedef unsigned tdb_len;
-typedef unsigned tdb_off;
+#ifdef  __cplusplus
+extern "C" {
+#endif
 
-#define TDB_MAGIC_FOOD "TDB file\n"
+#ifndef PRINTF_ATTRIBUTE
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#if (__GNUC__ >= 3)
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
 
-/* this is stored at the front of every database */
-struct tdb_header {
-	char magic_food[32]; /* for /etc/magic */
-	unsigned version; /* version of the code */
-	unsigned hash_size; /* number of hash entries */
-};
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+#define TDB_MODIFY 3
+
+/* flags for tdb_open() */
+#define TDB_DEFAULT 0 /* just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1
+#define TDB_INTERNAL 2 /* don't store on disk */
+#define TDB_NOLOCK   4 /* don't do any locking */
+#define TDB_NOMMAP   8 /* don't use mmap */
+#define TDB_CONVERT 16 /* convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
+
+#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
+		TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
+		TDB_ERR_NOEXIST};
+
+#ifndef u32
+#define u32 unsigned
+#endif
 
 typedef struct {
 	char *dptr;
 	size_t dsize;
 } TDB_DATA;
 
+typedef u32 tdb_len;
+typedef u32 tdb_off;
+
+/* this is stored at the front of every database */
+struct tdb_header {
+	char magic_food[32]; /* for /etc/magic */
+	u32 version; /* version of the code */
+	u32 hash_size; /* number of hash entries */
+	tdb_off rwlocks;
+	tdb_off reserved[31];
+};
+
+struct tdb_lock_type {
+	u32 count;
+	u32 ltype;
+};
+
+struct tdb_traverse_lock {
+	struct tdb_traverse_lock *next;
+	u32 off;
+	u32 hash;
+};
+
 /* this is the context structure that is returned from a db open */
-typedef struct {
+typedef struct tdb_context {
 	char *name; /* the name of the database */
 	void *map_ptr; /* where it is currently mapped */
 	int fd; /* open file descriptor for the database */
 	tdb_len map_size; /* how much space has been mapped */
 	int read_only; /* opened read-only */
-	int *locked; /* set if we have a chain locked */
-	int ecode; /* error code for last tdb error */
+	struct tdb_lock_type *locked; /* array of chain locks */
+	enum TDB_ERROR ecode; /* error code for last tdb error */
 	struct tdb_header header; /* a cached copy of the header */
+	u32 flags; /* the flags passed to tdb_open */
+	struct tdb_traverse_lock travlocks; /* current traversal locks */
+	struct tdb_context *next; /* all tdbs to avoid multiple opens */
+	dev_t device;	/* uniquely identifies this tdb */
+	ino_t inode;	/* uniquely identifies this tdb */
+	void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...) PRINTF_ATTRIBUTE(3,4); /* logging function */
+	u32 (*hash_fn)(TDB_DATA *key);
+	int open_flags; /* flags used in the open - needed by reopen */
 } TDB_CONTEXT;
 
-/* flags to tdb_store() */
-#define TDB_REPLACE 1
-#define TDB_INSERT 2
-
-/* flags for tdb_open() */
-#define TDB_CLEAR_IF_FIRST 1
-
-/* error codes */
-enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
-		TDB_ERR_OOM, TDB_ERR_EXISTS};
+typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
+typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
+typedef u32 (*tdb_hash_func)(TDB_DATA *key);
 
-#if STANDALONE
-TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
 		      int open_flags, mode_t mode);
-char *tdb_error(TDB_CONTEXT *tdb);
-int tdb_writelock(TDB_CONTEXT *tdb);
-int tdb_writeunlock(TDB_CONTEXT *tdb);
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+			 int open_flags, mode_t mode,
+			 tdb_log_func log_fn,
+			 tdb_hash_func hash_fn);
+
+int tdb_reopen(TDB_CONTEXT *tdb);
+int tdb_reopen_all(void);
+void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
+const char *tdb_errorstr(TDB_CONTEXT *tdb);
 TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
 int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
 int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
 int tdb_close(TDB_CONTEXT *tdb);
 TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
 TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
-int tdb_traverse(TDB_CONTEXT *tdb, 
-	int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state),
-	void *state);
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *);
 int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
+void tdb_unlockkeys(TDB_CONTEXT *tdb);
+int tdb_lockall(TDB_CONTEXT *tdb);
+void tdb_unlockall(TDB_CONTEXT *tdb);
+
+/* Low level locking functions: use with care */
+void tdb_set_lock_alarm(sig_atomic_t *palarm);
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
+
+/* Debug functions. Not used in production. */
+void tdb_dump_all(TDB_CONTEXT *tdb);
+int tdb_printfreelist(TDB_CONTEXT *tdb);
+
+extern TDB_DATA tdb_null;
+
+#ifdef  __cplusplus
+}
 #endif
+
+#endif /* tdb.h */
diff -Naur tomato/release/src/router/pppd/pppd/tty.c tomato121/release/src/router/pppd/pppd/tty.c
--- tomato/release/src/router/pppd/pppd/tty.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/tty.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,26 +1,74 @@
 /*
  * tty.c - code for handling serial ports in pppd.
  *
- * Copyright (C) 2000 Paul Mackerras.
- * All rights reserved.
+ * Copyright (C) 2000-2004 Paul Mackerras. All rights reserved.
  *
- * Portions Copyright (c) 1989 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:
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
+ *
+ * Portions derived from main.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.
  */
 
-#define RCSID	"$Id: tty.c,v 1.1 2003/07/10 07:43:05 honor Exp $"
+#define RCSID	"$Id: tty.c,v 1.25 2006/06/04 07:04:57 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -104,6 +152,8 @@
 
 extern uid_t uid;
 extern int kill_link;
+extern int asked_to_quit;
+extern int got_sigterm;
 
 /* XXX */
 extern int privopen;		/* don't lock, open device as root */
@@ -247,7 +297,7 @@
 	if (*cp == 0)
 		return 0;
 
-	if (strncmp("/dev/", cp, 5) != 0) {
+	if (*cp != '/') {
 		strlcpy(dev, "/dev/", sizeof(dev));
 		strlcat(dev, cp, sizeof(dev));
 		cp = dev;
@@ -395,7 +445,12 @@
 	struct stat statbuf;
 	int fdflags;
 
-	if (demand && connect_script == 0) {
+	if (demand && notty) {
+		option_error("demand-dialling is incompatible with notty");
+		exit(EXIT_OPTION_ERROR);
+	}
+	if (demand && connect_script == 0 && ptycommand == NULL
+	    && pty_socket == NULL) {
 		option_error("connect script is required for demand-dialling\n");
 		exit(EXIT_OPTION_ERROR);
 	}
@@ -406,7 +461,7 @@
 	if (using_pty) {
 		if (!default_device) {
 			option_error("%s option precludes specifying device name",
-				     notty? "notty": "pty");
+				     pty_socket? "socket": notty? "notty": "pty");
 			exit(EXIT_OPTION_ERROR);
 		}
 		if (ptycommand != NULL && notty) {
@@ -459,7 +514,9 @@
 {
 	char *connector;
 	int fdflags;
+#ifndef __linux__
 	struct stat statbuf;
+#endif
 	char numbuf[16];
 
 	/*
@@ -485,7 +542,7 @@
 	status = EXIT_LOCK_FAILED;
 	if (lockflag && !privopen) {
 		if (lock(devnam) < 0)
-			return -1;
+			goto errret;
 		locked = 1;
 	}
 
@@ -497,7 +554,7 @@
 	 * in order to wait for the carrier detect signal from the modem.
 	 */
 	hungup = 0;
-	kill_link = 0;
+	got_sigterm = 0;
 	connector = doing_callback? callback_script: connect_script;
 	if (devnam[0] != 0) {
 		for (;;) {
@@ -506,13 +563,17 @@
 			int err, prio;
 
 			prio = privopen? OPRIO_ROOT: tty_options[0].priority;
-			if (prio < OPRIO_ROOT)
-				seteuid(uid);
-			ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+			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 (ttyfd >= 0)
+			if (prio < OPRIO_ROOT && seteuid(0) == -1)
+				fatal("Unable to regain privileges");
+			if (real_ttyfd >= 0)
 				break;
 			errno = err;
 			if (err != EINTR) {
@@ -520,13 +581,18 @@
 				status = EXIT_OPEN_FAILED;
 			}
 			if (!persist || err != EINTR)
-				return -1;
+				goto errret;
 		}
-		real_ttyfd = ttyfd;
+		ttyfd = real_ttyfd;
 		if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
 		    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
 			warn("Couldn't reset non-blocking mode on device: %m");
 
+#ifndef __linux__
+		/*
+		 * Linux 2.4 and above blocks normal writes to the tty
+		 * when it is in PPP line discipline, so this isn't needed.
+		 */
 		/*
 		 * Do the equivalent of `mesg n' to stop broadcast messages.
 		 */
@@ -535,6 +601,7 @@
 			warn("Couldn't restrict write permissions to %s: %m", devnam);
 		} else
 			tty_mode = statbuf.st_mode;
+#endif /* __linux__ */
 
 		/*
 		 * Set line speed, flow control, etc.
@@ -561,6 +628,11 @@
 
 			if (pipe(ipipe) < 0 || pipe(opipe) < 0)
 				fatal("Couldn't create pipes for record option: %m");
+
+			/* don't leak these to the ptycommand */
+			(void) fcntl(ipipe[0], F_SETFD, FD_CLOEXEC);
+			(void) fcntl(opipe[1], F_SETFD, FD_CLOEXEC);
+
 			ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
 				&& start_charshunt(ipipe[0], opipe[1]);
 			close(ipipe[0]);
@@ -568,26 +640,37 @@
 			close(opipe[0]);
 			close(opipe[1]);
 			if (!ok)
-				return -1;
+				goto errret;
 		} else {
 			if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
-				return -1;
-			ttyfd = pty_slave;
-			close(pty_master);
-			pty_master = -1;
+				goto errret;
 		}
 	} else if (pty_socket != NULL) {
 		int fd = open_socket(pty_socket);
 		if (fd < 0)
-			return -1;
+			goto errret;
 		if (!start_charshunt(fd, fd))
-			return -1;
+			goto errret;
+		close(fd);
 	} else if (notty) {
 		if (!start_charshunt(0, 1))
-			return -1;
+			goto errret;
+		dup2(fd_devnull, 0);
+		dup2(fd_devnull, 1);
+		if (log_to_fd == 1)
+			log_to_fd = -1;
+		if (log_to_fd != 2)
+			dup2(fd_devnull, 2);
 	} else if (record_file != NULL) {
-		if (!start_charshunt(ttyfd, ttyfd))
-			return -1;
+		int fd = dup(ttyfd);
+		if (!start_charshunt(fd, fd))
+			goto errret;
+	}
+
+	if (using_pty || record_file != NULL) {
+		ttyfd = pty_slave;
+		close(pty_master);
+		pty_master = -1;
 	}
 
 	/* run connection script */
@@ -605,11 +688,11 @@
 			if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
 				error("Initializer script failed");
 				status = EXIT_INIT_FAILED;
-				return -1;
+				goto errret;
 			}
-			if (kill_link) {
+			if (got_sigterm) {
 				disconnect_tty();
-				return -1;
+				goto errret;
 			}
 			info("Serial port initialized.");
 		}
@@ -618,11 +701,11 @@
 			if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
 				error("Connect script failed");
 				status = EXIT_CONNECT_FAILED;
-				return -1;
+				goto errret;
 			}
-			if (kill_link) {
+			if (got_sigterm) {
 				disconnect_tty();
-				return -1;
+				goto errret;
 			}
 			info("Serial connection established.");
 		}
@@ -646,8 +729,8 @@
 				error("Failed to reopen %s: %m", devnam);
 				status = EXIT_OPEN_FAILED;
 			}
-			if (!persist || errno != EINTR || hungup || kill_link)
-				return -1;
+			if (!persist || errno != EINTR || hungup || got_sigterm)
+				goto errret;
 		}
 		close(i);
 	}
@@ -666,10 +749,20 @@
 	 * time for something from the peer.  This can avoid bouncing
 	 * our packets off his tty before he has it set up.
 	 */
-	if (connector != NULL || ptycommand != NULL)
+	if (connector != NULL || ptycommand != NULL || pty_socket != NULL)
 		listen_time = connect_delay;
 
 	return ttyfd;
+
+ errret:
+	if (pty_master >= 0) {
+		close(pty_master);
+		pty_master = -1;
+	}
+	ttyfd = -1;
+	if (got_sigterm)
+		asked_to_quit = 1;
+	return -1;
 }
 
 
@@ -684,12 +777,11 @@
 	} else {
 		info("Serial link disconnected.");
 	}
+	stop_charshunt(NULL, 0);
 }
 
 void tty_close_fds()
 {
-	if (pty_master >= 0)
-		close(pty_master);
 	if (pty_slave >= 0)
 		close(pty_slave);
 	if (real_ttyfd >= 0) {
@@ -742,12 +834,12 @@
 
 	restore_tty(real_ttyfd);
 
+#ifndef __linux__
 	if (tty_mode != (mode_t) -1) {
-		if (fchmod(real_ttyfd, tty_mode) != 0) {
-			/* XXX if devnam is a symlink, this will change the link */
-			chmod(devnam, tty_mode);
-		}
+		if (fchmod(real_ttyfd, tty_mode) != 0)
+			error("Couldn't restore tty permissions");
 	}
+#endif /* __linux__ */
 
 	close(real_ttyfd);
 	real_ttyfd = -1;
@@ -829,28 +921,26 @@
 {
     int cpid;
 
-    cpid = fork();
+    cpid = safe_fork(ifd, ofd, (log_to_fd >= 0? log_to_fd: 2));
     if (cpid == -1) {
 	error("Can't fork process for character shunt: %m");
 	return 0;
     }
     if (cpid == 0) {
 	/* child */
-	close(pty_slave);
+	reopen_log();
+	if (!nodetach)
+	    log_to_fd = -1;
+	else if (log_to_fd >= 0)
+	    log_to_fd = 2;
+	setgid(getgid());
 	setuid(uid);
 	if (getuid() != uid)
 	    fatal("setuid failed");
-	setgid(getgid());
-	if (!nodetach)
-	    log_to_fd = -1;
-	charshunt(ifd, ofd, record_file);
+	charshunt(0, 1, record_file);
 	exit(0);
     }
     charshunt_pid = cpid;
-    add_notifier(&sigreceived, stop_charshunt, 0);
-    close(pty_master);
-    pty_master = -1;
-    ttyfd = pty_slave;
     record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
     return 1;
 }
@@ -939,6 +1029,13 @@
 #endif
 
     /*
+     * Check that the fds won't overrun the fd_sets
+     */
+    if (ifd >= FD_SETSIZE || ofd >= FD_SETSIZE || pty_master >= FD_SETSIZE)
+	fatal("internal error: file descriptor too large (%d, %d, %d)",
+	      ifd, ofd, pty_master);
+
+    /*
      * Open the record file if required.
      */
     if (record_file != NULL) {
@@ -1040,9 +1137,6 @@
 	    } else if (nibuf == 0) {
 		/* end of file from stdin */
 		stdin_readable = 0;
-		/* do a 0-length write, hopefully this will generate
-		   an EOF (hangup) on the slave side. */
-		write(pty_master, inpacket_buf, 0);
 		if (recordf)
 		    if (!record_write(recordf, 4, NULL, 0, &lasttime))
 			recordf = NULL;
@@ -1079,7 +1173,8 @@
 		    if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
 			recordf = NULL;
 	    }
-	}
+	} else if (!stdin_readable)
+	    pty_readable = 0;
 	if (FD_ISSET(ofd, &writey)) {
 	    n = nobuf;
 	    if (olevel + n > max_level)
diff -Naur tomato/release/src/router/pppd/pppd/upap.c tomato121/release/src/router/pppd/pppd/upap.c
--- tomato/release/src/router/pppd/pppd/upap.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/upap.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,46 @@
 /*
  * upap.c - User/Password Authentication Protocol.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: upap.c,v 1.3 2004/10/06 10:23:18 honor Exp $"
+#define RCSID	"$Id: upap.c,v 1.30 2005/07/13 10:41:58 paulus Exp $"
 
 /*
  * TODO:
@@ -353,6 +376,7 @@
 {
     u_char ruserlen, rpasswdlen;
     char *ruser, *rpasswd;
+    char rhostname[256];
     int retcode;
     char *msg;
     int msglen;
@@ -401,17 +425,35 @@
     retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
 			   rpasswdlen, &msg);
     BZERO(rpasswd, rpasswdlen);
+
+    /*
+     * Check remote number authorization.  A plugin may have filled in
+     * the remote number or added an allowed number, and rather than
+     * return an authenticate failure, is leaving it for us to verify.
+     */
+    if (retcode == UPAP_AUTHACK) {
+	if (!auth_number()) {
+	    /* We do not want to leak info about the pap result. */
+	    retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */
+	    warn("calling number %q is not authorized", remote_number);
+	}
+    }
+
     msglen = strlen(msg);
     if (msglen > 255)
 	msglen = 255;
-
     upap_sresp(u, retcode, id, msg, msglen);
 
+    /* Null terminate and clean remote name. */
+    slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser);
+
     if (retcode == UPAP_AUTHACK) {
 	u->us_serverstate = UPAPSS_OPEN;
-	auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+	notice("PAP peer authentication succeeded for %q", rhostname);
+	auth_peer_success(u->us_unit, PPP_PAP, 0, ruser, ruserlen);
     } else {
 	u->us_serverstate = UPAPSS_BADAUTH;
+	warn("PAP peer authentication failed for %q", rhostname);
 	auth_peer_fail(u->us_unit, PPP_PAP);
     }
 
@@ -456,12 +498,12 @@
 
     u->us_clientstate = UPAPCS_OPEN;
 
-    auth_withpeer_success(u->us_unit, PPP_PAP);
+    auth_withpeer_success(u->us_unit, PPP_PAP, 0);
 }
 
 
 /*
- * upap_rauthnak - Receive Authenticate-Nakk.
+ * upap_rauthnak - Receive Authenticate-Nak.
  */
 static void
 upap_rauthnak(u, inp, id, len)
@@ -497,10 +539,6 @@
     u->us_clientstate = UPAPCS_BADAUTH;
 
     error("PAP authentication failed");
-
-    //log_to_file("PAP_AUTH_FAIL");
-    system("ppp_event -t PAP_AUTH_FAIL &");
-
     auth_withpeer_fail(u->us_unit, PPP_PAP);
 }
 
diff -Naur tomato/release/src/router/pppd/pppd/upap.h tomato121/release/src/router/pppd/pppd/upap.h
--- tomato/release/src/router/pppd/pppd/upap.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/upap.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,45 @@
 /*
  * upap.h - User/Password Authentication Protocol definitions.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: upap.h,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: upap.h,v 1.8 2002/12/04 23:03:33 paulus Exp $
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/pppd/utils.c tomato121/release/src/router/pppd/pppd/utils.c
--- tomato/release/src/router/pppd/pppd/utils.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppd/utils.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,23 +1,34 @@
 /*
  * utils.c - various utility functions used in pppd.
  *
- * Copyright (c) 1999 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1999-2002 Paul Mackerras. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University.  The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 
-#define RCSID	"$Id: utils.c,v 1.6 2004/09/23 06:40:55 tallest Exp $"
+#define RCSID	"$Id: utils.c,v 1.24 2004/11/04 10:02:26 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -29,6 +40,7 @@
 #include <fcntl.h>
 #include <syslog.h>
 #include <netdb.h>
+#include <time.h>
 #include <utmp.h>
 #include <pwd.h>
 #include <sys/param.h>
@@ -44,8 +56,8 @@
 #endif
 
 #include "pppd.h"
-
-#include <sys/sysinfo.h>
+#include "fsm.h"
+#include "lcp.h"
 
 static const char rcsid[] = RCSID;
 
@@ -106,7 +118,7 @@
 /*
  * slprintf - format a message into a buffer.  Like sprintf except we
  * also specify the length of the output buffer, and we handle
- * %r (recursive format), %m (error message), %v (visible string),
+ * %m (error message), %v (visible string),
  * %q (quoted string), %t (current time) and %I (IP address) formats.
  * Doesn't do floating-point formats.
  * Returns the number of chars put into buf.
@@ -208,6 +220,28 @@
 	neg = 0;
 	++fmt;
 	switch (c) {
+	case 'l':
+	    c = *fmt++;
+	    switch (c) {
+	    case 'd':
+		val = va_arg(args, long);
+		if (val < 0) {
+		    neg = 1;
+		    val = -val;
+		}
+		base = 10;
+		break;
+	    case 'u':
+		val = va_arg(args, unsigned long);
+		base = 10;
+		break;
+	    default:
+		*buf++ = '%'; --buflen;
+		*buf++ = 'l'; --buflen;
+		--fmt;		/* so %lz outputs %lz etc. */
+		continue;
+	    }
+	    break;
 	case 'd':
 	    i = va_arg(args, int);
 	    if (i < 0) {
@@ -253,6 +287,7 @@
 		     (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__
@@ -264,6 +299,7 @@
 	    buf += n;
 	    buflen -= n;
 	    continue;
+#endif
 	case 't':
 	    time(&t);
 	    str = ctime(&t);
@@ -644,6 +680,7 @@
 /*
  * fatal - log an error message and die horribly.
  */
+/*
 void
 fatal __V((char *fmt, ...))
 {
@@ -660,12 +697,9 @@
     logit(LOG_ERR, fmt, pvar);
     va_end(pvar);
 
-    die(1);			/* as promised */
+    die(1);			
 }
 
-/*
- * error - log an error message.
- */
 void
 error __V((char *fmt, ...))
 {
@@ -681,11 +715,9 @@
 
     logit(LOG_ERR, fmt, pvar);
     va_end(pvar);
+    ++error_count;
 }
 
-/*
- * warn - log a warning message.
- */
 void
 warn __V((char *fmt, ...))
 {
@@ -702,10 +734,8 @@
     logit(LOG_WARNING, fmt, pvar);
     va_end(pvar);
 }
-
+*/
 /*
- * notice - log a notice-level message.
- */
 void
 notice __V((char *fmt, ...))
 {
@@ -722,11 +752,8 @@
     logit(LOG_NOTICE, fmt, pvar);
     va_end(pvar);
 }
-
-/*
- * info - log an informational message.
- */
-void
+*/
+/*void
 info __V((char *fmt, ...))
 {
     va_list pvar;
@@ -742,10 +769,8 @@
     logit(LOG_INFO, fmt, pvar);
     va_end(pvar);
 }
-
+*/
 /*
- * dbglog - log a debug message.
- */
 void
 dbglog __V((char *fmt, ...))
 {
@@ -762,10 +787,66 @@
     logit(LOG_DEBUG, fmt, pvar);
     va_end(pvar);
 }
+*/
+/*
+ * dump_packet - print out a packet in readable form if it is interesting.
+ * Assumes len >= PPP_HDRLEN.
+ */
+void
+dump_packet(const char *tag, unsigned char *p, int len)
+{
+    int proto;
+
+    if (!debug)
+	return;
+
+    /*
+     * don't print LCP echo request/reply packets if debug <= 1
+     * and the link is up.
+     */
+    proto = (p[2] << 8) + p[3];
+    if (debug <= 1 && unsuccess == 0 && proto == PPP_LCP
+	&& len >= PPP_HDRLEN + HEADERLEN) {
+	unsigned char *lcp = p + PPP_HDRLEN;
+	int l = (lcp[2] << 8) + lcp[3];
+
+	if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
+	    && l >= HEADERLEN && l <= len - PPP_HDRLEN)
+	    return;
+    }
+
+    dbglog("%s %P", tag, p, len);
+}
+
+/*
+ * complete_read - read a full `count' bytes from fd,
+ * unless end-of-file or an error other than EINTR is encountered.
+ */
+ssize_t
+complete_read(int fd, void *buf, size_t count)
+{
+	size_t done;
+	ssize_t nb;
+	char *ptr = buf;
+
+	for (done = 0; done < count; ) {
+		nb = read(fd, ptr, count - done);
+		if (nb < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		if (nb == 0)
+			break;
+		done += nb;
+		ptr += nb;
+	}
+	return done;
+}
 
 /* Procedures for locking the serial device using a lock file. */
 #ifndef LOCK_DIR
-#ifdef _linux_
+#ifdef __linux__
 #define LOCK_DIR	"/var/lock"
 #else
 #ifdef SVR4
@@ -790,7 +871,7 @@
 
     result = mklock (dev, (void *) 0);
     if (result == 0) {
-	strlcpy(lock_file, sizeof(lock_file), dev);
+	strlcpy(lock_file, dev, sizeof(lock_file));
 	return 0;
     }
 
@@ -821,9 +902,20 @@
 	     major(sbuf.st_rdev), minor(sbuf.st_rdev));
 #else
     char *p;
+    char lockdev[MAXPATHLEN];
+
+    if ((p = strstr(dev, "dev/")) != NULL) {
+	dev = p + 4;
+	strncpy(lockdev, dev, MAXPATHLEN-1);
+	lockdev[MAXPATHLEN-1] = 0;
+	while ((p = strrchr(lockdev, '/')) != NULL) {
+	    *p = '_';
+	}
+	dev = lockdev;
+    } else
+	if ((p = strrchr(dev, '/')) != NULL)
+	    dev = p + 1;
 
-    if ((p = strrchr(dev, '/')) != NULL)
-	dev = p + 1;
     slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
 #endif
 
@@ -948,46 +1040,3 @@
     }
 }
 
-//===================================================
-#include <fcntl.h>
-#define GOT_IP                  0x01
-#define RELEASE_IP              0x02
-#define GET_IP_ERROR            0x03
-#define RELEASE_WAN_CONTROL     0x04
-#define SET_LED(val) \
-{ \
-        int filep; \
-        if ((filep = open("/dev/extio", O_RDWR,0))) \
-        { \
-                ioctl(filep, val, 0); \
-                close(filep); \
-        } \
-}
-//==================================================
-
-int
-log_to_file(char *buf)	// add by honor
-{	
-	FILE *fp;
-	
-	if ((fp = fopen("/tmp/ppp/log", "w"))) {
-		fprintf(fp, "%s", buf);
-		fclose(fp);
-		SET_LED(GET_IP_ERROR)
-		return 1;
-	}	
-	return 0;
-}
-
-int
-my_gettimeofday(struct timeval *timenow, struct timezone *tz)
-{
-	struct sysinfo info;
-
-        sysinfo(&info);
-
-	timenow->tv_sec = info.uptime;
-	timenow->tv_usec = 0;
-
-	return 0;
-}
diff -Naur tomato/release/src/router/pppd/pppdump/bsd-comp.c tomato121/release/src/router/pppd/pppdump/bsd-comp.c
--- tomato/release/src/router/pppd/pppdump/bsd-comp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/bsd-comp.c	2008-04-22 21:44:05.000000000 +1000
@@ -38,12 +38,14 @@
  */
 
 /*
- * $Id: bsd-comp.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * $Id: bsd-comp.c,v 1.4 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 #include <sys/types.h>
+#include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
+#include <string.h>
 #include "ppp_defs.h"
 #include "ppp-comp.h"
 
diff -Naur tomato/release/src/router/pppd/pppdump/deflate.c tomato121/release/src/router/pppd/pppdump/deflate.c
--- tomato/release/src/router/pppd/pppdump/deflate.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/deflate.c	2008-04-22 21:44:05.000000000 +1000
@@ -2,35 +2,45 @@
  * ppp_deflate.c - interface the zlib procedures for Deflate compression
  * and decompression (as used by gzip) to the PPP code.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: deflate.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: deflate.c,v 1.5 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 #include <sys/types.h>
+#include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
+#include <string.h>
 #include "ppp_defs.h"
 #include "ppp-comp.h"
 #include "zlib.h"
diff -Naur tomato/release/src/router/pppd/pppdump/Makefile tomato121/release/src/router/pppd/pppdump/Makefile
--- tomato/release/src/router/pppd/pppdump/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,22 @@
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+COPTS = -O
+CFLAGS= $(COPTS) -I../include/net
+OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+
+INSTALL= install
+
+all:	pppdump
+
+pppdump: $(OBJS)
+	$(CC) -o pppdump $(OBJS)
+
+clean:
+	rm -f pppdump $(OBJS) *~
+
+install:
+	mkdir -p $(BINDIR) $(MANDIR)
+	$(INSTALL) -c pppdump $(BINDIR)
+	$(INSTALL) -c -m 444 pppdump.8 $(MANDIR)
diff -Naur tomato/release/src/router/pppd/pppdump/Makefile.linux tomato121/release/src/router/pppd/pppdump/Makefile.linux
--- tomato/release/src/router/pppd/pppdump/Makefile.linux	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -1,4 +1,9 @@
-CFLAGS= -O -I../include/net
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+COPTS = -O
+CFLAGS= $(COPTS) -I../include/net
 OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
 
 INSTALL= install
@@ -12,6 +17,6 @@
 	rm -f pppdump $(OBJS) *~
 
 install:
-	mkdir -p $(BINDIR) $(MANDIR)/man8
-	$(INSTALL) -s -c pppdump $(BINDIR)
-	$(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8
+	mkdir -p $(BINDIR) $(MANDIR)
+	$(INSTALL) -c pppdump $(BINDIR)
+	$(INSTALL) -c -m 444 pppdump.8 $(MANDIR)
diff -Naur tomato/release/src/router/pppd/pppdump/Makefile.sol2 tomato121/release/src/router/pppd/pppdump/Makefile.sol2
--- tomato/release/src/router/pppd/pppdump/Makefile.sol2	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/Makefile.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -1,9 +1,9 @@
 #
 # pppdump Makefile for SVR4 systems
-# $Id: Makefile.sol2,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makefile.sol2,v 1.4 2002/09/07 05:15:25 carlsonj Exp $
 #
 
-include ../solaris/Makedefs
+include ../Makedefs.com
 
 CFLAGS= $(COPTS) -I../include/net
 OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
diff -Naur tomato/release/src/router/pppd/pppdump/Makefile.sunos4 tomato121/release/src/router/pppd/pppdump/Makefile.sunos4
--- tomato/release/src/router/pppd/pppdump/Makefile.sunos4	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/Makefile.sunos4	1970-01-01 10:00:00.000000000 +1000
@@ -1,21 +0,0 @@
-#
-# pppstats makefile
-# $Id: Makefile.sunos4,v 1.1 2003/07/10 07:43:05 honor Exp $
-#
-
-include ../sunos4/Makedefs
-
-OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
-CFLAGS = $(COPTS) -I../include/net
-
-all:	pppdump
-
-pppdump: $(OBJS)
-	$(CC) -o pppdump $(OBJS)
-
-clean:
-	rm -f pppdump $(OBJS) *~
-
-install: pppdump
-	$(INSTALL) -c pppdump $(BINDIR)/pppdump
-	$(INSTALL) -c -m 444 pppdump.8 $(MANDIR)/man8/pppdump.8
diff -Naur tomato/release/src/router/pppd/pppdump/ppp-comp.h tomato121/release/src/router/pppd/pppdump/ppp-comp.h
--- tomato/release/src/router/pppd/pppdump/ppp-comp.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/ppp-comp.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp-comp.h - Definitions for doing PPP packet compression.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ppp-comp.h,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: ppp-comp.h,v 1.2 2002/12/06 09:49:16 paulus Exp $
  */
 
 #ifndef _NET_PPP_COMP_H
diff -Naur tomato/release/src/router/pppd/pppdump/pppdump.8 tomato121/release/src/router/pppd/pppdump/pppdump.8
--- tomato/release/src/router/pppd/pppdump/pppdump.8	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/pppdump.8	2008-04-22 21:44:05.000000000 +1000
@@ -1,19 +1,19 @@
-.\"	@(#) $Id: pppdump.8,v 1.1 2003/07/10 07:43:05 honor Exp $
+.\"	@(#) $Id: pppdump.8,v 1.2 2004/11/13 12:22:49 paulus Exp $
 .TH PPPDUMP 8 "1 April 1999"
 .SH NAME
 pppdump \- convert PPP record file to readable format
 .SH SYNOPSIS
 .B pppdump
 [
-.B -h
+.B \-h
 |
-.B -p
+.B \-p
 [
-.B -d
+.B \-d
 ]] [
-.B -r
+.B \-r
 ] [
-.B -m \fImru
+.B \-m \fImru
 ] [
 .I file \fR...
 ]
@@ -30,33 +30,33 @@
 .PP
 The options are as follows:
 .TP
-.B -h
+.B \-h
 Prints the bytes sent and received in hexadecimal.  If neither this
-option nor the \fB-p\fR option is specified, the bytes are printed as
+option nor the \fB\-p\fR option is specified, the bytes are printed as
 the characters themselves, with non-printing and non-ASCII characters
 printed as escape sequences.
 .TP
-.B -p
+.B \-p
 Collects the bytes sent and received into PPP packets, interpreting
 the async HDLC framing and escape characters and checking the FCS
 (frame check sequence) of each packet.  The packets are printed as hex
 values and as characters (non-printable characters are printed as
 `.').
 .TP
-.B -d
-With the \fB-p\fR option, this option causes
+.B \-d
+With the \fB\-p\fR option, this option causes
 .B pppdump
 to decompress packets which have been compressed with the BSD-Compress
 or Deflate methods.
 .TP
-.B -r
+.B \-r
 Reverses the direction indicators, so that `sent' is printed for
 bytes or packets received, and `rcvd' is printed for bytes or packets
 sent.
 .TP
-.B -m \fImru
+.B \-m \fImru
 Use \fImru\fR as the MRU (maximum receive unit) for both directions of
-the link when checking for over-length PPP packets (with the \fB-p\fR
+the link when checking for over-length PPP packets (with the \fB\-p\fR
 option).
 .SH SEE ALSO
 pppd(8)
diff -Naur tomato/release/src/router/pppd/pppdump/pppdump.c tomato121/release/src/router/pppd/pppdump/pppdump.c
--- tomato/release/src/router/pppd/pppdump/pppdump.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/pppdump.c	2008-04-22 21:44:05.000000000 +1000
@@ -2,19 +2,40 @@
  * pppdump - print out the contents of a record file generated by
  * pppd in readable form.
  *
- * Copyright (C) 1999  Paul Mackerras.  All rights reserved.
+ * Copyright (c) 1999 Paul Mackerras. All rights reserved.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms.  The name of the author
- * may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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.
  */
 #include <stdio.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include <time.h>
 #include <sys/types.h>
 #include "ppp_defs.h"
@@ -33,6 +54,12 @@
 extern int optind;
 extern char *optarg;
 
+void dumplog();
+void dumpppp();
+void show_time();
+void handle_ccp();
+
+int
 main(ac, av)
     int ac;
     char **av;
@@ -85,6 +112,7 @@
     exit(0);
 }
 
+void
 dumplog(f)
     FILE *f;
 {
@@ -224,6 +252,7 @@
 
 unsigned char dbuf[8192];
 
+void
 dumpppp(f)
     FILE *f;
 {
@@ -409,6 +438,7 @@
     NULL
 };
 
+void
 handle_ccp(cp, dp, len)
     struct pkt *cp;
     u_char *dp;
@@ -465,6 +495,7 @@
     }
 }
 
+void
 show_time(f, c)
     FILE *f;
     int c;
diff -Naur tomato/release/src/router/pppd/pppdump/zlib.c tomato121/release/src/router/pppd/pppdump/zlib.c
--- tomato/release/src/router/pppd/pppdump/zlib.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/zlib.c	2008-04-22 21:44:05.000000000 +1000
@@ -11,7 +11,7 @@
  * - added Z_PACKET_FLUSH (see zlib.h for details)
  * - added inflateIncomp
  *
- * $Id: zlib.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * $Id: zlib.c,v 1.2 1999/04/01 07:26:30 paulus Exp $
  */
 
 
diff -Naur tomato/release/src/router/pppd/pppdump/zlib.h tomato121/release/src/router/pppd/pppdump/zlib.h
--- tomato/release/src/router/pppd/pppdump/zlib.h	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppdump/zlib.h	2008-04-22 21:44:05.000000000 +1000
@@ -1,4 +1,4 @@
-/*	$Id: zlib.h,v 1.1 2003/07/10 07:43:05 honor Exp $	*/
+/*	$Id: zlib.h,v 1.1 1999/03/23 03:21:58 paulus Exp $	*/
 
 /*
  * This file is derived from zlib.h and zconf.h from the zlib-0.95
diff -Naur tomato/release/src/router/pppd/pppnew.patch tomato121/release/src/router/pppd/pppnew.patch
--- tomato/release/src/router/pppd/pppnew.patch	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppnew.patch	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,761 @@
+diff -urN 1/README 2/README
+--- 1/README	2005-08-26 14:56:53.000000000 +0200
++++ 2/README	2006-05-30 01:51:29.000000000 +0200
+@@ -66,6 +66,10 @@
+ * Lots of bugs fixed, particularly in the area of demand-dialled and
+   persistent connections.
+ 
++* The rp-pppoe plugin now accepts any interface name (that isn't an
++  existing pppd option name) without putting "nic-" on the front of
++  it, not just eth*, nas*, tap* and br*.
++
+ 
+ What was new in ppp-2.4.3.
+ **************************
+@@ -272,4 +276,4 @@
+ 	ftp://ftp.samba.org/pub/ppp/
+ 
+ 
+-($Id: README,v 1.36 2005/08/26 12:56:53 paulus Exp $)
++($Id: README,v 1.37 2006/05/29 23:51:29 paulus Exp $)
+diff -urN 1/chat/Makefile.linux 2/chat/Makefile.linux
+--- 1/chat/Makefile.linux	2004-11-03 12:51:47.000000000 +0100
++++ 2/chat/Makefile.linux	2006-06-04 07:07:46.000000000 +0200
+@@ -1,6 +1,6 @@
+-#	$Id: Makefile.linux,v 1.13 2004/11/03 11:51:47 paulus Exp $
++#	$Id: Makefile.linux,v 1.15 2006/06/04 05:07:46 paulus Exp $
+ 
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ 
+@@ -24,7 +24,7 @@
+ 	$(CC) -c $(CFLAGS) -o chat.o chat.c
+ 
+ install: chat
+-	mkdir -p $(BINDIR)
++	mkdir -p $(BINDIR) $(MANDIR)
+ 	$(INSTALL) -s -c chat $(BINDIR)
+ 	$(INSTALL) -c -m 644 chat.8 $(MANDIR)
+ 
+diff -urN 1/linux/Makefile.top 2/linux/Makefile.top
+--- 1/linux/Makefile.top	2004-10-31 23:25:16.000000000 +0100
++++ 2/linux/Makefile.top	2006-06-04 07:04:14.000000000 +0200
+@@ -1,10 +1,10 @@
+ # PPP top-level Makefile for Linux.
+ 
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ INCDIR = $(DESTDIR)/include
+ MANDIR = $(DESTDIR)/share/man
+-ETCDIR = @SYSCONF@/ppp
++ETCDIR = $(INSTROOT)@SYSCONF@/ppp
+ 
+ # uid 0 = root
+ INSTALL= install
+diff -urN 1/pppd/Makefile.linux 2/pppd/Makefile.linux
+--- 1/pppd/Makefile.linux	2005-07-12 03:50:23.000000000 +0200
++++ 2/pppd/Makefile.linux	2006-06-04 07:07:46.000000000 +0200
+@@ -1,10 +1,10 @@
+ #
+ # pppd makefile for Linux
+-# $Id: Makefile.linux,v 1.68 2005/07/12 01:50:23 paulus Exp $
++# $Id: Makefile.linux,v 1.69 2006/06/04 05:07:46 paulus Exp $
+ #
+ 
+ # Default installation locations
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ INCDIR = $(DESTDIR)/include
+diff -urN 1/pppd/auth.c 2/pppd/auth.c
+--- 1/pppd/auth.c	2005-08-28 07:22:48.000000000 +0200
++++ 2/pppd/auth.c	2006-06-18 13:26:00.000000000 +0200
+@@ -68,7 +68,7 @@
+  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ 
+-#define RCSID	"$Id: auth.c,v 1.108 2005/08/28 05:22:48 paulus Exp $"
++#define RCSID	"$Id: auth.c,v 1.112 2006/06/18 11:26:00 paulus Exp $"
+ 
+ #include <stdio.h>
+ #include <stddef.h>
+@@ -409,6 +409,7 @@
+ {
+     FILE *ufile;
+     int l;
++    uid_t euid;
+     char u[MAXNAMELEN], p[MAXSECRETLEN];
+     char *fname;
+ 
+@@ -418,9 +419,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;
+@@ -746,8 +752,8 @@
+ 	    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;
+ 	}
+     }
+@@ -906,8 +912,8 @@
+     /*
+      * Authentication failure: take the link down
+      */
+-    lcp_close(unit, "Authentication failed");
+     status = EXIT_PEER_AUTH_FAILED;
++    lcp_close(unit, "Authentication failed");
+ }
+ 
+ /*
+@@ -984,8 +990,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");
+ }
+ 
+ /*
+@@ -1149,9 +1155,9 @@
+     diff = maxoctets - used;
+     if(diff < 0) {
+ 	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);
+     }
+@@ -1181,9 +1187,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);
+     }
+diff -urN 1/pppd/cbcp.c 2/pppd/cbcp.c
+--- 1/pppd/cbcp.c	2004-10-28 02:15:36.000000000 +0200
++++ 2/pppd/cbcp.c	2006-05-22 02:04:07.000000000 +0200
+@@ -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 -urN 1/pppd/chap_ms.c 2/pppd/chap_ms.c
+--- 1/pppd/chap_ms.c	2004-11-15 23:13:26.000000000 +0100
++++ 2/pppd/chap_ms.c	2006-05-21 13:56:40.000000000 +0200
+@@ -74,7 +74,7 @@
+  *
+  */
+ 
+-#define RCSID	"$Id: chap_ms.c,v 1.34 2004/11/15 22:13:26 paulus Exp $"
++#define RCSID	"$Id: chap_ms.c,v 1.36 2006/05/21 11:56:40 paulus Exp $"
+ 
+ #ifdef CHAPMS
+ 
+@@ -98,7 +98,7 @@
+ 
+ 
+ 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((u_char *, u_char[16], char *, char *, int,
+@@ -390,7 +390,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. */
+ 
+@@ -507,7 +507,7 @@
+ }
+ 
+ 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 __NetBSD__
+     /* NetBSD uses the libc md4 routines which take bytes instead of bits */
+@@ -518,7 +518,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);
+ 
+ }
+@@ -532,7 +538,7 @@
+ 
+     /* 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);
+ }
+@@ -549,7 +555,7 @@
+ 
+     /* 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);
+ }
+@@ -637,8 +643,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,
+diff -urN 1/pppd/lcp.c 2/pppd/lcp.c
+--- 1/pppd/lcp.c	2004-11-15 09:37:30.000000000 +0100
++++ 2/pppd/lcp.c	2006-05-22 02:04:07.000000000 +0200
+@@ -40,7 +40,7 @@
+  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ 
+-#define RCSID	"$Id: lcp.c,v 1.75 2004/11/14 22:53:42 carlsonj Exp $"
++#define RCSID	"$Id: lcp.c,v 1.76 2006/05/22 00:04:07 paulus Exp $"
+ 
+ /*
+  * TODO:
+@@ -1305,8 +1305,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;
+@@ -2233,8 +2233,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 -urN 1/pppd/main.c 2/pppd/main.c
+--- 1/pppd/main.c	2005-08-26 01:59:34.000000000 +0200
++++ 2/pppd/main.c	2006-06-04 05:52:50.000000000 +0200
+@@ -66,7 +66,7 @@
+  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ 
+-#define RCSID	"$Id: main.c,v 1.152 2005/08/25 23:59:34 paulus Exp $"
++#define RCSID	"$Id: main.c,v 1.153 2006/06/04 03:52:50 paulus Exp $"
+ 
+ #include <stdio.h>
+ #include <ctype.h>
+@@ -242,6 +242,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 *));
+ 
+@@ -1711,7 +1712,7 @@
+ 		    continue;
+ 		fatal("error waiting for script %s: %m", prog);
+ 	    }
+-	    reap_kids();
++	    forget_child(pid, status);
+ 	}
+ 	return pid;
+     }
+@@ -1789,6 +1790,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.
+  */
+@@ -1796,29 +1826,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)
+diff -urN 1/pppd/md4.c 2/pppd/md4.c
+--- 1/pppd/md4.c	1998-11-07 07:55:41.000000000 +0100
++++ 2/pppd/md4.c	2006-05-21 08:43:44.000000000 +0200
+@@ -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 -urN 1/pppd/options.c 2/pppd/options.c
+--- 1/pppd/options.c	2005-07-13 14:31:36.000000000 +0200
++++ 2/pppd/options.c	2006-06-18 13:26:00.000000000 +0200
+@@ -40,7 +40,7 @@
+  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ 
+-#define RCSID	"$Id: options.c,v 1.98 2005/07/13 12:31:36 paulus Exp $"
++#define RCSID	"$Id: options.c,v 1.100 2006/06/18 11:26:00 paulus Exp $"
+ 
+ #include <ctype.h>
+ #include <stdio.h>
+@@ -399,16 +399,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) {
+@@ -1510,15 +1514,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);
+diff -urN 1/pppd/patchlevel.h 2/pppd/patchlevel.h
+--- 1/pppd/patchlevel.h	2005-08-28 09:10:06.000000000 +0200
++++ 2/pppd/patchlevel.h	2006-06-28 02:21:23.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: patchlevel.h,v 1.64 2005/08/28 07:10:06 paulus Exp $ */
++/* $Id: patchlevel.h,v 1.66 2006/06/28 00:21:23 paulus Exp $ */
+ 
+-#define VERSION		"2.4.4b1"
+-#define DATE		"28 August 2005"
++#define VERSION		"2.4.4"
++#define DATE		"28 June 2006"
+diff -urN 1/pppd/plugins/Makefile.linux 2/pppd/plugins/Makefile.linux
+--- 1/pppd/plugins/Makefile.linux	2005-08-29 00:49:20.000000000 +0200
++++ 2/pppd/plugins/Makefile.linux	2006-06-04 07:04:14.000000000 +0200
+@@ -4,7 +4,7 @@
+ LDFLAGS	= -shared
+ INSTALL	= install
+ 
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+diff -urN 1/pppd/plugins/passprompt.c 2/pppd/plugins/passprompt.c
+--- 1/pppd/plugins/passprompt.c	2003-05-01 09:22:07.000000000 +0200
++++ 2/pppd/plugins/passprompt.c	2006-06-16 01:57:02.000000000 +0200
+@@ -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]);
+diff -urN 1/pppd/plugins/pppoatm/pppoatm.c 2/pppd/plugins/pppoatm/pppoatm.c
+--- 1/pppd/plugins/pppoatm/pppoatm.c	2004-11-06 07:55:28.000000000 +0100
++++ 2/pppd/plugins/pppoatm/pppoatm.c	2006-05-21 14:44:41.000000000 +0200
+@@ -133,7 +133,7 @@
+ 	int fd;
+ 	struct atm_qos qos;
+ 
+-	system ("/sbin/modprobe pppoatm");
++	system ("/sbin/modprobe -q pppoatm");
+ 
+ 	if (!device_got_set)
+ 		no_device_given_pppoatm();
+diff -urN 1/pppd/plugins/radius/Makefile.linux 2/pppd/plugins/radius/Makefile.linux
+--- 1/pppd/plugins/radius/Makefile.linux	2004-11-14 08:02:31.000000000 +0100
++++ 2/pppd/plugins/radius/Makefile.linux	2006-06-04 07:04:14.000000000 +0200
+@@ -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 -urN 1/pppd/plugins/radius/radius.c 2/pppd/plugins/radius/radius.c
+--- 1/pppd/plugins/radius/radius.c	2005-07-10 12:28:55.000000000 +0200
++++ 2/pppd/plugins/radius/radius.c	2006-05-22 02:01:40.000000000 +0200
+@@ -24,7 +24,7 @@
+ *
+ ***********************************************************************/
+ static char const RCSID[] =
+-"$Id: radius.c,v 1.30 2005/07/10 10:28:55 paulus Exp $";
++"$Id: radius.c,v 1.31 2006/05/22 00:01:40 paulus Exp $";
+ 
+ #include "pppd.h"
+ #include "chap-new.h"
+@@ -1019,6 +1019,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 -urN 1/pppd/plugins/rp-pppoe/Makefile.linux 2/pppd/plugins/rp-pppoe/Makefile.linux
+--- 1/pppd/plugins/rp-pppoe/Makefile.linux	2004-11-14 08:58:37.000000000 +0100
++++ 2/pppd/plugins/rp-pppoe/Makefile.linux	2006-06-04 07:07:46.000000000 +0200
+@@ -10,10 +10,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: Makefile.linux,v 1.6 2004/11/14 07:58:37 paulus Exp $
++# $Id: Makefile.linux,v 1.7 2006/06/04 05:07:46 paulus Exp $
+ #***********************************************************************
+ 
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION)
+ 
+diff -urN 1/pppd/plugins/rp-pppoe/plugin.c 2/pppd/plugins/rp-pppoe/plugin.c
+--- 1/pppd/plugins/rp-pppoe/plugin.c	2005-08-25 12:51:27.000000000 +0200
++++ 2/pppd/plugins/rp-pppoe/plugin.c	2006-05-30 01:29:16.000000000 +0200
+@@ -22,7 +22,7 @@
+ ***********************************************************************/
+ 
+ static char const RCSID[] =
+-"$Id: plugin.c,v 1.14 2005/08/25 10:51:27 paulus Exp $";
++"$Id: plugin.c,v 1.15 2006/05/29 23:29:16 paulus Exp $";
+ 
+ #define _GNU_SOURCE 1
+ #include "pppoe.h"
+@@ -253,16 +253,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 */
+@@ -280,8 +278,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;
+ 		}
+ 	    }
+ 	}
+@@ -289,7 +288,7 @@
+ 
+     /* Close socket */
+     close(fd);
+-    if (r) {
++    if (r && doit) {
+ 	strncpy(devnam, cmd, sizeof(devnam));
+ 	if (the_channel != &pppoe_channel) {
+ 
+diff -urN 1/pppd/plugins/winbind.c 2/pppd/plugins/winbind.c
+--- 1/pppd/plugins/winbind.c	2005-07-12 09:34:43.000000000 +0200
++++ 2/pppd/plugins/winbind.c	2006-06-04 08:15:32.000000000 +0200
+@@ -296,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 */
+diff -urN 1/pppd/pppd.8 2/pppd/pppd.8
+--- 1/pppd/pppd.8	2005-08-28 07:21:24.000000000 +0200
++++ 2/pppd/pppd.8	2006-06-16 02:01:23.000000000 +0200
+@@ -1,5 +1,5 @@
+ .\" manual page [] for pppd 2.4
+-.\" $Id: pppd.8,v 1.87 2005/08/28 05:21:24 paulus Exp $
++.\" $Id: pppd.8,v 1.88 2006/06/16 00:01:23 paulus Exp $
+ .\" SH section heading
+ .\" SS subsection heading
+ .\" LP paragraph
+@@ -1791,7 +1791,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
+@@ -1800,6 +1803,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.
+diff -urN 1/pppd/sha1.c 2/pppd/sha1.c
+--- 1/pppd/sha1.c	2004-10-25 01:28:02.000000000 +0200
++++ 2/pppd/sha1.c	2006-05-21 08:59:24.000000000 +0200
+@@ -121,18 +121,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 -urN 1/pppd/tty.c 2/pppd/tty.c
+--- 1/pppd/tty.c	2005-07-12 03:09:05.000000000 +0200
++++ 2/pppd/tty.c	2006-06-04 09:04:57.000000000 +0200
+@@ -68,7 +68,7 @@
+  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  */
+ 
+-#define RCSID	"$Id: tty.c,v 1.24 2005/07/12 01:09:05 paulus Exp $"
++#define RCSID	"$Id: tty.c,v 1.25 2006/06/04 07:04:57 paulus Exp $"
+ 
+ #include <stdio.h>
+ #include <ctype.h>
+@@ -563,12 +563,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;
+diff -urN 1/pppdump/Makefile.linux 2/pppdump/Makefile.linux
+--- 1/pppdump/Makefile.linux	2004-10-31 02:36:52.000000000 +0200
++++ 2/pppdump/Makefile.linux	2006-06-04 07:04:14.000000000 +0200
+@@ -1,4 +1,4 @@
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ 
+diff -urN 1/pppstats/Makefile.linux 2/pppstats/Makefile.linux
+--- 1/pppstats/Makefile.linux	2004-10-31 23:09:03.000000000 +0100
++++ 2/pppstats/Makefile.linux	2006-06-04 07:07:46.000000000 +0200
+@@ -1,8 +1,8 @@
+ #
+ # pppstats makefile
+-# $Id: Makefile.linux,v 1.8 2004/10/31 22:09:03 paulus Exp $
++# $Id: Makefile.linux,v 1.9 2006/06/04 05:07:46 paulus Exp $
+ #
+-DESTDIR = @DESTDIR@
++DESTDIR = $(INSTROOT)@DESTDIR@
+ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ 
diff -Naur tomato/release/src/router/pppd/ppp.patch tomato121/release/src/router/pppd/ppp.patch
--- tomato/release/src/router/pppd/ppp.patch	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/ppp.patch	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,1960 @@
+diff -urN ppp-2.4.4-orig/chat/Makefile.linux ppp-2.4.4/chat/Makefile.linux
+--- ppp-2.4.4-orig/chat/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/chat/Makefile.linux	2006-06-09 05:02:59.000000000 -0400
+@@ -24,8 +24,8 @@
+ 	$(CC) -c $(CFLAGS) -o chat.o chat.c
+ 
+ install: chat
+-	mkdir -p $(BINDIR) $(MANDIR)
+-	$(INSTALL) -s -c chat $(BINDIR)
++	mkdir -p $(BINDIR)
++	$(INSTALL) -c chat $(BINDIR)
+ 	$(INSTALL) -c -m 644 chat.8 $(MANDIR)
+ 
+ clean:
+diff -urN ppp-2.4.4-orig/include/linux/ppp-comp.h ppp-2.4.4/include/linux/ppp-comp.h
+--- ppp-2.4.4-orig/include/linux/ppp-comp.h	2002-12-06 04:50:10.000000000 -0500
++++ ppp-2.4.4/include/linux/ppp-comp.h	2006-06-09 05:06:57.000000000 -0400
+@@ -36,7 +36,7 @@
+  */
+ 
+ /*
+- *  ==FILEVERSION 20020319==
++ *  ==FILEVERSION 20020715==
+  *
+  *  NOTE TO MAINTAINERS:
+  *     If you modify this file at all, please set the above date.
+@@ -56,10 +56,10 @@
+  */
+ 
+ #ifndef DO_BSD_COMPRESS
+-#define DO_BSD_COMPRESS	1	/* by default, include BSD-Compress */
++#define DO_BSD_COMPRESS	0	/* by default, don't include BSD-Compress */
+ #endif
+ #ifndef DO_DEFLATE
+-#define DO_DEFLATE	1	/* by default, include Deflate */
++#define DO_DEFLATE	0	/* by default, don't include Deflate */
+ #endif
+ #define DO_PREDICTOR_1	0
+ #define DO_PREDICTOR_2	0
+@@ -288,6 +288,33 @@
+ 	    opts |= MPPE_OPT_UNKNOWN;		\
+     } while (/* CONSTCOND */ 0)
+ 
++/* MPPE/MPPC definitions by J.D.*/
++#define MPPE_STATELESS          MPPE_H_BIT	/* configuration bit H */
++#define MPPE_40BIT              MPPE_L_BIT	/* configuration bit L */
++#define MPPE_56BIT              MPPE_M_BIT	/* configuration bit M */
++#define MPPE_128BIT             MPPE_S_BIT	/* configuration bit S */
++#define MPPE_MPPC               MPPE_C_BIT	/* configuration bit C */
++
++/*
++ * Definitions for Stac LZS.
++ */
++
++#define CI_LZS			17	/* config option for Stac LZS */
++#define CILEN_LZS		5	/* length of config option */
++
++#define LZS_OVHD		4	/* max. LZS overhead */
++#define LZS_HIST_LEN		2048	/* LZS history size */
++#define LZS_MAX_CCOUNT		0x0FFF	/* max. coherency counter value */
++
++#define LZS_MODE_NONE		0
++#define LZS_MODE_LCB		1
++#define LZS_MODE_CRC		2
++#define LZS_MODE_SEQ		3
++#define LZS_MODE_EXT		4
++
++#define LZS_EXT_BIT_FLUSHED	0x80	/* bit A */
++#define LZS_EXT_BIT_COMP	0x20	/* bit C */
++
+ /*
+  * Definitions for other, as yet unsupported, compression methods.
+  */
+diff -urN ppp-2.4.4-orig/include/net/ppp-comp.h ppp-2.4.4/include/net/ppp-comp.h
+--- ppp-2.4.4-orig/include/net/ppp-comp.h	2002-12-06 04:50:10.000000000 -0500
++++ ppp-2.4.4/include/net/ppp-comp.h	2006-06-09 05:08:42.000000000 -0400
+@@ -43,10 +43,10 @@
+  * various compression methods.
+  */
+ #ifndef DO_BSD_COMPRESS
+-#define DO_BSD_COMPRESS	1	/* by default, include BSD-Compress */
++#define DO_BSD_COMPRESS	0	/* by default, don't include BSD-Compress */
+ #endif
+ #ifndef DO_DEFLATE
+-#define DO_DEFLATE	1	/* by default, include Deflate */
++#define DO_DEFLATE	0	/* by default, don't include Deflate */
+ #endif
+ #define DO_PREDICTOR_1	0
+ #define DO_PREDICTOR_2	0
+@@ -255,6 +255,33 @@
+ 	    opts |= MPPE_OPT_UNKNOWN;		\
+     } while (/* CONSTCOND */ 0)
+ 
++/* MPPE/MPPC definitions by J.D.*/
++#define MPPE_STATELESS          MPPE_H_BIT	/* configuration bit H */
++#define MPPE_40BIT              MPPE_L_BIT	/* configuration bit L */
++#define MPPE_56BIT              MPPE_M_BIT	/* configuration bit M */
++#define MPPE_128BIT             MPPE_S_BIT	/* configuration bit S */
++#define MPPE_MPPC               MPPE_C_BIT	/* configuration bit C */
++
++/*
++ * Definitions for Stac LZS.
++ */
++
++#define CI_LZS			17	/* config option for Stac LZS */
++#define CILEN_LZS		5	/* length of config option */
++
++#define LZS_OVHD		4	/* max. LZS overhead */
++#define LZS_HIST_LEN		2048	/* LZS history size */
++#define LZS_MAX_CCOUNT		0x0FFF	/* max. coherency counter value */
++
++#define LZS_MODE_NONE		0
++#define LZS_MODE_LCB		1
++#define LZS_MODE_CRC		2
++#define LZS_MODE_SEQ		3
++#define LZS_MODE_EXT		4
++
++#define LZS_EXT_BIT_FLUSHED	0x80	/* bit A */
++#define LZS_EXT_BIT_COMP	0x20	/* bit C */
++
+ /*
+  * Definitions for other, as yet unsupported, compression methods.
+  */
+diff -urN ppp-2.4.4-orig/pppd/ccp.c ppp-2.4.4/pppd/ccp.c
+--- ppp-2.4.4-orig/pppd/ccp.c	2005-06-26 15:35:09.000000000 -0400
++++ ppp-2.4.4/pppd/ccp.c	2006-06-09 05:47:55.000000000 -0400
+@@ -66,8 +66,9 @@
+  * Option variables.
+  */
+ #ifdef MPPE
+-bool refuse_mppe_stateful = 1;		/* Allow stateful mode? */
+-#endif
++static int setmppe(char **);
++static int setnomppe(void);
++#endif /* MPPE */
+ 
+ static option_t ccp_option_list[] = {
+     { "noccp", o_bool, &ccp_protent.enabled_flag,
+@@ -108,54 +109,36 @@
+       "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+       &ccp_allowoptions[0].predictor_1 },
+ 
++    { "lzs", o_bool, &ccp_wantoptions[0].lzs,
++      "request Stac LZS", 1, &ccp_allowoptions[0].lzs, OPT_PRIO },
++    { "+lzs", o_bool, &ccp_wantoptions[0].lzs,
++      "request Stac LZS", 1, &ccp_allowoptions[0].lzs, OPT_ALIAS | OPT_PRIO },
++    { "nolzs", o_bool, &ccp_wantoptions[0].lzs,
++      "don't allow Stac LZS", OPT_PRIOSUB | OPT_A2CLR,
++      &ccp_allowoptions[0].lzs },
++    { "-lzs", o_bool, &ccp_wantoptions[0].lzs,
++      "don't allow Stac LZS", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
++      &ccp_allowoptions[0].lzs },
++
+ #ifdef MPPE
+-    /* MPPE options are symmetrical ... we only set wantoptions here */
+-    { "require-mppe", o_bool, &ccp_wantoptions[0].mppe,
+-      "require MPPE encryption",
+-      OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+-    { "+mppe", o_bool, &ccp_wantoptions[0].mppe,
+-      "require MPPE encryption",
+-      OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+-    { "nomppe", o_bool, &ccp_wantoptions[0].mppe,
+-      "don't allow MPPE encryption", OPT_PRIO },
+-    { "-mppe", o_bool, &ccp_wantoptions[0].mppe,
+-      "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO },
+-
+-    /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */
+-    { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+-      "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+-      &ccp_wantoptions[0].mppe },
+-    { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+-      "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+-      &ccp_wantoptions[0].mppe },
+-    { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe,
+-      "don't allow MPPE 40-bit encryption",
+-      OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe },
+-    { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+-      "don't allow MPPE 40-bit encryption",
+-      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40,
+-      &ccp_wantoptions[0].mppe },
+-
+-    { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+-      "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+-      &ccp_wantoptions[0].mppe },
+-    { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+-      "require MPPE 128-bit encryption",
+-      OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+-      &ccp_wantoptions[0].mppe },
+-    { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe,
+-      "don't allow MPPE 128-bit encryption",
+-      OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe },
+-    { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+-      "don't allow MPPE 128-bit encryption",
+-      OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128,
+-      &ccp_wantoptions[0].mppe },
+-
+-    /* strange one; we always request stateless, but will we allow stateful? */
+-    { "mppe-stateful", o_bool, &refuse_mppe_stateful,
+-      "allow MPPE stateful mode", OPT_PRIO },
+-    { "nomppe-stateful", o_bool, &refuse_mppe_stateful,
+-      "disallow MPPE stateful mode", OPT_PRIO | 1 },
++    { "mppc", o_bool, &ccp_wantoptions[0].mppc,
++      "request MPPC compression", 1, &ccp_allowoptions[0].mppc },
++    { "+mppc", o_bool, &ccp_wantoptions[0].mppc,
++      "request MPPC compression", 1, &ccp_allowoptions[0].mppc, OPT_ALIAS },
++    { "nomppc", o_bool, &ccp_wantoptions[0].mppc,
++      "don't allow MPPC compression", OPT_PRIOSUB | OPT_A2CLR,
++      &ccp_allowoptions[0].mppc },
++    { "-mppc", o_bool, &ccp_wantoptions[0].mppc,
++      "don't allow MPPC compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
++      &ccp_allowoptions[0].mppc },
++    { "mppe", o_special, (void *)setmppe,
++      "request MPPE encryption" },
++    { "+mppe", o_special, (void *)setmppe,
++      "request MPPE encryption" },
++    { "nomppe", o_special_noarg, (void *)setnomppe,
++      "don't allow MPPE encryption" },
++    { "-mppe", o_special_noarg, (void *)setnomppe,
++      "don't allow MPPE encryption" },
+ #endif /* MPPE */
+ 
+     { NULL }
+@@ -241,7 +224,7 @@
+  */
+ #define ANY_COMPRESS(opt)	((opt).deflate || (opt).bsd_compress \
+ 				 || (opt).predictor_1 || (opt).predictor_2 \
+-				 || (opt).mppe)
++				 || (opt).lzs || (opt).mppc || (opt).mppe)
+ 
+ /*
+  * Local state (mainly for handling reset-reqs and reset-acks).
+@@ -344,6 +327,100 @@
+     return 1;
+ }
+ 
++#ifdef MPPE
++/*
++ * Functions called from config options
++ */
++/* 
++   MPPE suboptions:
++	required - require MPPE; disconnect if peer doesn't support it
++	stateless - use stateless mode
++	no40 - disable 40 bit keys
++	no56 - disable 56 bit keys
++	no128 - disable 128 bit keys
++*/
++int setmppe(char **argv)
++{
++    int i;
++    char *str, cmdbuf[16];
++
++    ccp_allowoptions[0].mppe = 1;
++    ccp_allowoptions[0].mppe_40 = 1;
++    ccp_allowoptions[0].mppe_56 = 1;
++    ccp_allowoptions[0].mppe_128 = 1;
++    ccp_allowoptions[0].mppe_stateless = 0;
++    ccp_wantoptions[0].mppe = 0;
++
++    str = *argv;
++
++    while (1) {
++	i = 0;
++	memset(cmdbuf, '\0', 16);
++	while ((i < 16) && (*str != ',') && (*str != '\0'))
++	    cmdbuf[i++] = *str++;
++	cmdbuf[i] = '\0';
++	if (!strncasecmp(cmdbuf, "no40", strlen("no40"))) {
++	    ccp_allowoptions[0].mppe_40 = 0;
++	    goto next_param;
++	} else if (!strncasecmp(cmdbuf, "no56", strlen("no56"))) {
++	    ccp_allowoptions[0].mppe_56 = 0;
++	    goto next_param;
++	} else if (!strncasecmp(cmdbuf, "no128", strlen("no128"))) {
++	    ccp_allowoptions[0].mppe_128 = 0;
++	    goto next_param;
++	} else if (!strncasecmp(cmdbuf, "stateless", strlen("stateless"))) {
++	    ccp_allowoptions[0].mppe_stateless = 1;
++	    goto next_param;
++	} else if (!strncasecmp(cmdbuf, "required", strlen("required"))) {
++	    ccp_wantoptions[0].mppe = 1;
++	    goto next_param;
++	} else {
++	    option_error("invalid parameter '%s' for mppe option", cmdbuf);
++	    return 0;
++	}
++
++    next_param:
++	if (*str == ',') {
++	    str++;
++	    continue;
++	}
++	if (*str == '\0') {
++	    if (!(ccp_allowoptions[0].mppe_40 || ccp_allowoptions[0].mppe_56 ||
++		  ccp_allowoptions[0].mppe_128)) {
++		if (ccp_wantoptions[0].mppe == 1) {
++		    option_error("You require MPPE but you have switched off "
++				 "all encryption key lengths.");
++		    return 0;
++		}
++		ccp_wantoptions[0].mppe = ccp_allowoptions[0].mppe = 0;
++		ccp_wantoptions[0].mppe_stateless =
++		    ccp_allowoptions[0].mppe_stateless = 0;
++	    } else {
++		ccp_allowoptions[0].mppe = 1;
++		ccp_wantoptions[0].mppe_stateless =
++		    ccp_allowoptions[0].mppe_stateless;
++		if (ccp_wantoptions[0].mppe == 1) {
++		    ccp_wantoptions[0].mppe_40 = ccp_allowoptions[0].mppe_40;
++		    ccp_wantoptions[0].mppe_56 = ccp_allowoptions[0].mppe_56;
++		    ccp_wantoptions[0].mppe_128 = ccp_allowoptions[0].mppe_128;
++		}
++	    }
++	    return 1;
++	}
++    }
++}
++
++int setnomppe(void)
++{
++    ccp_wantoptions[0].mppe = ccp_allowoptions[0].mppe = 0;
++    ccp_wantoptions[0].mppe_40 = ccp_allowoptions[0].mppe_40 = 0;
++    ccp_wantoptions[0].mppe_56 = ccp_allowoptions[0].mppe_56 = 0;
++    ccp_wantoptions[0].mppe_128 = ccp_allowoptions[0].mppe_128 = 0;
++    ccp_wantoptions[0].mppe_stateless = ccp_allowoptions[0].mppe_stateless = 0;
++    return 1;
++}
++#endif /* MPPE */
++
+ /*
+  * ccp_init - initialize CCP.
+  */
+@@ -378,6 +455,30 @@
+     ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
+ 
+     ccp_allowoptions[0].predictor_1 = 1;
++
++    ccp_wantoptions[0].lzs = 0; /* Stac LZS  - will be enabled in the future */
++    ccp_wantoptions[0].lzs_mode = LZS_MODE_SEQ;
++    ccp_wantoptions[0].lzs_hists = 1;
++    ccp_allowoptions[0].lzs = 0; /* Stac LZS  - will be enabled in the future */
++    ccp_allowoptions[0].lzs_mode = LZS_MODE_SEQ;
++    ccp_allowoptions[0].lzs_hists = 1;
++
++#ifdef MPPE
++    /* by default allow and request MPPC... */
++    ccp_wantoptions[0].mppc = ccp_allowoptions[0].mppc = 1;
++
++    /* ... and allow but don't request MPPE */
++    ccp_allowoptions[0].mppe = 1;
++    ccp_allowoptions[0].mppe_40 = 1;
++    ccp_allowoptions[0].mppe_56 = 1;
++    ccp_allowoptions[0].mppe_128 = 1;
++    ccp_allowoptions[0].mppe_stateless = 1;
++    ccp_wantoptions[0].mppe = 0;
++    ccp_wantoptions[0].mppe_40 = 0;
++    ccp_wantoptions[0].mppe_56 = 0;
++    ccp_wantoptions[0].mppe_128 = 0;
++    ccp_wantoptions[0].mppe_stateless = 0;
++#endif /* MPPE */
+ }
+ 
+ /*
+@@ -455,11 +556,11 @@
+     if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED) {
+ 	notice("Compression disabled by peer.");
+ #ifdef MPPE
+-	if (ccp_gotoptions[unit].mppe) {
++	if (ccp_wantoptions[unit].mppe) {
+ 	    error("MPPE disabled, closing LCP");
+ 	    lcp_close(unit, "MPPE disabled by peer");
+ 	}
+-#endif
++#endif /* MPPE */
+     }
+ 
+     /*
+@@ -487,6 +588,15 @@
+ 	    break;
+ 	/* send a reset-ack, which the transmitter will see and
+ 	   reset its compression state. */
++
++	/* In case of MPPE/MPPC or LZS we shouldn't send CCP_RESETACK,
++	   but we do it in order to reset compressor; CCP_RESETACK is
++	   then silently discarded. See functions ppp_send_frame and
++	   ppp_ccp_peek in ppp_generic.c (Linux only !!!). All the
++	   confusion is caused by the fact that CCP code is splited
++	   into two parts - one part is handled by pppd, the other one
++	   is handled by kernel. */
++
+ 	fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+ 	break;
+ 
+@@ -515,12 +625,11 @@
+     fsm_lowerdown(&ccp_fsm[unit]);
+ 
+ #ifdef MPPE
+-    if (ccp_gotoptions[unit].mppe) {
++    if (ccp_wantoptions[unit].mppe) {
+ 	error("MPPE required but peer negotiation failed");
+ 	lcp_close(unit, "MPPE required but peer negotiation failed");
+     }
+-#endif
+-
++#endif /* MPPE */
+ }
+ 
+ /*
+@@ -537,7 +646,7 @@
+     all_rejected[f->unit] = 0;
+ 
+ #ifdef MPPE
+-    if (go->mppe) {
++    if (go->mppe || go->mppc) {
+ 	ccp_options *ao = &ccp_allowoptions[f->unit];
+ 	int auth_mschap_bits = auth_done[f->unit];
+ 	int numbits;
+@@ -551,80 +660,109 @@
+ 	 * NB: If MPPE is required, all other compression opts are invalid.
+ 	 *     So, we return right away if we can't do it.
+ 	 */
++	if (ccp_wantoptions[f->unit].mppe) {
++	    /* Leave only the mschap auth bits set */
++	    auth_mschap_bits &= (CHAP_MS_WITHPEER  | CHAP_MS_PEER |
++				 CHAP_MS2_WITHPEER | CHAP_MS2_PEER);
++	    /* Count the mschap auths */
++	    auth_mschap_bits >>= CHAP_MS_SHIFT;
++	    numbits = 0;
++	    do {
++		numbits += auth_mschap_bits & 1;
++		auth_mschap_bits >>= 1;
++	    } while (auth_mschap_bits);
++	    if (numbits > 1) {
++		error("MPPE required, but auth done in both directions.");
++		lcp_close(f->unit, "MPPE required but not available");
++		return;
++	    }
++	    if (!numbits) {
++		error("MPPE required, but MS-CHAP[v2] auth not performed.");
++		lcp_close(f->unit, "MPPE required but not available");
++		return;
++	    }
+ 
+-	/* Leave only the mschap auth bits set */
+-	auth_mschap_bits &= (CHAP_MS_WITHPEER  | CHAP_MS_PEER |
+-			     CHAP_MS2_WITHPEER | CHAP_MS2_PEER);
+-	/* Count the mschap auths */
+-	auth_mschap_bits >>= CHAP_MS_SHIFT;
+-	numbits = 0;
+-	do {
+-	    numbits += auth_mschap_bits & 1;
+-	    auth_mschap_bits >>= 1;
+-	} while (auth_mschap_bits);
+-	if (numbits > 1) {
+-	    error("MPPE required, but auth done in both directions.");
+-	    lcp_close(f->unit, "MPPE required but not available");
+-	    return;
+-	}
+-	if (!numbits) {
+-	    error("MPPE required, but MS-CHAP[v2] auth not performed.");
+-	    lcp_close(f->unit, "MPPE required but not available");
+-	    return;
+-	}
+-
+-	/* A plugin (eg radius) may not have obtained key material. */
+-	if (!mppe_keys_set) {
+-	    error("MPPE required, but keys are not available.  "
+-		  "Possible plugin problem?");
+-	    lcp_close(f->unit, "MPPE required but not available");
+-	    return;
+-	}
+-
+-	/* LM auth not supported for MPPE */
+-	if (auth_done[f->unit] & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) {
+-	    /* This might be noise */
+-	    if (go->mppe & MPPE_OPT_40) {
+-		notice("Disabling 40-bit MPPE; MS-CHAP LM not supported");
+-		go->mppe &= ~MPPE_OPT_40;
+-		ccp_wantoptions[f->unit].mppe &= ~MPPE_OPT_40;
++	    /* A plugin (eg radius) may not have obtained key material. */
++	    if (!mppe_keys_set) {
++		error("MPPE required, but keys are not available.  "
++		      "Possible plugin problem?");
++		lcp_close(f->unit, "MPPE required but not available");
++		return;
+ 	    }
+ 	}
+ 
+-	/* Last check: can we actually negotiate something? */
+-	if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) {
+-	    /* Could be misconfig, could be 40-bit disabled above. */
+-	    error("MPPE required, but both 40-bit and 128-bit disabled.");
+-	    lcp_close(f->unit, "MPPE required but not available");
+-	    return;
++	/*
++	 * Check whether the kernel knows about the various
++	 * compression methods we might request. Key material
++	 * unimportant here.
++	 */
++	if (go->mppc) {
++	    opt_buf[0] = CI_MPPE;
++	    opt_buf[1] = CILEN_MPPE;
++	    opt_buf[2] = 0;
++	    opt_buf[3] = 0;
++	    opt_buf[4] = 0;
++	    opt_buf[5] = MPPE_MPPC;
++	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE, 0) <= 0)
++		go->mppc = 0;
++	}
++	if (go->mppe_40) {
++	    opt_buf[0] = CI_MPPE;
++	    opt_buf[1] = CILEN_MPPE;
++	    opt_buf[2] = MPPE_STATELESS;
++	    opt_buf[3] = 0;
++	    opt_buf[4] = 0;
++	    opt_buf[5] = MPPE_40BIT;
++	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0)
++		go->mppe_40 = 0;
++	}
++	if (go->mppe_56) {
++	    opt_buf[0] = CI_MPPE;
++	    opt_buf[1] = CILEN_MPPE;
++	    opt_buf[2] = MPPE_STATELESS;
++	    opt_buf[3] = 0;
++	    opt_buf[4] = 0;
++	    opt_buf[5] = MPPE_56BIT;
++	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0)
++		go->mppe_56 = 0;
++	}
++	if (go->mppe_128) {
++	    opt_buf[0] = CI_MPPE;
++	    opt_buf[1] = CILEN_MPPE;
++	    opt_buf[2] = MPPE_STATELESS;
++	    opt_buf[3] = 0;
++	    opt_buf[4] = 0;
++	    opt_buf[5] = MPPE_128BIT;
++	    if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0)
++		go->mppe_128 = 0;
++	}
++	if (!go->mppe_40 && !go->mppe_56 && !go->mppe_128) {
++	    if (ccp_wantoptions[f->unit].mppe) {
++		error("MPPE required, but kernel has no support.");
++		lcp_close(f->unit, "MPPE required but not available");
++	    }
++	    go->mppe = go->mppe_stateless = 0;
++	} else {
++	    /* MPPE is not compatible with other compression types */
++	    if (ccp_wantoptions[f->unit].mppe) {
++		ao->bsd_compress = go->bsd_compress = 0;
++		ao->predictor_1  = go->predictor_1  = 0;
++		ao->predictor_2  = go->predictor_2  = 0;
++		ao->deflate	 = go->deflate	    = 0;
++		ao->lzs		 = go->lzs	    = 0;
++	    }
+ 	}
+-
+-	/* sync options */
+-	ao->mppe = go->mppe;
+-	/* MPPE is not compatible with other compression types */
+-	ao->bsd_compress = go->bsd_compress = 0;
+-	ao->predictor_1  = go->predictor_1  = 0;
+-	ao->predictor_2  = go->predictor_2  = 0;
+-	ao->deflate      = go->deflate      = 0;
+     }
+ #endif /* MPPE */
+-
+-    /*
+-     * Check whether the kernel knows about the various
+-     * compression methods we might request.
+-     */
+-#ifdef MPPE
+-    if (go->mppe) {
+-	opt_buf[0] = CI_MPPE;
+-	opt_buf[1] = CILEN_MPPE;
+-	MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+-	/* Key material unimportant here. */
+-	if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0) {
+-	    error("MPPE required, but kernel has no support.");
+-	    lcp_close(f->unit, "MPPE required but not available");
+-	}
++    if (go->lzs) {
++	opt_buf[0] = CI_LZS;
++	opt_buf[1] = CILEN_LZS;
++	opt_buf[2] = go->lzs_hists >> 8;
++	opt_buf[3] = go->lzs_hists & 0xff;
++	opt_buf[4] = LZS_MODE_SEQ;
++	if (ccp_test(f->unit, opt_buf, CILEN_LZS, 0) <= 0)
++	    go->lzs = 0;
+     }
+-#endif
+     if (go->bsd_compress) {
+ 	opt_buf[0] = CI_BSD_COMPRESS;
+ 	opt_buf[1] = CILEN_BSD_COMPRESS;
+@@ -679,7 +817,8 @@
+ 	+ (go->deflate? CILEN_DEFLATE: 0)
+ 	+ (go->predictor_1? CILEN_PREDICTOR_1: 0)
+ 	+ (go->predictor_2? CILEN_PREDICTOR_2: 0)
+-	+ (go->mppe? CILEN_MPPE: 0);
++	+ (go->lzs? CILEN_LZS: 0)
++	+ ((go->mppe || go->mppc)? CILEN_MPPE: 0);
+ }
+ 
+ /*
+@@ -693,6 +832,8 @@
+ {
+     int res;
+     ccp_options *go = &ccp_gotoptions[f->unit];
++    ccp_options *ao = &ccp_allowoptions[f->unit];
++    ccp_options *wo = &ccp_wantoptions[f->unit];
+     u_char *p0 = p;
+ 
+     /*
+@@ -701,22 +842,43 @@
+      * in case it gets Acked.
+      */
+ #ifdef MPPE
+-    if (go->mppe) {
++    if (go->mppe || go->mppc || (!wo->mppe && ao->mppe)) {
+ 	u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+ 
+-	p[0] = opt_buf[0] = CI_MPPE;
+-	p[1] = opt_buf[1] = CILEN_MPPE;
+-	MPPE_OPTS_TO_CI(go->mppe, &p[2]);
+-	MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
++	p[0] = CI_MPPE;
++	p[1] = CILEN_MPPE;
++	p[2] = (go->mppe_stateless ? MPPE_STATELESS : 0);
++	p[3] = 0;
++	p[4] = 0;
++	p[5] = (go->mppe_40 ? MPPE_40BIT : 0) | (go->mppe_56 ? MPPE_56BIT : 0) |
++	    (go->mppe_128 ? MPPE_128BIT : 0) | (go->mppc ? MPPE_MPPC : 0);
++
++	BCOPY(p, opt_buf, CILEN_MPPE);
+ 	BCOPY(mppe_recv_key, &opt_buf[CILEN_MPPE], MPPE_MAX_KEY_LEN);
+ 	res = ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0);
+-	if (res > 0)
++	if (res > 0) {
+ 	    p += CILEN_MPPE;
+-	else
++	} else {
+ 	    /* This shouldn't happen, we've already tested it! */
+-	    lcp_close(f->unit, "MPPE required but not available in kernel");
++	    go->mppe = go->mppe_40 = go->mppe_56 = go->mppe_128 =
++		go->mppe_stateless = go->mppc = 0;
++	    if (ccp_wantoptions[f->unit].mppe)
++		lcp_close(f->unit, "MPPE required but not available in kernel");
++	}
++    }
++#endif /* MPPE */
++    if (go->lzs) {
++	p[0] = CI_LZS;
++	p[1] = CILEN_LZS;
++	p[2] = go->lzs_hists >> 8;
++	p[3] = go->lzs_hists & 0xff;
++	p[4] = LZS_MODE_SEQ;
++	res = ccp_test(f->unit, p, CILEN_LZS, 0);
++	if (res > 0) {
++	    p += CILEN_LZS;
++	} else
++	    go->lzs = 0;
+     }
+-#endif
+     if (go->deflate) {
+ 	p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
+ 	p[1] = CILEN_DEFLATE;
+@@ -802,7 +964,7 @@
+ 
+ /*
+  * ccp_ackci - process a received configure-ack, and return
+- * 1 iff the packet was OK.
++ * 1 if the packet was OK.
+  */
+ static int
+ ccp_ackci(f, p, len)
+@@ -811,24 +973,44 @@
+     int len;
+ {
+     ccp_options *go = &ccp_gotoptions[f->unit];
++    ccp_options *ao = &ccp_allowoptions[f->unit];
++    ccp_options *wo = &ccp_wantoptions[f->unit];
+     u_char *p0 = p;
+ 
+ #ifdef MPPE
+-    if (go->mppe) {
+-	u_char opt_buf[CILEN_MPPE];
+-
+-	opt_buf[0] = CI_MPPE;
+-	opt_buf[1] = CILEN_MPPE;
+-	MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+-	if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE))
++    if (go->mppe || go->mppc || (!wo->mppe && ao->mppe)) {
++	if (len < CILEN_MPPE
++	    || p[1] != CILEN_MPPE || p[0] != CI_MPPE
++	    || p[2] != (go->mppe_stateless ? MPPE_STATELESS : 0)
++	    || p[3] != 0
++	    || p[4] != 0
++	    || (p[5] != ((go->mppe_40 ? MPPE_40BIT : 0) |
++			 (go->mppc ? MPPE_MPPC : 0))
++		&& p[5] != ((go->mppe_56 ? MPPE_56BIT : 0) |
++			    (go->mppc ? MPPE_MPPC : 0))
++		&& p[5] != ((go->mppe_128 ? MPPE_128BIT : 0) |
++			    (go->mppc ? MPPE_MPPC : 0))))
+ 	    return 0;
++	if (go->mppe_40 || go->mppe_56 || go->mppe_128)
++	    go->mppe = 1;
+ 	p += CILEN_MPPE;
+ 	len -= CILEN_MPPE;
++	/* Cope with first/fast ack */
++	if (p == p0 && len == 0)
++	    return 1;
++    }
++#endif /* MPPE */
++    if (go->lzs) {
++	if (len < CILEN_LZS || p[0] != CI_LZS || p[1] != CILEN_LZS
++	    || p[2] != go->lzs_hists>>8 || p[3] != (go->lzs_hists&0xff)
++	    || p[4] != LZS_MODE_SEQ)
++	    return 0;
++	p += CILEN_LZS;
++	len -= CILEN_LZS;
+ 	/* XXX Cope with first/fast ack */
+-	if (len == 0)
++	if (p == p0 && len == 0)
+ 	    return 1;
+     }
+-#endif
+     if (go->deflate) {
+ 	if (len < CILEN_DEFLATE
+ 	    || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+@@ -891,7 +1073,7 @@
+ 
+ /*
+  * ccp_nakci - process received configure-nak.
+- * Returns 1 iff the nak was OK.
++ * Returns 1 if the nak was OK.
+  */
+ static int
+ ccp_nakci(f, p, len, treat_as_reject)
+@@ -901,6 +1083,8 @@
+     int treat_as_reject;
+ {
+     ccp_options *go = &ccp_gotoptions[f->unit];
++    ccp_options *ao = &ccp_allowoptions[f->unit];
++    ccp_options *wo = &ccp_wantoptions[f->unit];
+     ccp_options no;		/* options we've seen already */
+     ccp_options try;		/* options to ask for next time */
+ 
+@@ -908,28 +1092,100 @@
+     try = *go;
+ 
+ #ifdef MPPE
+-    if (go->mppe && len >= CILEN_MPPE
+-	&& p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+-	no.mppe = 1;
+-	/*
+-	 * Peer wants us to use a different strength or other setting.
+-	 * Fail if we aren't willing to use his suggestion.
+-	 */
+-	MPPE_CI_TO_OPTS(&p[2], try.mppe);
+-	if ((try.mppe & MPPE_OPT_STATEFUL) && refuse_mppe_stateful) {
+-	    error("Refusing MPPE stateful mode offered by peer");
+-	    try.mppe = 0;
+-	} else if (((go->mppe | MPPE_OPT_STATEFUL) & try.mppe) != try.mppe) {
+-	    /* Peer must have set options we didn't request (suggest) */
+-	    try.mppe = 0;
+-	}
++    if ((go->mppe || go->mppc || (!wo->mppe && ao->mppe)) &&
++	len >= CILEN_MPPE && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+ 
+-	if (!try.mppe) {
+-	    error("MPPE required but peer negotiation failed");
+-	    lcp_close(f->unit, "MPPE required but peer negotiation failed");
++	if (go->mppc) {
++	    no.mppc = 1;
++	    if (!(p[5] & MPPE_MPPC))
++		try.mppc = 0;
++	}
++
++	if (go->mppe)
++	    no.mppe = 1;
++	if (go->mppe_40)
++	    no.mppe_40 = 1;
++	if (go->mppe_56)
++	    no.mppe_56 = 1;
++	if (go->mppe_128)
++	    no.mppe_128 = 1;
++	if (go->mppe_stateless)
++	    no.mppe_stateless = 1;
++
++	if (ao->mppe_40) {
++	    if ((p[5] & MPPE_40BIT))
++		try.mppe_40 = 1;
++	    else
++		try.mppe_40 = (p[5] == 0) ? 1 : 0;
++	}
++	if (ao->mppe_56) {
++	    if ((p[5] & MPPE_56BIT))
++		try.mppe_56 = 1;
++	    else
++		try.mppe_56 = (p[5] == 0) ? 1 : 0;
++	}
++	if (ao->mppe_128) {
++	    if ((p[5] & MPPE_128BIT))
++		try.mppe_128 = 1;
++	    else
++		try.mppe_128 = (p[5] == 0) ? 1 : 0;
++	}
++
++	if (ao->mppe_stateless) {
++	    if ((p[2] & MPPE_STATELESS) || wo->mppe_stateless)
++		try.mppe_stateless = 1;
++	    else
++		try.mppe_stateless = 0;
++	}
++
++	if (!try.mppe_56 && !try.mppe_40 && !try.mppe_128) {
++	    try.mppe = try.mppe_stateless = 0;
++	    if (wo->mppe) {
++		/* we require encryption, but peer doesn't support it
++		   so we close connection */
++		wo->mppc = wo->mppe = wo->mppe_stateless = wo->mppe_40 =
++		    wo->mppe_56 = wo->mppe_128 = 0;
++		lcp_close(f->unit, "MPPE required but cannot negotiate MPPE "
++			  "key length");
++	    }
++        }
++	if (wo->mppe && (wo->mppe_40 != try.mppe_40) &&
++	    (wo->mppe_56 != try.mppe_56) && (wo->mppe_128 != try.mppe_128)) {
++	    /* cannot negotiate key length */
++	    wo->mppc = wo->mppe = wo->mppe_stateless = wo->mppe_40 =
++		wo->mppe_56 = wo->mppe_128 = 0;
++	    lcp_close(f->unit, "Cannot negotiate MPPE key length");
+ 	}
++	if (try.mppe_40 && try.mppe_56 && try.mppe_128)
++	    try.mppe_40 = try.mppe_56 = 0;
++	else
++	    if (try.mppe_56 && try.mppe_128)
++		try.mppe_56 = 0;
++	    else
++		if (try.mppe_40 && try.mppe_128)
++		    try.mppe_40 = 0;
++		else
++		    if (try.mppe_40 && try.mppe_56)
++			try.mppe_40 = 0;
++
++	p += CILEN_MPPE;
++	len -= CILEN_MPPE;
+     }
+ #endif /* MPPE */
++
++    if (go->lzs && len >= CILEN_LZS && p[0] == CI_LZS && p[1] == CILEN_LZS) {
++	no.lzs = 1;
++	if (((p[2]<<8)|p[3]) > 1 || (p[4] != LZS_MODE_SEQ &&
++				     p[4] != LZS_MODE_EXT))
++	    try.lzs = 0;
++	else {
++	    try.lzs_mode = p[4];
++	    try.lzs_hists = (p[2] << 8) | p[3];
++	}
++	p += CILEN_LZS;
++	len -= CILEN_LZS;
++    }
++
+     if (go->deflate && len >= CILEN_DEFLATE
+ 	&& p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ 	&& p[1] == CILEN_DEFLATE) {
+@@ -1002,14 +1258,50 @@
+ 	return -1;
+ 
+ #ifdef MPPE
+-    if (go->mppe && len >= CILEN_MPPE
++    if ((go->mppe || go->mppc) && len >= CILEN_MPPE
+ 	&& p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+-	error("MPPE required but peer refused");
+-	lcp_close(f->unit, "MPPE required but peer refused");
++	ccp_options *wo = &ccp_wantoptions[f->unit];
++	if (p[2] != (go->mppe_stateless ? MPPE_STATELESS : 0) ||
++	    p[3] != 0 ||
++	    p[4] != 0 ||
++	    p[5] != ((go->mppe_40 ? MPPE_40BIT : 0) |
++		     (go->mppe_56 ? MPPE_56BIT : 0) |
++		     (go->mppe_128 ? MPPE_128BIT : 0) |
++		     (go->mppc ? MPPE_MPPC : 0)))
++	    return 0;
++	if (go->mppc)
++	    try.mppc = 0;
++	if (go->mppe) {
++	    try.mppe = 0;
++	    if (go->mppe_40)
++		try.mppe_40 = 0;
++	    if (go->mppe_56)
++		try.mppe_56 = 0;
++	    if (go->mppe_128)
++		try.mppe_128 = 0;
++	    if (go->mppe_stateless)
++		try.mppe_stateless = 0;
++	    if (!try.mppe_56 && !try.mppe_40 && !try.mppe_128)
++		try.mppe = try.mppe_stateless = 0;
++	    if (wo->mppe) { /* we want MPPE but cannot negotiate key length */
++		wo->mppc = wo->mppe = wo->mppe_stateless = wo->mppe_40 =
++		    wo->mppe_56 = wo->mppe_128 = 0;
++		lcp_close(f->unit, "MPPE required but cannot negotiate MPPE "
++			  "key length");
++	    }
++	}
+ 	p += CILEN_MPPE;
+ 	len -= CILEN_MPPE;
+     }
+-#endif
++#endif /* MPPE */
++    if (go->lzs && len >= CILEN_LZS && p[0] == CI_LZS && p[1] == CILEN_LZS) {
++	if (p[2] != go->lzs_hists>>8 || p[3] != (go->lzs_hists&0xff) 
++	    || p[4] != go->lzs_mode)
++	    return 0;
++	try.lzs = 0;
++	p += CILEN_LZS;
++	len -= CILEN_LZS;
++    }
+     if (go->deflate_correct && len >= CILEN_DEFLATE
+ 	&& p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
+ 	if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+@@ -1073,14 +1365,15 @@
+     int dont_nak;
+ {
+     int ret, newret, res;
+-    u_char *p0, *retp;
++    u_char *p0, *retp, p2, p5;
+     int len, clen, type, nb;
+     ccp_options *ho = &ccp_hisoptions[f->unit];
+     ccp_options *ao = &ccp_allowoptions[f->unit];
++    ccp_options *wo = &ccp_wantoptions[f->unit];
+ #ifdef MPPE
+-    bool rej_for_ci_mppe = 1;	/* Are we rejecting based on a bad/missing */
+-				/* CI_MPPE, or due to other options?       */
+-#endif
++    u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
++/*     int mtu; */
++#endif /* MPPE */
+ 
+     ret = CONFACK;
+     retp = p0 = p;
+@@ -1103,106 +1396,305 @@
+ 	    switch (type) {
+ #ifdef MPPE
+ 	    case CI_MPPE:
+-		if (!ao->mppe || clen != CILEN_MPPE) {
++		if ((!ao->mppc && !ao->mppe) || clen != CILEN_MPPE) {
+ 		    newret = CONFREJ;
+ 		    break;
+ 		}
+-		MPPE_CI_TO_OPTS(&p[2], ho->mppe);
+ 
+-		/* Nak if anything unsupported or unknown are set. */
+-		if (ho->mppe & MPPE_OPT_UNSUPPORTED) {
++		p2 = p[2];
++		p5 = p[5];
++		/* not sure what they want, tell 'em what we got */
++		if (((p[2] & ~MPPE_STATELESS) != 0 || p[3] != 0 || p[4] != 0 ||
++		     (p[5] & ~(MPPE_40BIT | MPPE_56BIT | MPPE_128BIT |
++			       MPPE_MPPC)) != 0 || p[5] == 0) ||
++		    (p[2] == 0 && p[3] == 0 && p[4] == 0 &&  p[5] == 0)) {
+ 		    newret = CONFNAK;
+-		    ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
+-		}
+-		if (ho->mppe & MPPE_OPT_UNKNOWN) {
+-		    newret = CONFNAK;
+-		    ho->mppe &= ~MPPE_OPT_UNKNOWN;
++		    p[2] = (wo->mppe_stateless ? MPPE_STATELESS : 0);
++		    p[3] = 0;
++		    p[4] = 0;
++		    p[5] = (wo->mppe_40 ? MPPE_40BIT : 0) |
++			(wo->mppe_56 ? MPPE_56BIT : 0) |
++			(wo->mppe_128 ? MPPE_128BIT : 0) |
++			(wo->mppc ? MPPE_MPPC : 0);
++		    break;
+ 		}
+ 
+-		/* Check state opt */
+-		if (ho->mppe & MPPE_OPT_STATEFUL) {
+-		    /*
+-		     * We can Nak and request stateless, but it's a
+-		     * lot easier to just assume the peer will request
+-		     * it if he can do it; stateful mode is bad over
+-		     * the Internet -- which is where we expect MPPE.
+-		     */
+-		   if (refuse_mppe_stateful) {
+-			error("Refusing MPPE stateful mode offered by peer");
++		if ((p[5] & MPPE_MPPC)) {
++		    if (ao->mppc) {
++			ho->mppc = 1;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			opt_buf[2] = opt_buf[3] = opt_buf[4] = 0;
++			opt_buf[5] = MPPE_MPPC;
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE, 1) <= 0) {
++			    ho->mppc = 0;
++			    p[5] &= ~MPPE_MPPC;
++			    newret = CONFNAK;
++			}
++		    } else {
+ 			newret = CONFREJ;
+-			break;
++			if (wo->mppe || ao->mppe) {
++			    p[5] &= ~MPPE_MPPC;
++			    newret = CONFNAK;
++			}
++		    }
++		}
++
++		if (ao->mppe)
++		    ho->mppe = 1;
++
++		if ((p[2] & MPPE_STATELESS)) {
++		    if (ao->mppe_stateless) {
++			if (wo->mppe_stateless)
++			    ho->mppe_stateless = 1;
++			else {
++			    newret = CONFNAK;
++			    if (!dont_nak)
++				p[2] &= ~MPPE_STATELESS;
++			}
++		    } else {
++			newret = CONFNAK;
++			if (!dont_nak)
++			    p[2] &= ~MPPE_STATELESS;
++		    }
++		} else {
++		    if (wo->mppe_stateless && !dont_nak) {
++			wo->mppe_stateless = 0;
++			newret = CONFNAK;
++			p[2] |= MPPE_STATELESS;
+ 		    }
+ 		}
+ 
+-		/* Find out which of {S,L} are set. */
+-		if ((ho->mppe & MPPE_OPT_128)
+-		     && (ho->mppe & MPPE_OPT_40)) {
+-		    /* Both are set, negotiate the strongest. */
++		if ((p[5] & ~MPPE_MPPC) == (MPPE_40BIT|MPPE_56BIT|MPPE_128BIT)) {
+ 		    newret = CONFNAK;
+-		    if (ao->mppe & MPPE_OPT_128)
+-			ho->mppe &= ~MPPE_OPT_40;
+-		    else if (ao->mppe & MPPE_OPT_40)
+-			ho->mppe &= ~MPPE_OPT_128;
+-		    else {
+-			newret = CONFREJ;
+-			break;
++		    if (ao->mppe_128) {
++			ho->mppe_128 = 1;
++			p[5] &= ~(MPPE_40BIT|MPPE_56BIT);
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_128 = 0;
++			    p[5] |= (MPPE_40BIT|MPPE_56BIT);
++			    p[5] &= ~MPPE_128BIT;
++			    goto check_mppe_56_40;
++			}
++			goto check_mppe;
+ 		    }
+-		} else if (ho->mppe & MPPE_OPT_128) {
+-		    if (!(ao->mppe & MPPE_OPT_128)) {
+-			newret = CONFREJ;
+-			break;
++		    p[5] &= ~MPPE_128BIT;
++		    goto check_mppe_56_40;
++		}
++		if ((p[5] & ~MPPE_MPPC) == (MPPE_56BIT|MPPE_128BIT)) {
++		    newret = CONFNAK;
++		    if (ao->mppe_128) {
++			ho->mppe_128 = 1;
++			p[5] &= ~MPPE_56BIT;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_128 = 0;
++			    p[5] |= MPPE_56BIT;
++			    p[5] &= ~MPPE_128BIT;
++			    goto check_mppe_56;
++			}
++			goto check_mppe;
+ 		    }
+-		} else if (ho->mppe & MPPE_OPT_40) {
+-		    if (!(ao->mppe & MPPE_OPT_40)) {
+-			newret = CONFREJ;
+-			break;
++		    p[5] &= ~MPPE_128BIT;
++		    goto check_mppe_56;
++		}
++		if ((p[5] & ~MPPE_MPPC) == (MPPE_40BIT|MPPE_128BIT)) {
++		    newret = CONFNAK;
++		    if (ao->mppe_128) {
++			ho->mppe_128 = 1;
++			p[5] &= ~MPPE_40BIT;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_128 = 0;
++			    p[5] |= MPPE_40BIT;
++			    p[5] &= ~MPPE_128BIT;
++			    goto check_mppe_40;
++			}
++			goto check_mppe;
+ 		    }
+-		} else {
+-		    /* Neither are set. */
+-		    /* We cannot accept this.  */
++		    p[5] &= ~MPPE_128BIT;
++		    goto check_mppe_40;
++		}
++		if ((p[5] & ~MPPE_MPPC) == MPPE_128BIT) {
++		    if (ao->mppe_128) {
++			ho->mppe_128 = 1;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_128 = 0;
++			    p[5] &= ~MPPE_128BIT;
++			    newret = CONFNAK;
++			}
++			goto check_mppe;
++		    }
++		    p[5] &= ~MPPE_128BIT;
+ 		    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 */
+-		MPPE_OPTS_TO_CI(ho->mppe, &p[2]);
+-		if (newret == CONFACK) {
+-		    u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN];
+-		    int mtu;
+-
+-		    BCOPY(p, opt_buf, CILEN_MPPE);
+-		    BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
+-			  MPPE_MAX_KEY_LEN);
+-		    if (ccp_test(f->unit, opt_buf,
+-				 CILEN_MPPE + MPPE_MAX_KEY_LEN, 1) <= 0) {
+-			/* This shouldn't happen, we've already tested it! */
+-			error("MPPE required, but kernel has no support.");
+-			lcp_close(f->unit, "MPPE required but not available");
+-			newret = CONFREJ;
+-			break;
++		    goto check_mppe;
++		}
++	    check_mppe_56_40:
++		if ((p[5] & ~MPPE_MPPC) == (MPPE_40BIT|MPPE_56BIT)) {
++		    newret = CONFNAK;
++		    if (ao->mppe_56) {
++			ho->mppe_56 = 1;
++			p[5] &= ~MPPE_40BIT;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_56 = 0;
++			    p[5] |= MPPE_40BIT;
++			    p[5] &= ~MPPE_56BIT;
++			    newret = CONFNAK;
++			    goto check_mppe_40;
++			}
++			goto check_mppe;
+ 		    }
+-		    /*
+-		     * We need to decrease the interface MTU by MPPE_PAD
+-		     * because MPPE frames **grow**.  The kernel [must]
+-		     * allocate MPPE_PAD extra bytes in xmit buffers.
+-		     */
+-		    mtu = netif_get_mtu(f->unit);
+-		    if (mtu)
+-			netif_set_mtu(f->unit, mtu - MPPE_PAD);
+-		    else
+-			newret = CONFREJ;
++		    p[5] &= ~MPPE_56BIT;
++		    goto check_mppe_40;
++		}
++	    check_mppe_56:
++		if ((p[5] & ~MPPE_MPPC) == MPPE_56BIT) {
++		    if (ao->mppe_56) {
++			ho->mppe_56 = 1;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_56 = 0;
++			    p[5] &= ~MPPE_56BIT;
++			    newret = CONFNAK;
++			}
++			goto check_mppe;
++		    }
++		    p[5] &= ~MPPE_56BIT;
++		    newret = CONFNAK;
++		    goto check_mppe;
++		}
++	    check_mppe_40:
++		if ((p[5] & ~MPPE_MPPC) == MPPE_40BIT) {
++		    if (ao->mppe_40) {
++			ho->mppe_40 = 1;
++			BCOPY(p, opt_buf, CILEN_MPPE);
++			BCOPY(mppe_send_key, &opt_buf[CILEN_MPPE],
++			      MPPE_MAX_KEY_LEN);
++			if (ccp_test(f->unit, opt_buf, CILEN_MPPE +
++				     MPPE_MAX_KEY_LEN, 1) <= 0) {
++			    ho->mppe_40 = 0;
++			    p[5] &= ~MPPE_40BIT;
++			    newret = CONFNAK;
++			}
++			goto check_mppe;
++		    }
++		    p[5] &= ~MPPE_40BIT;
+ 		}
+ 
++	    check_mppe:
++		if (!ho->mppe_40 && !ho->mppe_56 && !ho->mppe_128) {
++		    if (wo->mppe_40 || wo->mppe_56 || wo->mppe_128) {
++			newret = CONFNAK;
++			p[2] |= (wo->mppe_stateless ? MPPE_STATELESS : 0);
++			p[5] |= (wo->mppe_40 ? MPPE_40BIT : 0) |
++			    (wo->mppe_56 ? MPPE_56BIT : 0) |
++			    (wo->mppe_128 ? MPPE_128BIT : 0) |
++			    (wo->mppc ? MPPE_MPPC : 0);
++		    } else {
++			ho->mppe = ho->mppe_stateless = 0;
++		    }
++		} else {
++		    /* MPPE is not compatible with other compression types */
++		    if (wo->mppe) {
++			ao->bsd_compress = 0;
++			ao->predictor_1 = 0;
++			ao->predictor_2 = 0;
++			ao->deflate = 0;
++			ao->lzs = 0;
++		    }
++		}
++		if ((!ho->mppc || !ao->mppc) && !ho->mppe) {
++		    p[2] = p2;
++		    p[5] = p5;
++		    newret = CONFREJ;
++		    break;
++		}
++
++		/*
++		 * I have commented the code below because according to RFC1547
++		 * MTU is only information for higher level protocols about
++		 * "the maximum allowable length for a packet (q.v.) transmitted
++		 * over a point-to-point link without incurring network layer
++		 * fragmentation." Of course a PPP implementation should be able
++		 * to handle overhead added by MPPE - in our case apropriate code
++		 * is located in drivers/net/ppp_generic.c in the kernel sources.
++		 *
++		 * According to RFC1661:
++		 * - when negotiated MRU is less than 1500 octets, a PPP
++		 *   implementation must still be able to receive at least 1500
++		 *   octets,
++		 * - when PFC is negotiated, a PPP implementation is still
++		 *   required to receive frames with uncompressed protocol field.
++		 *
++		 * So why not to handle MPPE overhead without changing MTU value?
++		 * I am sure that RFC3078, unfortunately silently, assumes that.
++		 */
++
+ 		/*
+-		 * We have accepted MPPE or are willing to negotiate
+-		 * MPPE parameters.  A CONFREJ is due to subsequent
+-		 * (non-MPPE) processing.
++		 * We need to decrease the interface MTU by MPPE_PAD
++		 * because MPPE frames **grow**.  The kernel [must]
++		 * allocate MPPE_PAD extra bytes in xmit buffers.
+ 		 */
+-		rej_for_ci_mppe = 0;
++/*
++		mtu = netif_get_mtu(f->unit);
++		if (mtu) {
++		    netif_set_mtu(f->unit, mtu - MPPE_PAD);
++		} else {
++		    newret = CONFREJ;
++		    if (ccp_wantoptions[f->unit].mppe) {
++			error("Cannot adjust MTU needed by MPPE.");
++			lcp_close(f->unit, "Cannot adjust MTU needed by MPPE.");
++		    }
++		}
++*/
+ 		break;
+ #endif /* MPPE */
++
++	    case CI_LZS:
++		if (!ao->lzs || clen != CILEN_LZS) {
++		    newret = CONFREJ;
++		    break;
++		}
++
++		ho->lzs = 1;
++		ho->lzs_hists = (p[2] << 8) | p[3];
++		ho->lzs_mode = p[4];
++		if ((ho->lzs_hists != ao->lzs_hists) ||
++		    (ho->lzs_mode != ao->lzs_mode)) {
++		    newret = CONFNAK;
++		    if (!dont_nak) {
++			p[2] = ao->lzs_hists >> 8;
++			p[3] = ao->lzs_hists & 0xff;
++			p[4] = ao->lzs_mode;
++		    } else
++			break;
++		}
++
++		if (p == p0 && ccp_test(f->unit, p, CILEN_LZS, 1) <= 0) {
++		    newret = CONFREJ;
++		}
++		break;
++
+ 	    case CI_DEFLATE:
+ 	    case CI_DEFLATE_DRAFT:
+ 		if (!ao->deflate || clen != CILEN_DEFLATE
+@@ -1344,12 +1836,6 @@
+ 	else
+ 	    *lenp = retp - p0;
+     }
+-#ifdef MPPE
+-    if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) {
+-	error("MPPE required but peer negotiation failed");
+-	lcp_close(f->unit, "MPPE required but peer negotiation failed");
+-    }
+-#endif
+     return ret;
+ }
+ 
+@@ -1371,24 +1857,35 @@
+ 	char *p = result;
+ 	char *q = result + sizeof(result); /* 1 past result */
+ 
+-	slprintf(p, q - p, "MPPE ");
+-	p += 5;
+-	if (opt->mppe & MPPE_OPT_128) {
+-	    slprintf(p, q - p, "128-bit ");
+-	    p += 8;
+-	}
+-	if (opt->mppe & MPPE_OPT_40) {
+-	    slprintf(p, q - p, "40-bit ");
+-	    p += 7;
+-	}
+-	if (opt->mppe & MPPE_OPT_STATEFUL)
+-	    slprintf(p, q - p, "stateful");
+-	else
+-	    slprintf(p, q - p, "stateless");
+-
++	if (opt->mppe) {
++	    if (opt->mppc) {
++		slprintf(p, q - p, "MPPC/MPPE ");
++		p += 10;
++	    } else {
++		slprintf(p, q - p, "MPPE ");
++		p += 5;
++	    }
++	    if (opt->mppe_128) {
++		slprintf(p, q - p, "128-bit ");
++		p += 8;
++	    } else if (opt->mppe_56) {
++		slprintf(p, q - p, "56-bit ");
++		p += 7;
++	    } else if (opt->mppe_40) {
++		slprintf(p, q - p, "40-bit ");
++		p += 7;
++	    }
++	    if (opt->mppe_stateless)
++		slprintf(p, q - p, "stateless");
++	    else
++		slprintf(p, q - p, "stateful");
++	} else if (opt->mppc)
++	    slprintf(p, q - p, "MPPC");
+ 	break;
+     }
+-#endif
++#endif /* MPPE */
++    case CI_LZS:
++	return "Stac LZS";
+     case CI_DEFLATE:
+     case CI_DEFLATE_DRAFT:
+ 	if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+@@ -1444,12 +1941,12 @@
+     } else if (ANY_COMPRESS(*ho))
+ 	notice("%s transmit compression enabled", method_name(ho, NULL));
+ #ifdef MPPE
+-    if (go->mppe) {
++    if (go->mppe || go->mppc) {
+ 	BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN);
+ 	BZERO(mppe_send_key, MPPE_MAX_KEY_LEN);
+ 	continue_networks(f->unit);		/* Bring up IP et al */
+     }
+-#endif
++#endif /* MPPE */
+ }
+ 
+ /*
+@@ -1472,7 +1969,7 @@
+ 	    lcp_close(f->unit, "MPPE disabled");
+ 	}
+     }
+-#endif
++#endif /* MPPE */
+ }
+ 
+ /*
+@@ -1532,24 +2029,28 @@
+ #ifdef MPPE
+ 	    case CI_MPPE:
+ 		if (optlen >= CILEN_MPPE) {
+-		    u_char mppe_opts;
+-
+-		    MPPE_CI_TO_OPTS(&p[2], mppe_opts);
+-		    printer(arg, "mppe %s %s %s %s %s %s%s",
+-			    (p[2] & MPPE_H_BIT)? "+H": "-H",
+-			    (p[5] & MPPE_M_BIT)? "+M": "-M",
+-			    (p[5] & MPPE_S_BIT)? "+S": "-S",
+-			    (p[5] & MPPE_L_BIT)? "+L": "-L",
++		    printer(arg, "mppe %s %s %s %s %s %s",
++			    (p[2] & MPPE_STATELESS)? "+H": "-H",
++			    (p[5] & MPPE_56BIT)? "+M": "-M",
++			    (p[5] & MPPE_128BIT)? "+S": "-S",
++			    (p[5] & MPPE_40BIT)? "+L": "-L",
+ 			    (p[5] & MPPE_D_BIT)? "+D": "-D",
+-			    (p[5] & MPPE_C_BIT)? "+C": "-C",
+-			    (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": "");
+-		    if (mppe_opts & MPPE_OPT_UNKNOWN)
++			    (p[5] & MPPE_MPPC)? "+C": "-C");
++		    if ((p[5] & ~(MPPE_56BIT | MPPE_128BIT | MPPE_40BIT |
++				  MPPE_D_BIT | MPPE_MPPC)) ||
++			(p[2] & ~MPPE_STATELESS))
+ 			printer(arg, " (%.2x %.2x %.2x %.2x)",
+ 				p[2], p[3], p[4], p[5]);
+ 		    p += CILEN_MPPE;
+ 		}
+ 		break;
+-#endif
++#endif /* MPPE */
++	    case CI_LZS:
++		if (optlen >= CILEN_LZS) {
++		    printer(arg, "lzs %.2x %.2x %.2x", p[2], p[3], p[4]);
++		    p += CILEN_LZS;
++		}
++		break;
+ 	    case CI_DEFLATE:
+ 	    case CI_DEFLATE_DRAFT:
+ 		if (optlen >= CILEN_DEFLATE) {
+@@ -1635,6 +2136,7 @@
+ 	    error("Lost compression sync: disabling compression");
+ 	    ccp_close(unit, "Lost compression sync");
+ #ifdef MPPE
++	    /* My module dosn't need this. J.D., 2003-07-06 */
+ 	    /*
+ 	     * If we were doing MPPE, we must also take the link down.
+ 	     */
+@@ -1642,9 +2144,18 @@
+ 		error("Too many MPPE errors, closing LCP");
+ 		lcp_close(unit, "Too many MPPE errors");
+ 	    }
+-#endif
++#endif /* MPPE */
+ 	} else {
+ 	    /*
++	     * When LZS or MPPE/MPPC is negotiated we just send CCP_RESETREQ
++	     * and don't wait for CCP_RESETACK
++	     */
++	    if ((ccp_gotoptions[f->unit].method == CI_LZS) ||
++		(ccp_gotoptions[f->unit].method == CI_MPPE)) {
++		fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
++		return;
++	    }
++	    /*
+ 	     * Send a reset-request to reset the peer's compressor.
+ 	     * We don't do that if we are still waiting for an
+ 	     * acknowledgement to a previous reset-request.
+diff -urN ppp-2.4.4-orig/pppd/ccp.h ppp-2.4.4/pppd/ccp.h
+--- ppp-2.4.4-orig/pppd/ccp.h	2004-11-04 05:05:11.000000000 -0500
++++ ppp-2.4.4/pppd/ccp.h	2006-06-09 05:49:45.000000000 -0400
+@@ -37,9 +37,17 @@
+     bool predictor_2;		/* do Predictor-2? */
+     bool deflate_correct;	/* use correct code for deflate? */
+     bool deflate_draft;		/* use draft RFC code for deflate? */
++    bool lzs;			/* do Stac LZS? */
++    bool mppc;			/* do MPPC? */
+     bool mppe;			/* do MPPE? */
++    bool mppe_40;		/* allow 40 bit encryption? */
++    bool mppe_56;		/* allow 56 bit encryption? */
++    bool mppe_128;		/* allow 128 bit encryption? */
++    bool mppe_stateless;	/* allow stateless encryption */
+     u_short bsd_bits;		/* # bits/code for BSD Compress */
+     u_short deflate_size;	/* lg(window size) for Deflate */
++    u_short lzs_mode;		/* LZS check mode */
++    u_short lzs_hists;		/* number of LZS histories */
+     short method;		/* code for chosen compression method */
+ } ccp_options;
+ 
+diff -urN ppp-2.4.4-orig/pppd/chap_ms.c ppp-2.4.4/pppd/chap_ms.c
+--- ppp-2.4.4-orig/pppd/chap_ms.c	2006-05-21 08:00:18.000000000 -0400
++++ ppp-2.4.4/pppd/chap_ms.c	2006-06-09 05:54:19.000000000 -0400
+@@ -897,13 +897,17 @@
+     /*
+      * Disable undesirable encryption types.  Note that we don't ENABLE
+      * any encryption types, to avoid overriding manual configuration.
++     *
++     * It seems that 56 bit keys are unsupported in MS-RADIUS (see RFC 2548)
+      */
+     switch(types) {
+ 	case MPPE_ENC_TYPES_RC4_40:
+-	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_128;	/* disable 128-bit */
++	    ccp_wantoptions[0].mppe_128 = 0;	/* disable 128-bit */
++	    ccp_wantoptions[0].mppe_56 = 0;	/* disable 56-bit */
+ 	    break;
+ 	case MPPE_ENC_TYPES_RC4_128:
+-	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_40;	/* disable 40-bit */
++	    ccp_wantoptions[0].mppe_56 = 0;	/* disable 56-bit */
++	    ccp_wantoptions[0].mppe_40 = 0;	/* disable 40-bit */
+ 	    break;
+ 	default:
+ 	    break;
+diff -urN ppp-2.4.4-orig/pppd/ipcp.c ppp-2.4.4/pppd/ipcp.c
+--- ppp-2.4.4-orig/pppd/ipcp.c	2005-08-25 20:00:17.000000000 -0400
++++ ppp-2.4.4/pppd/ipcp.c	2006-06-09 06:16:07.000000000 -0400
+@@ -171,6 +171,12 @@
+     { "ipparam", o_string, &ipparam,
+       "Set ip script parameter", OPT_PRIO },
+ 
++    { "ip-up-script", o_string, &ipupcustom,
++      "Specify custom ip-up script", OPT_PRIO },
++
++    { "ip-down-script", o_string, &ipdowncustom,
++      "Specify custom ip-down script", OPT_PRIO },
++
+     { "noipdefault", o_bool, &disable_defaultip,
+       "Don't use name for default IP adrs", 1 },
+ 
+@@ -1850,7 +1856,7 @@
+      */
+     if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
+ 	ipcp_script_state = s_up;
+-	ipcp_script(_PATH_IPUP, 0);
++	ipcp_script(ipupcustom ? ipupcustom : _PATH_IPUP, 0);
+     }
+ }
+ 
+@@ -1900,7 +1906,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, 0);
++	ipcp_script(ipdowncustom ? ipdowncustom : _PATH_IPDOWN, 0);
+     }
+ }
+ 
+@@ -1954,13 +1960,13 @@
+     case s_up:
+ 	if (ipcp_fsm[0].state != OPENED) {
+ 	    ipcp_script_state = s_down;
+-	    ipcp_script(_PATH_IPDOWN, 0);
++	    ipcp_script(ipdowncustom ? ipdowncustom : _PATH_IPDOWN, 0);
+ 	}
+ 	break;
+     case s_down:
+ 	if (ipcp_fsm[0].state == OPENED) {
+ 	    ipcp_script_state = s_up;
+-	    ipcp_script(_PATH_IPUP, 0);
++	    ipcp_script(ipupcustom ? ipupcustom : _PATH_IPUP, 0);
+ 	}
+ 	break;
+     }
+diff -urN ppp-2.4.4-orig/pppd/Makefile.linux ppp-2.4.4/pppd/Makefile.linux
+--- ppp-2.4.4-orig/pppd/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/pppd/Makefile.linux	2006-06-09 05:13:40.000000000 -0400
+@@ -48,19 +48,19 @@
+ # Uncomment the next line to include support for PPP packet filtering.
+ # This requires that the libpcap library and headers be installed
+ # and that the kernel driver support PPP packet filtering.
+-FILTER=y
++#FILTER=y
+ 
+ # Uncomment the next line to enable multilink PPP (enabled by default)
+ # Linux distributions: Please leave multilink ENABLED in your builds
+ # of pppd!
+-HAVE_MULTILINK=y
++#HAVE_MULTILINK=y
+ 
+ # Uncomment the next line to enable the TDB database (enabled by default.)
+ # If you enable multilink, then TDB is automatically enabled also.
+ # Linux distributions: Please leave TDB ENABLED in your builds.
+-USE_TDB=y
++#USE_TDB=y
+ 
+-HAS_SHADOW=y
++#HAS_SHADOW=y
+ #USE_PAM=y
+ #HAVE_INET6=y
+ 
+@@ -77,7 +77,7 @@
+ 
+ INCLUDE_DIRS= -I../include
+ 
+-COMPILE_FLAGS= -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MMAP
++COMPILE_FLAGS= -DHAVE_PATHS_H -DHAVE_MMAP
+ 
+ CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS) '-DDESTDIR="@DESTDIR@"'
+ 
+@@ -99,7 +99,7 @@
+ CFLAGS	+= -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include
+ LIBS	+= -lsrp -L/usr/local/ssl/lib -lcrypto
+ TARGETS	+= srp-entry
+-EXTRAINSTALL = $(INSTALL) -s -c -m 555 srp-entry $(BINDIR)/srp-entry
++EXTRAINSTALL = $(INSTALL) -c -m 555 srp-entry $(BINDIR)/srp-entry
+ MANPAGES += srp-entry.8
+ EXTRACLEAN += srp-entry.o
+ NEEDDES=y
+@@ -117,10 +117,12 @@
+ #LIBS     += -lshadow $(LIBS)
+ endif
+ 
+-ifneq ($(wildcard /usr/include/crypt.h),)
+-CFLAGS  += -DHAVE_CRYPT_H=1
++#ifneq ($(wildcard /usr/include/crypt.h),)
++CFLAGS   += -DHAVE_CRYPT_H=1
++#endif
++#ifneq ($(wildcard /usr/lib/libcrypt.*),)
+ LIBS	+= -lcrypt
+-endif
++#endif
+ 
+ ifdef NEEDDES
+ ifndef USE_CRYPT
+@@ -200,7 +202,7 @@
+ install: pppd
+ 	mkdir -p $(BINDIR) $(MANDIR)
+ 	$(EXTRAINSTALL)
+-	$(INSTALL) -s -c -m 555 pppd $(BINDIR)/pppd
++	$(INSTALL) -c -m 555 pppd $(BINDIR)/pppd
+ 	if chgrp pppusers $(BINDIR)/pppd 2>/dev/null; then \
+ 	  chmod o-rx,u+s $(BINDIR)/pppd; fi
+ 	$(INSTALL) -c -m 444 pppd.8 $(MANDIR)
+diff -urN ppp-2.4.4-orig/pppd/options.c ppp-2.4.4/pppd/options.c
+--- ppp-2.4.4-orig/pppd/options.c	2006-06-04 03:05:46.000000000 -0400
++++ ppp-2.4.4/pppd/options.c	2006-06-09 06:18:32.000000000 -0400
+@@ -103,6 +103,12 @@
+ char	our_name[MAXNAMELEN];	/* Our name for authentication purposes */
+ bool	demand = 0;		/* do dial-on-demand */
+ char	*ipparam = NULL;	/* Extra parameter for ip up/down scripts */
++char	*ipupcustom = NULL;	/* Custom ip up scripts */
++char	*ipdowncustom = NULL;	/* Custom ip down scripts */
++char	*chapseccustom = NULL;	/* Custom chap-secrets file */
++char	*papseccustom = NULL;	/* Custom pap-secrets file */
++char	*srpseccustom = NULL;	/* Custom pap-secrets file */
++
+ int	idle_time_limit = 0;	/* Disconnect if idle for this many seconds */
+ int	holdoff = 30;		/* # seconds to pause before reconnecting */
+ bool	holdoff_specified;	/* true if a holdoff value has been given */
+diff -urN ppp-2.4.4-orig/pppd/plugins/Makefile.linux ppp-2.4.4/pppd/plugins/Makefile.linux
+--- ppp-2.4.4-orig/pppd/plugins/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/pppd/plugins/Makefile.linux	2006-06-09 05:58:00.000000000 -0400
+@@ -9,9 +9,9 @@
+ MANDIR = $(DESTDIR)/share/man/man8
+ LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+ 
+-SUBDIRS := rp-pppoe pppoatm
++SUBDIRS := rp-pppoe pppoatm radius
+ # Uncomment the next line to include the radius authentication plugin
+-SUBDIRS += radius
++# SUBDIRS += radius
+ PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so
+ 
+ # include dependencies if present
+diff -urN ppp-2.4.4-orig/pppd/plugins/radius/config.c ppp-2.4.4/pppd/plugins/radius/config.c
+--- ppp-2.4.4-orig/pppd/plugins/radius/config.c	2004-11-14 02:26:26.000000000 -0500
++++ ppp-2.4.4/pppd/plugins/radius/config.c	2006-06-09 06:01:00.000000000 -0400
+@@ -369,31 +369,37 @@
+ 	}
+ #endif
+ 
++#if 0
+ 	if (rc_conf_int("login_tries") <= 0)
+ 	{
+ 		error("%s: login_tries <= 0 is illegal", filename);
+ 		return (-1);
+ 	}
++#endif
+ 	if (rc_conf_str("seqfile") == NULL)
+ 	{
+ 		error("%s: seqfile not specified", filename);
+ 		return (-1);
+ 	}
++#if 0
+ 	if (rc_conf_int("login_timeout") <= 0)
+ 	{
+ 		error("%s: login_timeout <= 0 is illegal", filename);
+ 		return (-1);
+ 	}
++#endif
+ 	if (rc_conf_str("mapfile") == NULL)
+ 	{
+ 		error("%s: mapfile not specified", filename);
+ 		return (-1);
+ 	}
++#if 0
+ 	if (rc_conf_str("nologin") == NULL)
+ 	{
+ 		error("%s: nologin not specified", filename);
+ 		return (-1);
+ 	}
++#endif
+ 
+ 	return 0;
+ }
+diff -urN ppp-2.4.4-orig/pppd/plugins/radius/Makefile.linux ppp-2.4.4/pppd/plugins/radius/Makefile.linux
+--- ppp-2.4.4-orig/pppd/plugins/radius/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/pppd/plugins/radius/Makefile.linux	2006-06-09 05:59:40.000000000 -0400
+@@ -12,7 +12,8 @@
+ INSTALL	= install
+ 
+ PLUGIN=radius.so radattr.so radrealms.so
+-CFLAGS=-I. -I../.. -I../../../include -O2 -fPIC -DRC_LOG_FACILITY=LOG_DAEMON
++COPTS = -O2
++CFLAGS=-I. -I../.. -I../../../include $(COPTS) -fPIC -DRC_LOG_FACILITY=LOG_DAEMON
+ 
+ # Uncomment the next line to include support for Microsoft's
+ # MS-CHAP authentication protocol.
+@@ -36,9 +37,9 @@
+ 
+ install: all
+ 	$(INSTALL) -d -m 755 $(LIBDIR)
+-	$(INSTALL) -s -c -m 755 radius.so $(LIBDIR)
+-	$(INSTALL) -s -c -m 755 radattr.so $(LIBDIR)
+-	$(INSTALL) -s -c -m 755 radrealms.so $(LIBDIR)
++	$(INSTALL) -c -m 755 radius.so $(LIBDIR)
++	$(INSTALL) -c -m 755 radattr.so $(LIBDIR)
++	$(INSTALL) -c -m 755 radrealms.so $(LIBDIR)
+ 	$(INSTALL) -c -m 444 pppd-radius.8 $(MANDIR)
+ 	$(INSTALL) -c -m 444 pppd-radattr.8 $(MANDIR)
+ 
+diff -urN ppp-2.4.4-orig/pppd/plugins/radius/options.h ppp-2.4.4/pppd/plugins/radius/options.h
+--- ppp-2.4.4-orig/pppd/plugins/radius/options.h	2004-11-14 02:26:26.000000000 -0500
++++ ppp-2.4.4/pppd/plugins/radius/options.h	2006-06-09 06:02:06.000000000 -0400
+@@ -31,24 +31,21 @@
+ static SERVER acctserver = {0};
+ static SERVER authserver = {0};
+ 
+-int default_tries = 4;
+-int default_timeout = 60;
+-
+ static OPTION config_options[] = {
+ /* internally used options */
+ {"config_file",		OT_STR, ST_UNDEF, NULL},
+ /* General options */
+ {"auth_order",	 	OT_AUO, ST_UNDEF, NULL},
+-{"login_tries",	 	OT_INT, ST_UNDEF, &default_tries},
+-{"login_timeout",	OT_INT, ST_UNDEF, &default_timeout},
+-{"nologin",		OT_STR, ST_UNDEF, "/etc/nologin"},
+-{"issue",		OT_STR, ST_UNDEF, "/etc/radiusclient/issue"},
++{"login_tries",	 	OT_INT, ST_UNDEF, NULL},
++{"login_timeout",	OT_INT, ST_UNDEF, NULL},
++{"nologin",		OT_STR, ST_UNDEF, NULL},
++{"issue",		OT_STR, ST_UNDEF, NULL},
+ /* RADIUS specific options */
+ {"authserver",		OT_SRV, ST_UNDEF, &authserver},
+ {"acctserver",		OT_SRV, ST_UNDEF, &acctserver},
+ {"servers",		OT_STR, ST_UNDEF, NULL},
+ {"dictionary",		OT_STR, ST_UNDEF, NULL},
+-{"login_radius",	OT_STR, ST_UNDEF, "/usr/sbin/login.radius"},
++{"login_radius",	OT_STR, ST_UNDEF, NULL},
+ {"seqfile",		OT_STR, ST_UNDEF, NULL},
+ {"mapfile",		OT_STR, ST_UNDEF, NULL},
+ {"default_realm",	OT_STR, ST_UNDEF, NULL},
+diff -urN ppp-2.4.4-orig/pppd/plugins/rp-pppoe/Makefile.linux ppp-2.4.4/pppd/plugins/rp-pppoe/Makefile.linux
+--- ppp-2.4.4-orig/pppd/plugins/rp-pppoe/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/pppd/plugins/rp-pppoe/Makefile.linux	2006-06-09 06:03:08.000000000 -0400
+@@ -39,9 +39,9 @@
+ 
+ install: all
+ 	$(INSTALL) -d -m 755 $(LIBDIR)
+-	$(INSTALL) -s -c -m 4550 rp-pppoe.so $(LIBDIR)
++	$(INSTALL) -c -m 4550 rp-pppoe.so $(LIBDIR)
+ 	$(INSTALL) -d -m 755 $(BINDIR)
+-	$(INSTALL) -s -c -m 555 pppoe-discovery $(BINDIR)
++	$(INSTALL) -c -m 555 pppoe-discovery $(BINDIR)
+ 
+ clean:
+ 	rm -f *.o *.so
+diff -urN ppp-2.4.4-orig/pppd/plugins/rp-pppoe/plugin.c ppp-2.4.4/pppd/plugins/rp-pppoe/plugin.c
+--- ppp-2.4.4-orig/pppd/plugins/rp-pppoe/plugin.c	2006-05-29 19:30:13.000000000 -0400
++++ ppp-2.4.4/pppd/plugins/rp-pppoe/plugin.c	2006-06-09 06:05:26.000000000 -0400
+@@ -253,14 +253,16 @@
+     int fd;
+     struct ifreq ifr;
+ 
+-    /*
+-     * 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.
+-     */
++    /* 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 */
+     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, "vlan", 4)
++		   && strncmp(cmd, "tap", 3) && strncmp(cmd, "br", 2))) {
++	return 0;
+     }
+ 
+     /* Open a socket */
+diff -urN ppp-2.4.4-orig/pppd/pppd.8 ppp-2.4.4/pppd/pppd.8
+--- ppp-2.4.4-orig/pppd/pppd.8	2005-08-28 01:25:10.000000000 -0400
++++ ppp-2.4.4/pppd/pppd.8	2006-06-09 06:09:21.000000000 -0400
+@@ -620,16 +620,35 @@
+ 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.  This is the opposite of the
+-\fBlocal\fR option.
++control, as for the \fIcrtscts\fR option.
+ .TP
+ .B mp
+ Enables the use of PPP multilink; this is an alias for the `multilink'
+ option.  This option is currently only available under Linux.
+ .TP
+-.B mppe\-stateful
+-Allow MPPE to use stateful mode.  Stateless mode is still attempted first.
+-The default is to disallow stateful mode.  
++.B mppc
++Enables MPPC (Microsoft Point to Point Compression).  This is the default.
++.TP
++.B mppe \fIsubopt1[,subopt2[,subopt3[..]]]
++Modify MPPE (Microsoft Point to Point Encryption) parameters. In order
++for MPPE to successfully come up, you must have authenticated with either
++MS-CHAP or MS-CHAPv2. By default MPPE is optional, it means that pppd will
++not propose MPPE to the peer, but will negotiate MPPE if peer wants that.
++You can change this using \fIrequired\fR suboption.
++This option is presently only supported under Linux, and only if your
++kernel has been configured to include MPPE support.
++.IP
++MPPE suboptions:
++.br
++\fIrequired\fR - require MPPE; disconnect if peer doesn't support it,
++.br
++\fIstateless\fR - try to negotiate stateless mode; default is stateful,
++.br
++\fIno40\fR - disable 40 bit keys,
++.br
++\fIno56\fR - disable 56 bit keys,
++.br
++\fIno128\fR - disable 128 bit keys
+ .TP
+ .B mpshortseq
+ Enables the use of short (12-bit) sequence numbers in multilink
+@@ -767,17 +786,11 @@
+ Disables the use of PPP multilink.  This option is currently only
+ available under Linux.
+ .TP
+-.B nomppe
+-Disables MPPE (Microsoft Point to Point Encryption).  This is the default.
+-.TP
+-.B nomppe\-40
+-Disable 40-bit encryption with MPPE.
++.B nomppc
++Disables MPPC (Microsoft Point to Point Compression).
+ .TP
+-.B nomppe\-128
+-Disable 128-bit encryption with MPPE.
+-.TP
+-.B nomppe\-stateful
+-Disable MPPE stateful mode.  This is the default.
++.B nomppe
++Disables MPPE (Microsoft Point to Point Encryption).
+ .TP
+ .B nompshortseq
+ Disables the use of short (12-bit) sequence numbers in the PPP
+@@ -958,19 +971,6 @@
+ Require the peer to authenticate itself using CHAP [Challenge
+ Handshake Authentication Protocol] authentication.
+ .TP
+-.B require\-mppe
+-Require the use of MPPE (Microsoft Point to Point Encryption).  This
+-option disables all other compression types.  This option enables
+-both 40-bit and 128-bit encryption.  In order for MPPE to successfully
+-come up, you must have authenticated with either MS\-CHAP or MS\-CHAPv2.
+-This option is presently only supported under Linux, and only if your
+-kernel has been configured to include MPPE support.
+-.TP
+-.B require\-mppe\-40
+-Require the use of MPPE, with 40-bit encryption.
+-.TP
+-.B require\-mppe\-128
+-Require the use of MPPE, with 128-bit encryption.
+ .TP
+ .B require\-mschap
+ Require the peer to authenticate itself using MS\-CHAP [Microsoft Challenge
+diff -urN ppp-2.4.4-orig/pppd/pppd.h ppp-2.4.4/pppd/pppd.h
+--- ppp-2.4.4-orig/pppd/pppd.h	2005-08-25 20:00:17.000000000 -0400
++++ ppp-2.4.4/pppd/pppd.h	2006-06-09 06:20:00.000000000 -0400
+@@ -298,6 +298,11 @@
+ extern bool	explicit_remote;/* remote_name specified with remotename opt */
+ extern bool	demand;		/* Do dial-on-demand */
+ extern char	*ipparam;	/* Extra parameter for ip up/down scripts */
++extern char	*ipupcustom;	/* Custom ip up script */
++extern char	*ipdowncustom;	/* Custom ip down script */
++extern char	*chapseccustom;	/* Custom chap-secrets file */
++extern char	*papseccustom;	/* Custom pap-secrets file */
++extern char	*srpseccustom;	/* Custom srp-secrets file */
+ extern bool	cryptpap;	/* Others' PAP passwords are encrypted */
+ extern int	idle_time_limit;/* Shut down link if idle for this long */
+ extern int	holdoff;	/* Dead time before restarting */
+@@ -319,6 +324,10 @@
+ extern bool	dryrun;		/* check everything, print options, exit */
+ extern int	child_wait;	/* # seconds to wait for children at end */
+ 
++#ifdef CHAPMS
++extern bool	ms_ignore_domain; /* Ignore any MS domain prefix */
++#endif
++
+ #ifdef MAXOCTETS
+ extern unsigned int maxoctets;	     /* Maximum octetes per session (in bytes) */
+ extern int       maxoctets_dir;      /* Direction :
+diff -urN ppp-2.4.4-orig/pppdump/Makefile.linux ppp-2.4.4/pppdump/Makefile.linux
+--- ppp-2.4.4-orig/pppdump/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/pppdump/Makefile.linux	2006-06-09 06:10:33.000000000 -0400
+@@ -2,7 +2,8 @@
+ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ 
+-CFLAGS= -O -I../include/net
++COPTS = -O
++CFLAGS= $(COPTS) -I../include/net
+ OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
+ 
+ INSTALL= install
+@@ -17,5 +18,5 @@
+ 
+ install:
+ 	mkdir -p $(BINDIR) $(MANDIR)
+-	$(INSTALL) -s -c pppdump $(BINDIR)
++	$(INSTALL) -c pppdump $(BINDIR)
+ 	$(INSTALL) -c -m 444 pppdump.8 $(MANDIR)
+diff -urN ppp-2.4.4-orig/pppstats/Makefile.linux ppp-2.4.4/pppstats/Makefile.linux
+--- ppp-2.4.4-orig/pppstats/Makefile.linux	2006-06-04 01:10:20.000000000 -0400
++++ ppp-2.4.4/pppstats/Makefile.linux	2006-06-09 06:11:58.000000000 -0400
+@@ -22,7 +22,7 @@
+ 
+ install: pppstats
+ 	-mkdir -p $(MANDIR)
+-	$(INSTALL) -s -c pppstats $(BINDIR)
++	$(INSTALL) -c pppstats $(BINDIR)
+ 	$(INSTALL) -c -m 444 pppstats.8 $(MANDIR)
+ 
+ pppstats: $(PPPSTATSRCS)
diff -Naur tomato/release/src/router/pppd/pppstats/Makefile tomato121/release/src/router/pppd/pppstats/Makefile
--- tomato/release/src/router/pppd/pppstats/Makefile	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/pppstats/Makefile	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,36 @@
+#
+# pppstats makefile
+# $Id: Makefile.linux,v 1.9 2006/06/04 05:07:46 paulus Exp $
+#
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
+
+PPPSTATSRCS = pppstats.c
+PPPSTATOBJS = pppstats.o
+
+#CC = gcc
+COPTS = -O
+COMPILE_FLAGS = -I../include
+LIBS =
+
+INSTALL= install
+
+CFLAGS = $(COPTS) $(COMPILE_FLAGS)
+
+all: pppstats
+
+install: pppstats
+	-mkdir -p $(MANDIR)
+	$(INSTALL) -c pppstats $(BINDIR)
+	$(INSTALL) -c -m 444 pppstats.8 $(MANDIR)
+
+pppstats: $(PPPSTATSRCS)
+	$(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS)
+
+clean:
+	rm -f pppstats *~ #* core
+
+depend:
+	cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend
+#	makedepend $(CFLAGS) $(PPPSTATSRCS)
diff -Naur tomato/release/src/router/pppd/pppstats/Makefile.linux tomato121/release/src/router/pppd/pppstats/Makefile.linux
--- tomato/release/src/router/pppd/pppstats/Makefile.linux	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppstats/Makefile.linux	2008-04-22 21:44:05.000000000 +1000
@@ -1,25 +1,29 @@
 #
 # pppstats makefile
-# $Id: Makefile.linux,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makefile.linux,v 1.9 2006/06/04 05:07:46 paulus Exp $
 #
+DESTDIR = $(INSTROOT)@DESTDIR@
+BINDIR = $(DESTDIR)/sbin
+MANDIR = $(DESTDIR)/share/man/man8
 
 PPPSTATSRCS = pppstats.c
 PPPSTATOBJS = pppstats.o
 
 #CC = gcc
 COPTS = -O
-COMPILE_FLAGS = -D_linux_ -I../include
+COMPILE_FLAGS = -I../include
 LIBS =
 
-INSTALL= install -o root -g daemon
+INSTALL= install
 
 CFLAGS = $(COPTS) $(COMPILE_FLAGS)
 
 all: pppstats
 
 install: pppstats
-	$(INSTALL) -s -c pppstats $(BINDIR)/pppstats
-	$(INSTALL) -c -m 444 pppstats.8 $(MANDIR)/man8/pppstats.8
+	-mkdir -p $(MANDIR)
+	$(INSTALL) -c pppstats $(BINDIR)
+	$(INSTALL) -c -m 444 pppstats.8 $(MANDIR)
 
 pppstats: $(PPPSTATSRCS)
 	$(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS)
diff -Naur tomato/release/src/router/pppd/pppstats/Makefile.sol2 tomato121/release/src/router/pppd/pppstats/Makefile.sol2
--- tomato/release/src/router/pppd/pppstats/Makefile.sol2	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppstats/Makefile.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -1,9 +1,9 @@
 #
 # pppstats Makefile for SVR4 systems
-# $Id: Makefile.sol2,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makefile.sol2,v 1.10 2002/09/07 05:15:25 carlsonj Exp $
 #
 
-include ../solaris/Makedefs
+include ../Makedefs.com
 
 CFLAGS = -DSTREAMS -I../include $(COPTS)
 
diff -Naur tomato/release/src/router/pppd/pppstats/Makefile.sunos4 tomato121/release/src/router/pppd/pppstats/Makefile.sunos4
--- tomato/release/src/router/pppd/pppstats/Makefile.sunos4	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppstats/Makefile.sunos4	1970-01-01 10:00:00.000000000 +1000
@@ -1,30 +0,0 @@
-#
-# pppstats makefile
-# $Id: Makefile.sunos4,v 1.1 2003/07/10 07:43:05 honor Exp $
-#
-
-include ../sunos4/Makedefs
-
-PPPSTATSRCS = pppstats.c
-PPPSTATOBJS = pppstats.o
-
-COMPILE_FLAGS = -DSTREAMS -DSUNOS4
-LIBS =
-
-CFLAGS = -I../include $(COPTS) $(COMPILE_FLAGS)
-
-all: pppstats
-
-install: pppstats
-	$(INSTALL) -c pppstats $(BINDIR)/pppstats
-	$(INSTALL) -c -m 444 pppstats.8 $(MANDIR)/man8/pppstats.8
-
-pppstats: $(PPPSTATSRCS)
-	$(CC) $(CFLAGS) -o pppstats pppstats.c $(LIBS)
-
-clean:
-	rm -f pppstats *~ #* core
-
-depend:
-	cpp -M $(CFLAGS) $(PPPSTATSRCS) >.depend
-#	makedepend $(CFLAGS) $(PPPSTATSRCS)
diff -Naur tomato/release/src/router/pppd/pppstats/pppstats.8 tomato121/release/src/router/pppd/pppstats/pppstats.8
--- tomato/release/src/router/pppd/pppstats/pppstats.8	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppstats/pppstats.8	2008-04-22 21:44:05.000000000 +1000
@@ -1,22 +1,22 @@
-.\"	@(#) $Id: pppstats.8,v 1.1 2003/07/10 07:43:05 honor Exp $
+.\"	@(#) $Id: pppstats.8,v 1.4 2004/11/13 12:22:49 paulus Exp $
 .TH PPPSTATS 8 "26 June 1995"
 .SH NAME
 pppstats \- print PPP statistics
 .SH SYNOPSIS
 .B pppstats
 [
-.B -a
+.B \-a
 ] [
-.B -v
+.B \-v
 ] [
-.B -r
+.B \-r
 ] [
-.B -z
+.B \-z
 ] [
-.B -c
+.B \-c
 .I <count>
 ] [
-.B -w
+.B \-w
 .I <secs>
 ] [
 .I interface
@@ -25,7 +25,7 @@
 .SH DESCRIPTION
 The
 .B pppstats
-utility reports PPP-related statistics at regular intervals for the
+utility reports PPP\-related statistics at regular intervals for the
 specified PPP interface.  If the interface is unspecified, it will
 default to ppp0.
 The display is split horizontally
@@ -35,40 +35,40 @@
 .PP
 The options are as follows:
 .TP
-.B -a
+.B \-a
 Display absolute values rather than deltas.  With this option, all
 reports show statistics for the time since the link was initiated.
 Without this option, the second and subsequent reports show statistics
 for the time since the last report.
 .TP
-.B -c \fIcount
+.B \-c \fIcount
 Repeat the display
 .I count
 times.  If this option is not specified, the default repeat count is 1
 if the
-.B -w
+.B \-w
 option is not specified, otherwise infinity.
 .TP
-.B -r
+.B \-r
 Display additional statistics summarizing the compression ratio
 achieved by the packet compression algorithm in use.
 .TP
-.B -v
+.B \-v
 Display additional statistics relating to the performance of the Van
 Jacobson TCP header compression algorithm.
 .TP
-.B -w \fIwait
+.B \-w \fIwait
 Pause
 .I wait
 seconds between each display.  If this option is not specified, the
 default interval is 5 seconds.
 .TP
-.B -z
+.B \-z
 Instead of the standard display, show statistics indicating the
 performance of the packet compression algorithm in use.
 .PP
 The following fields are printed on the input side when the
-.B -z
+.B \-z
 option is not used:
 .TP
 .B IN
@@ -83,25 +83,25 @@
 .B VJUNC
 The number of header-uncompressed TCP packets received by this
 interface.  Not reported when the
-.B -r
+.B \-r
 option is specified.
 .TP
 .B VJERR
 The number of corrupted or bogus header-compressed TCP packets
 received by this interface.  Not reported when the
-.B -r
+.B \-r
 option is specified.
 .TP
 .B VJTOSS
 The number of VJ header-compressed TCP packets dropped on reception by
 this interface because of preceding errors.  Only reported when the
-.B -v
+.B \-v
 option is specified.
 .TP
 .B NON-VJ
 The total number of non-TCP packets received by this interface. Only
 reported when the
-.B -v
+.B \-v
 option is specified.
 .TP
 .B RATIO
@@ -109,13 +109,13 @@
 packet compression scheme in use, defined as the uncompressed size
 divided by the compressed size.
 Only reported when the
-.B -r
+.B \-r
 option is specified.
 .TP
 .B UBYTE
 The total number of bytes received, after decompression of compressed
 packets.  Only reported when the
-.B -r
+.B \-r
 option is specified.
 .PP
 The following fields are printed on the output side:
@@ -134,25 +134,25 @@
 The number of TCP packets transmitted from this interface with
 VJ-uncompressed TCP headers.
 Not reported when the
-.B -r
+.B \-r
 option is specified.
 .TP
 .B NON-VJ
 The total number of non-TCP packets transmitted from this interface.
 Not reported when the
-.B -r
+.B \-r
 option is specified.
 .TP
 .B VJSRCH
 The number of searches for the cached header entry for a VJ header
 compressed TCP packet.  Only reported when the
-.B -v
+.B \-v
 option is specified.
 .TP
 .B VJMISS
 The number of failed searches for the cached header entry for a
 VJ header compressed TCP packet.  Only reported when the
-.B -v
+.B \-v
 option is specified.
 .TP
 .B RATIO
@@ -160,17 +160,17 @@
 packet compression scheme in use, defined as the size
 before compression divided by the compressed size.
 Only reported when the
-.B -r
+.B \-r
 option is specified.
 .TP
 .B UBYTE
 The total number of bytes to be transmitted, before packet compression
 is applied.  Only reported when the
-.B -r
+.B \-r
 option is specified.
 .PP
 When the
-.B -z
+.B \-z
 option is specified,
 .Nm pppstats
 instead displays the following fields, relating to the packet
diff -Naur tomato/release/src/router/pppd/pppstats/pppstats.c tomato121/release/src/router/pppd/pppstats/pppstats.c
--- tomato/release/src/router/pppd/pppstats/pppstats.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/pppstats/pppstats.c	2008-04-22 21:44:05.000000000 +1000
@@ -36,7 +36,7 @@
 #endif
 
 #ifndef lint
-static const char rcsid[] = "$Id: pppstats.c,v 1.1 2003/07/10 07:43:05 honor Exp $";
+static const char rcsid[] = "$Id: pppstats.c,v 1.29 2002/10/27 12:56:26 fcusack Exp $";
 #endif
 
 #include <stdio.h>
@@ -53,13 +53,13 @@
 #include <sys/ioctl.h>
 
 #ifndef STREAMS
-#if defined(_linux_) && defined(__powerpc__) \
+#if defined(__linux__) && defined(__powerpc__) \
     && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
 /* kludge alert! */
 #undef __GLIBC__
 #endif
 #include <sys/socket.h>		/* *BSD, Linux, NeXT, Ultrix etc. */
-#ifndef _linux_
+#ifndef __linux__
 #include <net/if.h>
 #include <net/ppp_defs.h>
 #include <net/if_ppp.h>
@@ -74,7 +74,7 @@
 #endif
 #include <linux/ppp_defs.h>
 #include <linux/if_ppp.h>
-#endif /* _linux_ */
+#endif /* __linux__ */
 
 #else	/* STREAMS */
 #include <sys/stropts.h>	/* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
@@ -144,7 +144,7 @@
 
     memset (&req, 0, sizeof (req));
 
-#ifdef _linux_
+#ifdef __linux__
     req.stats_ptr = (caddr_t) &req.stats;
 #undef ifr_name
 #define ifr_name ifr__name
@@ -170,7 +170,7 @@
 
     memset (&creq, 0, sizeof (creq));
 
-#ifdef _linux_
+#ifdef __linux__
     creq.stats_ptr = (caddr_t) &creq.stats;
 #undef  ifr_name
 #define ifr_name ifr__name
@@ -190,7 +190,7 @@
 	}
     }
 
-#ifdef _linux_
+#ifdef __linux__
     if (creq.stats.c.bytes_out == 0) {
 	creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes;
 	creq.stats.c.in_count = creq.stats.c.unc_bytes;
@@ -522,7 +522,7 @@
 	    exit(1);
 	}
 
-#ifdef _linux_
+#ifdef __linux__
 #undef  ifr_name
 #define ifr_name ifr_ifrn.ifrn_name
 #endif
diff -Naur tomato/release/src/router/pppd/README tomato121/release/src/router/pppd/README
--- tomato/release/src/router/pppd/README	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/README	2008-04-22 21:44:05.000000000 +1000
@@ -17,18 +17,17 @@
 - Kernel code, which establishes a network interface and passes
 packets between the serial port, the kernel networking code and the
 PPP daemon (pppd).  This code is implemented using STREAMS modules on
-SunOS 4.x and Solaris, and as a line discipline under Linux and FreeBSD.
+Solaris, and as a line discipline under Linux.
 
 - The PPP daemon (pppd), which negotiates with the peer to establish
 the link and sets up the ppp network interface.  Pppd includes support
 for authentication, so you can control which other systems may make a
 PPP connection and what IP addresses they may use.
 
-The primary platforms supported by this package are Linux and Solaris.
-Code for SunOS 4.x is included here but is largely untested.  I have
-code for NeXTStep, FreeBSD, SVR4, Tru64 (Digital Unix), AIX and Ultrix
-but no active maintainers for these platforms.  Code for all of these
-except AIX is included in the ppp-2.3.11 release.
+The platforms supported by this package are Linux and Solaris.  I have
+code for NeXTStep, FreeBSD, SunOS 4.x, SVR4, Tru64 (Digital Unix), AIX
+and Ultrix but no active maintainers for these platforms.  Code for
+all of these except AIX is included in the ppp-2.3.11 release.
 
 
 Installation.
@@ -41,14 +40,13 @@
 files, are:
 
 	Linux				README.linux
-	Solaris 2			README.sol2
-	SunOS 4.x			README.sunos4
+	Solaris				README.sol2
 
 In each case you start by running the ./configure script.  This works
-out which operating system you are using and creates symbolic links to
-the appropriate makefiles.  You then run `make' to compile the
-user-level code, and (as root) `make install' to install the
-user-level programs pppd, chat and pppstats.
+out which operating system you are using and creates the appropriate
+makefiles.  You then run `make' to compile the user-level code, and
+(as root) `make install' to install the user-level programs pppd, chat
+and pppstats.
 
 N.B. Since 2.3.0, leaving the permitted IP addresses column of the
 pap-secrets or chap-secrets file empty means that no addresses are
@@ -57,9 +55,120 @@
 authenticating itself to you, of course.)
 
 
-What's new in ppp-2.4.1.
+What's new in ppp-2.4.4.
 ************************
 
+* Pppd will now run /etc/ppp/ip-pre-up, if it exists, after creating
+  the ppp interface and configuring its IP addresses but before
+  bringing it up.  This can be used, for example, for adding firewall
+  rules for the interface.
+
+* Lots of bugs fixed, particularly in the area of demand-dialled and
+  persistent connections.
+
+* The rp-pppoe plugin now accepts any interface name (that isn't an
+  existing pppd option name) without putting "nic-" on the front of
+  it, not just eth*, nas*, tap* and br*.
+
+
+What was new in ppp-2.4.3.
+**************************
+
+* The configure script now accepts --prefix and --sysconfdir options.
+  These default to /usr/local and /etc.  If you want pppd put in
+  /usr/sbin as before, use ./configure --prefix=/usr.
+
+* Doing `make install' no longer puts example configuration files in
+  /etc/ppp.  Use `make install-etcppp' if you want that.
+
+* The code has been updated to work with version 0.8.3 of libpcap.
+  Unfortunately the libpcap maintainers removed support for the
+  "inbound" and "outbound" keywords on PPP links, meaning that if you
+  link pppd with libpcap-0.8.3, you can't use those keywords in the
+  active-filter and pass-filter expressions.  The support has been
+  reinstated in the CVS version and should be in future libpcap
+  releases.  If you need the in/outbound keywords, use a later release
+  than 0.8.3, or get the CVS version from http://www.tcpdump.org.
+
+* There is a new option, child-timeout, which sets the length of time
+  that pppd will wait for child processes (such as the command
+  specified with the pty option) to exit before exiting itself.  It
+  defaults to 5 seconds.  After the timeout, pppd will send a SIGTERM
+  to any remaining child processes and exit.  A value of 0 means no
+  timeout.
+
+* Various bugs have been fixed, including some CBCP packet parsing
+  bugs that could lead to the peer being able to crash pppd if CBCP
+  support is enabled.
+
+* Various fixes and enhancements to the radius and rp-pppoe plugins
+  have been added.
+
+* There is a new winbind plugin, from Andrew Bartlet of the Samba
+  team, which provides the ability to authenticate the peer against an
+  NT domain controller using MS-CHAP or MS-CHAPV2.
+
+* There is a new pppoatm plugin, by various authors, sent in by David
+  Woodhouse.
+
+* The multilink code has been substantially reworked.  The first pppd
+  for a bundle still controls the ppp interface, but it doesn't exit
+  until all the links in the bundle have terminated.  If the first
+  pppd is signalled to exit, it signals all the other pppds
+  controlling links in the bundle.
+
+* The TDB code has been updated to the latest version.  This should
+  eliminate the problem that some people have seen where the database
+  file (/var/run/pppd.tdb) keeps on growing.  Unfortunately, however,
+  the new code uses an incompatible database format.  For this reason,
+  pppd now uses /var/run/pppd2.tdb as the database filename.
+
+
+What was new in ppp-2.4.2.
+**************************
+
+* The CHAP code has been rewritten.  Pppd now has support for MS-CHAP
+  V1 and V2 authentication, both as server and client.  The new CHAP
+  code is cleaner than the old code and avoids some copyright problems
+  that existed in the old code.
+
+* MPPE (Microsoft Point-to-Point Encryption) support has been added,
+  although the current implementation shouldn't be considered
+  completely secure.  (There is no assurance that the current code
+  won't ever transmit an unencrypted packet.)
+
+* James Carlson's implementation of the Extensible Authentication
+  Protocol (EAP) has been added.
+
+* Support for the Encryption Control Protocol (ECP) has been added.
+
+* Some new plug-ins have been included:
+  - A plug-in for kernel-mode PPPoE (PPP over Ethernet)
+  - A plug-in for supplying the PAP password over a pipe from another
+    process
+  - A plug-in for authenticating using a Radius server.
+
+* Updates and bug-fixes for the Solaris port.
+
+* The CBCP (Call Back Control Protocol) code has been updated.  There
+  are new options `remotenumber' and `allow-number'.
+
+* Extra hooks for plugins to use have been added.
+
+* There is now a `maxoctets' option, which causes pppd to terminate
+  the link once the number of bytes passed on the link exceeds a given
+  value.
+
+* There are now options to control whether pppd can use the IPCP
+  IP-Address and IP-Addresses options: `ipcp-no-address' and
+  `ipcp-no-addresses'.
+
+* Fixed several bugs, including potential buffer overflows in chat.
+
+
+What was new in ppp-2.4.1.
+**************************
+
 * Pppd can now print out the set of options that are in effect.  The
   new `dump' option causes pppd to print out the option values after
   option parsing is complete.  The `dryrun' option causes pppd to
@@ -94,7 +203,7 @@
 * Multilink: this package now allows you to combine multiple serial
   links into one logical link or `bundle', for increased bandwidth and
   reduced latency.  This is currently only supported under the
-  Linux-2.3.99pre5 or later kernels.
+  2.4.x and later Linux kernels.
 
 * All the pppd processes running on a system now write information
   into a common database.  I used the `tdb' code from samba for this.
@@ -113,23 +222,25 @@
 include Predictor, LZS, and MPPC.  These methods are not supported for
 two reasons - they are patent-encumbered, and they cause some packets
 to expand slightly, which pppd doesn't currently allow for.
-BSD-Compress is also patent-encumbered (its inclusion in this package
-can be considered a historical anomaly :-) but it doesn't ever expand
-packets.  Neither does Deflate, which uses the same algorithm as gzip.
+BSD-Compress and Deflate (which uses the same algorithm as gzip) don't
+ever expand packets.
 
 
 Patents.
 ********
 
 The BSD-Compress algorithm used for packet compression is the same as
-that used in the Unix "compress" command.  It is apparently covered by
-U.S. patents 4,814,746 (owned by IBM) and 4,558,302 (owned by Unisys),
-and corresponding patents in various other countries (but not
-Australia).  If this is of concern, you can build the package without
-including BSD-Compress.  To do this, edit net/ppp-comp.h to change the
-definition of DO_BSD_COMPRESS to 0.  The bsd-comp.c files are then no
-longer needed, so the references to bsd-comp.o may optionally be
-removed from the Makefiles.
+that used in the Unix "compress" command.  It was apparently covered
+by U.S. patents 4,814,746 (owned by IBM) and 4,558,302 (owned by
+Unisys), and corresponding patents in various other countries (but not
+Australia).  Apparently the Unisys patent expired in the US on 20 June
+2003, but the IBM patent is still pending.
+
+If these patents are of concern in your situation, you can build the
+package without including BSD-Compress.  To do this, edit
+net/ppp-comp.h to change the definition of DO_BSD_COMPRESS to 0.  The
+bsd-comp.c files are then no longer needed, so the references to
+bsd-comp.o may optionally be removed from the Makefiles.
 
 
 Contacts.
@@ -144,17 +255,17 @@
 If you find bugs in this package, please report them to the maintainer
 for the port for the operating system you are using:
 
-Linux			Paul Mackerras <paulus@linuxcare.com>
-Solaris 2		James Carlson <james.d.carlson@east.sun.com>
-SunOS 4.x		Adi Masputra <adi.masputra@sun.com>
+Linux			Paul Mackerras <paulus@samba.org>
+Solaris			James Carlson <carlson@workingcode.com>
 
 
 Copyrights:
 ***********
 
 All of the code can be freely used and redistributed.  The individual
-source files each have their own copyright and permission notice; some
-have a BSD-style notice and some are under the GPL.
+source files each have their own copyright and permission notice.
+Pppd, pppstats and pppdump are under BSD-style notices.  Some of the
+pppd plugins are GPL'd.  Chat is public domain.
 
 
 Distribution:
@@ -162,7 +273,7 @@
 
 The primary site for releases of this software is:
 
-	ftp://linuxcare.com.au/pub/ppp/
+	ftp://ftp.samba.org/pub/ppp/
 
 
-($Id: README,v 1.1 2003/07/10 07:43:04 honor Exp $)
+($Id: README,v 1.37 2006/05/29 23:51:29 paulus Exp $)
diff -Naur tomato/release/src/router/pppd/README.cbcp tomato121/release/src/router/pppd/README.cbcp
--- tomato/release/src/router/pppd/README.cbcp	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/README.cbcp	2008-04-22 21:44:05.000000000 +1000
@@ -8,7 +8,7 @@
 connect with it are required that following a valid authentication to
 leave a method by which the number may be returned call.
 
-It is a requirement of servers so configured that the protocol be
+It is a requirement of servers to be so configured that the protocol be
 exchanged.
 
 So, this set of patches may be applied to the pppd process to enable
@@ -22,76 +22,30 @@
 extended to permit the callback operation. For this reason, these
 patches are not 'part' of pppd but are an adjunct to the code.
 
-To enable CBCP support, all that is required is to change the
-appropriate Makefile in the pppd subdirectory to add "-DCBCP_SUPPORT"
-to the CFLAGS definition and add cbcp.o to the list of object files,
-and then recompile pppd.  The patch below does this for Makefile.bsd
-and Makefile.linux.
-
-
---------------------------------cut here-------------------------------
-diff -r -c ppp-2.3.orig/pppd/Makefile.bsd ppp-2.3/pppd/Makefile.bsd
-*** ppp-2.3.orig/pppd/Makefile.bsd	Tue Oct  8 13:33:33 1996
---- ppp-2.3/pppd/Makefile.bsd	Fri Apr 11 23:59:15 1997
-***************
-*** 4,14 ****
-  # -D_BITYPES is for FreeBSD, which doesn't define anything to
-  # tell us that u_int32_t gets defined if <sys/types.h> is included.
-  # Remove for older *BSD systems for which this isn't true.
-! CFLAGS+= -g -I.. -DHAVE_PATHS_H -D_BITYPES
-  
-  PROG=	pppd
-  SRCS=	main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-! 	demand.c auth.c options.c sys-bsd.c
-  MAN=	pppd.cat8
-  MAN8=	pppd.8
-  BINMODE=4555
---- 4,14 ----
-  # -D_BITYPES is for FreeBSD, which doesn't define anything to
-  # tell us that u_int32_t gets defined if <sys/types.h> is included.
-  # Remove for older *BSD systems for which this isn't true.
-! CFLAGS+= -I.. -DHAVE_PATHS_H -D_BITYPES -DCBCP_SUPPORT
-  
-  PROG=	pppd
-  SRCS=	main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
-! 	demand.c auth.c options.c sys-bsd.c cbcp.c
-  MAN=	pppd.cat8
-  MAN8=	pppd.8
-  BINMODE=4555
-diff -r -c ppp-2.3.orig/pppd/Makefile.linux ppp-2.3/pppd/Makefile.linux
-*** ppp-2.3.orig/pppd/Makefile.linux	Tue Oct  8 15:42:41 1996
---- ppp-2.3/pppd/Makefile.linux	Sat Apr 12 00:02:28 1997
-***************
-*** 14,20 ****
-  	   ipxcp.h cbcp.h
-  MANPAGES = pppd.8
-  PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-! 	   auth.o options.o demand.o sys-linux.o ipxcp.o
-  
-  all: pppd
-  
---- 14,20 ----
-  	   ipxcp.h cbcp.h
-  MANPAGES = pppd.8
-  PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-! 	   auth.o options.o demand.o sys-linux.o ipxcp.o cbcp.o
-  
-  all: pppd
-  
-***************
-*** 36,42 ****
-  #INCLUDE_DIRS= -I/usr/include -I..
-  INCLUDE_DIRS=
-  
-! COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE
-  
-  CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
-  
---- 36,42 ----
-  #INCLUDE_DIRS= -I/usr/include -I..
-  INCLUDE_DIRS=
-  
-! COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DCBCP_SUPPORT
-  
-  CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
-  
+To enable CBCP support, all that is required is to uncomment the line
+in Makefile.linux that sets CBCP=y and recompile pppd.
+
+I use such script to make a callback:
+
+pppd debug nodetach /dev/modem 115200 crtscts modem	\
+callback 222222 name NAME remotename SERVER	\
+connect 'chat -v "" atz OK atdt111111 CONNECT ""'
+sleep 1
+pppd debug /dev/modem 115200 crtscts modem	\
+name NAME remotename SERVER defaultroute	\
+connect 'chat -v RING ATA CONNECT "\c"'
+
+First we invoke pppd with 'nodetach' option in order to not detach from
+the controlling terminal and 'callback NUMBER' option, then wait for
+1 second and invoke pppd again which waits for a callback (RING) and
+then answers (ATA). Number 222222 is a callback number, i.e. server will
+call us back at this number, while number 111111 is the number we are
+calling to.
+
+You have to put in /etc/ppp/chap-secrets the following two lines:
+
+NAME SERVER PASSWORD
+SERVER NAME PASSWORD
+
+You have to use your real login name, remote server name and password.
+
diff -Naur tomato/release/src/router/pppd/README.eap-srp tomato121/release/src/router/pppd/README.eap-srp
--- tomato/release/src/router/pppd/README.eap-srp	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/README.eap-srp	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,149 @@
+EAP with MD5-Challenge and SRP-SHA1 support
+by James Carlson, Sun Microsystems
+Version 2, September 22nd, 2002
+
+
+1.  What it does
+
+    The Extensible Authentication Protocol (EAP; RFC 2284) is a
+    security protocol that can be used with PPP.  It provides a means
+    to plug in multiple optional authentication methods.
+
+    This implementation includes the required default MD5-Challenge
+    method, which is similar to CHAP (RFC 1994), as well as the new
+    SRP-SHA1 method.  This latter method relies on an exchange that is
+    not vulnerable to dictionary attacks (as is CHAP), does not
+    require the server to keep a cleartext copy of the secret (as in
+    CHAP), supports identity privacy, and produces a temporary shared
+    key that could be used for data encryption.
+
+    The SRP-SHA1 method is based on draft-ietf-pppext-eap-srp-03.txt,
+    a work in progress.
+
+2.  Required libraries
+
+    Two other packages are required first.  Download and install
+    OpenSSL and Thomas Wu's SRP implementation.
+
+	http://www.openssl.org/ (or ftp://ftp.openssl.org/source/)
+	http://srp.stanford.edu/
+
+    Follow the directions in each package to install the SSL and SRP
+    libraries.  Once SRP is installed, you may run tconf as root to
+    create known fields, if desired.  (This step is not required.)
+
+3.  Installing the patch
+
+    The EAP-SRP patch described here is integrated into this version
+    of pppd.  The following patch may be used with older pppd sources:
+
+	ftp://playground.sun.com/carlsonj/eap/ppp-2.4.1-eap-1.tar.gz
+
+    Configure, compile, and install as root.  You may want to edit
+    pppd/Makefile after configuring to enable or disable optional
+    features.
+
+	% ./configure
+	% make
+	% su
+	# make install
+
+    If you use csh or tcsh, run "rehash" to pick up the new commands.
+
+    If you're using Solaris, and you run into trouble with the
+    pseudonym feature on the server side ("no DES here" shows in the
+    log file), make sure that you have the "domestic" versions of the
+    DES libraries linked.  You should see "crypt_d" in "ldd
+    /usr/local/bin/pppd".  If you see "crypt_i" instead, then make
+    sure that /usr/lib/libcrypt.* links to /usr/lib/libcrypt_d.*.  (If
+    you have the international version of Solaris, then you won't have
+    crypt_d.  You might want to find an alternative DES library.)
+
+4.  Adding the secrets
+
+    On the EAP SRP-SHA1 client side, access to the cleartext secret is
+    required.  This can be done in two ways:
+
+	- Enter the client name, server name, and password in the
+          /etc/ppp/srp-secrets file.  This file has the same format as
+          the existing chap-secrets and pap-secrets files.
+
+	  clientname servername "secret here"
+
+	- Use the "password" option in any of the standard
+          configuration files (or the command line) to specify the
+          secret.
+
+	  password "secret here"
+
+    On the EAP SRP-SHA1 server side, a secret verifier is required.
+    This is a one-way hash of the client's name and password.  To
+    generate this value, run the srp-entry program (see srp-entry(8)).
+    This program prompts for the client name and the passphrase (the
+    secret).  The output will be an entry, such as the following,
+    suitable for use in the server's srp-secrets file.  Note that if
+    this is transferred by cut-and-paste, the entry must be a single
+    line of text in the file.
+
+pppuser srpserver 0:LFDpwg4HBLi4/kWByzbZpW6pE95/iIWBSt7L.DAkHsvwQphtiq0f6reoUy/1LC1qYqjcrV97lCDmQHQd4KIACGgtkhttLdP3KMowvS0wLXLo25FPJeG2sMAUEWu/HlJPn2/gHyh9aT.ZxUs5MsoQ1E61sJkVBc.2qze1CdZiQGTK3qtWRP6DOpM1bfhKtPoVm.g.MiCcTMWzc54xJUIA0mgKtpthE3JrqCc81cXUt4DYi5yBzeeGTqrI0z2/Gj8Jp7pS4Fkq3GmnYjMxnKfQorFXNwl3m7JSaPa8Gj9/BqnorJOsnSMlIhBe6dy4CYytuTbNb4Wv/nFkmSThK782V:2cIyMp1yKslQgE *
+
+    The "secret" field consists of three entries separated by colons.
+    The first entry is the index of the modulus and generator from
+    SRP's /etc/tpasswd.conf.  If the special value 0 is used, then the
+    well-known modulus/generator value is used (this is recommended,
+    because it is much faster).  The second value is the verifier
+    value.  The third is the password "salt."  These latter two values
+    are encoded in base64 notation.
+
+    For EAP MD5-Challenge, both client and server use the existing
+    /etc/ppp/chap-secrets file.
+
+5.  Configuration options
+
+    There are two main options relating to EAP available for the
+    client.  These are:
+
+	refuse-eap		- refuse to authenticate with EAP
+	srp-use-pseudonym	- use the identity privacy if
+				  offered by server
+
+    The second option stores a pseudonym, if offered by the EAP
+    SRP-SHA1 server, in the $HOME/.ppp_pseudonym file.  The pseudonym
+    is typically an encrypted version of the client identity.  During
+    EAP start-up, the pseudonym stored in this file is offered to the
+    peer as the identity.  If this is accepted by the peer, then
+    eavesdroppers will be unable to determine the identity of the
+    client.  Each time the client is authenticated, the server will
+    offer a new pseudoname to the client using an obscured (reversibly
+    encrypted) message.  Thus, access across successive sessions
+    cannot be tracked.
+
+    There are two main options for EAP on the server:
+
+	require-eap		- require client to use EAP
+	srp-pn-secret "string"	- set server's pseudoname secret
+
+    The second option sets the long-term secret used on the server to
+    encrypt the user's identity to produce pseudonames.  The
+    pseudoname is constructed by hashing this string with the current
+    date (to the nearest day) with SHA1, then using this hash as the
+    key for a DES encryption of the client's name.  The date is added
+    to the hash for two reasons.  First, this allows the pseudonym to
+    change daily.  Second, it allows the server to decode any previous
+    pseudonym by trying previous dates.
+
+    See the pppd(8) man page for additional options.
+
+6.  Comments welcome!
+
+    This is still an experimental implementation.  It has been tested
+    and reviewed carefully for correctness, but may still be
+    incomplete or have other flaws.  All comments are welcome.  Please
+    address them to the author:
+
+		james.d.carlson@sun.com
+
+    or, for EAP itself or the SRP extensions to EAP, to the IETF PPP
+    Extensions working group:
+
+		ietf-ppp@merit.edu
diff -Naur tomato/release/src/router/pppd/README.linux tomato121/release/src/router/pppd/README.linux
--- tomato/release/src/router/pppd/README.linux	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/README.linux	2008-04-22 21:44:05.000000000 +1000
@@ -4,7 +4,7 @@
 		Paul Mackerras
 		8 March 2001
 
-		for ppp-2.4.1
+		for ppp-2.4.2
 
 1. Introduction
 ---------------
@@ -169,9 +169,8 @@
 To send a message to the list, email it to linux-ppp@vger.kernel.org.
 You don't have to be subscribed to send messages to the list.
 
-You can also email me (paulus@linuxcare.com.au) but I am overloaded
-with email and I can't respond to most messages I get in a timely
-fashion.
+You can also email me (paulus@samba.org) but I am overloaded with
+email and I can't respond to most messages I get in a timely fashion.
 
 There are also several relevant news groups, such as comp.protocols.ppp,
 comp.os.linux.networking, or comp.os.linux.setup.
diff -Naur tomato/release/src/router/pppd/README.MPPE tomato121/release/src/router/pppd/README.MPPE
--- tomato/release/src/router/pppd/README.MPPE	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/README.MPPE	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,85 @@
+PPP Support for MPPE (Microsoft Point to Point Encryption)
+==========================================================
+
+Frank Cusack		frank@google.com
+Mar 19, 2002
+
+
+DISCUSSION
+
+MPPE is Microsoft's encryption scheme for PPP links.  It is pretty much
+solely intended for use with PPP over Internet links -- if you have a true
+point to point link you have little need for encryption.  It is generally
+used with PPTP.
+
+MPPE is negotiated within CCP (Compression Control Protocol) as option
+18.  In order for MPPE to work, both peers must agree to do it.  This
+complicates things enough that I chose to implement it as strictly a binary
+option, off by default.  If you turn it on, all other compression options
+are disabled and MPPE *must* be negotiated successfully in both directions
+(CCP is unidirectional) or the link will be disconnected.  I think this is
+reasonable since, if you want encryption, you want encryption.  That is,
+I am not convinced that optional encryption is useful.
+
+While PPP regards MPPE as a "compressor", it actually expands every frame
+by 4 bytes, the MPPE overhead (encapsulation).
+
+Because of the data expansion, you'll see that ppp interfaces get their
+mtu reduced by 4 bytes whenever MPPE is negotiated.  This is because
+when MPPE is active, it is *required* that *every* packet be encrypted.
+PPPD sets the mtu = MIN(peer mru, configured mtu).  To ensure that
+MPPE frames are not larger than the peer's mru, we reduce the mtu by 4
+bytes so that the network layer never sends ppp a packet that's too large.
+
+There is an option to compress the data before encrypting (MPPC), however
+the algorithm is patented and requires execution of a license with Hifn.
+MPPC as an RFC is a complete farce.  I have no further details on MPPC.
+
+Some recommendations:
+
+- Use stateless mode.  Stateful mode is disabled by default.  Unfortunately,
+  stateless mode is very expensive as the peers must rekey for every packet.
+- Use 128-bit encryption.
+- Use MS-CHAPv2 only.
+
+Reference documents:
+
+    <http://www.ietf.org/rfc/rfc3078.txt> MPPE
+    <http://www.ietf.org/rfc/rfc3079.txt> MPPE Key Derivation
+    <http://www.ietf.org/rfc/rfc2118.txt> MPPC
+    <http://www.ietf.org/rfc/rfc2637.txt> PPTP
+    <http://www.ietf.org/rfc/rfc2548.txt> MS RADIUS Attributes
+
+You might be interested in PoPToP, a Linux PPTP server.  You can find it at
+<http://www.poptop.org/>
+
+RADIUS support for MPPE is from Ralf Hofmann, <ralf.hofmann@elvido.net>.
+
+
+BUILDING THE PPPD
+
+The userland component of PPPD has no additional requirements above
+those for MS-CHAP and MS-CHAPv2.  The kernel, however, requires SHA-1
+and ARCFOUR.  Public domain implementations of these are provided.
+
+Until such time as MPPE support ships with kernels, you can use
+the Linux 2.2 or 2.4 implementation that comes with PPPD.  Run the
+ppp/linux/mppe/mppeinstall.sh script, giving it the location to your
+kernel source.  Then add the CONFIG_PPP_MPPE option to your config and
+rebuild the kernel.  The ppp_mppe.o module is added, and the ppp.o module
+(2.2) or ppp_generic.o (2.4) is modified (unfortunately).  You'll need
+the new ppp.o/ppp_generic.o since it does the right thing for the 4
+extra bytes problem discussed above.
+
+
+CONFIGURATION
+
+See pppd(8) for the MPPE options.  Under Linux, if your modutils is earlier
+than 2.4.15, you will need to add
+
+    alias ppp-compress-18 ppp_mppe
+
+to /etc/modules.conf.  (A patch for earlier versions of modutils is included
+with the kernel patches.)
+
+
diff -Naur tomato/release/src/router/pppd/README.MSCHAP80 tomato121/release/src/router/pppd/README.MSCHAP80
--- tomato/release/src/router/pppd/README.MSCHAP80	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/README.MSCHAP80	2008-04-22 21:44:05.000000000 +1000
@@ -1,10 +1,11 @@
-PPP Client Support for Microsoft's CHAP-80
-==========================================
+PPP Support for Microsoft's CHAP-80
+===================================
 
 Eric Rosenquist          rosenqui@strataware.com
 (updated by Paul Mackerras)
 (updated by Al Longyear)
 (updated by Farrell Woods)
+(updated by Frank Cusack)
 
 INTRODUCTION
 
@@ -16,7 +17,7 @@
 the password were stored in cleartext.)  The details of the Microsoft
 extensions can be found in the document:
 
-    <ftp://ftp.microsoft.com/developr/rfc/chapexts.txt>
+    <http://www.ietf.org/rfc/rfc2433.txt>
 
 In short, MS-CHAP is identified as <auth chap 80> since the hex value
 of 80 is used to designate Microsoft's scheme.  Standard PPP CHAP uses
@@ -35,12 +36,7 @@
 Windows NT Server systems are often configured to "Accept only
 Microsoft Authentication" (this is intended to enhance security).  Up
 until now, that meant that you couldn't use this version of PPPD to
-connect to such a system.  I've managed to get a client-only
-implementation of MS-CHAP working; it will authenticate itself to
-another system using MS-CHAP, but if you're using PPPD as a dial-in
-server, you won't be able to use MS-CHAP to authenticate the clients.
-This would not be a lot of extra work given that the framework is in
-place, but I didn't need it myself so I didn't implement it.
+connect to such a system.
 
 
 BUILDING THE PPPD
@@ -166,7 +162,7 @@
 CHAP, the MS-CHAP-specific problems you're likely to encounter are mostly
 related to your Windows NT account and its settings.  A Microsoft server
 returns error codes in its CHAP response.  The following are extracted from
-Microsoft's "chapexts.txt" file referenced above:
+RFC 2433:
 
  646 ERROR_RESTRICTED_LOGON_HOURS
  647 ERROR_ACCT_DISABLED
@@ -196,76 +192,6 @@
 without getting any reply, try putting something in your chat script
 to send the word CLIENT after the modem has connected.
 
-If everything compiles cleanly, but fails at authentication time, then
-it might be a case of the MD4 or DES code screwing up.  The following
-small program can be used to test the MS-CHAP code to see if it
-produces a known response:
-
------------------
-#include <stdio.h>
-
-#include "pppd.h"
-#include "chap.h"
-#include "chap_ms.h"
-
-int main(argc, argv)
-    int     argc;
-    char    *argv[];
-{
-    u_char          challenge[8];
-    int             challengeInt[sizeof(challenge)];
-    chap_state      cstate;
-    int             i;
-
-    if (argc != 3) {
-        fprintf(stderr, "Usage: %s <16-hexchar challenge> <password>\n",
-        argv[0]); exit(1);
-    }
-
-    sscanf(argv[1], "%2x%2x%2x%2x%2x%2x%2x%2x",
-           challengeInt + 0, challengeInt + 1, challengeInt + 2,
-           challengeInt + 3, challengeInt + 4, challengeInt + 5,
-           challengeInt + 6, challengeInt + 7);
-
-    for (i = 0; i < sizeof(challenge); i++)
-        challenge[i] = (u_char)challengeInt[i];
-
-    ChapMS(&cstate, challenge, sizeof(challenge), argv[2], strlen(argv[2]));
-    printf("Response length is %d, response is:", cstate.resp_length);
-
-    for (i = 0; i < cstate.resp_length; i++) {
-        if (i % 8 == 0)
-            putchar('\n');
-        printf("%02X ", (unsigned int)cstate.response[i]);
-    }
-
-    putchar('\n');
-
-    exit(0);
-}
--------------
-
-This needs to link against chap_ms.o, md4.o, and the DES library.  When 
-you run it with the command line:
-
- $ testchap 00000000000000000000000000000000 hello
-
-it should output the following:
-
- Response length is 49, response is:
- 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00
- F4 D9 9D AF 82 64 DC 3C
- 53 F9 BC 92 14 B5 5D 9E
- 78 C4 21 48 9D B7 A8 B4
- 01
-
-if not, then either the DES library is not working, the MD4 code isn't 
-working, or there are some problems with the port of the code in 
-chap_ms.c.
-
-
 STILL TO DO
 
 A site using only MS-CHAP to authenticate has no need to store cleartext
@@ -275,10 +201,5 @@
 could quite easily be lifted from chap_ms.c (you have to convert the
 password to Unicode before hashing it).  The chap_ms.c file would also have
 to be changed to recognize a password hash (16 binary bytes == 32 ASCII hex
-characters) and skip the hashing stage.
-
-A server implementation would allow MS-CHAP to be used with Windows NT and
-Windows 95 clients for enhanced security.  Some new command-line options
-would be required, as would code to generate the Challenge packet and
-verify the response.  Most of the helper functions are in place, so this
-shouldn't be too hard for someone to add.
+characters) and skip the hashing stage.  This would have no real security
+value as the hash is plaintext-equivalent.
diff -Naur tomato/release/src/router/pppd/README.MSCHAP81 tomato121/release/src/router/pppd/README.MSCHAP81
--- tomato/release/src/router/pppd/README.MSCHAP81	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/README.MSCHAP81	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,65 @@
+PPP Support for Microsoft's CHAP-81
+===================================
+
+Frank Cusack		frank@google.com
+
+Some text verbatim from README.MSCHAP80,
+by Eric Rosenquist, rosenqui@strataware.com
+
+INTRODUCTION
+
+First, please read README.MSCHAP80; almost everything there applies here.
+MS-CHAP was basically devised by Microsoft because rather than store
+plaintext passwords, they (Microsoft) store the md4 hash of passwords.
+It provides no advantage over standard CHAP, since the hash is used
+as plaintext-equivalent.  (Well, the Change-Password packet is arguably
+an advantage.)  It does introduce a significant weakness if the LM hash
+is used.  Additionally, the format of the failure packet potentially
+gives information to an attacker.  The weakness of the LM hash is partly
+addressed in RFC 2433, which deprecates its use.
+
+MS-CHAPv2 adds 2 benefits to MS-CHAP.  (1) The LM hash is no longer
+used.  (2) Mutual authentication is required.  Note that the mutual
+authentication in MS-CHAPv2 is different than the case where both PPP
+peers require authentication from the other; the former proves that
+the server has access to the client's password, the latter proves that
+the server has access to a secret which the client also has -- which
+may or may not be the same as the client's password (but should not be
+the same, per RFC 1994).  Whether this provides any actual benefit is
+outside the scope of this document.  The details of MS-CHAPv2 can be
+found in the document:
+
+    <http://www.ietf.org/rfc/rfc2759.txt>
+
+
+BUILDING THE PPPD
+
+In addition to the requirements for MS-CHAP, MS-CHAPv2 uses the SHA-1
+hash algorithm.  A public domain implementation is provided with pppd.
+
+
+TROUBLESHOOTING
+
+Assuming that everything else has been configured correctly for PPP and
+CHAP, the MS-CHAPv2-specific problems you're likely to encounter are mostly
+related to your Windows NT account and its settings.  A Microsoft server
+returns error codes in its CHAP response.  The following are extracted from
+RFC 2759:
+
+ 646 ERROR_RESTRICTED_LOGON_HOURS
+ 647 ERROR_ACCT_DISABLED
+ 648 ERROR_PASSWD_EXPIRED
+ 649 ERROR_NO_DIALIN_PERMISSION
+ 691 ERROR_AUTHENTICATION_FAILURE
+ 709 ERROR_CHANGING_PASSWORD
+
+You'll see these in your pppd log as a line similar to:
+
+   Remote message: E=649 No dialin permission
+
+Previously, pppd would log this as:
+
+   Remote message: E=649 R=0
+
+Now, the text message is logged (both for MS-CHAP and MS-CHAPv2).
+
diff -Naur tomato/release/src/router/pppd/README.pppoe tomato121/release/src/router/pppd/README.pppoe
--- tomato/release/src/router/pppd/README.pppoe	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/README.pppoe	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,94 @@
+		PPPoE Support
+		-------------
+
+		Michal Ostrowski
+		8 August 2001
+
+		for ppp-2.4.2
+
+1. Introduction
+---------------
+
+This document describes the support for PPP over Ethernet (PPPoE)
+included with this packages.  It is assumed that the reader is
+familiar with Linux PPP (as it pertains to tty/modem-based
+connections).  In particular, users of PPP in the Linux 2.2 series
+kernels should ensure they are familiar with the changes to the PPP
+implementation in the 2.4 series kernels before attempting to use
+PPPoE features.
+
+If you are not familiar with PPP, I recommend looking at other
+packages which include end-user configuration tools, such as Roaring
+Penguin (http://www.roaringpenguin.com/pppoe)
+
+PPPoE is a protocol typically used by *DSL providers to manage IP
+addresses and authenticate users.  Essentially, PPPoE provides for a
+PPP connection to be established not over a physical serial-line or
+modem, but over a logical connection between two unique MAC-addresses
+on an ethernet network.  Once the PPPoE layer discovers the end-points
+to be used in the link and negotiates it, frames may be sent to and
+received from the PPPoE layer just as if the link was a serial line
+(or that is how it's supposed to be).
+
+With this in mind, the goal of the implementation of PPPoE support in
+Linux is to allow users to simply specify that the device they intend
+to use for the PPP connection is an ethernet device (i.e. "eth0") and
+the rest of the system should function as usual.
+
+2. Using PPPoE
+--------------
+
+This section is a quick guide for getting PPPoE working, to allow one
+to connect to their ISP who is providing PPPoE based services.
+
+1.  Enable "Prompt for development and/or incomplete code/drivers" and
+    "PPP over Ethernet" in your kernel configuration.  If you choose to
+    use the PPP over Ethernet driver as a module adding "alias
+    net-pf-24 pppoe" to /etc/modules.conf will enable auto-loading
+    of the modules.
+
+2.  Compile and install your kernel.
+
+3.  Install the ppp package.
+
+4.  Add the following line to /etc/ppp/options:
+
+    plugin rp-pppoe.so
+
+    The effect of this line is simply to make "eth0", "eth1",
+    ....,"ethx" all valid device names for pppd (just like ttyS0,
+    ttyS1).
+
+5.  Add the necessary authentication options to your pppd
+    configuration (i.e. PAP/CHAP information).  If you wish to
+    maintain seperate configurations for different devices you may
+    place configuration options in device-specific configuration
+    files: /etc/ppp/options.devname (devname=ttyS0, ttyS1, eth0, eth1
+    or any other valid device name).
+
+6.  Invoke pppd with the appropriate device name: e.g. "pppd eth0"
+
+
+Do not include any compression or flow control options in your PPPoE
+configuration.  They will be ignored.
+
+Again, here it is assumed that the reader is familiar with the general
+process of configuring PPP.  The steps outlined here refer only to the
+steps and configuration options which are PPPoE specific, and it is
+assumed that the reader will also configure other aspects of the system
+(e.g. PAP authentication parameters).
+
+3.  Advanced Functionality
+--------------------------
+
+For more advanced functionality (such as providing PPPoE services) and
+user configuration tools, look to the Roaring Penguin PPPoE software
+package (http://www.roaringpenguin.com/pppoe).
+
+4.  Credits
+-----------
+
+The PPPoE plugin included in this package is a component of the
+Roaring Penguin PPPoE package, included in this package courtesy of
+Roaring Penguin Software. (http://www.roaringpenguin.com).
+
diff -Naur tomato/release/src/router/pppd/README.pwfd tomato121/release/src/router/pppd/README.pwfd
--- tomato/release/src/router/pppd/README.pwfd	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/README.pwfd	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,174 @@
+
+	Support to pass the password via a pipe to the pppd
+	---------------------------------------------------
+
+	Arvin Schnell <arvin@suse.de>
+	2002-02-08
+
+
+1. Introduction
+---------------
+
+Normally programs like wvdial or kppp read the online password from their
+config file and store them in the pap- and chap-secrets before they start the
+pppd and remove them afterwards. Sure they need special privileges to do so.
+
+The passwordfd feature offers a simpler and more secure solution. The program
+that starts the pppd opens a pipe and writes the password into it. The pppd
+simply reads the password from that pipe.
+
+This methods is used for quiet a while on SuSE Linux by the programs wvdial,
+kppp and smpppd.
+
+
+2. Example
+----------
+
+Here is a short C program that uses the passwordfd feature. It starts the pppd
+to buildup a pppoe connection.
+
+
+--snip--
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <paths.h>
+
+#ifndef _PATH_PPPD
+#define _PATH_PPPD "/usr/sbin/pppd"
+#endif
+
+
+// Of course these values can be read from a configuration file or
+// entered in a graphical dialog.
+char *device = "eth0";
+char *username = "1122334455661122334455660001@t-online.de";
+char *password = "hello";
+
+pid_t pid = 0;
+
+
+void
+sigproc (int src)
+{
+    fprintf (stderr, "Sending signal %d to pid %d\n", src, pid);
+    kill (pid, src);
+    exit (EXIT_SUCCESS);
+}
+
+
+void
+sigchild (int src)
+{
+    fprintf (stderr, "Daemon died\n");
+    exit (EXIT_SUCCESS);
+}
+
+
+int
+start_pppd ()
+{
+    signal (SIGINT, &sigproc);
+    signal (SIGTERM, &sigproc);
+    signal (SIGCHLD, &sigchild);
+
+    pid = fork ();
+    if (pid < 0) {
+	fprintf (stderr, "unable to fork() for pppd: %m\n");
+	return 0;
+    }
+
+    if (pid == 0) {
+
+	int i, pppd_argc = 0;
+	char *pppd_argv[20];
+	char buffer[32] = "";
+	int pppd_passwdfd[2];
+
+	for (i = 0; i < 20; i++)
+	    pppd_argv[i] = NULL;
+
+	pppd_argv[pppd_argc++] = "pppd";
+
+	pppd_argv[pppd_argc++] = "call";
+	pppd_argv[pppd_argc++] = "pwfd-test";
+
+	// The device must be after the call, since the call loads the plugin.
+	pppd_argv[pppd_argc++] = device;
+
+	pppd_argv[pppd_argc++] = "user";
+	pppd_argv[pppd_argc++] = username;
+
+	// Open a pipe to pass the password to pppd.
+	if (pipe (pppd_passwdfd) == -1) {
+	    fprintf (stderr, "pipe failed: %m\n");
+	    exit (EXIT_FAILURE);
+	}
+
+	// Of course this only works it the password is shorter
+	// than the pipe buffer. Otherwise you have to fork to
+	// prevent that your main program blocks.
+	write (pppd_passwdfd[1], password, strlen (password));
+	close (pppd_passwdfd[1]);
+
+	// Tell the pppd to read the password from the fd.
+	pppd_argv[pppd_argc++] = "passwordfd";
+	snprintf (buffer, 32, "%d", pppd_passwdfd[0]);
+	pppd_argv[pppd_argc++] = buffer;
+
+	if (execv (_PATH_PPPD, (char **) pppd_argv) < 0) {
+	    fprintf (stderr, "cannot execl %s: %m\n", _PATH_PPPD);
+	    exit (EXIT_FAILURE);
+	}
+    }
+
+    pause ();
+
+    return 1;
+}
+
+
+int
+main (int argc, char **argv)
+{
+    if (start_pppd ())
+	exit (EXIT_SUCCESS);
+
+    exit (EXIT_FAILURE);
+}
+
+---snip---
+
+
+Copy this file to /etc/ppp/peers/pwfd-test. The plugins can't be loaded on the
+command line (unless you are root) since the plugin option is privileged.
+
+
+---snip---
+
+#
+# PPPoE plugin for kernel 2.4
+#
+plugin pppoe.so
+
+#
+# This plugin enables us to pipe the password to pppd, thus we don't have
+# to fiddle with pap-secrets and chap-secrets. The user is also passed
+# on the command line.
+#
+plugin passwordfd.so
+
+noauth
+usepeerdns
+defaultroute
+hide-password
+nodetach
+nopcomp
+novjccomp
+noccp
+
+---snip---
+
diff -Naur tomato/release/src/router/pppd/README.sol2 tomato121/release/src/router/pppd/README.sol2
--- tomato/release/src/router/pppd/README.sol2	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/README.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -1,8 +1,8 @@
-This file describes the installation process for ppp-2.3 on systems
-running Solaris 2.  The Solaris 2 and SVR4 ports share a lot of code
-but are not identical.  The STREAMS kernel modules and driver for
-Solaris 2 are in the svr4 directory (and use some code from the
-modules directory).
+This file describes the installation process for ppp-2.4 on systems
+running Solaris.  The Solaris and SVR4 ports share a lot of code but
+are not identical.  The STREAMS kernel modules and driver for Solaris
+are in the solaris directory (and use some code from the modules
+directory).
 
 NOTE: Although the kernel driver and modules have been designed to
 operate correctly on SMP systems, they have not been extensively
@@ -15,64 +15,105 @@
 *************
 
 1. Run the configure script and make the user-level programs and the
-kernel modules.
+   kernel modules.
 
 	./configure
 	make
 
-If you wish to use gcc (or another compiler) instead of Sun's cc, edit
-the svr4/Makedefs file and uncomment the definition of CC.  You can
-also change the options passed to the C compiler by editing the COPTS
-definition.
+    The configure script will automatically find Sun's cc if it's in
+    the standard location (/opt/SUNWspro/bin/cc).  If you do not have
+    Sun's WorkShop compiler, configure will attempt to use 'gcc'.  If
+    this is found and you have a 64 bit kernel, it will check that gcc
+    accepts the "-m64" option, which is required to build kernel
+    modules.
+
+    You should not have to edit the Makefiles for most ordinary cases.
 
 2. Install the programs and kernel modules: as root, do
 
 	make install
 
-This installs pppd, chat and pppstats in /usr/local/bin and the kernel
-modules in /kernel/drv and /kernel/strmod, and creates the /etc/ppp
-directory and populates it with default configuration files.  You can
-change the installation directories by editing svr4/Makedefs.
-
-If your system normally has only one network interface, the default
-Solaris 2 system startup scripts will disable IP forwarding in the IP
-kernel module.  This will prevent the remote machine from using the
-local machine as a gateway to access other hosts.  The solution is to
-create an /etc/ppp/ip-up script containing something like this:
+    This installs pppd, chat and pppstats in /usr/local/bin and the
+    kernel modules in /kernel/drv and /kernel/strmod, and creates the
+    /etc/ppp directory and populates it with default configuration
+    files.  You can change the installation directories by editing
+    solaris/Makedefs.  If you have a 64 bit kernel, the 64-bit drivers
+    are installed in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9.
+
+    If your system normally has only one network interface at boot
+    time, the default Solaris system startup scripts will disable IP
+    forwarding in the IP kernel module.  This will prevent the remote
+    machine from using the local machine as a gateway to access other
+    hosts.  The solution is to create an /etc/ppp/ip-up script
+    containing something like this:
 
 	#!/bin/sh
 	/usr/sbin/ndd -set /dev/ip ip_forwarding 1
 
-See the man page for ip(7p) for details.
+    See the man page for ip(7p) for details.
+
+Integrated pppd
+***************
+
+  Solaris 8 07/01 (Update 5) and later have an integrated version of
+  pppd, known as "Solaris PPP 4.0," and is based on ppp-2.4.0.  This
+  version comes with the standard Solaris software distribution and is
+  supported by Sun.  It is fully tested in 64-bit and SMP modes, and
+  with bundled and unbundled synchronous drivers.  Solaris 8 10/01
+  (Update 6) and later includes integrated PPPoE client and server
+  support, with kernel-resident data handling.  See pppd(1M).
+
+  The feature is part of the regular full installation, and is
+  provided by these packages:
+
+	SUNWpppd	- 32-bit mode kernel drivers
+	SUNWpppdr	- root-resident /etc/ppp config samples
+	SUNWpppdu	- /usr/bin/pppd itself, plus chat
+	SUNWpppdx	- 64-bit mode kernel drivers
+	SUNWpppdt	- PPPoE support
+	SUNWpppg	- GPL'd optional 'pppdump' and plugins
+	SUNWpppgS	- Source for GPL'd optional features
+
+  Use the open source version of pppd if you wish to recompile to add
+  new features or to experiment with the code.  Production systems,
+  however, should run the Sun-supplied version, if at all possible.
+
+  You can run both versions on a single system if you wish.  The
+  Solaris PPP 4.0 interfaces are named "spppN," while this open source
+  version names its interfaces as "pppN".  The STREAMS modules are
+  similarly separated.  The Sun-supplied pppd lives in /usr/bin/pppd,
+  while the open source version installs (by default) in
+  /usr/local/bin/pppd.
 
 Dynamic STREAMS Re-Plumbing Support.
 ************************************
 
-Solaris 8 includes dynamic re-plumbing support. With this, modules
-below ip can be inserted, or removed, without having the ip stream be
-unplumbed, and re-plumbed again. All states in ip for an interface
-will therefore now be preserved. Users can install (or upgrade)
-modules like firewall, bandwidth manager, cache manager, tunneling,
-etc., without shutting the machine down.
-
-To support this, ppp driver now uses /dev/udp instead of /dev/ip for
-the ip stream. The interface stream (where ip module pushed on top of
-ppp) is then I_PLINK'ed below the ip stream. /dev/udp is used because
-STREAMS will not let a driver be PLINK'ed under itself, and /dev/ip is
-typically the driver at the bottom of the tunneling interfaces
-stream.  The mux ids of the ip streams are then added using
-SIOCSxIFMUXID ioctl.
+  Solaris 8 (and later) includes dynamic re-plumbing support.  With
+  this feature, modules below ip can be inserted, or removed, without
+  having the ip stream be unplumbed, and re-plumbed again.  All state
+  in ip for the interface will be preserved as modules are added or
+  removed.  Users can install (or upgrade) modules such as firewall,
+  bandwidth manager, cache manager, tunneling, etc., without shutting
+  the interface down.
+
+  To support this, ppp driver now uses /dev/udp instead of /dev/ip for
+  the ip stream. The interface stream (where ip module pushed on top
+  of ppp) is then I_PLINK'ed below the ip stream. /dev/udp is used
+  because STREAMS will not let a driver be PLINK'ed under itself, and
+  /dev/ip is typically the driver at the bottom of the tunneling
+  interfaces stream.  The mux ids of the ip streams are then added
+  using SIOCSxIFMUXID ioctl.
 
-Users will be able to see the modules on the interface stream by, for
-example:
+  Users will be able to see the modules on the interface stream by,
+  for example:
 
-    pikapon% ifconfig ppp modlist
+    pikapon# ifconfig ppp modlist
     0 ip
     1 ppp
 
-Or arbitrarily if bandwidth manager and firewall modules are installed:
+  Or arbitrarily if bandwidth manager and firewall modules are installed:
 
-    pikapon% ifconfig hme0 modlist
+    pikapon# ifconfig hme0 modlist
     0 arp
     1 ip
     2 ipqos
@@ -82,50 +123,52 @@
 Snoop Support.
 **************
 
-This version includes support for /usr/sbin/snoop. Tests has been done
-on both Solaris 7 and 8. Only IPv4 and IPv6 packets will be sent up to
-stream(s) marked as promiscuous, e.g, snoop et al.
+  This version includes support for /usr/sbin/snoop.  Tests have been
+  done on Solaris 7 through 9. Only IPv4 and IPv6 packets will be sent
+  up to stream(s) marked as promiscuous (i.e., those used by snoop).
 
-Users will be able to see the packets on the ppp interface by, for example:
+  Users will be able to see the packets on the ppp interface by, for
+  example:
 
     snoop -d ppp0
 
-See the man page for snoop(1M) for details.
+  See the man page for snoop(1M) for details.
 
 IPv6 Support.
 *************
 
-This is for Solaris 8 and later.
+  This is for Solaris 8 and later.
 
-This version has been tested under Solaris 8 running IPv6. As of now,
-interoperability testing has only been done between Solaris machines
-in terms of the IPV6 NCP. An additional command line option for the
-pppd daemon has been added: ipv6cp-use-persistent.
-
-By default, compilation for IPv6 support is not enabled.  Uncomment
-the necessary lines in pppd/Makefile.sol2 to enable it. Once done, the
-quickest way to get IPv6 running is to add the following somewhere in
-the command line option:
+  This version has been tested under Solaris 8 and 9 running IPv6.
+  Interoperability testing has only been done between Solaris machines
+  in terms of the IPV6 NCP.  An additional command line option for the
+  pppd daemon has been added: ipv6cp-use-persistent.
+
+  By default, compilation for IPv6 support is not enabled.  Uncomment
+  the necessary lines in pppd/Makefile.sol2 to enable it.  Once done,
+  the quickest way to get IPv6 running is to add the following
+  somewhere in the command line option:
 
 	+ipv6 ipv6cp-use-persistent
 
-The persistent id for the link-local address was added to conform to
-RFC 2472; such that if there's an EUI-48 available, use that to make
-up the EUI-64.  As of now, the Solaris implementation extracts the
-EUI-48 id from the Ethernet's MAC address (the ethernet interface
-needs to be up).  Future works might support other ways of obtaining a
-unique yet persistent id, such as EEPROM serial numbers, etc.
-
-There need not be any up/down scripts for ipv6, e.g. /etc/ppp/ipv6-up
-or /etc/ppp/ipv6-down, to trigger IPv6 neighbor discovery for auto
-configuration and routing.  The in.ndpd daemon will perform all of the
-necessary jobs in the background. /etc/inet/ndpd.conf can be further
-customized to enable the machine as an IPv6 router. See the man page
-for in.ndpd(1M) and ndpd.conf(4) for details.
-
-Below is a sample output of "ifconfig -a" with persistent link-local
-address.  Note the UNNUMBERED flag is set because hme0 and ppp0 both
-have identical link-local IPv6 addresses:
+  The persistent id for the link-local address was added to conform to
+  RFC 2472; such that if there's an EUI-48 available, use that to make
+  up the EUI-64.  As of now, the Solaris implementation extracts the
+  EUI-48 id from the Ethernet's MAC address (the ethernet interface
+  needs to be up).  Future work might support other ways of obtaining
+  a unique yet persistent id, such as EEPROM serial numbers, etc.
+
+  There need not be any up/down scripts for ipv6,
+  e.g. /etc/ppp/ipv6-up or /etc/ppp/ipv6-down, to trigger IPv6
+  neighbor discovery for auto configuration and routing.  The in.ndpd
+  daemon will perform all of the necessary jobs in the
+  background. /etc/inet/ndpd.conf can be further customized to enable
+  the machine as an IPv6 router. See the man page for in.ndpd(1M) and
+  ndpd.conf(4) for details.
+
+  Below is a sample output of "ifconfig -a" with persistent link-local
+  address.  Note the UNNUMBERED flag is set because hme0 and ppp0 both
+  have identical link-local IPv6 addresses:
 
 lo0: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1
         inet 127.0.0.1 netmask ff000000 
@@ -148,73 +191,70 @@
 ppp0: flags=2202851<UP,POINTOPOINT,RUNNING,MULTICAST,UNNUMBERED,NONUD,IPv6> mtu 1500 index 12
         inet6 fe80::a00:20ff:fe8d:38c1/10 --> fe80::a00:20ff:fe7a:24fb
 
-Note also that a plumbed ipv6 interface stream will exist throughout
-the entire PPP session in the case where the peer rejects IPV6CP,
-which further causes the interface state to stay down. Unplumbing will
-happen when the daemon exits. This is done by design and is not a bug.
+  Note also that a plumbed ipv6 interface stream will exist throughout
+  the entire PPP session in the case where the peer rejects IPV6CP,
+  which further causes the interface state to stay down. Unplumbing
+  will happen when the daemon exits. This is done by design and is not
+  a bug.
 
 64-bit Support.
 ***************
 
-This version has been tested under Solaris 7 (and Solaris 8 ) in both
-32- and 64-bits environments (Ultra class machines).  Installing the
-package by executing "make install" will result in additional files
-residing in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9
-subdirectories.
+  This version has been tested under Solaris 7 through 9 in both 32-
+  and 64-bit environments (Ultra class machines).  Installing the
+  package by executing "make install" will result in additional files
+  residing in /kernel/drv/sparcv9 and /kernel/strmod/sparcv9
+  subdirectories.
 
-64-bit modules and driver have been compiled and tested using Sun's cc.
+  64-bit modules and driver have been compiled and tested using Sun's
+  cc and gcc.
 
 Synchronous Serial Support.
 ***************************
 
-This version has working but limited support for the on-board
-synchronous HDLC interfaces. It has been tested with the /dev/se_hdlc
-and /dev/zsh drivers.  Synchronous mode was tested with a Cisco
-router.
-
-There ppp daemon does not directly support controlling the serial
-interface.  It relies on the /usr/sbin/syncinit command to initialize
-HDLC mode and clocking.
-
-Some bugs remain: large sized frames are not sent/received properly,
-and may be related to the IP mtu.  This may be due to bugs in pppd
-itself, bugs in Solaris or the serial drivers.  The /dev/zsh driver
-seems more larger and can send/receive larger frames than the
-/dev/se_hdlc driver. There is a confirmed bug with NRZ/NRZI mode in
-the /dev/se_hdlc driver, and Solaris patch 104596-11 is needed to
-correct it. (However this patch seems to introduce other serial
-problems. If you don't apply the patch, the workaround is to change
-the nrzi mode to yes or no, whichever works)
-
-How to start pppd with synchronous support:
-
-#!/bin/sh
-
-local=1.1.1.1   # your ip address here
-baud=38400	# needed, but ignored by serial driver
-
-# Change to the correct serial driver/port
-#dev=/dev/zsh0
-dev=/dev/se_hdlc0
- 
-# Change the driver, nrzi mode, speed and clocking to match your setup
-# This configuration is for external clocking from the DCE
-connect="syncinit se_hdlc0 nrzi=no speed=64000 txc=rxc rxc=rxc"
- 
-/usr/sbin/pppd $dev sync $baud novj noauth $local: connect "$connect"
+  This version has working but limited support for the on-board
+  synchronous HDLC interfaces.  It has been tested with the
+  /dev/se_hdlc, /dev/zsh, HSI/S, and HSI/P drivers.  Synchronous mode
+  was tested with a Cisco router.
+
+  The ppp daemon does not directly support controlling the serial
+  interface.  It relies on the /usr/sbin/syncinit command to
+  initialize HDLC mode and clocking.
+
+  There is a confirmed bug with NRZ/NRZI mode in the /dev/se_hdlc
+  driver, and Solaris patch 104596-11 is needed to correct it.
+  (However this patch seems to introduce other serial problems.  If
+  you don't apply the patch, the workaround is to change the nrzi mode
+  to yes or no, whichever works.)
 
+  How to start pppd with synchronous support:
 
-Sample Cisco router config excerpt:
+	#!/bin/sh
+
+	local=1.1.1.1   # your ip address here
+	baud=38400	# needed, but ignored by serial driver
+
+	# Change to the correct serial driver/port
+	#dev=/dev/zsh0
+	dev=/dev/se_hdlc0
+ 
+	# Change the driver, nrzi mode, speed and clocking to match
+	# your setup.
+	# This configuration is for external clocking from the DCE
+	connect="syncinit se_hdlc0 nrzi=no speed=64000 txc=rxc rxc=rxc"
+ 
+	/usr/sbin/pppd $dev sync $baud novj noauth $local: connect "$connect"
 
-!
-! Cisco router setup as DCE with RS-232 DCE cable
-! 
-!         
-interface Serial0
- ip address 1.1.1.2 255.255.255.0
- encapsulation ppp
- clockrate 64000
- no nrzi-encoding
- no shutdown
-!         
+  Sample Cisco router config excerpt:
 
+	!
+	! Cisco router setup as DCE with RS-232 DCE cable
+	! 
+	!         
+	interface Serial0
+	 ip address 1.1.1.2 255.255.255.0
+	 encapsulation ppp
+	 clockrate 64000
+	 no nrzi-encoding
+	 no shutdown
+	!         
diff -Naur tomato/release/src/router/pppd/README.sunos4 tomato121/release/src/router/pppd/README.sunos4
--- tomato/release/src/router/pppd/README.sunos4	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/README.sunos4	1970-01-01 10:00:00.000000000 +1000
@@ -1,62 +0,0 @@
-This file describes the installation process for ppp-2.3 on systems
-running SunOS 4.1.x (or the equivalent).
-
-The STREAMS modules in the sunos4 directory provide kernel support for
-PPP on SunOS 4.1.x systems.  They have been tested under SunOS 4.1.3
-on a SparcStation 1+.  They should work under earlier SunOS 4.1.x
-systems, but no guarantees are given.
-
-These modules are designed to be loaded into the running kernel using
-the `modload' command.
-
-
-Installation.
-*************
-
-1. Run the configure script and make the user-level programs and the
-kernel modules.
-
-	./configure
-	make
-
-If you wish to compile using gcc instead of cc, edit the
-sunos4/Makedefs file and uncomment the line saying "CC = gcc".  You
-can also change the C compiler options by editing the COPTS
-definition.
-
-2. Install the pppd, pppstats and chat programs and the loadable
-module object files (you need to be root to do this):
-
-	make install
-
-By default, the programs and the loadable module object files go into
-/usr/local/etc.  Doing `make install' also copies a script called
-ppp.INSTALL into /dev, and makes ppp.LOAD, ppp.UNLOAD, ppp.MKDEV and
-ppp.RMDEV links to it.  You can change the installation directories by
-editing sunos4/Makedefs.
-
-3. Load the ppp modules (you need to be root for this too):
-
-	/dev/ppp.LOAD
-
-You will want to do this in your /etc/rc.local file once you have
-everything installed.  I suggest you put something like the following
-in /etc/rc.local (or /etc/loadable, if you have one):
-
-	if [ -f /dev/ppp.AUTOLOAD ]; then
-		/dev/ppp.LOAD
-	fi
-
-and then create a /dev/ppp.AUTOLOAD file with the command
-
-	touch /dev/ppp.AUTOLOAD
-
-It is not advisable to unload the "if_ppp" module, because it creates
-network interface units, and SunOS does not provide any way to destroy
-network interfaces once created.  If you do unload it, the system will
-probably crash at some later stage.
-
-If you have previously had ppp-2.2 installed, you may have statements
-in your /etc/rc.local to load the ppp module.  You will need to remove
-those.  Having done this, you will need to reboot to remove the old
-ppp module and load the new modules.
diff -Naur tomato/release/src/router/pppd/scripts/autopppd tomato121/release/src/router/pppd/scripts/autopppd
--- tomato/release/src/router/pppd/scripts/autopppd	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/autopppd	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,160 @@
+#!/usr/bin/perl -w
+
+# Auto dial script by Brian May <bam@snoopy.apana.org.au>
+
+use Proc::Daemon;
+use strict;
+use Sys::Syslog qw(:DEFAULT setlogsock);  # default set, plus setlogsock
+use Proc::WaitStat qw(:DEFAULT waitstat);
+
+
+Proc::Daemon::Init;
+open(PIDFILE,">/var/run/autopppd.pid");
+print(PIDFILE "$$");
+close(PIDFILE);
+
+sub toseconds($) {
+  my ($hours,$minutes,$seconds) = split(/:/,shift);
+  return ($hours*60+$minutes)*60+$seconds;
+}
+
+sub dseconds($) {
+  my ($total) = @_;
+
+  my $seconds = $total % 60; $total = ($total - $seconds)/60;
+  my $minutes = $total % 60; $total = ($total - $minutes)/60;
+  my $hours   = $total % 24; $total = ($total - $hours)/24;
+  my $days   = $total;
+  if ($days > 0) {
+    return(sprintf("%d-%02d:%02d:%02d",$days,$hours,$minutes,$seconds));
+  } else {
+    return(sprintf("%02d:%02d:%02d",$hours,$minutes,$seconds));
+  }
+}
+
+my $program="autopppd";
+
+setlogsock('unix');
+openlog($program, 'cons,pid', 'daemon');
+
+my $pppd_start_time;
+my $pppd_end_time;
+my $pppd_run_time;
+my $pppd_fail;
+my $delay=0;
+my $idelay=0;
+
+my @delays = (
+               toseconds("00:01:00"), # 1 minute
+               toseconds("00:07:00"), # 8 minutes
+               toseconds("00:07:00"), # 15 minutes
+               toseconds("00:15:00"), # 30 minutes
+               toseconds("00:30:00"), # 1 hour
+               toseconds("01:00:00"), # 2 hours
+               toseconds("01:00:00"), # 3 hours
+               toseconds("03:00:00"), # 6 hours
+               toseconds("06:00:00"), # 12 hours
+               toseconds("12:00:00"), # 24 hours
+               toseconds("24:00:00")  # 48 hours
+             );
+
+# action == 0 => immediate retry (!FIXME! needs to have some delay)
+# action == 1 => delayed retry
+# action == 2 => abort
+
+my $code = {
+  0 => { message=>"pppd detached",                              action=> 2 },
+  1 => { message=>"fatal error",                                action=> 2 },
+  2 => { message=>"options error",                              action=> 2 },
+  3 => { message=>"not setuid-root error",                      action=> 2 },
+  4 => { message=>"no kernel support for PPP",                  action=> 2 },
+  5 => { message=>"SIGINT or SIGTERM or SIGHUP",                action=> 1 },
+  6 => { message=>"Serial port locked",                         action=> 1 }, # should be 0
+  7 => { message=>"Serial port open error",                     action=> 1 },
+  8 => { message=>"Connect failed",                             action=> 1 },
+  9 => { message=>"Could not execute pty command",              action=> 1 },
+ 10 => { message=>"PPP negotiation failed",                     action=> 1 },
+ 11 => { message=>"Peer failed to authenticate",                action=> 1 },
+ 12 => { message=>"Link was idle",                              action=> 1 },
+ 13 => { message=>"Time limit exceeded",                        action=> 1 },
+ 14 => { message=>"call back not implemented",                  action=> 2 },
+ 15 => { message=>"peer not responding",                        action=> 1 },
+ 16 => { message=>"modem hang up",                              action=> 1 },
+ 17 => { message=>"Serial loopback detected",                   action=> 1 },
+ 18 => { message=>"Init script failed",                         action=> 1 },
+ 19 => { message=>"We failed to authenticate",                  action=> 1 },
+};
+
+while (1)
+{
+  $pppd_start_time=time;
+  syslog('info', 'restarting pppd');
+
+  # logging sometimes stopped working after ppp was running for
+  # some time. lets see if closing and reopening the log file helps...
+  closelog();
+
+  # run ppp
+  my $rc=system("pppd","-detach",@ARGV);
+
+  # reopon log file
+  openlog($program, 'cons,pid', 'daemon');
+
+  # calculate run time
+  $pppd_end_time=time;
+  $pppd_run_time=$pppd_end_time-$pppd_start_time;
+
+  my $pppd_code = ($? >> 8);
+  my $pppd_signal = $? & 127;
+  my $pppd_coredump = $? & 128;
+
+  $pppd_fail = 1;
+  if ($pppd_signal != 0) {
+    if ($pppd_coredump)
+    { syslog('err',"pppd died with signal $pppd_signal, coredump"); }
+    else
+    { syslog('err',"pppd died with signal $pppd_signal"); }
+  }
+  elsif ($pppd_coredump) {
+    syslog('err',"pppd died with coredump");
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} == 0) {
+    syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), immediate retry");
+    $pppd_fail = 0;
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} == 1) {
+    syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), delayed retry");
+    $pppd_fail = 1;
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} >= 2) {
+    syslog('err', "pppd returned: ".$code->{$pppd_code}{"message"}." ($pppd_code), aborting");
+    exit(255);
+  }
+  elsif (defined($code->{$pppd_code}) && $code->{$pppd_code}{"action"} >= 2) {
+    syslog('err', "pppd returned: unknown error ($pppd_code), delayed retry");
+    $pppd_fail = 1;
+  }
+  # if it hasn't ran for at least an hour, then somthing went wrong
+  elsif ($pppd_run_time < toseconds("01:00:00")) {
+    syslog('err',"pppd session didn't last 1 hour, delayed retry");
+    $pppd_fail = 1;
+  }
+  else { $pppd_fail = 0; }
+
+  # if not failed, then reset delay.
+  if (!$pppd_fail) { $idelay = 0; }
+
+  # get next delay.
+  $delay = $delays[$idelay];
+
+  # log statistics.
+  syslog('info',"rc=".waitstat($rc)." runtime=".dseconds($pppd_run_time)." delay[$idelay]=".dseconds($delay)."");
+
+  # delay for desired time.
+  sleep($delay);
+
+  # increment delay for next time.
+  if (defined($delays[$idelay+1])) { $idelay++; }
+}
+
+closelog();
diff -Naur tomato/release/src/router/pppd/scripts/callback tomato121/release/src/router/pppd/scripts/callback
--- tomato/release/src/router/pppd/scripts/callback	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/callback	2008-04-22 21:44:05.000000000 +1000
@@ -33,7 +33,7 @@
 	ABORT		'\nNO ANSWER\r'			\
 	ABORT		'\nRINGING\r\n\r\nRINGING\r'	\
 	''		AT				\
-	'OK-+++\c-OK'	'AT&C0&D2S0=0H0			\
+	'OK-+++\c-OK'	'AT&C0&D2S0=0H0'		\
 	TIMEOUT		30				\
 	OK		ATDT$TELEPHONE			\
 	CONNECT		''				\
diff -Naur tomato/release/src/router/pppd/scripts/chatchat/chatchat.c tomato121/release/src/router/pppd/scripts/chatchat/chatchat.c
--- tomato/release/src/router/pppd/scripts/chatchat/chatchat.c	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/chatchat/chatchat.c	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,409 @@
+/* ************************************************************************* 
+* NAME: chatchat.c
+*
+* DESCRIPTION:
+*
+* This program creates a pipe for the chat process to read. The user
+* can supply information (like a password) that will be picked up
+* by chat and sent just like the regular contents of a chat script.
+*
+* Usage is:
+*
+* chatchat <filename>
+*
+*   where <filename> matches the option given in the chat script.
+*
+* for instance the chat script fragment:
+*
+*       ...
+*        name:           \\dmyname  \
+*        word:           @/var/tmp/p \
+*       ...
+*                                   ^
+*                    (note: leave some whitespace after the filename)
+*
+* expect "name:", reply with a delay followed by "myname"
+* expect "word:", reply with the data read from the pipe /var/tmp/p 
+*
+* the matching usage of chatchat would be:
+*
+* chatchat /var/tmp/p
+*
+* eg:
+*
+* $chatchat /var/tmp/p
+* ...
+*                           some other process eventually starts:
+*                           chat ...
+*                           chat parses the "@/var/tmp/p" option and opens
+*                              /var/tmp/p
+*  (chatchat prompts:)
+*
+* type PIN into SecurID card
+*   enter resulting passcode: [user inputs something]
+*
+*                           chat reads /var/tmp/p & gets what the
+*                              user typed at chatchat's "enter string" prompt
+*                           chat removes the pipe file
+*                           chat sends the user's input as a response in
+*                              place of "@/var/tmp/p"
+*                             
+* PROCESS:
+*
+* gcc -g -o chatchat chatchat.c
+*
+* 
+* GLOBALS: none
+*
+* REFERENCES:
+*
+* see the man pages and documentation that come with the 'chat' program
+* (part of the ppp package). you will need to use the modified chat
+* program that accepts the '@' operator.
+*
+* LIMITATIONS:
+*
+* REVISION HISTORY:
+*
+*   STR                Description                          Author
+*
+*   23-Mar-99          initial coding                        gpk
+*   12-May-99	       unlink the pipe after closing	     paulus
+*
+* TARGET: ANSI C
+* This program is in the public domain.
+* 
+*
+* ************************************************************************* */
+
+
+
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+/* MAXINPUT - the data typed into chatchat must be fewer   */
+/* characters than this.                                 */
+
+#define MAXINPUT 80
+
+
+
+
+
+
+/* ************************************************************************* 
+
+
+   NAME:  main
+
+
+   USAGE: 
+
+   int argc;
+   char * argv[];
+
+   main(argc, argv[]);
+
+   returns: int
+
+   DESCRIPTION:
+                 if the pipe file name is given on the command line,
+		    create the pipe, prompt the user and put whatever
+		    is typed into the pipe.
+
+		 returns -1 on error
+		     else # characters entered
+   REFERENCES:
+
+   LIMITATIONS:
+
+   GLOBAL VARIABLES:
+
+      accessed: none
+
+      modified: none
+
+   FUNCTIONS CALLED:
+
+   REVISION HISTORY:
+
+        STR                  Description of Revision                 Author
+
+     25-Mar-99               initial coding                           gpk
+
+ ************************************************************************* */
+
+int main(int argc, char * argv[])
+{
+  int retval;
+  
+  int create_and_write_pipe(char * pipename);
+
+  if (argc != 2)
+    {
+      fprintf(stderr, "usage: %s pipename\n", argv[0]);
+      retval = -1;
+    }
+  else
+    {
+     retval =  create_and_write_pipe(argv[1]);
+    }
+  return (retval);
+}
+
+
+
+
+/* ************************************************************************* 
+
+
+   NAME:  create_and_write_pipe
+
+
+   USAGE: 
+
+   int some_int;
+   char * pipename;
+
+   some_int =  create_and_write_pipe(pipename);
+
+   returns: int
+
+   DESCRIPTION:
+                 given the pipename, create the pipe, open it,
+		 prompt the user for a string to put into the
+		 pipe, write the string, and close the pipe
+
+		 on error, print out an error message and return -1
+		 
+		 returns -1 on error
+		   else #bytes written into the pipe
+   REFERENCES:
+
+   LIMITATIONS:
+
+   GLOBAL VARIABLES: 
+
+      accessed: none
+
+      modified: none
+
+   FUNCTIONS CALLED:
+
+   REVISION HISTORY:
+
+        STR                  Description of Revision                 Author
+
+     25-Mar-99               initial coding                           gpk
+     12-May-99		     remove pipe after closing		     paulus
+
+ ************************************************************************* */
+
+int create_and_write_pipe(char * pipename)
+{
+  int retval, created, pipefd, nread, nwritten;
+  char input[MAXINPUT];
+  char errstring[180];
+  
+  int create_pipe(char * pipename);
+  int write_to_pipe(int pipefd, char * input, int nchar);
+
+  created = create_pipe(pipename);
+
+  if (-1 == created)
+    {
+      sprintf(errstring, "unable to create pipe '%s'", pipename);
+      perror(errstring);
+      retval = -1;
+    }
+  else
+    {
+
+      /* note: this open won't succeed until chat has the pipe  */
+      /* open and ready to read. this makes for nice timing.    */
+      
+      pipefd = open(pipename, O_WRONLY);
+
+      if (-1 == pipefd)
+	{
+	  sprintf(errstring, "unable to open pipe '%s'", pipename);
+	  perror(errstring);
+	  retval =  -1;
+	}
+      else
+	{
+	  fprintf(stderr, "%s \n %s",
+		  "type PIN into SecurID card and",
+		  "enter resulting passcode:");
+	  nread = read(STDIN_FILENO, (void *)input, MAXINPUT);
+
+	  
+	  if (0 >= nread)
+	    {
+	      perror("unable to read from stdin");
+	      retval = -1;
+	    }
+	  else
+	    {
+	      /* munch off the newline character, chat supplies  */
+	      /* a return when it sends the string out.          */
+	      input[nread -1] = 0;
+	      nread--;
+	      nwritten = write_to_pipe(pipefd, input, nread);
+	      /* printf("wrote [%d]: '%s'\n", nwritten, input); */
+	      retval = nwritten;
+	    }
+	  close(pipefd);
+
+	  /* Now make the pipe go away.  It won't actually go away
+	     completely until chat closes it. */
+	  if (unlink(pipename) < 0)
+	      perror("Warning: couldn't remove pipe");
+	}
+    }
+  return(retval);
+}
+
+
+
+
+
+
+
+/* ************************************************************************* 
+
+
+   NAME:  create_pipe
+
+
+   USAGE: 
+
+   int some_int;
+   char * pipename;
+   
+   some_int =  create_pipe(pipename);
+   
+   returns: int
+   
+   DESCRIPTION:
+                 create a pipe of the given name
+
+		 if there is an error (like the pipe already exists)
+		    print an error message and return
+		    
+		 return -1 on failure else success
+
+   REFERENCES:
+
+   LIMITATIONS:
+
+   GLOBAL VARIABLES:
+
+      accessed: none
+
+      modified: none
+
+   FUNCTIONS CALLED:
+
+   REVISION HISTORY:
+
+        STR                  Description of Revision                 Author
+
+     25-Mar-99               initial coding                           gpk
+
+ ************************************************************************* */
+
+int create_pipe(char * pipename)
+{
+  mode_t old_umask;
+  int created;
+
+  /* hijack the umask temporarily to get the mode I want  */
+  /* on the pipe.                                         */
+  
+  old_umask = umask(000);
+
+  created = mknod(pipename, S_IFIFO | S_IRWXU | S_IWGRP | S_IWOTH,
+		  (dev_t)NULL);
+
+  /* now restore umask.  */
+  
+  (void)umask(old_umask);
+  
+  if (-1 == created)
+    {
+      perror("unable to create pipe");
+    }
+
+  return(created);
+}
+
+
+
+
+
+
+/* ************************************************************************* 
+
+
+   NAME:  write_to_pipe
+
+
+   USAGE: 
+
+   int some_int;
+   int pipefd;
+   char * input;
+   int nchar;
+
+   some_int =  write_to_pipe(pipefd, input, nchar);
+
+   returns: int
+
+   DESCRIPTION:
+                 write nchars of data from input to pipefd
+
+		 on error print a message to stderr
+
+		 return -1 on error, else # bytes written
+   REFERENCES:
+
+   LIMITATIONS:
+
+   GLOBAL VARIABLES:
+
+      accessed: none
+
+      modified: none
+
+   FUNCTIONS CALLED:
+
+   REVISION HISTORY:
+
+        STR                  Description of Revision                 Author
+
+     25-Mar-99               initial coding                           gpk
+     12-May-99		     don't write count word first	      paulus
+
+ ************************************************************************* */
+
+int write_to_pipe(int pipefd, char * input, int nchar)
+{
+  int nwritten;
+
+  /* nwritten = write(pipefd, (void *)&nchar, sizeof(nchar)); */
+  nwritten = write(pipefd, (void *)input, nchar);
+
+  if (-1 == nwritten)
+    {
+      perror("unable to write to pipe");
+    }
+
+  return(nwritten);
+}
diff -Naur tomato/release/src/router/pppd/scripts/chatchat/README tomato121/release/src/router/pppd/scripts/chatchat/README
--- tomato/release/src/router/pppd/scripts/chatchat/README	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/chatchat/README	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,134 @@
+v 0.1 gpk@onramp.net 3/27/99
+
+I Intro
+
+   This document covers the use of the modified "chat" program and its 
+adjunct "chatchat" to login using the Security Dynamics SecurID card
+on a linux system.
+
+   This set of files comprises a modified version of the chat program
+(the one distributed with ppp-2.3.5) and a new program called chatchat
+that allows you to supply data from the keyboard to the chat program.
+
+   The SecurID card generates passwords that have a lifetime of one
+minute and are used as a first layer in dial up security. The only
+software I know of for this card is for windows, so I wrote my own. 
+This software allows you to type in the time-sensitive password right
+when your chat script is asked to supply the passcode by the remote
+system. 
+
+
+II How It Works
+
+   This version of chat his an additional command that can be put into
+its options that says "Don't reply with this string. Open this pipe,
+read the contents, and reply with that instead." Chatchat creates a
+pipe and lets you type your passcode into it, then chat picks that up
+and sends it out just as though the passcode was hardcoded into the
+options. 
+
+
+III Installation 
+
+  I've provided intel binaries and source code the the modified chat 
+program and the chatchat program. I'll recommend that you copy the 
+chat.c program into your ppp-2.3.5/chat directory (save your original 
+chat.c program first!) and re-make it using the Makefile that comes 
+with chat. Copy the new chat somewhere into your path. (On my system
+chat lives in /usr/sbin/chat, so I've copied the modified one into 
+/usr/sbin/chat.new and changed my dial in script to call chat.new
+instead of chat.
+
+  Second, compile chatchat.c and install it somewhere in your path:
+
+ gcc -g -o chatchat chatchat.c
+ cp chatchat /usr/sbin
+
+ Third, modify your chat script to use the chatchat program. Mine
+looks something like this:
+
+
+                       --------------------
+
+#!/bin/sh
+#
+# This is part 2 of the ppp-on script. It will perform the connection
+# protocol for the desired connection.
+# use atm0 to turn down the speaker volume on my sportster x2 voice modem
+# gpk 11/2/97
+
+exec /usr/sbin/chat.new  -V -v                       \
+         ABORT           "BUSY"                     \
+     ABORT              "NO DIAL TONE"      \
+         ABORT           "NO ANSWER"                \
+         TIMEOUT         50                             \
+         ""             "atm0"                  \
+         OK              ATDT$TELEPHONE                  \
+        CONNECT         ''       \
+    name:           \\da0xxxxxx  \
+    word:           @/var/tmp/p \
+        compress.       ''
+
+
+                     -----------------------
+
+ This is a standard chat script:
+
+* abort if the modem is busy, you don't get a dial tone, no one
+  answers, or 50 seconds elapses.
+
+* use atm0 to mute the modem
+
+* dial the modem, when it connects, wait to be asked for account name
+
+* when we see "name:" prompt, delay briefly then respond with your 
+  account name (fill in your account name)
+
+Now we get to the new stuff:
+
+* when we see "word:" in the password prompt, instead of responding
+  with "@/var/tmp/p", the modified chat program will open the pipe 
+  /var/tmp/p, read the passcode out of there, and send it
+
+* when we see "compress." (the last word before ppp starts), reply
+  with nothing. The script ends and we start ppp.
+
+Note:
+
+* Make sure there is some whitespace between the filename and the \.
+
+
+IV Usage
+
+   To use this install the modified chat and chatchat programs, and
+modify your chat script similar to the above. Before you dial in,
+start that chatchat program giving it the same pipe as in your config
+file. In the above case:
+
+chatchat /var/tmp/p
+
+   Wait until you have one or two tick marks left on your card's
+current number, then start your dial up process that eventually calls
+chat. When chat goes to open and read the pipe, chatchat will prompt:
+
+
+type PIN into SecurID card and 
+ enter resulting passcode:
+
+   At that point, type your PIN number into your Securid card, press
+the diamond, and type the resulting numbers in as your passcode. If
+you've left the -V -v options on your chat command you'll see
+everything so out, otherwise it works silently.
+
+   If you type the number wrong or run out of time, the server will 
+respond with an authentication failure. In that case you will have to 
+hang up and start again. I don't know how to build a conditional script 
+that says either expect "compress" next, but if you see "name:" again, 
+do this instead. 
+
+
+V Additional Information
+
+  You can obtain additional information about chat and ppp from the
+man pages for chat and pppd, as well as the PPP-HOWTO.
+
diff -Naur tomato/release/src/router/pppd/scripts/ipv6-down.sample tomato121/release/src/router/pppd/scripts/ipv6-down.sample
--- tomato/release/src/router/pppd/scripts/ipv6-down.sample	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/ipv6-down.sample	2008-04-22 21:44:05.000000000 +1000
@@ -8,24 +8,24 @@
 # Kill the router advertisement daemon on this interface.
 # The killing procedure is copied from RedHat 6.0 initscripts.
 
-DEVICE=$1
+DEVICE="$1"
 
-PIDFILE=/var/run/radvd-$DEVICE.pid
+PIDFILE="/var/run/radvd-$DEVICE.pid"
 
-[ -f $PIDFILE ] || exit 0
+[ -f "$PIDFILE" ] || exit 0
 
-PID=`cat $PIDFILE`
+PID="$(cat "$PIDFILE")"
 if [ "$PID" != "" ]; then
-    if ps h $PID >/dev/null 2>&1; then
-	kill -TERM $PID
+    if ps h "$PID" >/dev/null 2>&1; then
+	kill -TERM "$PID"
 	usleep 10000
-	if ps h $PID >/dev/null 2>&1; then
+	if ps h "$PID" >/dev/null 2>&1; then
 	    sleep 1
-	    if ps h $PID >/dev/null 2>&1; then
-		kill -KILL $PID
+	    if ps h "$PID" >/dev/null 2>&1; then
+		kill -KILL "$PID"
 	    fi
 	fi
     fi
 fi
 
-rm -f $PIDFILE
+rm -f "$PIDFILE"
diff -Naur tomato/release/src/router/pppd/scripts/ipv6-up.sample tomato121/release/src/router/pppd/scripts/ipv6-up.sample
--- tomato/release/src/router/pppd/scripts/ipv6-up.sample	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/ipv6-up.sample	2008-04-22 21:44:05.000000000 +1000
@@ -8,26 +8,27 @@
 # Start router advertisements on this link.
 # Based on radvd 0.5.0 behaviour
 
-DEVICE=$1
+DEVICE="$1"
 
-CFGFILE=/usr/inet6/etc/radvd.conf-$DEVICE
-PIDFILE=/var/run/radvd-$DEVICE.pid
+CFGFILE="/etc/radvd.conf-$DEVICE"
+PIDFILE="/var/run/radvd-$DEVICE.pid"
+EXEFILE="/usr/sbin/radvd"
 
-if [ -x /usr/inet6/sbin/radvd && -f $CFGFILE ]; then
-    touch $PIDFILE
-    if [ ! -f $PIDFILE ]; then
+if [ -x "$EXEFILE" -a -f "$CFGFILE" ]; then
+    touch "$PIDFILE"
+    if [ ! -f "$PIDFILE" ]; then
 	echo "error: $PIDFILE is not a regular file. Aborting"
 	exit 0
     fi
 
-    PID=`cat $PIDFILE`
-    if [ "$PID" != "" ]; then
-	ps h $PID >/dev/null 2>&1 && exit 0
+    PID="$(cat "$PIDFILE")"
+    if [ -n "$PID" ]; then
+	ps h "$PID" >/dev/null 2>&1 && exit 0
     fi
 
     # radvd 0.5.0 doesn't write a pid-file so we do it here
     # enabling debugging keeps radvd in foreground, putting it
     # on background gives us the PID.
-    /usr/inet6/sbin/radvd -d 1 -C $CFGFILE &
-    echo $! >$PIDFILE
+    "$EXEFILE" -d 1 -C "$CFGFILE" &
+    echo $! >"$PIDFILE"
 fi
diff -Naur tomato/release/src/router/pppd/scripts/plog tomato121/release/src/router/pppd/scripts/plog
--- tomato/release/src/router/pppd/scripts/plog	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/plog	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ -s /var/log/ppp.log ]; then
+  exec tail "$@" /var/log/ppp.log
+else
+  exec tail "$@" /var/log/syslog | grep ' \(pppd\|chat\)\['
+fi
diff -Naur tomato/release/src/router/pppd/scripts/poff tomato121/release/src/router/pppd/scripts/poff
--- tomato/release/src/router/pppd/scripts/poff	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/poff	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# $Id: poff,v 1.1 2002/11/24 23:30:44 etbe Exp $
+# Written by John Hasler <john@dhh.gt.org> and based on work 
+# by Phil Hands <phil@hands.com>.  Distributed under the GNU GPL
+
+if [ -x /usr/bin/kill ]; then
+  KILL="/usr/bin/kill"
+else
+  KILL="/bin/kill"
+fi
+SIG=TERM
+DONE="stopped"
+MODE=""
+
+usage ()
+{
+   cat <<!EOF!
+usage: $0 [option] [provider]
+options:
+  -r        Cause pppd to drop the line and redial.
+  -d        Toggle the state of pppd's debug option.
+  -c        Cause pppd to renegotiate compression.
+  -a        Stop all pppd's.  'provider' will be ignored.
+  -h        Print this help summary and exit.
+  -v        Print version and exit.
+  none      Stop pppd.
+
+Options may not be combined.
+
+If 'provider' is omitted pppd will be stopped or signalled if and only if
+there is exactly one running unless the '-a' option was given.  If
+'provider' is supplied the pppd controlling the connection to that
+provider will be stopped or signalled.
+!EOF!
+}
+
+# Get option.  If there are none replace the "?" that getopts puts in
+# FLAG on error with "null".
+getopts rdcavh FLAG
+if [ "$?" -ne 0 ]; then
+    FLAG="null"
+fi
+
+# Check for additional options.  Should be none.
+getopts :rdcavh DUMMY
+if [ "$?" -eq 0 ]; then
+    echo "$0: Illegal option -- ${OPTARG}."
+    exit 1
+fi
+
+case $FLAG in
+ "r") SIG=HUP;  DONE=signalled; shift ;;
+ "d") SIG=USR1; DONE=signalled; shift ;;
+ "c") SIG=USR2; DONE=signalled; shift ;;
+ "a") MODE="all"; shift ;;
+ "v") echo "$0$Revision: 1.1 $_TrickToPrint_RCS_Revision"; exit 0 ;;
+ "h") usage; exit 0 ;;
+ "?") exit 1;
+esac
+
+# Get the PIDs of all the pppds running.  Could also get these from
+# /var/run, but pppd doesn't create .pid files until ppp is up.
+PIDS=`pidof pppd`
+
+# poff is pointless if pppd isn't running.
+if test -z "$PIDS"; then
+    echo "$0: No pppd is running.  None ${DONE}."
+    exit 1
+fi
+
+# Find out how many pppd's are running.
+N=`echo "$PIDS" | wc -w`
+
+# If there are no arguments we can't do anything if there is more than one
+# pppd running.
+if test "$#" -eq 0 -a "$N" -gt 1 -a $FLAG != "a" ; then
+    echo "$0: More than one pppd running and no "-a" option and 
+no arguments supplied. Nothing ${DONE}."
+    exit 1
+fi
+
+# If either there are no arguments or '-a' was specified kill all the
+# pppd's.
+if test "$#" -eq 0 -o "$MODE" = "all" ; then
+    $KILL -$SIG $PIDS || {
+        echo "$0: $KILL failed.  None ${DONE}."
+        exit 1
+    }
+    exit 0
+fi
+
+# There is an argument, so kill the pppd started on that provider.
+PID=`ps axw | grep "[ /]pppd call $1 *\$" | awk '{print $1}'`
+if test -n "$PID" ; then
+    $KILL -$SIG $PID || {
+        echo "$0: $KILL failed.  None ${DONE}."
+        exit 1
+    }
+else
+   echo "$0: I could not find a pppd process for provider '$1'. None ${DONE}."
+   exit 1
+fi
+exit 0
diff -Naur tomato/release/src/router/pppd/scripts/pon tomato121/release/src/router/pppd/scripts/pon
--- tomato/release/src/router/pppd/scripts/pon	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/pon	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+PPP_ON_BOOT=/etc/ppp/ppp_on_boot
+
+case "$1" in
+  -*) echo "
+Usage: pon [provider] [arguments]
+
+If pon is invoked without arguments, $PPP_ON_BOOT file will be
+run, presuming it exists and is executable. Otherwise, a PPP connection
+will be started using settings from /etc/ppp/peers/provider.
+If you specify one argument, a PPP connection will be started using
+settings from the appropriate file in the /etc/ppp/peers/ directory, and
+any additional arguments supplied will be passed as extra arguments to
+pppd.
+"
+      exit 0
+      ;;
+esac
+
+if [ -z "$1" -a -x "$PPP_ON_BOOT" ]; then
+  exec "$PPP_ON_BOOT"
+fi
+
+if [ -z "$1" -a ! -f /etc/ppp/peers/provider ]; then
+  echo "
+Please configure /etc/ppp/peers/provider or use a command line argument to
+use another file in /etc/ppp/peers/ directory.
+"
+  exit 1
+fi
+
+if [ "$1" -a ! -f "/etc/ppp/peers/$1" ]; then
+  echo "
+The file /etc/ppp/peers/$1 does not exist.
+"
+  exit 1
+fi
+
+exec /usr/sbin/pppd call ${@:-provider}
diff -Naur tomato/release/src/router/pppd/scripts/pon.1 tomato121/release/src/router/pppd/scripts/pon.1
--- tomato/release/src/router/pppd/scripts/pon.1	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/pon.1	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,120 @@
+.\" This manual is published under the GPL.
+.\" All guidelines specified in the GPL apply here.
+.\" To get an ascii file:
+.\" groff -man -Tascii pon.1 > pon.txt
+.\"
+.TH PON 1 "July 2000" "Debian Project" "Debian PPPD"
+.SH NAME
+pon, poff, plog \- starts up, shuts down or lists the log of PPP connections
+.SH SYNOPSIS
+.B pon
+[ isp\-name [ options ] ]
+.br
+.B poff
+[ \-r ] [ \-d ] [ \-c ] [ \-a ] [ \-h ] [ isp\-name ]
+.br
+.B plog
+[ arguments ]
+.SH DESCRIPTION
+This manual page describes the \fBpon\fP, \fBplog\fP and \fBpoff\fP
+scripts, which allow users to control PPP connections.
+..
+.SS pon
+\fBpon\fP, invoked without arguments, runs the \fI/etc/ppp/ppp_on_boot\fP
+file, if it exists and is executable. Otherwise, a PPP connection will be
+started using configuration from \fI/etc/ppp/peers/provider\fP.
+This is the default behaviour unless an \fBisp\-name\fP argument is given.
+.PP
+For instance, to use ISP configuration "myisp" run:
+.IP
+pon myisp
+.PP
+\fBpon\fP will then use the options file \fI/etc/ppp/peers/myisp\fP.
+You can pass additional \fBoptions\fP after the ISP name, too.
+\fBpon\fP can be used to run multiple, simultaneous PPP connections.
+..
+.SS poff
+\fBpoff\fP closes a PPP connection. If more than one PPP connection exists,
+the one named in the argument to \fBpoff\fP will be killed, e.g.
+.IP
+poff myprovider2
+.PP
+will terminate the connection to myprovider2, and leave the PPP connections
+to e.g. "myprovider1" or "myprovider3" up and running.
+.PP
+\fBpoff\fP takes the following command line options:
+.RS
+.TP
+.B "\-r"
+causes the connection to be redialed after it is dropped.
+.TP
+.B "\-d"
+toggles the state of pppd's debug option.
+.TP
+.B "\-c"
+causes
+.BR pppd (8)
+to renegotiate compression.
+.TP
+.B "\-a"
+stops all running ppp connections. If the argument \fBisp\-name\fP
+is given it will be ignored.
+.TP
+.B "\-h"
+displays help information.
+.TP
+.B "\-v"
+prints the version and exits.
+.PP
+If no argument is given, \fBpoff\fP will stop or signal pppd if and only
+if there is exactly one running. If more than one connection is active,
+it will exit with an error code of 1.
+..
+.SS plog
+\fBplog\fP shows you the last few lines of \fI/var/log/ppp.log\fP. If that
+file doesn't exist, it shows you the last few lines of your
+\fI/var/log/syslog\fP file, but excluding the lines not generated by pppd.
+This script makes use of the
+.BR tail (1)
+command, so arguments that can be passed to
+.BR tail (1)
+can also be passed to \fBplog\fP.
+.PP
+Note: the \fBplog\fP script can only be used by root or another system
+administrator in group "adm", due to security reasons. Also, to have all
+pppd-generated information in one logfile, that plog can show, you need the
+following line in your \fI/etc/syslog.conf\fP file:
+.PP
+local2.*		\-/var/log/ppp.log
+.RE
+.SH FILES
+.TP
+.I /etc/ppp/options
+PPPd system options file.
+.TP
+.I /etc/ppp/pap\-secrets
+System PAP passwords file.
+.TP
+.I /etc/ppp/chap\-secrets
+System CHAP passwords file.
+.TP
+.I /etc/ppp/peers/
+Directory holding the peer options files. The default file is called
+\fIprovider\fP.
+.TP
+.I /etc/chatscripts/provider
+The chat script invoked from the default \fI/etc/ppp/peers/provider\fP.
+.TP
+.I /var/log/ppp.log
+The default PPP log file.
+.SH AUTHORS
+The p-commands were written by Christoph Lameter <clameter@debian.org>.
+Updated and revised by Philip Hands <phil@hands.com>.
+.br
+This manual was written by Othmar Pasteka <othmar@tron.at>. Modified
+by Rob Levin <lilo@openprojects.net>, with some extensions taken from
+the old p-commands manual written by John Hasler <jhasler@debian.org>.
+.SH "SEE ALSO"
+.BR pppd (8),
+.BR chat (8),
+.BR tail (1).
diff -Naur tomato/release/src/router/pppd/scripts/secure-card tomato121/release/src/router/pppd/scripts/secure-card
--- tomato/release/src/router/pppd/scripts/secure-card	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/scripts/secure-card	2008-04-22 21:44:05.000000000 +1000
@@ -17,14 +17,14 @@
 
 send_user "hello, starting ppp\n"
 
-system "stty 19200 -echoe -echo raw < /dev/cua3 > /dev/cua3"
+system "stty 19200 -echoe -echo raw < /dev/ttyS3 > /dev/ttyS3"
 
 #
 # These are the parameters for the program.
 #
 set user      Pxxxxxx
 set password  xxxxxxx 
-set modem     /dev/cua3
+set modem     /dev/ttyS3
 set dialup    <put phone number here>
 set timeout   60
 
@@ -107,5 +107,5 @@
 	}
 }
 
-overlay -0 $spawn_id -1 $spawn_id pppd /dev/cua3 19200 192.111.187.215: \
+overlay -0 $spawn_id -1 $spawn_id pppd /dev/ttyS3 19200 192.111.187.215: \
 	crtscts modem defaultroute debug 
diff -Naur tomato/release/src/router/pppd/SETUP tomato121/release/src/router/pppd/SETUP
--- tomato/release/src/router/pppd/SETUP	2006-08-03 17:07:33.000000000 +1000
+++ tomato121/release/src/router/pppd/SETUP	2008-04-22 21:44:05.000000000 +1000
@@ -21,7 +21,7 @@
 that you will be dialling.  For example, suppose the file is called
 /etc/ppp/peers/isp.  This file would contain something like this:
 
-cua0		# modem is connected to /dev/cua0
+ttyS0		# modem is connected to /dev/ttyS0
 38400		# run the serial port at 38400 baud
 crtscts		# use hardware flow control
 noauth		# don't require the ISP to authenticate itself
diff -Naur tomato/release/src/router/pppd/solaris/Makedefs tomato121/release/src/router/pppd/solaris/Makedefs
--- tomato/release/src/router/pppd/solaris/Makedefs	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makedefs	2008-04-22 21:44:05.000000000 +1000
@@ -4,13 +4,11 @@
 
 INSTALL= /usr/sbin/install
 
-BINDIR = /usr/local/bin
-MANDIR = /usr/local/man
-ETCDIR = /etc/ppp
+BINDIR = @DESTDIR@/bin
+MANDIR = @DESTDIR@/man
+ETCDIR = @SYSCONF@/ppp
 
+CC = /opt/SUNWspro/bin/cc
 COPTS = -O -Xa
 
-# For compiling with gcc, comment out the COPTS definition above and
-# uncomment the next 2 definitions.
-#CC = gcc
-#COPTS = -O2
+LD = /usr/ccs/bin/ld
diff -Naur tomato/release/src/router/pppd/solaris/Makedefs.gcc tomato121/release/src/router/pppd/solaris/Makedefs.gcc
--- tomato/release/src/router/pppd/solaris/Makedefs.gcc	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makedefs.gcc	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,14 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= /usr/sbin/install
+
+BINDIR = @DESTDIR@/bin
+MANDIR = @DESTDIR@/man
+ETCDIR = @SYSCONF@/ppp
+
+CC = gcc
+COPTS = -O2
+
+LD = /usr/ccs/bin/ld
diff -Naur tomato/release/src/router/pppd/solaris/Makedefs.sol2 tomato121/release/src/router/pppd/solaris/Makedefs.sol2
--- tomato/release/src/router/pppd/solaris/Makedefs.sol2	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makedefs.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -1,10 +1,10 @@
 #
 # Generic make definitions for Solaris 2
 #
-# $Id: Makedefs.sol2,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makedefs.sol2,v 1.2 2002/09/07 05:15:25 carlsonj Exp $
 #
 
-include ../solaris/Makedefs
+include ../Makedefs.com
 
 CPPFLAGS	= -D_KERNEL -DSVR4 -DSOL2 -DPRIOQ -DDEBUG -I../include
 CFLAGS		= $(CPPFLAGS) $(COPTS)
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.sol2 tomato121/release/src/router/pppd/solaris/Makefile.sol2
--- tomato/release/src/router/pppd/solaris/Makefile.sol2	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.sol2	2008-04-22 21:44:05.000000000 +1000
@@ -1,7 +1,7 @@
 #
 # Makefile for STREAMS modules for Solaris 2.
 #
-# $Id: Makefile.sol2,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makefile.sol2,v 1.3 2004/11/15 00:57:54 carlsonj Exp $
 #
 
 include Makedefs.sol2
@@ -14,15 +14,15 @@
 all:	ppp ppp_ahdl ppp_comp
 
 ppp:	ppp.o ppp_mod.o
-	ld -r -o $@ ppp.o ppp_mod.o
+	$(LD) -r -o $@ ppp.o ppp_mod.o
 	chmod +x $@
 
 ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
-	ld -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+	$(LD) -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
 	chmod +x $@
 
 ppp_comp: $(COMP_OBJS)
-	ld -r -o $@ $(COMP_OBJS)
+	$(LD) -r -o $@ $(COMP_OBJS)
 	chmod +x $@
 
 bsd-comp.o:	../modules/bsd-comp.c
@@ -47,11 +47,13 @@
 	$(CC) $(CFLAGS) -c $?
 
 install:
+	/usr/sbin/modunload -i 0
 	cp ppp ppp.conf /kernel/drv
 	cp ppp_comp ppp_ahdl /kernel/strmod
 	if grep clone:ppp /etc/minor_perm; then :; else \
 	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
 	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
 	/usr/sbin/add_drv ppp
 
 SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.sol2-64 tomato121/release/src/router/pppd/solaris/Makefile.sol2-64
--- tomato/release/src/router/pppd/solaris/Makefile.sol2-64	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.sol2-64	2008-04-22 21:44:05.000000000 +1000
@@ -1,7 +1,7 @@
 #
 # Makefile for 64-bit STREAMS modules for Solaris 2.
 #
-# $Id: Makefile.sol2-64,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makefile.sol2-64,v 1.3 2004/11/15 00:57:54 carlsonj Exp $
 #
 
 include Makedefs.sol2
@@ -25,15 +25,16 @@
 	$(MAKE) -f $(STD_MAKE) all
 
 ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
-	ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
 	chmod +x $(LP64DIR)/$@
 
 ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
-	ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
 	chmod +x $(LP64DIR)/$@
 
 ppp_comp: $(COMP_OBJS)
-	ld -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
 	chmod +x $(LP64DIR)/$@
 
 $(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
@@ -61,6 +62,7 @@
 	mkdir -m 755 -p $@
 
 install:
+	/usr/sbin/modunload -i 0
 	cp ppp ppp.conf /kernel/drv
 	cp ppp_comp ppp_ahdl /kernel/strmod
 	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
@@ -68,6 +70,7 @@
 	if grep clone:ppp /etc/minor_perm; then :; else \
 	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
 	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
 	/usr/sbin/add_drv ppp
 
 SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.sol2-64x tomato121/release/src/router/pppd/solaris/Makefile.sol2-64x
--- tomato/release/src/router/pppd/solaris/Makefile.sol2-64x	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.sol2-64x	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,89 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2 on x64
+#
+# $Id: Makefile.sol2-64x,v 1.1 2005/06/26 23:53:17 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# Sun's cc flag for LP64 compilation / linkage
+COPTS 		+= -errwarn -xtarget=opteron -xarch=amd64 -xmodel=kernel \
+		   -Ui386 -U__i386 -D__amd64 -xO2
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= amd64
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.sol2gcc tomato121/release/src/router/pppd/solaris/Makefile.sol2gcc
--- tomato/release/src/router/pppd/solaris/Makefile.sol2gcc	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.sol2gcc	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,68 @@
+#
+# Makefile for STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2gcc,v 1.4 2004/11/15 00:57:54 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+COPTS += -fno-builtin
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+	ppp_comp_mod.o
+
+all:	ppp ppp_ahdl ppp_comp
+
+ppp:	ppp.o ppp_mod.o
+	$(LD) -r -o $@ ppp.o ppp_mod.o
+	chmod +x $@
+
+ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
+	$(LD) -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+	chmod +x $@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $@ $(COMP_OBJS)
+	chmod +x $@
+
+bsd-comp.o:	../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $?
+deflate.o:	../modules/deflate.c
+	$(CC) $(CFLAGS) -c $?
+ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $?
+ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $?
+ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $?
+ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $?
+ppp_comp_mod.o:	ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $?
+vjcompress.o:	../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $?
+zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $?
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT32) $(SRCS)
+
+clean:
+	rm -f ppp ppp_comp ppp_ahdl *.o *~ core
+	rm -f *.ln
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.sol2gcc-64 tomato121/release/src/router/pppd/solaris/Makefile.sol2gcc-64
--- tomato/release/src/router/pppd/solaris/Makefile.sol2gcc-64	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.sol2gcc-64	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,88 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2gcc-64,v 1.3 2004/11/15 00:57:54 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# gcc flags for LP64 compilation / linkage
+COPTS           += -mcpu=v9 -m64 -mcmodel=medlow -mstack-bias -fno-builtin
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= sparcv9
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2gcc
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.sol2gcc-64x tomato121/release/src/router/pppd/solaris/Makefile.sol2gcc-64x
--- tomato/release/src/router/pppd/solaris/Makefile.sol2gcc-64x	1970-01-01 10:00:00.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.sol2gcc-64x	2008-04-22 21:44:05.000000000 +1000
@@ -0,0 +1,94 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2 on x64 with gcc.
+#
+# $Id: Makefile.sol2gcc-64x,v 1.1 2005/06/26 23:53:17 carlsonj Exp $
+#
+
+include Makedefs.sol2
+
+# gcc flags for LP64 compilation / linkage
+COPTS           += -finline -fno-inline-functions -fno-builtin -fno-asm \
+		   -nodefaultlibs -D__sun -m64 -mtune=opteron -Ui386 \
+		   -U__i386 -fno-strict-aliasing -fno-unit-at-a-time \
+		   -fno-optimize-sibling-calls -O2 -D_ASM_INLINES \
+		   -ffreestanding -mcmodel=kernel -mno-red-zone -gdwarf-2 \
+		   -std=gnu89 -D_KERNEL -D_SYSCALL32 -D_SYSCALL32_IMPL \
+		   -D_ELF64 -Dsun -D__sun -D__SVR4
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR		= amd64
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE	= Makefile.sol2gcc
+
+COMP_OBJS	= $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+		$(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+		$(LP64DIR)/ppp_comp_mod.o
+
+all:	std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+	$(MAKE) -f $(STD_MAKE) all
+
+ppp:	$(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+	$(LD) -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o \
+		$(LP64DIR)/ppp_ahdlc_mod.o
+	chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+	$(LD) -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+	chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:	ppp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:	ppp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+	$(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:	../common/zlib.c
+	$(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+	mkdir -m 755 -p $@
+
+install:
+	/usr/sbin/modunload -i 0
+	cp ppp ppp.conf /kernel/drv
+	cp ppp_comp ppp_ahdl /kernel/strmod
+	cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+	cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+	if grep clone:ppp /etc/minor_perm; then :; else \
+	  echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+	/usr/sbin/rem_drv ppp 2>/dev/null || true
+	/usr/sbin/modunload -i 0
+	/usr/sbin/add_drv ppp
+
+SRCS	= ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+	ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+	../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+	$(LINT64) $(SRCS)
+
+lint-32:
+	$(LINT32) $(SRCS)
+
+clean:
+	$(MAKE) -f $(STD_MAKE) clean
+	rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff -Naur tomato/release/src/router/pppd/solaris/Makefile.top tomato121/release/src/router/pppd/solaris/Makefile.top
--- tomato/release/src/router/pppd/solaris/Makefile.top	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/Makefile.top	2008-04-22 21:44:05.000000000 +1000
@@ -1,10 +1,10 @@
 #
 # ppp top level makefile for SVR4 and Solaris 2
 #
-# $Id: Makefile.top,v 1.1 2003/07/10 07:43:05 honor Exp $
+# $Id: Makefile.top,v 1.3 2004/11/01 09:31:07 paulus Exp $
 #
 
-include solaris/Makedefs
+include Makedefs.com
 
 all:
 	cd chat; $(MAKE) all
@@ -13,18 +13,20 @@
 	cd pppdump; $(MAKE) all
 	cd solaris; $(MAKE) all
 
-install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+install: $(BINDIR) $(MANDIR)/man8 install-progs
 
 install-progs:
 	cd chat; $(MAKE) install
 	cd pppd; $(MAKE) install
 	cd pppstats; $(MAKE) install
 	cd pppdump; $(MAKE) install
-	cd solaris; $(MAKE) install
 
 install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
 	$(ETCDIR)/chap-secrets
 
+install-modules:
+	cd solaris; $(MAKE) install
+
 $(ETCDIR)/options:
 	cp etc.ppp/options $@
 	chmod go-w $@
@@ -48,3 +50,6 @@
 	cd pppdump; $(MAKE) clean
 	cd solaris; $(MAKE) clean
 
+# no tests yet, one day...
+installcheck:
+	true
diff -Naur tomato/release/src/router/pppd/solaris/ppp_ahdlc.c tomato121/release/src/router/pppd/solaris/ppp_ahdlc.c
--- tomato/release/src/router/pppd/solaris/ppp_ahdlc.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/ppp_ahdlc.c	2008-04-22 21:44:05.000000000 +1000
@@ -18,30 +18,38 @@
  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: ppp_ahdlc.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: ppp_ahdlc.c,v 1.5 2005/06/27 00:59:57 carlsonj Exp $
  */
 
 /*
@@ -50,6 +58,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/stream.h>
+#include <sys/stropts.h>
 #include <sys/errno.h>
 
 #ifdef SVR4
@@ -75,6 +84,14 @@
 #define USE_MUTEX
 #endif /* SOL2 */
 
+#ifdef USE_MUTEX
+#define	MUTEX_ENTER(x)	mutex_enter(x)
+#define	MUTEX_EXIT(x)	mutex_exit(x)
+#else
+#define	MUTEX_ENTER(x)
+#define	MUTEX_EXIT(x)
+#endif
+
 /*
  * intpointer_t and uintpointer_t are signed and unsigned integer types 
  * large enough to hold any data pointer; that is, data pointers can be 
@@ -236,6 +253,7 @@
 MOD_OPEN(ahdlc_open)
 {
     ahdlc_state_t   *state;
+    mblk_t *mp;
 
     /*
      * Return if it's already opened
@@ -248,7 +266,7 @@
      * This can only be opened as a module
      */
     if (sflag != MODOPEN) {
-	return 0;
+	OPEN_ERROR(EINVAL);
     }
 
     state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t));
@@ -261,7 +279,6 @@
 
 #if defined(USE_MUTEX)
     mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL);
-    mutex_enter(&state->lock);
 #endif /* USE_MUTEX */
 
     state->xaccm[0] = ~0;	    /* escape 0x00 through 0x1f */
@@ -271,16 +288,18 @@
     state->flag_time = drv_usectohz(FLAG_TIME);
 #endif /* SOL2 */
 
-#if defined(USE_MUTEX)
-    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */	
-
 #if defined(SUNOS4)
     ppp_ahdlc_count++;
 #endif /* SUNOS4 */
 
     qprocson(q);
-    
+
+    if ((mp = allocb(1, BPRI_HI)) != NULL) {
+	    mp->b_datap->db_type = M_FLUSH;
+	    *mp->b_wptr++ = FLUSHR;
+	    putnext(q, mp);
+    }
+
     return 0;
 }
 
@@ -300,17 +319,12 @@
 	return 0;
     }
 
-#if defined(USE_MUTEX)
-    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
-
     if (state->rx_buf != 0) {
 	freemsg(state->rx_buf);
 	state->rx_buf = 0;
     }
 
 #if defined(USE_MUTEX)
-    mutex_exit(&state->lock);
     mutex_destroy(&state->lock);
 #endif /* USE_MUTEX */
 
@@ -370,16 +384,12 @@
 		DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
 		break;
 	    }
-#if defined(USE_MUTEX)
-	    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_ENTER(&state->lock);
 	    bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
 		  iop->ioc_count);
 	    state->xaccm[2] &= ~0x40000000;	/* don't escape 0x5e */
 	    state->xaccm[3] |= 0x60000000;	/* do escape 0x7d, 0x7e */
-#if defined(USE_MUTEX)
-	    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_EXIT(&state->lock);
 	    iop->ioc_count = 0;
 	    error = 0;
 	    break;
@@ -391,14 +401,10 @@
 		DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
 		break;
 	    }
-#if defined(USE_MUTEX)
-	    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_ENTER(&state->lock);
 	    bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
 		  sizeof(u_int32_t));
-#if defined(USE_MUTEX)
-	    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_EXIT(&state->lock);
 	    iop->ioc_count = 0;
 	    error = 0;
 	    break;
@@ -412,13 +418,9 @@
 	    if (mp->b_cont != 0)
 		freemsg(mp->b_cont);
 	    mp->b_cont = np;
-#if defined(USE_MUTEX)
-	    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_ENTER(&state->lock);
 	    *(int *)np->b_wptr = state->flags & RCV_FLAGS;
-#if defined(USE_MUTEX)
-	    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_EXIT(&state->lock);
 	    np->b_wptr += sizeof(int);
 	    iop->ioc_count = sizeof(int);
 	    error = 0;
@@ -467,37 +469,25 @@
     case M_CTL:
 	switch (*mp->b_rptr) {
 	case PPPCTL_MTU:
-#if defined(USE_MUTEX)
-	    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_ENTER(&state->lock);
 	    state->mtu = ((unsigned short *)mp->b_rptr)[1];
-#if defined(USE_MUTEX)
-	    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
-	    freemsg(mp);
+	    MUTEX_EXIT(&state->lock);
 	    break;
 	case PPPCTL_MRU:
-#if defined(USE_MUTEX)
-	    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_ENTER(&state->lock);
 	    state->mru = ((unsigned short *)mp->b_rptr)[1];
-#if defined(USE_MUTEX)
-	    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
-	    freemsg(mp);
+	    MUTEX_EXIT(&state->lock);
 	    break;
 	case PPPCTL_UNIT:
-#if defined(USE_MUTEX)
-	    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_ENTER(&state->lock);
 	    state->unit = mp->b_rptr[1];
-#if defined(USE_MUTEX)
-	    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+	    MUTEX_EXIT(&state->lock);
 	    break;
 	default:
 	    putnext(q, mp);
+	    return 0;
 	}
+	freemsg(mp);
 	break;
 
     default:
@@ -527,22 +517,17 @@
     switch (mp->b_datap->db_type) {
     case M_DATA:
 	ahdlc_decode(q, mp);
-	freemsg(mp);
 	break;
 
     case M_HANGUP:
-#if defined(USE_MUTEX)
-	mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+	MUTEX_ENTER(&state->lock);
 	if (state->rx_buf != 0) {
 	    /* XXX would like to send this up for debugging */
 	    freemsg(state->rx_buf);
 	    state->rx_buf = 0;
 	}
 	state->flags = IFLUSH;
-#if defined(USE_MUTEX)
-	mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+	MUTEX_EXIT(&state->lock);
 	putnext(q, mp);
 	break;
 
@@ -578,9 +563,7 @@
     }
 
     state = (ahdlc_state_t *)q->q_ptr;
-#if defined(USE_MUTEX)
-    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+    MUTEX_ENTER(&state->lock);
 
     /*
      * Allocate an output buffer large enough to handle a case where all
@@ -593,9 +576,7 @@
     outmp = allocb(outmp_len, BPRI_MED);
     if (outmp == NULL) {
 	state->stats.ppp_oerrors++;
-#if defined(USE_MUTEX)
-	mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+	MUTEX_EXIT(&state->lock);
 	putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
 	return;
     }
@@ -689,12 +670,9 @@
     state->stats.ppp_obytes += msgdsize(outmp);
     state->stats.ppp_opackets++;
 
-#if defined(USE_MUTEX)
-    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+    MUTEX_EXIT(&state->lock);
 
     putnext(q, outmp);
-    return;
 }
 
 /*
@@ -715,35 +693,14 @@
     ahdlc_state_t   *state;
     mblk_t	    *om;
     uchar_t	    *dp;
-    ushort_t	    fcs;
-#if defined(SOL2)
-    mblk_t	    *zmp;
-#endif /* SOL2 */
-
-#if defined(SOL2)
-    /*
-     * In case the driver (or something below) doesn't send
-     * data upstream in one message block, concatenate everything
-     */
-    if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) && 
-         ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) {
-
-	zmp = msgpullup(mp, -1);
-	freemsg(mp);
-	mp = zmp;
-	if (mp == 0)
-	    return; 
-    }
-#endif /* SOL2 */
 
     state = (ahdlc_state_t *) q->q_ptr;
 
-#if defined(USE_MUTEX)
-    mutex_enter(&state->lock);
-#endif /* USE_MUTEX */
+    MUTEX_ENTER(&state->lock);
 
     state->stats.ppp_ibytes += msgdsize(mp);
 
+    for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om)
     for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
 
 	/*
@@ -860,9 +817,7 @@
 	}
     }
 
-#if defined(USE_MUTEX)
-    mutex_exit(&state->lock);
-#endif /* USE_MUTEX */
+    MUTEX_EXIT(&state->lock);
 }
 
 static int
diff -Naur tomato/release/src/router/pppd/solaris/ppp.c tomato121/release/src/router/pppd/solaris/ppp.c
--- tomato/release/src/router/pppd/solaris/ppp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/ppp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: ppp.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: ppp.c,v 1.4 2005/06/27 00:59:57 carlsonj Exp $
  */
 
 /*
@@ -268,7 +276,9 @@
 static int send_data __P((mblk_t *, upperstr_t *));
 static void new_ppa __P((queue_t *, mblk_t *));
 static void attach_ppa __P((queue_t *, mblk_t *));
+#ifndef NO_DLPI
 static void detach_ppa __P((queue_t *, mblk_t *));
+#endif
 static void detach_lower __P((queue_t *, mblk_t *));
 static void debug_dump __P((queue_t *, mblk_t *));
 static upperstr_t *find_dest __P((upperstr_t *, int));
@@ -637,11 +647,12 @@
 	    break;
 	}
 #ifdef NO_DLPI
+	/* pass_packet frees the packet on returning 0 */
 	if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1))
 	    break;
 #endif
-	if (!send_data(mp, us))
-	    putq(q, mp);
+	if (!send_data(mp, us) && !putq(q, mp))
+	    freemsg(mp);
 	break;
 
     case M_IOCTL:
@@ -714,6 +725,7 @@
 #endif
 	    iop->ioc_count = 0;
 	    qwriter(q, mp, detach_lower, PERIM_OUTER);
+	    /* mp is now gone */
 	    error = -1;
 	    break;
 
@@ -740,6 +752,7 @@
 	    iop->ioc_count = sizeof(int);
 	    mq->b_wptr = mq->b_rptr + sizeof(int);
 	    qwriter(q, mp, new_ppa, PERIM_OUTER);
+	    /* mp is now gone */
 	    error = -1;
 	    break;
 
@@ -761,6 +774,7 @@
 	    us->ppa = ppa;
 	    iop->ioc_count = 0;
 	    qwriter(q, mp, attach_ppa, PERIM_OUTER);
+	    /* mp is now gone */
 	    error = -1;
 	    break;
 
@@ -843,8 +857,8 @@
 	    }
 	    n = *(int *)mp->b_cont->b_rptr;
 	    if (n == PPPDBG_DUMP + PPPDBG_DRIVER) {
-		qwriter(q, NULL, debug_dump, PERIM_OUTER);
-		iop->ioc_count = 0;
+		qwriter(q, mp, debug_dump, PERIM_OUTER);
+		/* mp is now gone */
 		error = -1;
 	    } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) {
 		DPRINT1("ppp/%d: debug log enabled\n", us->mn);
@@ -855,6 +869,7 @@
 		if (us->ppa == 0 || us->ppa->lowerq == 0)
 		    break;
 		putnext(us->ppa->lowerq, mp);
+		/* mp is now gone */
 		error = -1;
 	    }
 	    break;
@@ -1002,7 +1017,8 @@
 	    ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ;
 	    mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t);
 	    dlpi_request(q, mq, us);
-	    error = 0;
+	    /* mp is now gone */
+	    error = -1;
 	    break;
 
 	case SIOCGIFNETMASK:
@@ -1337,9 +1353,10 @@
 	mp->b_rptr[1] = PPP_UI;
 	mp->b_rptr[2] = us->sap >> 8;
 	mp->b_rptr[3] = us->sap;
+	/* pass_packet frees the packet on returning 0 */
 	if (pass_packet(us, mp, 1)) {
-	    if (!send_data(mp, us))
-		putq(q, mp);
+	    if (!send_data(mp, us) && !putq(q, mp))
+		freemsg(mp);
 	}
 	return;
 
@@ -1422,7 +1439,8 @@
 #endif
 
     default:
-	cmn_err(CE_CONT, "ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
+	if (us->flags & US_DBGLOG)
+	    DPRINT1("ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
 	/* fall through */
     badprim:
 	dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0);
@@ -1475,6 +1493,9 @@
 }
 #endif /* NO_DLPI */
 
+/*
+ * If return value is 0, then the packet has already been freed.
+ */
 static int
 pass_packet(us, mp, outbound)
     upperstr_t *us;
@@ -1667,9 +1688,11 @@
 #ifndef NO_DLPI
 	dlpi_ok(q, DL_ATTACH_REQ);
 #endif
+	freemsg(mp);
     }
 }
 
+#ifndef NO_DLPI
 static void
 detach_ppa(q, mp)
     queue_t *q;
@@ -1690,11 +1713,11 @@
 	}
     us->next = 0;
     us->ppa = 0;
-#ifndef NO_DLPI
     us->state = DL_UNATTACHED;
     dlpi_ok(q, DL_DETACH_REQ);
-#endif
+    freemsg(mp);
 }
+#endif
 
 /*
  * We call this with qwriter in order to give the upper queue procedures
@@ -1864,53 +1887,54 @@
 	putnext(ppa->q, mp);
 	break;
 
-    default:
-	if (mp->b_datap->db_type == M_DATA) {
-	    len = msgdsize(mp);
-	    if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) {
-		PULLUP(mp, PPP_HDRLEN);
-		if (mp == 0) {
-		    DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len);
-		    break;
-		}
+    case M_DATA:
+	len = msgdsize(mp);
+	if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) {
+	    PULLUP(mp, PPP_HDRLEN);
+	    if (mp == 0) {
+		DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len);
+		break;
 	    }
-	    MT_ENTER(&ppa->stats_lock);
-	    ppa->stats.ppp_ipackets++;
-	    ppa->stats.ppp_ibytes += len;
+	}
+	MT_ENTER(&ppa->stats_lock);
+	ppa->stats.ppp_ipackets++;
+	ppa->stats.ppp_ibytes += len;
 #ifdef INCR_IPACKETS
-	    INCR_IPACKETS(ppa);
+	INCR_IPACKETS(ppa);
 #endif
-	    MT_EXIT(&ppa->stats_lock);
+	MT_EXIT(&ppa->stats_lock);
 
-	    proto = PPP_PROTOCOL(mp->b_rptr);
+	proto = PPP_PROTOCOL(mp->b_rptr);
 
 #if defined(SOL2)
-	    /*
-	     * Should there be any promiscuous stream(s), send the data
-	     * up for each promiscuous stream that we recognize.
-	     */
-	    promisc_sendup(ppa, mp, proto, 1);
+	/*
+	 * Should there be any promiscuous stream(s), send the data
+	 * up for each promiscuous stream that we recognize.
+	 */
+	promisc_sendup(ppa, mp, proto, 1);
 #endif /* defined(SOL2) */
 
-	    if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
-		/*
-		 * A data packet for some network protocol.
-		 * Queue it on the upper stream for that protocol.
-		 * XXX could we just putnext it?  (would require thought)
-		 * The rblocked flag is there to ensure that we keep
-		 * messages in order for each network protocol.
-		 */
-		if (!pass_packet(us, mp, 0))
-		    break;
-		if (!us->rblocked && !canput(us->q))
-		    us->rblocked = 1;
-		if (!us->rblocked)
-		    putq(us->q, mp);
-		else
-		    putq(q, mp);
+	if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
+	    /*
+	     * A data packet for some network protocol.
+	     * Queue it on the upper stream for that protocol.
+	     * XXX could we just putnext it?  (would require thought)
+	     * The rblocked flag is there to ensure that we keep
+	     * messages in order for each network protocol.
+	     */
+	    /* pass_packet frees the packet on returning 0 */
+	    if (!pass_packet(us, mp, 0))
 		break;
-	    }
+	    if (!us->rblocked && !canput(us->q))
+		us->rblocked = 1;
+	    if (!putq(us->rblocked ? q : us->q, mp))
+		freemsg(mp);
+	    break;
 	}
+
+	/* FALLTHROUGH */
+
+   default:
 	/*
 	 * A control frame, a frame for an unknown protocol,
 	 * or some other message type.
@@ -1918,8 +1942,8 @@
 	 */
 	if (queclass(mp) == QPCTL || canputnext(ppa->q))
 	    putnext(ppa->q, mp);
-	else
-	    putq(q, mp);
+	else if (!putq(q, mp))
+	    freemsg(mp);
 	break;
     }
 
@@ -1971,7 +1995,8 @@
 	    if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) {
 		if (!canput(as->q))
 		    break;
-		putq(as->q, mp);
+		if (!putq(as->q, mp))
+		    freemsg(mp);
 	    } else {
 		if (!canputnext(q))
 		    break;
@@ -2178,11 +2203,12 @@
 		    if (canputnext(prus->q)) {
 			if (prus->flags & US_RAWDATA) {
 			    dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto);
-			    putnext(prus->q, dup_dup_mp);
 			} else {
 			    dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto);
-			    putnext(prus->q, dup_dup_mp);
 			}
+			if (dup_dup_mp == 0)
+			    continue;
+			putnext(prus->q, dup_dup_mp);
 		    } else {
 			DPRINT("ppp_urput: data to promisc q dropped\n");
 			freemsg(dup_dup_mp);
@@ -2193,11 +2219,11 @@
 	    if (canputnext(prus->q)) {
 		if (prus->flags & US_RAWDATA) {
 		    dup_mp = prepend_ether(prus, dup_mp, proto);
-		    putnext(prus->q, dup_mp);
 		} else {
 		    dup_mp = prepend_udind(prus, dup_mp, proto);
-		    putnext(prus->q, dup_mp);
 		}
+		if (dup_mp != 0)
+		    putnext(prus->q, dup_mp);
 	    } else {
 		DPRINT("ppp_urput: data to promisc q dropped\n");
 		freemsg(dup_mp);
@@ -2244,7 +2270,8 @@
      * rather than blocking, to avoid the possibility of deadlock.
      */
     if (!TRYLOCK_LOWER_R) {
-	putq(q, mp);
+	if (!putq(q, mp))
+	    freemsg(mp);
 	return 0;
     }
 
@@ -2267,8 +2294,8 @@
      */
     if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq)))
 	put(uq, mp);
-    else
-	putq(q, mp);
+    else if (!putq(q, mp))
+	freemsg(mp);
 
     UNLOCK_LOWER;
     return 0;
@@ -2402,6 +2429,9 @@
 };
 
 
+/*
+ * Packet has already been freed if return value is 0.
+ */
 static int
 ip_hard_filter(us, mp, outbound)
     upperstr_t *us;
@@ -2475,7 +2505,10 @@
 				us->mn, pft->proto, pft->port);
 	    /* Discard if not connected, or if not pass_with_link_up */
 	    /* else, if link is up let go by, but don't update time */
-	    return pft->ok_if_link_up? -1: 0;
+	    if (pft->ok_if_link_up)
+		return -1;
+	    freemsg(mp);
+	    return 0;
 	}
         break;
     } /* end switch (proto) */
diff -Naur tomato/release/src/router/pppd/solaris/ppp_comp.c tomato121/release/src/router/pppd/solaris/ppp_comp.c
--- tomato/release/src/router/pppd/solaris/ppp_comp.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/ppp_comp.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * 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.
  *
- * $Id: ppp_comp.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: ppp_comp.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 /*
@@ -616,7 +624,7 @@
         if (!bcanputnext(q,mp->b_band))
 #else
         if (!canputnext(q))
-#endif PRIOQ
+#endif /* PRIOQ */
 	{
 	    putbq(q, mp);
 	    break;
@@ -694,7 +702,7 @@
 	    if (cmp != NULL) {
 #ifdef PRIOQ
 		cmp->b_band=mp->b_band;
-#endif PRIOQ
+#endif /* PRIOQ */
 		freemsg(mp);
 		mp = cmp;
 	    }
diff -Naur tomato/release/src/router/pppd/solaris/ppp_comp_mod.c tomato121/release/src/router/pppd/solaris/ppp_comp_mod.c
--- tomato/release/src/router/pppd/solaris/ppp_comp_mod.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/ppp_comp_mod.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp_comp_mod.c - modload support for PPP compression STREAMS module.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ppp_comp_mod.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: ppp_comp_mod.c,v 1.2 2002/12/06 09:49:16 paulus Exp $
  */
 
 /*
diff -Naur tomato/release/src/router/pppd/solaris/ppp_mod.c tomato121/release/src/router/pppd/solaris/ppp_mod.c
--- tomato/release/src/router/pppd/solaris/ppp_mod.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/solaris/ppp_mod.c	2008-04-22 21:44:05.000000000 +1000
@@ -1,30 +1,38 @@
 /*
  * ppp_mod.c - modload support for PPP pseudo-device driver.
  *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies.  This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * $Id: ppp_mod.c,v 1.1 2003/07/10 07:43:05 honor Exp $
+ * 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.
+ *
+ * $Id: ppp_mod.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $
  */
 
 /*
@@ -116,8 +124,13 @@
 ppp_identify(dip)
     dev_info_t *dip;
 {
+    /* This entry point is not used as of Solaris 10 */
+#ifdef DDI_IDENTIFIED
     return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED:
 	DDI_NOT_IDENTIFIED;
+#else
+    return 0;
+#endif
 }
 
 static int
diff -Naur tomato/release/src/router/pppd/sunos4/if_ppp_vdcmd.c tomato121/release/src/router/pppd/sunos4/if_ppp_vdcmd.c
--- tomato/release/src/router/pppd/sunos4/if_ppp_vdcmd.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/if_ppp_vdcmd.c	1970-01-01 10:00:00.000000000 +1000
@@ -1,57 +0,0 @@
-#include <sys/types.h>
-#include <sys/errno.h>
-#include <sys/conf.h>
-#include <sun/vddrv.h>
-
-extern struct streamtab if_pppinfo;
-
-static struct vdldrv vd = {
-    VDMAGIC_USER,
-    "if_ppp"
-};
-
-static int fmodsw_index = -1;
-
-int
-if_ppp_vdcmd(fun, vdp, vdi, vds)
-    unsigned int fun;
-    struct vddrv *vdp;
-    addr_t vdi;
-    struct vdstat *vds;
-{
-    int n, error;
-
-    switch (fun) {
-    case VDLOAD:
-	vdp->vdd_vdtab = (struct vdlinkage *) &vd;
-	if (fmodsw_index >= 0)
-	    return EBUSY;
-	for (n = 0; n < fmodcnt; ++n)
-	    if (fmodsw[n].f_str == 0)
-		break;
-	if (n >= fmodcnt)
-	    return ENODEV;
-	strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1);
-	fmodsw[n].f_str = &if_pppinfo;
-	fmodsw_index = n;
-	break;
-
-    case VDUNLOAD:
-	if (fmodsw_index <= 0)
-	    return EINVAL;
-	error = if_ppp_unload();
-	if (error != 0)
-	    return error;
-	fmodsw[fmodsw_index].f_name[0] = 0;
-	fmodsw[fmodsw_index].f_str = 0;
-	fmodsw_index = -1;
-	break;
-
-    case VDSTAT:
-	break;
-
-    default:
-	return EIO;
-    }
-    return 0;
-}
diff -Naur tomato/release/src/router/pppd/sunos4/Makedefs tomato121/release/src/router/pppd/sunos4/Makedefs
--- tomato/release/src/router/pppd/sunos4/Makedefs	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/Makedefs	1970-01-01 10:00:00.000000000 +1000
@@ -1,13 +0,0 @@
-#
-# defines common to several Makefiles
-#
-
-INSTALL= install -o root -g daemon
-
-BINDIR = /usr/local/etc
-MANDIR = /usr/local/man
-ETCDIR = /etc/ppp
-
-# To use gcc, uncomment the next line.
-#CC = gcc
-COPTS = -O
diff -Naur tomato/release/src/router/pppd/sunos4/Makefile tomato121/release/src/router/pppd/sunos4/Makefile
--- tomato/release/src/router/pppd/sunos4/Makefile	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/Makefile	1970-01-01 10:00:00.000000000 +1000
@@ -1,57 +0,0 @@
-#
-# Makefile for STREAMS modules for SunOS 4.
-#
-# $Id: Makefile,v 1.1 2003/07/10 07:43:05 honor Exp $
-#
-
-include Makedefs
-
-LD = /usr/bin/ld		# make sure we don't get gnu ld
-
-# Defining __$(ARCH)__ is for gcc's broken version of sun/vddrv.h.
-ARCH = `/bin/arch -k`
-DEFINES= -DKERNEL -D_KERNEL -DSUNOS4 -D$(ARCH) -D__$(ARCH)__ \
-	 -DDEBUG -DNO_DLPI -DSNIT_SUPPORT
-CFLAGS= $(DEFINES) -I../include $(COPTS)
-
-MODULES= ppp_mod.o ppp_ahdl_mod.o ppp_comp_mod.o if_ppp_mod.o
-
-all:	$(MODULES)
-
-ppp_mod.o:	ppp.o ppp_vdcmd.o
-	$(LD) -r -o ppp_mod.o ppp.o ppp_vdcmd.o
-
-ppp_ahdl_mod.o: ppp_ahdlc.o ppp_ahdlc_vdcmd.o
-	$(LD) -r -o ppp_ahdl_mod.o ppp_ahdlc.o ppp_ahdlc_vdcmd.o
-
-COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
-	ppp_comp_vdcmd.o
-ppp_comp_mod.o: $(COMP_OBJS)
-	$(LD) -r -o $@ $(COMP_OBJS)
-
-if_ppp.o: ../modules/if_ppp.c
-	$(CC) $(CFLAGS) -c $?
-bsd-comp.o: ../modules/bsd-comp.c
-	$(CC) $(CFLAGS) -c $?
-deflate.o: ../modules/deflate.c
-	$(CC) $(CFLAGS) -c $?
-ppp.o:	../modules/ppp.c
-	$(CC) $(CFLAGS) -c $?
-ppp_ahdlc.o: ../modules/ppp_ahdlc.c
-	$(CC) $(CFLAGS) -c $?
-ppp_comp.o: ../modules/ppp_comp.c
-	$(CC) $(CFLAGS) -c $?
-vjcompress.o: ../modules/vjcompress.c
-	$(CC) $(CFLAGS) -c $?
-zlib.o:	../common/zlib.c
-	$(CC) $(CFLAGS) -c $?
-
-if_ppp_mod.o:	if_ppp.o if_ppp_vdcmd.o
-	$(LD) -r -o if_ppp_mod.o if_ppp.o if_ppp_vdcmd.o
-
-install: all
-	$(INSTALL) $(MODULES) $(BINDIR)
-	./ppp.INSTALL
-
-clean:
-	rm -f ppp ppp_comp ppp_ahdl *.o *~ core
diff -Naur tomato/release/src/router/pppd/sunos4/Makefile.top tomato121/release/src/router/pppd/sunos4/Makefile.top
--- tomato/release/src/router/pppd/sunos4/Makefile.top	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/Makefile.top	1970-01-01 10:00:00.000000000 +1000
@@ -1,46 +0,0 @@
-#
-# ppp top level makefile
-#
-
-include sunos4/Makedefs
-
-all:
-	cd chat; $(MAKE) all
-	cd pppd; $(MAKE) all
-	cd pppstats; $(MAKE) all
-	cd sunos4; $(MAKE) all
-	cd pppdump; $(MAKE) all
-
-install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
-
-install-progs:
-	cd chat; $(MAKE) install
-	cd pppd; $(MAKE) install
-	cd pppstats; $(MAKE) install
-	cd pppdump; $(MAKE) install
-	cd sunos4; $(MAKE) install
-
-install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
-	$(ETCDIR)/chap-secrets
-
-$(ETCDIR)/options:
-	$(INSTALL) -c -m 644 etc.ppp/options $@
-$(ETCDIR)/pap-secrets:
-	$(INSTALL) -c -m 600 etc.ppp/pap-secrets $@
-$(ETCDIR)/chap-secrets:
-	$(INSTALL) -c -m 600 etc.ppp/chap-secrets $@
-
-$(BINDIR):
-	$(INSTALL) -d -m 755 $@
-$(MANDIR)/man8:
-	$(INSTALL) -d -m 755 $@
-$(ETCDIR):
-	$(INSTALL) -d -m 755 $@
-
-clean:
-	rm -f *~
-	cd chat; $(MAKE) clean
-	cd pppd; $(MAKE) clean
-	cd pppstats; $(MAKE) clean
-	cd sunos4; $(MAKE) clean
-
diff -Naur tomato/release/src/router/pppd/sunos4/ppp_ahdlc_vdcmd.c tomato121/release/src/router/pppd/sunos4/ppp_ahdlc_vdcmd.c
--- tomato/release/src/router/pppd/sunos4/ppp_ahdlc_vdcmd.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/ppp_ahdlc_vdcmd.c	1970-01-01 10:00:00.000000000 +1000
@@ -1,57 +0,0 @@
-#include <sys/types.h>
-#include <sys/errno.h>
-#include <sys/conf.h>
-#include <sun/vddrv.h>
-
-extern struct streamtab ppp_ahdlcinfo;
-extern int ppp_ahdlc_count;
-
-static struct vdldrv vd = {
-    VDMAGIC_USER,
-    "ppp_ahdl"
-};
-
-static int fmodsw_index = -1;
-
-int
-ppp_ahdlc_vdcmd(fun, vdp, vdi, vds)
-    unsigned int fun;
-    struct vddrv *vdp;
-    addr_t vdi;
-    struct vdstat *vds;
-{
-    int n;
-
-    switch (fun) {
-    case VDLOAD:
-	vdp->vdd_vdtab = (struct vdlinkage *) &vd;
-	if (fmodsw_index >= 0)
-	    return EBUSY;
-	for (n = 0; n < fmodcnt; ++n)
-	    if (fmodsw[n].f_str == 0)
-		break;
-	if (n >= fmodcnt)
-	    return ENODEV;
-	strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1);
-	fmodsw[n].f_str = &ppp_ahdlcinfo;
-	fmodsw_index = n;
-	break;
-
-    case VDUNLOAD:
-	if (ppp_ahdlc_count > 0)
-	    return EBUSY;
-	if (fmodsw_index <= 0)
-	    return EINVAL;
-	fmodsw[fmodsw_index].f_name[0] = 0;
-	fmodsw[fmodsw_index].f_str = 0;
-	fmodsw_index = -1;
-	break;
-
-    case VDSTAT:
-	break;
-
-    default:
-	return EIO;
-    }
-    return 0;
-}
diff -Naur tomato/release/src/router/pppd/sunos4/ppp_comp_vdcmd.c tomato121/release/src/router/pppd/sunos4/ppp_comp_vdcmd.c
--- tomato/release/src/router/pppd/sunos4/ppp_comp_vdcmd.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/ppp_comp_vdcmd.c	1970-01-01 10:00:00.000000000 +1000
@@ -1,57 +0,0 @@
-#include <sys/types.h>
-#include <sys/errno.h>
-#include <sys/conf.h>
-#include <sun/vddrv.h>
-
-extern struct streamtab ppp_compinfo;
-extern int ppp_comp_count;
-
-static struct vdldrv vd = {
-    VDMAGIC_USER,
-    "ppp_comp"
-};
-
-static int fmodsw_index = -1;
-
-int
-ppp_comp_vdcmd(fun, vdp, vdi, vds)
-    unsigned int fun;
-    struct vddrv *vdp;
-    addr_t vdi;
-    struct vdstat *vds;
-{
-    int n;
-
-    switch (fun) {
-    case VDLOAD:
-	vdp->vdd_vdtab = (struct vdlinkage *) &vd;
-	if (fmodsw_index >= 0)
-	    return EBUSY;
-	for (n = 0; n < fmodcnt; ++n)
-	    if (fmodsw[n].f_str == 0)
-		break;
-	if (n >= fmodcnt)
-	    return ENODEV;
-	strncpy(fmodsw[n].f_name, vd.Drv_name, FMNAMESZ+1);
-	fmodsw[n].f_str = &ppp_compinfo;
-	fmodsw_index = n;
-	break;
-
-    case VDUNLOAD:
-	if (ppp_comp_count > 0)
-	    return EBUSY;
-	if (fmodsw_index <= 0)
-	    return EINVAL;
-	fmodsw[fmodsw_index].f_name[0] = 0;
-	fmodsw[fmodsw_index].f_str = 0;
-	fmodsw_index = -1;
-	break;
-
-    case VDSTAT:
-	break;
-
-    default:
-	return EIO;
-    }
-    return 0;
-}
diff -Naur tomato/release/src/router/pppd/sunos4/ppp.INSTALL tomato121/release/src/router/pppd/sunos4/ppp.INSTALL
--- tomato/release/src/router/pppd/sunos4/ppp.INSTALL	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/ppp.INSTALL	1970-01-01 10:00:00.000000000 +1000
@@ -1,104 +0,0 @@
-#!/bin/sh
-
-# Script for loading, unloading, etc. ppp modules.
-
-moddir=/usr/local/etc
-etcppp=/etc/ppp
-
-PATH=/usr/etc:/usr/bin
-
-# Check that we're superuser
-touch /tmp/su$$
-if chown root /tmp/su$$ >/dev/null; then :
-else
-    echo "$0: must be root."
-    rm -f /tmp/su$$
-    exit 1
-fi
-rm -f /tmp/su$$
-
-case "$0" in
-*ppp.INSTALL)
-    if [ ! -f ppp.INSTALL ]; then
-	echo "ppp.INSTALL: not found"
-	exit 1
-    fi
-    for n in INSTALL LOAD UNLOAD MKDEV RMDEV; do
-	if [ -h /dev/ppp.$n -o -f /dev/ppp.$n ]; then
-	    rm /dev/ppp.$n
-	fi
-    done
-    cp ppp.INSTALL /dev
-    for n in LOAD UNLOAD MKDEV RMDEV; do
-	ln -s ppp.INSTALL /dev/ppp.$n
-    done
-    ;;
-
-*ppp.LOAD)
-    if modstat | grep -w ppp >/dev/null; then
-	echo "ppp driver is already loaded."
-	exit 1
-    fi
-    if modstat | grep -w if_ppp >/dev/null; then
-	echo "if_ppp module already loaded: not reloading."
-    else
-	echo -n "if_ppp: "
-	modload $moddir/if_ppp_mod.o -sym -entry _if_ppp_vdcmd \
-	    -o $etcppp/if_ppp_mod
-    fi
-    echo -n "ppp: "
-    modload $moddir/ppp_mod.o -sym -entry _ppp_vdcmd -exec /dev/ppp.MKDEV \
-	-o $etcppp/ppp_mod
-    echo -n "ppp_comp: "
-    modload $moddir/ppp_comp_mod.o -sym -entry _ppp_comp_vdcmd \
-	-o $etcppp/ppp_comp
-    echo -n "ppp_ahdl: "
-    modload $moddir/ppp_ahdl_mod.o -sym -entry _ppp_ahdlc_vdcmd \
-	-o $etcppp/ppp_ahdl
-    exit 0
-    ;;
-
-*ppp.MKDEV)
-    # args: module number, type, b-major, c-major
-    if [ $# -ne 4 ]; then
-	echo "Usage: $0 module-id module-type b-major c-major"
-	exit 1
-    fi
-    if [ "$2" -ne "12345607" -a "$2" -ne "12345600" ]; then
-	echo "$0: $2: bad module type"
-	exit 1
-    fi
-    rm -f /dev/ppp
-    # we "just know" that 37 is the major number of the clone driver
-    mknod /dev/ppp c 37 $4
-    chmod 644 /dev/ppp
-    exit 0
-    ;;
-
-*ppp.UNLOAD)
-    stat=0
-    if modstat | grep -w if_ppp >/dev/null; then
-	echo "$0: not unloading if_ppp module."
-    fi
-    for mod in ppp ppp_comp ppp_ahdl; do
-	id=`modstat | grep -w $mod | awk '{print $1}'`
-	if [ x$id = x ]; then
-	    echo "$mod is not loaded."
-	    stat=1
-	else
-	    modunload -id $id
-	fi
-    done
-    exit $stat
-    ;;
-
-*ppp.RMDEV)
-    rm -f /dev/ppp
-    exit 0
-    ;;
-
-*)
-    echo "Invocation names: ppp.INSTALL ppp.LOAD ppp.UNLOAD ppp.MKDEV ppp.RMDEV"
-    exit 1
-    ;;
-esac
diff -Naur tomato/release/src/router/pppd/sunos4/ppp_vdcmd.c tomato121/release/src/router/pppd/sunos4/ppp_vdcmd.c
--- tomato/release/src/router/pppd/sunos4/ppp_vdcmd.c	2006-08-03 17:07:34.000000000 +1000
+++ tomato121/release/src/router/pppd/sunos4/ppp_vdcmd.c	1970-01-01 10:00:00.000000000 +1000
@@ -1,81 +0,0 @@
-#include <sys/types.h>
-#include <sys/errno.h>
-#include <sys/conf.h>
-#include <sun/vddrv.h>
-
-extern struct streamtab pppinfo;
-extern int ppp_count;
-extern int nchrdev;
-
-static struct vdldrv vd = {
-    VDMAGIC_PSEUDO,
-    "ppp"
-};
-
-extern int nodev();
-
-static struct cdevsw ppp_cdevsw = {
-    nodev, nodev, nodev, nodev, nodev, nodev, nodev, 0,
-    &pppinfo
-};
-
-static struct cdevsw old_entry;
-
-int
-ppp_vdcmd(fun, vdp, vdi, vds)
-    unsigned int fun;
-    struct vddrv *vdp;
-    addr_t vdi;
-    struct vdstat *vds;
-{
-    static int majnum = -1;
-    int n, maj;
-
-    switch (fun) {
-    case VDLOAD:
-	/*
-	 * It seems like modload doesn't install the cdevsw entry
-	 * for us.  Oh well...
-	 */
-	for (maj = 1; maj < nchrdev; ++maj)
-	    if (cdevsw[maj].d_open == vd_unuseddev)
-		break;
-	if (maj >= nchrdev)
-	    return ENODEV;
-	vd.Drv_charmajor = maj;
-	old_entry = cdevsw[maj];
-	cdevsw[maj] = ppp_cdevsw;
-	vd.Drv_cdevsw = &ppp_cdevsw;
-	vdp->vdd_vdtab = (struct vdlinkage *) &vd;
-	majnum = maj;
-	break;
-
-    case VDUNLOAD:
-	if (ppp_count > 0)
-	    return EBUSY;
-	if (vd.Drv_charmajor > 0)
-	    cdevsw[vd.Drv_charmajor] = old_entry;
-	break;
-
-    case VDSTAT:
-	/*
-	 * We have to fool the modstat command into thinking
-	 * that this module is actually a driver! This is
-	 * so that installation commands that use the -exec
-	 * option of modload to run a shell script find out
-	 * the block and/or char major numbers of the driver
-	 * loaded (so that the shell script can go off to
-	 * /dev and *MAKE* the bloody device nodes- remember
-	 * they might change from one load to another if
-	 * you don't hardwire the number!).
-	 */
-	vds->vds_magic = VDMAGIC_DRV;
-	vds->vds_modinfo[0] = (char) 0;
-	vds->vds_modinfo[1] = (char) majnum;
-	break;
-
-    default:
-	return EIO;
-    }
-    return 0;
-}
