aboutsummaryrefslogtreecommitdiffstats
path: root/package/network/services/ppp/patches
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2012-10-10 12:32:29 +0000
committerFelix Fietkau <nbd@openwrt.org>2012-10-10 12:32:29 +0000
commit405e21d16731b2764ab82aaaadcf36a813b105f7 (patch)
treec6f9a8402389a5081519b91ce62c4a8cafcb8917 /package/network/services/ppp/patches
parentd0ec348ded6f715b43b396b06ccb10599b37969d (diff)
downloadupstream-405e21d16731b2764ab82aaaadcf36a813b105f7.tar.gz
upstream-405e21d16731b2764ab82aaaadcf36a813b105f7.tar.bz2
upstream-405e21d16731b2764ab82aaaadcf36a813b105f7.zip
packages: sort network related packages into package/network/
SVN-Revision: 33688
Diffstat (limited to 'package/network/services/ppp/patches')
-rw-r--r--package/network/services/ppp/patches/010-use_target_for_configure.patch24
-rw-r--r--package/network/services/ppp/patches/100-debian_ip-ip_option.patch96
-rw-r--r--package/network/services/ppp/patches/101-debian_close_dev_ppp.patch28
-rw-r--r--package/network/services/ppp/patches/103-debian_fix_link_pidfile.patch23
-rw-r--r--package/network/services/ppp/patches/105-debian_demand.patch172
-rw-r--r--package/network/services/ppp/patches/106-debian_stripMSdomain.patch47
-rw-r--r--package/network/services/ppp/patches/107-debian_pppoatm_wildcard.patch25
-rw-r--r--package/network/services/ppp/patches/110-debian_defaultroute.patch313
-rw-r--r--package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch95
-rw-r--r--package/network/services/ppp/patches/200-makefile.patch55
-rw-r--r--package/network/services/ppp/patches/201-mppe_mppc_1.1.patch1495
-rw-r--r--package/network/services/ppp/patches/202-no_strip.patch88
-rw-r--r--package/network/services/ppp/patches/203-opt_flags.patch32
-rw-r--r--package/network/services/ppp/patches/204-radius_config.patch72
-rw-r--r--package/network/services/ppp/patches/205-no_exponential_timeout.patch29
-rw-r--r--package/network/services/ppp/patches/206-compensate_time_change.patch94
-rw-r--r--package/network/services/ppp/patches/207-lcp_mtu_max.patch25
-rw-r--r--package/network/services/ppp/patches/208-fix_status_code.patch24
-rw-r--r--package/network/services/ppp/patches/300-filter-pcap-includes-lib.patch20
-rw-r--r--package/network/services/ppp/patches/310-precompile_filter.patch196
-rw-r--r--package/network/services/ppp/patches/320-custom_iface_names.patch135
-rw-r--r--package/network/services/ppp/patches/330-retain_foreign_default_routes.patch22
-rw-r--r--package/network/services/ppp/patches/340-populate_default_gateway.patch34
-rw-r--r--package/network/services/ppp/patches/400-simplify_kernel_checks.patch154
-rw-r--r--package/network/services/ppp/patches/401-no_record_file.patch39
-rw-r--r--package/network/services/ppp/patches/403-no_wtmp.patch25
-rw-r--r--package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch151
-rw-r--r--package/network/services/ppp/patches/405-no_multilink_option.patch28
-rw-r--r--package/network/services/ppp/patches/430-pppol2tpv3-struct.patch30
-rw-r--r--package/network/services/ppp/patches/500-add-pptp-plugin.patch3065
30 files changed, 6636 insertions, 0 deletions
diff --git a/package/network/services/ppp/patches/010-use_target_for_configure.patch b/package/network/services/ppp/patches/010-use_target_for_configure.patch
new file mode 100644
index 0000000000..aff0df67f8
--- /dev/null
+++ b/package/network/services/ppp/patches/010-use_target_for_configure.patch
@@ -0,0 +1,24 @@
+configure: Allow overriding uname results
+
+In a cross compile setting it makes no sense to rely on the "uname" values
+reported by the build host system. This patch allows overriding the
+"uname -r", "uname -s" and "uname -m" results with the "UNAME_R", "UNAME_S"
+and "UNAME_M" environment variables.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/configure
++++ b/configure
+@@ -8,9 +8,9 @@ SYSCONF=/etc
+ # if [ -d /NextApps ]; then
+ # system="NeXTStep"
+ # else
+- system=`uname -s`
+- release=`uname -r`
+- arch=`uname -m`
++ system=${UNAME_S:-`uname -s`}
++ release=${UNAME_R:-`uname -r`}
++ arch=${UNAME_M:-`uname -m`}
+ # fi
+ state="unknown"
+
diff --git a/package/network/services/ppp/patches/100-debian_ip-ip_option.patch b/package/network/services/ppp/patches/100-debian_ip-ip_option.patch
new file mode 100644
index 0000000000..ca43cb2780
--- /dev/null
+++ b/package/network/services/ppp/patches/100-debian_ip-ip_option.patch
@@ -0,0 +1,96 @@
+pppd: Allow specifying ip-up and ip-down scripts
+
+This patch implements the "ip-up-script" and "ip-down-script" options which
+allow to specify the path of the ip-up and ip-down scripts to call.
+
+These options default to _PATH_IPUP and _PATH_IPDOWN to retain the
+existing behaviour.
+
+The patch originated from the Debian project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/ipcp.c
++++ b/pppd/ipcp.c
+@@ -1939,7 +1939,7 @@ ipcp_up(f)
+ */
+ if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
+ ipcp_script_state = s_up;
+- ipcp_script(_PATH_IPUP, 0);
++ ipcp_script(path_ipup, 0);
+ }
+ }
+
+@@ -1989,7 +1989,7 @@ ipcp_down(f)
+ /* 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(path_ipdown, 0);
+ }
+ }
+
+@@ -2043,13 +2043,13 @@ ipcp_script_done(arg)
+ case s_up:
+ if (ipcp_fsm[0].state != OPENED) {
+ ipcp_script_state = s_down;
+- ipcp_script(_PATH_IPDOWN, 0);
++ ipcp_script(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(path_ipup, 0);
+ }
+ break;
+ }
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -316,6 +316,9 @@ main(argc, argv)
+ struct protent *protp;
+ char numbuf[16];
+
++ strlcpy(path_ipup, _PATH_IPUP, sizeof(path_ipup));
++ strlcpy(path_ipdown, _PATH_IPDOWN, sizeof(path_ipdown));
++
+ link_stats_valid = 0;
+ new_phase(PHASE_INITIALIZE);
+
+--- a/pppd/options.c
++++ b/pppd/options.c
+@@ -113,6 +113,8 @@ char linkname[MAXPATHLEN]; /* logical na
+ 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 */
++char path_ipup[MAXPATHLEN]; /* pathname of ip-up script */
++char path_ipdown[MAXPATHLEN];/* pathname of ip-down script */
+ bool multilink = 0; /* Enable multilink operation */
+ char *bundle_name = NULL; /* bundle name for multilink */
+ bool dump_options; /* print out option values */
+@@ -281,6 +283,13 @@ option_t general_options[] = {
+ "Number of seconds to wait for child processes at exit",
+ OPT_PRIO },
+
++ { "ip-up-script", o_string, path_ipup,
++ "Set pathname of ip-up script",
++ OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
++ { "ip-down-script", o_string, path_ipdown,
++ "Set pathname of ip-down script",
++ OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
++
+ #ifdef HAVE_MULTILINK
+ { "multilink", o_bool, &multilink,
+ "Enable multilink operation", OPT_PRIO | 1 },
+--- a/pppd/pppd.h
++++ b/pppd/pppd.h
+@@ -313,6 +313,8 @@ extern bool tune_kernel; /* May alter ke
+ extern int connect_delay; /* Time to delay after connect script */
+ extern int max_data_rate; /* max bytes/sec through charshunt */
+ extern int req_unit; /* interface unit number to use */
++extern char path_ipup[MAXPATHLEN]; /* pathname of ip-up script */
++extern char path_ipdown[MAXPATHLEN]; /* pathname of ip-down script */
+ extern bool multilink; /* enable multilink operation */
+ extern bool noendpoint; /* don't send or accept endpt. discrim. */
+ extern char *bundle_name; /* bundle name for multilink */
diff --git a/package/network/services/ppp/patches/101-debian_close_dev_ppp.patch b/package/network/services/ppp/patches/101-debian_close_dev_ppp.patch
new file mode 100644
index 0000000000..232b10b521
--- /dev/null
+++ b/package/network/services/ppp/patches/101-debian_close_dev_ppp.patch
@@ -0,0 +1,28 @@
+pppd: Close already open ppp descriptors
+
+When using the kernel PPPoE driver in conjunction with the "persist" option,
+the already open descriptor to /dev/ppp is not closed when the link is
+reestablished. This eventually leads to high CPU load because the stray
+descriptors are always reported as ready by select().
+
+This patch closes the descriptor if it is already open when establishing a
+new connection. It originated from the Debian project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -453,6 +453,13 @@ int generic_establish_ppp (int fd)
+ if (new_style_driver) {
+ int flags;
+
++ /* if a ppp_fd is already open, close it first */
++ if(ppp_fd > 0) {
++ close(ppp_fd);
++ remove_fd(ppp_fd);
++ ppp_fd = -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");
diff --git a/package/network/services/ppp/patches/103-debian_fix_link_pidfile.patch b/package/network/services/ppp/patches/103-debian_fix_link_pidfile.patch
new file mode 100644
index 0000000000..c8a23edd6a
--- /dev/null
+++ b/package/network/services/ppp/patches/103-debian_fix_link_pidfile.patch
@@ -0,0 +1,23 @@
+pppd: Fix creation of linkpidfile
+
+When pppd is run without "nodetach" or with "updetach", the linkpidfile is
+never created. The call to create_linkpidfile() is protected by a check for
+linkpidfile[0] but this is only filled in when create_linkpidfile() is called.
+
+This patch changes to code to allways uncondiationally call
+create_linkpidfile(), it originated from the Debian project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -773,8 +773,7 @@ detach()
+ /* update pid files if they have been written already */
+ if (pidfilename[0])
+ create_pidfile(pid);
+- if (linkpidfile[0])
+- create_linkpidfile(pid);
++ create_linkpidfile(pid);
+ exit(0); /* parent dies */
+ }
+ setsid();
diff --git a/package/network/services/ppp/patches/105-debian_demand.patch b/package/network/services/ppp/patches/105-debian_demand.patch
new file mode 100644
index 0000000000..c278656987
--- /dev/null
+++ b/package/network/services/ppp/patches/105-debian_demand.patch
@@ -0,0 +1,172 @@
+--- a/pppd/demand.c
++++ b/pppd/demand.c
+@@ -36,6 +36,8 @@
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <netdb.h>
++#include <unistd.h>
++#include <syslog.h>
+ #include <sys/param.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+@@ -43,6 +45,8 @@
+ #include <sys/resource.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
+ #ifdef PPP_FILTER
+ #include <pcap-bpf.h>
+ #endif
+@@ -221,6 +225,14 @@ loop_chars(p, n)
+ int c, rv;
+
+ rv = 0;
++
++/* check for synchronous connection... */
++
++ if ( (p[0] == 0xFF) && (p[1] == 0x03) ) {
++ rv = loop_frame(p,n);
++ return rv;
++ }
++
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+@@ -299,17 +311,102 @@ loop_frame(frame, len)
+ * loopback, now that the real serial link is up.
+ */
+ void
+-demand_rexmit(proto)
++demand_rexmit(proto, newip)
+ int proto;
++ u_int32_t newip;
+ {
+ struct packet *pkt, *prev, *nextpkt;
++ unsigned short checksum;
++ unsigned short pkt_checksum = 0;
++ unsigned iphdr;
++ struct timeval tv;
++ char cv = 0;
++ char ipstr[16];
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
++ tv.tv_sec = 1;
++ tv.tv_usec = 0;
++ select(0,NULL,NULL,NULL,&tv); /* Sleep for 1 Seconds */
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
++ if ( (proto == PPP_IP) && newip ) {
++ /* Get old checksum */
++
++ iphdr = (pkt->data[4] & 15) << 2;
++ checksum = *((unsigned short *) (pkt->data+14));
++ if (checksum == 0xFFFF) {
++ checksum = 0;
++ }
++
++
++ if (pkt->data[13] == 17) {
++ pkt_checksum = *((unsigned short *) (pkt->data+10+iphdr));
++ if (pkt_checksum) {
++ cv = 1;
++ if (pkt_checksum == 0xFFFF) {
++ pkt_checksum = 0;
++ }
++ }
++ else {
++ cv = 0;
++ }
++ }
++
++ if (pkt->data[13] == 6) {
++ pkt_checksum = *((unsigned short *) (pkt->data+20+iphdr));
++ cv = 1;
++ if (pkt_checksum == 0xFFFF) {
++ pkt_checksum = 0;
++ }
++ }
++
++ /* Delete old Source-IP-Address */
++ checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
++ checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
++
++ pkt_checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
++ pkt_checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
++
++ /* Change Source-IP-Address */
++ * ((u_int32_t *) (pkt->data + 16)) = newip;
++
++ /* Add new Source-IP-Address */
++ checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
++ checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
++
++ pkt_checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
++ pkt_checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
++
++ /* Write new checksum */
++ if (!checksum) {
++ checksum = 0xFFFF;
++ }
++ *((unsigned short *) (pkt->data+14)) = checksum;
++ if (pkt->data[13] == 6) {
++ *((unsigned short *) (pkt->data+20+iphdr)) = pkt_checksum;
++ }
++ if (cv && (pkt->data[13] == 17) ) {
++ *((unsigned short *) (pkt->data+10+iphdr)) = pkt_checksum;
++ }
++
++ /* Log Packet */
++ strcpy(ipstr,inet_ntoa(*( (struct in_addr *) (pkt->data+16))));
++ if (pkt->data[13] == 1) {
++ syslog(LOG_INFO,"Open ICMP %s -> %s\n",
++ ipstr,
++ inet_ntoa(*( (struct in_addr *) (pkt->data+20))));
++ } else {
++ syslog(LOG_INFO,"Open %s %s:%d -> %s:%d\n",
++ pkt->data[13] == 6 ? "TCP" : "UDP",
++ ipstr,
++ ntohs(*( (short *) (pkt->data+iphdr+4))),
++ inet_ntoa(*( (struct in_addr *) (pkt->data+20))),
++ ntohs(*( (short *) (pkt->data+iphdr+6))));
++ }
++ }
+ output(0, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+--- a/pppd/ipcp.c
++++ b/pppd/ipcp.c
+@@ -1864,7 +1864,7 @@ ipcp_up(f)
+ proxy_arp_set[f->unit] = 1;
+
+ }
+- demand_rexmit(PPP_IP);
++ demand_rexmit(PPP_IP,go->ouraddr);
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ } else {
+--- a/pppd/ipv6cp.c
++++ b/pppd/ipv6cp.c
+@@ -1232,7 +1232,7 @@ ipv6cp_up(f)
+ }
+
+ }
+- demand_rexmit(PPP_IPV6);
++ demand_rexmit(PPP_IPV6,0);
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ } else {
+--- a/pppd/pppd.h
++++ b/pppd/pppd.h
+@@ -566,7 +566,7 @@ void demand_conf __P((void)); /* config
+ void demand_block __P((void)); /* set all NPs to queue up packets */
+ void demand_unblock __P((void)); /* set all NPs to pass packets */
+ void demand_discard __P((void)); /* set all NPs to discard packets */
+-void demand_rexmit __P((int)); /* retransmit saved frames for an NP */
++void demand_rexmit __P((int, u_int32_t)); /* retransmit saved frames for an NP*/
+ int loop_chars __P((unsigned char *, int)); /* process chars from loopback */
+ int loop_frame __P((unsigned char *, int)); /* should we bring link up? */
+
diff --git a/package/network/services/ppp/patches/106-debian_stripMSdomain.patch b/package/network/services/ppp/patches/106-debian_stripMSdomain.patch
new file mode 100644
index 0000000000..86af1ef412
--- /dev/null
+++ b/package/network/services/ppp/patches/106-debian_stripMSdomain.patch
@@ -0,0 +1,47 @@
+pppd: Implement option to strip domain part from MS CHAP response
+
+This patch implements a new boolean option "chapms-strip-domain" which
+strips the leading domain part of the username in a received MS Chap
+response.
+
+When the option is set, all leading chars up to and including the last
+backslash in the username are stripped. The option defaults to false.
+
+The patch originated from the Debian project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/chap-new.c
++++ b/pppd/chap-new.c
+@@ -58,6 +58,7 @@ int (*chap_verify_hook)(char *name, char
+ int chap_timeout_time = 3;
+ int chap_max_transmits = 10;
+ int chap_rechallenge_time = 0;
++int chapms_strip_domain = 0;
+
+ /*
+ * Command-line options.
+@@ -69,6 +70,8 @@ static option_t chap_option_list[] = {
+ "Set max #xmits for challenge", OPT_PRIO },
+ { "chap-interval", o_int, &chap_rechallenge_time,
+ "Set interval for rechallenge", OPT_PRIO },
++ { "chapms-strip-domain", o_bool, &chapms_strip_domain,
++ "Strip the domain prefix before the Username", 1 },
+ { NULL }
+ };
+
+@@ -336,6 +339,14 @@ chap_handle_response(struct chap_server_
+ /* Null terminate and clean remote name. */
+ slprintf(rname, sizeof(rname), "%.*v", len, name);
+ name = rname;
++
++ /* strip the MS domain name */
++ if (chapms_strip_domain && strrchr(rname, '\\')) {
++ char tmp[MAXNAMELEN+1];
++
++ strcpy(tmp, strrchr(rname, '\\') + 1);
++ strcpy(rname, tmp);
++ }
+ }
+
+ if (chap_verify_hook)
diff --git a/package/network/services/ppp/patches/107-debian_pppoatm_wildcard.patch b/package/network/services/ppp/patches/107-debian_pppoatm_wildcard.patch
new file mode 100644
index 0000000000..eaad2cdaae
--- /dev/null
+++ b/package/network/services/ppp/patches/107-debian_pppoatm_wildcard.patch
@@ -0,0 +1,25 @@
+pppoatm: Allow wildcard ATM devices
+
+When operating pppd's pppoatm plugin with an USB ADSL modem, e.g. an
+Alcatel Speedtouch, the ATM device number might change when the modem is
+reconnected to the USB port or when the host controller resets the USB
+device.
+
+This patch allows to specify the ATM device as wildcard which gives
+enough flexibility to cope with changing device names.
+
+The patch originated from the Debain project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/plugins/pppoatm/pppoatm.c
++++ b/pppd/plugins/pppoatm/pppoatm.c
+@@ -75,7 +75,7 @@ static int setdevname_pppoatm(const char
+ //info("PPPoATM setdevname_pppoatm: '%s'", cp);
+ memset(&addr, 0, sizeof addr);
+ if (text2atm(cp, (struct sockaddr *) &addr, sizeof(addr),
+- T2A_PVC | T2A_NAME) < 0) {
++ T2A_PVC | T2A_NAME | T2A_WILDCARD) < 0) {
+ if(doit)
+ info("atm does not recognize: %s", cp);
+ return 0;
diff --git a/package/network/services/ppp/patches/110-debian_defaultroute.patch b/package/network/services/ppp/patches/110-debian_defaultroute.patch
new file mode 100644
index 0000000000..41d28909be
--- /dev/null
+++ b/package/network/services/ppp/patches/110-debian_defaultroute.patch
@@ -0,0 +1,313 @@
+pppd: Add "replacedefaultroute" and "noreplacedefaultroute" options
+
+This patch implements two new options, "replacedefaultroute" to replace any
+existing system default route when specified and "noreplacedefaultroute" to
+disable the "replacedefaultroute" option, which is useful in multi user
+environments where the administrator wants to allow users to dial pppd
+connections but not allow them to change the system default route.
+
+The patch originated from the Debian project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/ipcp.c
++++ b/pppd/ipcp.c
+@@ -198,6 +198,14 @@ static option_t ipcp_option_list[] = {
+ "disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_wantoptions[0].default_route },
+
++ { "replacedefaultroute", o_bool,
++ &ipcp_wantoptions[0].replace_default_route,
++ "Replace default route", 1
++ },
++ { "noreplacedefaultroute", o_bool,
++ &ipcp_allowoptions[0].replace_default_route,
++ "Never replace default route", OPT_A2COPY,
++ &ipcp_wantoptions[0].replace_default_route },
+ { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
+ "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
+ { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+@@ -271,7 +279,7 @@ struct protent ipcp_protent = {
+ ip_active_pkt
+ };
+
+-static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t));
++static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t, bool));
+ static void ipcp_script __P((char *, int)); /* Run an up/down script */
+ static void ipcp_script_done __P((void *));
+
+@@ -1742,7 +1750,8 @@ ip_demand_conf(u)
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
+ if (wo->default_route)
+- if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
++ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr,
++ wo->replace_default_route))
+ default_route_set[u] = 1;
+ if (wo->proxy_arp)
+ if (sifproxyarp(u, wo->hisaddr))
+@@ -1830,7 +1839,8 @@ ipcp_up(f)
+ */
+ if (demand) {
+ if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+- ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
++ ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr,
++ wo->replace_default_route);
+ if (go->ouraddr != wo->ouraddr) {
+ warn("Local IP address changed to %I", go->ouraddr);
+ script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
+@@ -1855,7 +1865,8 @@ ipcp_up(f)
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+- if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
++ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr,
++ wo->replace_default_route))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+@@ -1905,7 +1916,8 @@ ipcp_up(f)
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+- if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
++ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr,
++ wo->replace_default_route))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+@@ -1983,7 +1995,7 @@ ipcp_down(f)
+ sifnpmode(f->unit, PPP_IP, NPMODE_DROP);
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
+- ipcp_hisoptions[f->unit].hisaddr);
++ ipcp_hisoptions[f->unit].hisaddr, 0);
+ }
+
+ /* Execute the ip-down script */
+@@ -1999,16 +2011,25 @@ ipcp_down(f)
+ * proxy arp entries, etc.
+ */
+ static void
+-ipcp_clear_addrs(unit, ouraddr, hisaddr)
++ipcp_clear_addrs(unit, ouraddr, hisaddr, replacedefaultroute)
+ int unit;
+ u_int32_t ouraddr; /* local address */
+ u_int32_t hisaddr; /* remote address */
++ bool replacedefaultroute;
+ {
+ if (proxy_arp_set[unit]) {
+ cifproxyarp(unit, hisaddr);
+ proxy_arp_set[unit] = 0;
+ }
+- if (default_route_set[unit]) {
++ /* If replacedefaultroute, sifdefaultroute will be called soon
++ * with replacedefaultroute set and that will overwrite the current
++ * default route. This is the case only when doing demand, otherwise
++ * during demand, this cifdefaultroute would restore the old default
++ * route which is not what we want in this case. In the non-demand
++ * case, we'll delete the default route and restore the old if there
++ * is one saved by an sifdefaultroute with replacedefaultroute.
++ */
++ if (!replacedefaultroute && default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+--- a/pppd/ipcp.h
++++ b/pppd/ipcp.h
+@@ -70,6 +70,7 @@ typedef struct ipcp_options {
+ bool old_addrs; /* Use old (IP-Addresses) option? */
+ bool req_addr; /* Ask peer to send IP address? */
+ bool default_route; /* Assign default route through interface? */
++ bool replace_default_route; /* Replace default route through interface? */
+ bool proxy_arp; /* Make proxy ARP entry for peer? */
+ bool neg_vj; /* Van Jacobson Compression? */
+ bool old_vj; /* use old (short) form of VJ option? */
+--- a/pppd/pppd.8
++++ b/pppd/pppd.8
+@@ -121,6 +121,11 @@ the gateway, when IPCP negotiation is su
+ This entry is removed when the PPP connection is broken. This option
+ is privileged if the \fInodefaultroute\fR option has been specified.
+ .TP
++.B replacedefaultroute
++This option is a flag to the defaultroute option. If defaultroute is
++set and this flag is also set, pppd replaces an existing default route
++with the new default route.
++.TP
+ .B disconnect \fIscript
+ Execute the command specified by \fIscript\fR, by passing it to a
+ shell, after
+@@ -717,7 +722,12 @@ disable both forms of hardware flow cont
+ .TP
+ .B nodefaultroute
+ Disable the \fIdefaultroute\fR option. The system administrator who
+-wishes to prevent users from creating default routes with pppd
++wishes to prevent users from adding a default route with pppd
++can do so by placing this option in the /etc/ppp/options file.
++.TP
++.B noreplacedefaultroute
++Disable the \fIreplacedefaultroute\fR option. The system administrator who
++wishes to prevent users from replacing a default route with pppd
+ can do so by placing this option in the /etc/ppp/options file.
+ .TP
+ .B nodeflate
+--- a/pppd/pppd.h
++++ b/pppd/pppd.h
+@@ -645,7 +645,7 @@ int sif6addr __P((int, eui64_t, eui64_t
+ int cif6addr __P((int, eui64_t, eui64_t));
+ /* Remove an IPv6 address from i/f */
+ #endif
+-int sifdefaultroute __P((int, u_int32_t, u_int32_t));
++int sifdefaultroute __P((int, u_int32_t, u_int32_t, bool replace_default_rt));
+ /* Create default route through i/f */
+ int cifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Delete default route through i/f */
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -206,6 +206,8 @@ static unsigned char inbuf[512]; /* buff
+
+ static int if_is_up; /* Interface has been marked up */
+ static int have_default_route; /* Gateway for default route added */
++static struct rtentry old_def_rt; /* Old default route */
++static int default_rt_repl_rest; /* replace and restore old default rt */
+ 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 */
+@@ -1544,6 +1546,9 @@ static int read_route_table(struct rtent
+ p = NULL;
+ }
+
++ SET_SA_FAMILY (rt->rt_dst, AF_INET);
++ SET_SA_FAMILY (rt->rt_gateway, AF_INET);
++
+ SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16);
+ SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16);
+ SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16);
+@@ -1613,20 +1618,51 @@ int have_route_to(u_int32_t addr)
+ /********************************************************************
+ *
+ * sifdefaultroute - assign a default route through the address given.
+- */
+-
+-int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
+-{
+- struct rtentry rt;
+-
+- if (defaultroute_exists(&rt) && strcmp(rt.rt_dev, ifname) != 0) {
+- if (rt.rt_flags & RTF_GATEWAY)
+- error("not replacing existing default route via %I",
+- SIN_ADDR(rt.rt_gateway));
+- else
++ *
++ * If the global default_rt_repl_rest flag is set, then this function
++ * already replaced the original system defaultroute with some other
++ * route and it should just replace the current defaultroute with
++ * another one, without saving the current route. Use: demand mode,
++ * when pppd sets first a defaultroute it it's temporary ppp0 addresses
++ * and then changes the temporary addresses to the addresses for the real
++ * ppp connection when it has come up.
++ */
++
++int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway, bool replace)
++{
++ struct rtentry rt, tmp_rt;
++ struct rtentry *del_rt = NULL;
++
++ if (default_rt_repl_rest) {
++ /* We have already reclaced the original defaultroute, if we
++ are called again, we will delete the current default route
++ and set the new default route in this function.
++ - this is normally only the case the doing demand: */
++ if (defaultroute_exists(&tmp_rt))
++ del_rt = &tmp_rt;
++ } else if (defaultroute_exists(&old_def_rt) &&
++ strcmp(old_def_rt.rt_dev, ifname) != 0) {
++ /* We did not yet replace an existing default route, let's
++ check if we should save and replace a default route: */
++ if (old_def_rt.rt_flags & RTF_GATEWAY) {
++ if (!replace) {
++ error("not replacing existing default route via %I",
++ SIN_ADDR(old_def_rt.rt_gateway));
++ return 0;
++ } else {
++ /* we need to copy rt_dev because we need it permanent too: */
++ char *tmp_dev = malloc(strlen(old_def_rt.rt_dev) + 1);
++ strcpy(tmp_dev, old_def_rt.rt_dev);
++ old_def_rt.rt_dev = tmp_dev;
++
++ notice("replacing old default route to %s [%I]",
++ old_def_rt.rt_dev, SIN_ADDR(old_def_rt.rt_gateway));
++ default_rt_repl_rest = 1;
++ del_rt = &old_def_rt;
++ }
++ } else
+ error("not replacing existing default route through %s",
+- rt.rt_dev);
+- return 0;
++ old_def_rt.rt_dev);
+ }
+
+ memset (&rt, 0, sizeof (rt));
+@@ -1641,10 +1677,16 @@ int sifdefaultroute (int unit, u_int32_t
+
+ rt.rt_flags = RTF_UP;
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+- if ( ! ok_error ( errno ))
++ if (!ok_error(errno))
+ error("default route ioctl(SIOCADDRT): %m");
+ return 0;
+ }
++ if (default_rt_repl_rest && del_rt)
++ if (ioctl(sock_fd, SIOCDELRT, del_rt) < 0) {
++ if (!ok_error(errno))
++ error("del old default route ioctl(SIOCDELRT): %m");
++ return 0;
++ }
+
+ have_default_route = 1;
+ return 1;
+@@ -1675,11 +1717,21 @@ int cifdefaultroute (int unit, u_int32_t
+ rt.rt_flags = RTF_UP;
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp()) {
+- if ( ! ok_error ( errno ))
++ if (!ok_error(errno))
+ error("default route ioctl(SIOCDELRT): %m");
+ return 0;
+ }
+ }
++ if (default_rt_repl_rest) {
++ notice("restoring old default route to %s [%I]",
++ old_def_rt.rt_dev, SIN_ADDR(old_def_rt.rt_gateway));
++ if (ioctl(sock_fd, SIOCADDRT, &old_def_rt) < 0) {
++ if (!ok_error(errno))
++ error("restore default route ioctl(SIOCADDRT): %m");
++ return 0;
++ }
++ default_rt_repl_rest = 0;
++ }
+
+ return 1;
+ }
+--- a/pppd/sys-solaris.c
++++ b/pppd/sys-solaris.c
+@@ -2036,12 +2036,18 @@ cifaddr(u, o, h)
+ * sifdefaultroute - assign a default route through the address given.
+ */
+ int
+-sifdefaultroute(u, l, g)
++sifdefaultroute(u, l, g, replace)
+ int u;
+ u_int32_t l, g;
++ bool replace;
+ {
+ struct rtentry rt;
+
++ if (replace) {
++ error("replacedefaultroute not supported on this platform");
++ return 0;
++ }
++
+ #if defined(__USLC__)
+ g = l; /* use the local address as gateway */
+ #endif
diff --git a/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch b/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch
new file mode 100644
index 0000000000..c5457fa515
--- /dev/null
+++ b/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch
@@ -0,0 +1,95 @@
+pppd: Allow specifying ipv6-up and ipv6-down scripts
+
+This patch implements the "ipv6-up-script" and "ipv6-down-script" options
+which allow to specify the path of the ipv6-up and ipv6-down scripts to call.
+
+These options default to _PATH_IPV6UP and _PATH_IPV6DOWN to retain the
+existing behaviour.
+
+The patch originated from the Debian project.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -318,6 +318,8 @@ main(argc, argv)
+
+ strlcpy(path_ipup, _PATH_IPUP, sizeof(path_ipup));
+ strlcpy(path_ipdown, _PATH_IPDOWN, sizeof(path_ipdown));
++ strlcpy(path_ipv6up, _PATH_IPV6UP, sizeof(path_ipv6up));
++ strlcpy(path_ipv6down, _PATH_IPV6DOWN, sizeof(path_ipv6down));
+
+ link_stats_valid = 0;
+ new_phase(PHASE_INITIALIZE);
+--- a/pppd/options.c
++++ b/pppd/options.c
+@@ -115,6 +115,8 @@ int connect_delay = 1000; /* wait this m
+ int req_unit = -1; /* requested interface unit */
+ char path_ipup[MAXPATHLEN]; /* pathname of ip-up script */
+ char path_ipdown[MAXPATHLEN];/* pathname of ip-down script */
++char path_ipv6up[MAXPATHLEN]; /* pathname of ipv6-up script */
++char path_ipv6down[MAXPATHLEN];/* pathname of ipv6-down script */
+ bool multilink = 0; /* Enable multilink operation */
+ char *bundle_name = NULL; /* bundle name for multilink */
+ bool dump_options; /* print out option values */
+@@ -290,6 +292,13 @@ option_t general_options[] = {
+ "Set pathname of ip-down script",
+ OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
+
++ { "ipv6-up-script", o_string, path_ipv6up,
++ "Set pathname of ipv6-up script",
++ OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
++ { "ipv6-down-script", o_string, path_ipv6down,
++ "Set pathname of ipv6-down script",
++ OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
++
+ #ifdef HAVE_MULTILINK
+ { "multilink", o_bool, &multilink,
+ "Enable multilink operation", OPT_PRIO | 1 },
+--- a/pppd/ipv6cp.c
++++ b/pppd/ipv6cp.c
+@@ -1288,7 +1288,7 @@ ipv6cp_up(f)
+ */
+ if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_up;
+- ipv6cp_script(_PATH_IPV6UP);
++ ipv6cp_script(path_ipv6up);
+ }
+ }
+
+@@ -1339,7 +1339,7 @@ ipv6cp_down(f)
+ /* Execute the ipv6-down script */
+ if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_down;
+- ipv6cp_script(_PATH_IPV6DOWN);
++ ipv6cp_script(path_ipv6down);
+ }
+ }
+
+@@ -1382,13 +1382,13 @@ ipv6cp_script_done(arg)
+ case s_up:
+ if (ipv6cp_fsm[0].state != OPENED) {
+ ipv6cp_script_state = s_down;
+- ipv6cp_script(_PATH_IPV6DOWN);
++ ipv6cp_script(path_ipv6down);
+ }
+ break;
+ case s_down:
+ if (ipv6cp_fsm[0].state == OPENED) {
+ ipv6cp_script_state = s_up;
+- ipv6cp_script(_PATH_IPV6UP);
++ ipv6cp_script(path_ipv6up);
+ }
+ break;
+ }
+--- a/pppd/pppd.h
++++ b/pppd/pppd.h
+@@ -315,6 +315,8 @@ extern int max_data_rate; /* max bytes/s
+ extern int req_unit; /* interface unit number to use */
+ extern char path_ipup[MAXPATHLEN]; /* pathname of ip-up script */
+ extern char path_ipdown[MAXPATHLEN]; /* pathname of ip-down script */
++extern char path_ipv6up[MAXPATHLEN]; /* pathname of ipv6-up script */
++extern char path_ipv6down[MAXPATHLEN]; /* pathname of ipv6-down script */
+ extern bool multilink; /* enable multilink operation */
+ extern bool noendpoint; /* don't send or accept endpt. discrim. */
+ extern char *bundle_name; /* bundle name for multilink */
diff --git a/package/network/services/ppp/patches/200-makefile.patch b/package/network/services/ppp/patches/200-makefile.patch
new file mode 100644
index 0000000000..9db908de8d
--- /dev/null
+++ b/package/network/services/ppp/patches/200-makefile.patch
@@ -0,0 +1,55 @@
+pppd: tune Linux config defaults for OpenWrt
+
+This patch adjusts a number defaults to properly match the OpenWrt environment.
+It is not intended for upstream.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/Makefile.linux
++++ b/pppd/Makefile.linux
+@@ -48,7 +48,7 @@ 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.
+-FILTER=y
++#FILTER=y
+
+ # Uncomment the next line to enable multilink PPP (enabled by default)
+ # Linux distributions: Please leave multilink ENABLED in your builds
+@@ -58,11 +58,11 @@ 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
++HAVE_INET6=y
+
+ # Enable plugins
+ PLUGIN=y
+@@ -77,7 +77,7 @@ MAXOCTETS=y
+
+ 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@"'
+
+@@ -117,10 +117,10 @@ CFLAGS += -DHAS_SHADOW
+ #LIBS += -lshadow $(LIBS)
+ endif
+
+-ifneq ($(wildcard /usr/include/crypt.h),)
++#ifneq ($(wildcard /usr/include/crypt.h),)
+ CFLAGS += -DHAVE_CRYPT_H=1
+ LIBS += -lcrypt
+-endif
++#endif
+
+ ifdef NEEDDES
+ ifndef USE_CRYPT
diff --git a/package/network/services/ppp/patches/201-mppe_mppc_1.1.patch b/package/network/services/ppp/patches/201-mppe_mppc_1.1.patch
new file mode 100644
index 0000000000..3edd11e7f6
--- /dev/null
+++ b/package/network/services/ppp/patches/201-mppe_mppc_1.1.patch
@@ -0,0 +1,1495 @@
+pppd: add support for MPPE and MPPC encryption and compression protocols
+
+This is a forward ported version of ppp-2.4.3-mppe-mppc-1.1.patch.gz found on
+http://mppe-mppc.alphacron.de/ .
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/include/linux/ppp-comp.h
++++ b/include/linux/ppp-comp.h
+@@ -36,7 +36,7 @@
+ */
+
+ /*
+- * ==FILEVERSION 20020319==
++ * ==FILEVERSION 20020715==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+@@ -201,6 +201,33 @@ struct compressor {
+ #define CI_MPPE 18 /* config option for MPPE */
+ #define CILEN_MPPE 6 /* length of config option */
+
++/* 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.
+ */
+--- a/include/net/ppp-comp.h
++++ b/include/net/ppp-comp.h
+@@ -168,6 +168,33 @@ struct compressor {
+ #define CI_MPPE 18 /* config option for MPPE */
+ #define CILEN_MPPE 6 /* length of config option */
+
++/* 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.
+ */
+--- a/pppd/ccp.c
++++ b/pppd/ccp.c
+@@ -62,12 +62,10 @@ static int setdeflate __P((char **));
+ static char bsd_value[8];
+ static char deflate_value[8];
+
+-/*
+- * 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 +106,36 @@ static option_t ccp_option_list[] = {
+ "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 +221,7 @@ static fsm_callbacks ccp_callbacks = {
+ */
+ #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 +324,100 @@ setdeflate(argv)
+ 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 +452,30 @@ ccp_init(unit)
+ 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 +553,11 @@ ccp_input(unit, p, len)
+ 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 +585,15 @@ ccp_extcode(f, code, id, p, len)
+ 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 +622,11 @@ ccp_protrej(unit)
+ 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 +643,7 @@ ccp_resetci(f)
+ 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 +657,109 @@ ccp_resetci(f)
+ * 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 +814,8 @@ ccp_cilen(f)
+ + (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 +829,8 @@ ccp_addci(f, p, lenp)
+ {
+ 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 +839,43 @@ ccp_addci(f, p, lenp)
+ * 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 +961,7 @@ ccp_addci(f, p, lenp)
+
+ /*
+ * 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 +970,44 @@ ccp_ackci(f, p, len)
+ 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)
+@@ -901,6 +1080,8 @@ ccp_nakci(f, p, len, treat_as_reject)
+ 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 +1089,100 @@ ccp_nakci(f, p, len, treat_as_reject)
+ 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 +1255,50 @@ ccp_rejci(f, p, len)
+ 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 +1362,15 @@ ccp_reqci(f, p, lenp, dont_nak)
+ 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 +1393,302 @@ ccp_reqci(f, p, lenp, dont_nak)
+ 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) {
+- newret = CONFNAK;
+- ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
+- }
+- if (ho->mppe & MPPE_OPT_UNKNOWN) {
++ 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_UNKNOWN;
+- }
+-
+- /* 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");
+- newret = CONFREJ;
+- break;
++ 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;
++ }
+ }
+ }
+-
+- /* 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. */
+- 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;
+- }
+- } else if (ho->mppe & MPPE_OPT_128) {
+- if (!(ao->mppe & MPPE_OPT_128)) {
+- newret = CONFREJ;
+- break;
+- }
+- } else if (ho->mppe & MPPE_OPT_40) {
+- if (!(ao->mppe & MPPE_OPT_40)) {
+- newret = CONFREJ;
+- break;
+- }
++ 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 {
+- /* Neither are set. */
+- /* We cannot accept this. */
+- newret = CONFNAK;
+- /* Give the peer our idea of what can be used,
+- so it can choose and confirm */
+- ho->mppe = ao->mppe;
+- }
+-
+- /* rebuild the opts */
+- 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;
+- }
+- /*
+- * 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;
+- }
+-
+- /*
+- * We have accepted MPPE or are willing to negotiate
+- * MPPE parameters. A CONFREJ is due to subsequent
+- * (non-MPPE) processing.
+- */
+- rej_for_ci_mppe = 0;
+- break;
+-#endif /* MPPE */
++ /* 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
+@@ -1344,12 +1830,6 @@ ccp_reqci(f, p, lenp, dont_nak)
+ 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 +1851,35 @@ method_name(opt, opt2)
+ 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 +1935,12 @@ ccp_up(f)
+ } 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 +1963,7 @@ ccp_down(f)
+ lcp_close(f->unit, "MPPE disabled");
+ }
+ }
+-#endif
++#endif /* MPPE */
+ }
+
+ /*
+@@ -1532,24 +2023,28 @@ ccp_printpkt(p, plen, printer, arg)
+ #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 +2130,7 @@ ccp_datainput(unit, pkt, len)
+ 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 +2138,18 @@ ccp_datainput(unit, pkt, len)
+ 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.
+--- a/pppd/ccp.h
++++ b/pppd/ccp.h
+@@ -37,9 +37,17 @@ typedef struct ccp_options {
+ 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;
+
+--- a/pppd/chap_ms.c
++++ b/pppd/chap_ms.c
+@@ -898,13 +898,17 @@ set_mppe_enc_types(int policy, int types
+ /*
+ * 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 --git a/package/network/services/ppp/patches/202-no_strip.patch b/package/network/services/ppp/patches/202-no_strip.patch
new file mode 100644
index 0000000000..87c76ad0e7
--- /dev/null
+++ b/package/network/services/ppp/patches/202-no_strip.patch
@@ -0,0 +1,88 @@
+build: Do not strip binaries on install
+
+Strippign executables should be handled by the distro packaging, not by ppp
+itself. This patch removes the "-s" (strip) switch from all "install" commands
+in order to install unstripped binaries into the destination prefix.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/chat/Makefile.linux
++++ b/chat/Makefile.linux
+@@ -25,7 +25,7 @@ chat.o: chat.c
+
+ install: chat
+ mkdir -p $(BINDIR) $(MANDIR)
+- $(INSTALL) -s -c chat $(BINDIR)
++ $(INSTALL) -c chat $(BINDIR)
+ $(INSTALL) -c -m 644 chat.8 $(MANDIR)
+
+ clean:
+--- a/pppd/Makefile.linux
++++ b/pppd/Makefile.linux
+@@ -99,7 +99,7 @@ ifdef USE_SRP
+ 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
+@@ -200,7 +200,7 @@ all: $(TARGETS)
+ 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)
+--- a/pppd/plugins/radius/Makefile.linux
++++ b/pppd/plugins/radius/Makefile.linux
+@@ -36,9 +36,9 @@ all: $(PLUGIN)
+
+ 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)
+
+--- a/pppd/plugins/rp-pppoe/Makefile.linux
++++ b/pppd/plugins/rp-pppoe/Makefile.linux
+@@ -43,9 +43,9 @@ rp-pppoe.so: plugin.o discovery.o if.o c
+
+ 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 pppoe-discovery
+--- a/pppdump/Makefile.linux
++++ b/pppdump/Makefile.linux
+@@ -17,5 +17,5 @@ clean:
+
+ install:
+ mkdir -p $(BINDIR) $(MANDIR)
+- $(INSTALL) -s -c pppdump $(BINDIR)
++ $(INSTALL) -c pppdump $(BINDIR)
+ $(INSTALL) -c -m 444 pppdump.8 $(MANDIR)
+--- a/pppstats/Makefile.linux
++++ b/pppstats/Makefile.linux
+@@ -22,7 +22,7 @@ all: pppstats
+
+ install: pppstats
+ -mkdir -p $(MANDIR)
+- $(INSTALL) -s -c pppstats $(BINDIR)
++ $(INSTALL) -c pppstats $(BINDIR)
+ $(INSTALL) -c -m 444 pppstats.8 $(MANDIR)
+
+ pppstats: $(PPPSTATSRCS)
diff --git a/package/network/services/ppp/patches/203-opt_flags.patch b/package/network/services/ppp/patches/203-opt_flags.patch
new file mode 100644
index 0000000000..a369163527
--- /dev/null
+++ b/package/network/services/ppp/patches/203-opt_flags.patch
@@ -0,0 +1,32 @@
+build: Move optimization flags into a separate variable
+
+Isolate optimization related compiler flags from CFLAGS and move them into a
+separate COPTS variable so that it is easier to override optimizations from
+the environment.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/plugins/radius/Makefile.linux
++++ b/pppd/plugins/radius/Makefile.linux
+@@ -12,7 +12,8 @@ VERSION = $(shell awk -F '"' '/VERSION/
+ 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.
+--- a/pppdump/Makefile.linux
++++ b/pppdump/Makefile.linux
+@@ -2,7 +2,8 @@ DESTDIR = $(INSTROOT)@DESTDIR@
+ 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
diff --git a/package/network/services/ppp/patches/204-radius_config.patch b/package/network/services/ppp/patches/204-radius_config.patch
new file mode 100644
index 0000000000..c97a535079
--- /dev/null
+++ b/package/network/services/ppp/patches/204-radius_config.patch
@@ -0,0 +1,72 @@
+--- a/pppd/plugins/radius/config.c
++++ b/pppd/plugins/radius/config.c
+@@ -369,31 +369,37 @@ static int test_config(char *filename)
+ }
+ #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;
+ }
+--- a/pppd/plugins/radius/options.h
++++ b/pppd/plugins/radius/options.h
+@@ -31,24 +31,21 @@ typedef struct _option {
+ 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 --git a/package/network/services/ppp/patches/205-no_exponential_timeout.patch b/package/network/services/ppp/patches/205-no_exponential_timeout.patch
new file mode 100644
index 0000000000..7119fb83f2
--- /dev/null
+++ b/package/network/services/ppp/patches/205-no_exponential_timeout.patch
@@ -0,0 +1,29 @@
+pppd: Don't use exponential timeout in discovery phase
+
+This patch removes the exponential timeout increase between PADO or PADS
+discovery attempts.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/plugins/rp-pppoe/discovery.c
++++ b/pppd/plugins/rp-pppoe/discovery.c
+@@ -548,7 +548,9 @@ discovery(PPPoEConnection *conn)
+ conn->discoveryState = STATE_SENT_PADI;
+ waitForPADO(conn, timeout);
+
++#if 0
+ timeout *= 2;
++#endif
+ } while (conn->discoveryState == STATE_SENT_PADI);
+
+ timeout = conn->discoveryTimeout;
+@@ -563,7 +565,9 @@ discovery(PPPoEConnection *conn)
+ sendPADR(conn);
+ conn->discoveryState = STATE_SENT_PADR;
+ waitForPADS(conn, timeout);
++#if 0
+ timeout *= 2;
++#endif
+ } while (conn->discoveryState == STATE_SENT_PADR);
+
+ /* We're done. */
diff --git a/package/network/services/ppp/patches/206-compensate_time_change.patch b/package/network/services/ppp/patches/206-compensate_time_change.patch
new file mode 100644
index 0000000000..fb6c65679e
--- /dev/null
+++ b/package/network/services/ppp/patches/206-compensate_time_change.patch
@@ -0,0 +1,94 @@
+pppd: Watch out for time warps
+
+On many embedded systems there is no battery backed RTC and a proper system
+time only becomes available through NTP after establishing a connection.
+
+When the clock suddenly jumps forward, the internal accounting (connect time)
+is confused resulting in unreliable data.
+
+This patch implements periodic clock checking to look for time warps, if one
+is detected, the internal counters are adjusted accordingly.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -90,6 +90,7 @@
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
++#include <sys/sysinfo.h>
+
+ #include "pppd.h"
+ #include "magic.h"
+@@ -228,6 +229,7 @@ static struct subprocess *children;
+
+ /* Prototypes for procedures local to this file. */
+
++static void check_time(void);
+ static void setup_signals __P((void));
+ static void create_pidfile __P((int pid));
+ static void create_linkpidfile __P((int pid));
+@@ -535,6 +537,7 @@ main(argc, argv)
+ info("Starting link");
+ }
+
++ check_time();
+ gettimeofday(&start_time, NULL);
+ script_unsetenv("CONNECT_TIME");
+ script_unsetenv("BYTES_SENT");
+@@ -1267,6 +1270,36 @@ struct callout {
+
+ static struct callout *callout = NULL; /* Callout list */
+ static struct timeval timenow; /* Current time */
++static long uptime_diff = 0;
++static int uptime_diff_set = 0;
++
++static void check_time(void)
++{
++ long new_diff;
++ struct timeval t;
++ struct sysinfo i;
++ struct callout *p;
++
++ gettimeofday(&t, NULL);
++ sysinfo(&i);
++ new_diff = t.tv_sec - i.uptime;
++
++ if (!uptime_diff_set) {
++ uptime_diff = new_diff;
++ uptime_diff_set = 1;
++ return;
++ }
++
++ if ((new_diff - 5 > uptime_diff) || (new_diff + 5 < uptime_diff)) {
++ /* system time has changed, update counters and timeouts */
++ info("System time change detected.");
++ start_time.tv_sec += new_diff - uptime_diff;
++
++ for (p = callout; p != NULL; p = p->c_next)
++ p->c_time.tv_sec += new_diff - uptime_diff;
++ }
++ uptime_diff = new_diff;
++}
+
+ /*
+ * timeout - Schedule a timeout.
+@@ -1337,6 +1370,8 @@ calltimeout()
+ {
+ struct callout *p;
+
++ check_time();
++
+ while (callout != NULL) {
+ p = callout;
+
+@@ -1364,6 +1399,8 @@ timeleft(tvp)
+ {
+ if (callout == NULL)
+ return NULL;
++
++ check_time();
+
+ gettimeofday(&timenow, NULL);
+ tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
diff --git a/package/network/services/ppp/patches/207-lcp_mtu_max.patch b/package/network/services/ppp/patches/207-lcp_mtu_max.patch
new file mode 100644
index 0000000000..1ebcf412fc
--- /dev/null
+++ b/package/network/services/ppp/patches/207-lcp_mtu_max.patch
@@ -0,0 +1,25 @@
+pppd: Cap MTU to the user configured value
+
+This patchs caps the calculated MTU value in lcp.c to the user specified "mru"
+option value. Without this patch pppd would advertise a different MTU value
+compared to what is set on the local interface in some cases.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/lcp.c
++++ b/pppd/lcp.c
+@@ -1904,12 +1904,12 @@ lcp_up(f)
+ * the interface MTU is set to the lowest of that, the
+ * MTU we want to use, and our link MRU.
+ */
+- mtu = ho->neg_mru? ho->mru: PPP_MRU;
++ mtu = MIN(ho->neg_mru? ho->mru: PPP_MRU, ao->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, MIN(MIN(mtu, mru), ao->mru));
++ netif_set_mtu(f->unit, MIN(mtu, mru));
+ ppp_send_config(f->unit, mtu,
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+ ho->neg_pcompression, ho->neg_accompression);
diff --git a/package/network/services/ppp/patches/208-fix_status_code.patch b/package/network/services/ppp/patches/208-fix_status_code.patch
new file mode 100644
index 0000000000..25e2a10b85
--- /dev/null
+++ b/package/network/services/ppp/patches/208-fix_status_code.patch
@@ -0,0 +1,24 @@
+pppd: Do not clobber exit codes on hangup
+
+When a modem hangup occurs, pppd unconditionally sets the exit status code
+to EXIT_HANGUP. This patch only sets EXIT_HANGUP if the exit status code is
+not already set to an error value.
+
+The motiviation of this patch is to allow applications which remote control
+pppd to react properly on errors, e.g. only redial (relaunch pppd) if there
+was a hangup, but not if the CHAP authentication failed.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -1048,7 +1048,8 @@ get_input()
+ }
+ notice("Modem hangup");
+ hungup = 1;
+- status = EXIT_HANGUP;
++ if (status == EXIT_OK)
++ status = EXIT_HANGUP;
+ lcp_lowerdown(0); /* serial link is no longer available */
+ link_terminated(0);
+ return;
diff --git a/package/network/services/ppp/patches/300-filter-pcap-includes-lib.patch b/package/network/services/ppp/patches/300-filter-pcap-includes-lib.patch
new file mode 100644
index 0000000000..d8dcc64c8b
--- /dev/null
+++ b/package/network/services/ppp/patches/300-filter-pcap-includes-lib.patch
@@ -0,0 +1,20 @@
+build: Add required CFLAGS for libpcap
+
+This patch adds some flags to required to properly link libpcap within the
+OpenWrt environment.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/Makefile.linux
++++ b/pppd/Makefile.linux
+@@ -170,8 +170,8 @@ endif
+
+ ifdef FILTER
+ ifneq ($(wildcard /usr/include/pcap-bpf.h),)
+-LIBS += -lpcap
+-CFLAGS += -DPPP_FILTER
++LIBS += -lpcap -L$(STAGING_DIR)/usr/lib
++CFLAGS += -DPPP_FILTER -I$(STAGING_DIR)/usr/include
+ endif
+ endif
+
diff --git a/package/network/services/ppp/patches/310-precompile_filter.patch b/package/network/services/ppp/patches/310-precompile_filter.patch
new file mode 100644
index 0000000000..87b9687ef7
--- /dev/null
+++ b/package/network/services/ppp/patches/310-precompile_filter.patch
@@ -0,0 +1,196 @@
+pppd: Implement support for precompiled pcap filters
+
+This patch implements support for precompiled pcap filters which is useful to
+support dial-on-demand on memory constrained embedded devices without having
+to link the full libpcap into pppd to generate the filters during runtime.
+
+Two new options are introduced; "precompiled-pass-filter" specifies a pre-
+compiled filter file containing rules to match packets which should be passed,
+"precompiled-active-filter" specifies a filter file containing rules to match
+packets which are treated as active.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/Makefile.linux
++++ b/pppd/Makefile.linux
+@@ -50,6 +50,9 @@ MPPE=y
+ # and that the kernel driver support PPP packet filtering.
+ #FILTER=y
+
++# Support for precompiled filters
++PRECOMPILED_FILTER=y
++
+ # Uncomment the next line to enable multilink PPP (enabled by default)
+ # Linux distributions: Please leave multilink ENABLED in your builds
+ # of pppd!
+@@ -175,6 +178,14 @@ CFLAGS += -DPPP_FILTER -I$(STAGING_DIR)
+ endif
+ endif
+
++ifdef PRECOMPILED_FILTER
++PPPDSRCS += pcap_pcc.c
++HEADERS += pcap_pcc.h
++PPPDOBJS += pcap_pcc.o
++LIBS += $(STAGING_DIR)/usr/lib/libpcap.a
++CFLAGS += -DPPP_FILTER -DPPP_PRECOMPILED_FILTER -I$(STAGING_DIR)/usr/include
++endif
++
+ ifdef HAVE_INET6
+ PPPDSRCS += ipv6cp.c eui64.c
+ HEADERS += ipv6cp.h eui64.h
+--- a/pppd/options.c
++++ b/pppd/options.c
+@@ -57,6 +57,7 @@
+
+ #ifdef PPP_FILTER
+ #include <pcap.h>
++#include <pcap-bpf.h>
+ /*
+ * There have been 3 or 4 different names for this in libpcap CVS, but
+ * this seems to be what they have settled on...
+@@ -162,6 +163,13 @@ static int setlogfile __P((char **));
+ static int loadplugin __P((char **));
+ #endif
+
++#ifdef PPP_PRECOMPILED_FILTER
++#include "pcap_pcc.h"
++static int setprecompiledpassfilter __P((char **));
++static int setprecompiledactivefilter __P((char **));
++#undef PPP_FILTER
++#endif
++
+ #ifdef PPP_FILTER
+ static int setpassfilter __P((char **));
+ static int setactivefilter __P((char **));
+@@ -326,6 +334,14 @@ option_t general_options[] = {
+ "set filter for active pkts", OPT_PRIO },
+ #endif
+
++#ifdef PPP_PRECOMPILED_FILTER
++ { "precompiled-pass-filter", 1, setprecompiledpassfilter,
++ "set precompiled filter for packets to pass", OPT_PRIO },
++
++ { "precompiled-active-filter", 1, setprecompiledactivefilter,
++ "set precompiled filter for active pkts", OPT_PRIO },
++#endif
++
+ #ifdef MAXOCTETS
+ { "maxoctets", o_int, &maxoctets,
+ "Set connection traffic limit",
+@@ -1472,6 +1488,29 @@ callfile(argv)
+ return ok;
+ }
+
++#ifdef PPP_PRECOMPILED_FILTER
++/*
++ * setprecompiledpassfilter - Set the pass filter for packets using a
++ * precompiled expression
++ */
++static int
++setprecompiledpassfilter(argv)
++ char **argv;
++{
++ return pcap_pre_compiled (*argv, &pass_filter);
++}
++
++/*
++ * setactivefilter - Set the active filter for packets
++ */
++static int
++setprecompiledactivefilter(argv)
++ char **argv;
++{
++ return pcap_pre_compiled (*argv, &active_filter);
++}
++#endif
++
+ #ifdef PPP_FILTER
+ /*
+ * setpassfilter - Set the pass filter for packets
+--- /dev/null
++++ b/pppd/pcap_pcc.c
+@@ -0,0 +1,74 @@
++#include <pcap.h>
++#include <pcap-bpf.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include "pppd.h"
++
++int pcap_pre_compiled (char * fname, struct bpf_program *p)
++{
++ char buf[128];
++ int line = 0, size = 0, index=0, ret=1;
++ FILE *f = fopen (fname, "r");
++ if (!f)
++ {
++ option_error("error opening precompiled active-filter '%s': %s",
++ fname, strerror (errno));
++ return 0;
++ }
++ while (fgets (buf, 127, f))
++ {
++ line++;
++ if (*buf == '#')
++ continue;
++ if (size)
++ {
++ /*
++ struct bpf_insn {
++ u_short code;
++ u_char jt;
++ u_char jf;
++ bpf_int32 k;
++ }
++ */
++ struct bpf_insn * insn = & p->bf_insns[index];
++ unsigned code, jt, jf, k;
++ if (sscanf (buf, "%u %u %u %u", &code, &jt, &jf, &k) != 4)
++ {
++ goto err;
++ }
++ insn->code = code;
++ insn->jt = jt;
++ insn->jf = jf;
++ insn->k = k;
++ index++;
++ }
++ else
++ {
++ if (sscanf (buf, "%u", &size) != 1)
++ {
++ goto err;
++ }
++ p->bf_len = size;
++ p->bf_insns = (struct bpf_insn *)
++ malloc (size * sizeof (struct bpf_insn));
++ }
++ }
++ if (size != index)
++ {
++ option_error("error in precompiled active-filter,"
++ " expected %d expressions, got %dn",
++ size, index);
++ ret = 0;
++ }
++ fclose(f);
++ return ret;
++
++err:
++ option_error("error in precompiled active-filter"
++ " expression line %s:%d (wrong size)\n",
++ fname, line);
++ fclose (f);
++ return 0;
++}
+--- /dev/null
++++ b/pppd/pcap_pcc.h
+@@ -0,0 +1,7 @@
++#ifndef PCAP_PCC_H
++#define PCAP_PCC_H
++
++#include <pcap.h>
++
++int pcap_pre_compiled (char * fname, struct bpf_program *p);
++#endif /* PCAP_PCC_H */
diff --git a/package/network/services/ppp/patches/320-custom_iface_names.patch b/package/network/services/ppp/patches/320-custom_iface_names.patch
new file mode 100644
index 0000000000..a95f4f8c81
--- /dev/null
+++ b/package/network/services/ppp/patches/320-custom_iface_names.patch
@@ -0,0 +1,135 @@
+pppd: Support arbitrary interface names
+
+This patch implements a new string option "ifname" which allows to specify
+fully custom PPP interface names on Linux. It does so by renaming the
+allocated pppX device immediately after it has been created to the requested
+interface name.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -745,8 +745,11 @@ void
+ set_ifunit(iskey)
+ int iskey;
+ {
+- info("Using interface %s%d", PPP_DRV_NAME, ifunit);
+- slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
++ if (use_ifname[0] == 0)
++ slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
++ else
++ slprintf(ifname, sizeof(ifname), "%s", use_ifname);
++ info("Using interface %s", ifname);
+ script_setenv("IFNAME", ifname, iskey);
+ if (iskey) {
+ create_pidfile(getpid()); /* write pid to file */
+--- a/pppd/options.c
++++ b/pppd/options.c
+@@ -111,6 +111,7 @@ int log_to_fd = 1; /* send log messages
+ 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 */
++char use_ifname[IFNAMSIZ]; /* physical name for PPP 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 */
+@@ -266,6 +267,9 @@ option_t general_options[] = {
+ { "linkname", o_string, linkname,
+ "Set logical name for link",
+ OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN },
++ { "ifname", o_string, use_ifname,
++ "Set physical name for PPP interface",
++ OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, IFNAMSIZ },
+
+ { "maxfail", o_int, &maxfail,
+ "Maximum number of unsuccessful connection attempts to allow",
+--- a/pppd/pppd.h
++++ b/pppd/pppd.h
+@@ -71,6 +71,10 @@
+ #include "eui64.h"
+ #endif
+
++#ifndef IFNAMSIZ
++#define IFNAMSIZ 16
++#endif
++
+ /*
+ * Limits.
+ */
+@@ -309,6 +313,7 @@ extern char *record_file; /* File to rec
+ extern bool sync_serial; /* Device is synchronous serial device */
+ extern int maxfail; /* Max # of unsuccessful connection attempts */
+ extern char linkname[MAXPATHLEN]; /* logical name for link */
++extern char use_ifname[IFNAMSIZ]; /* physical name for PPP interface */
+ extern bool tune_kernel; /* May alter kernel settings as necessary */
+ extern int connect_delay; /* Time to delay after connect script */
+ extern int max_data_rate; /* max bytes/sec through charshunt */
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -168,6 +168,10 @@ struct in6_ifreq {
+ /* We can get an EIO error on an ioctl if the modem has hung up */
+ #define ok_error(num) ((num)==EIO)
+
++#if !defined(PPP_DRV_NAME)
++#define PPP_DRV_NAME "ppp"
++#endif /* !defined(PPP_DRV_NAME) */
++
+ static int tty_disc = N_TTY; /* The TTY discipline */
+ static int ppp_disc = N_PPP; /* The PPP discpline */
+ static int initfdflags = -1; /* Initial file descriptor flags for fd */
+@@ -622,7 +626,8 @@ void generic_disestablish_ppp(int dev_fd
+ */
+ static int make_ppp_unit()
+ {
+- int x, flags;
++ struct ifreq ifr;
++ int x, flags, s;
+
+ if (ppp_dev_fd >= 0) {
+ dbglog("in make_ppp_unit, already had /dev/ppp open?");
+@@ -645,6 +650,30 @@ static int make_ppp_unit()
+ }
+ if (x < 0)
+ error("Couldn't create new ppp unit: %m");
++
++ if (use_ifname[0] != 0) {
++ s = socket(PF_INET, SOCK_DGRAM, 0);
++ if (s < 0)
++ s = socket(PF_PACKET, SOCK_DGRAM, 0);
++ if (s < 0)
++ s = socket(PF_INET6, SOCK_DGRAM, 0);
++ if (s < 0)
++ s = socket(PF_UNIX, SOCK_DGRAM, 0);
++ if (s >= 0) {
++ slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", PPP_DRV_NAME, ifunit);
++ slprintf(ifr.ifr_newname, sizeof(ifr.ifr_newname), "%s", use_ifname);
++ x = ioctl(s, SIOCSIFNAME, &ifr);
++ close(s);
++ } else {
++ x = s;
++ }
++ if (x < 0) {
++ error("Couldn't rename %s to %s", ifr.ifr_name, ifr.ifr_newname);
++ close(ppp_dev_fd);
++ ppp_dev_fd = -1;
++ }
++ }
++
+ return x;
+ }
+
+--- a/pppstats/pppstats.c
++++ b/pppstats/pppstats.c
+@@ -506,10 +506,12 @@ main(argc, argv)
+ if (argc > 0)
+ interface = argv[0];
+
++#if 0
+ if (sscanf(interface, PPP_DRV_NAME "%d", &unit) != 1) {
+ fprintf(stderr, "%s: invalid interface '%s' specified\n",
+ progname, interface);
+ }
++#endif
+
+ #ifndef STREAMS
+ {
diff --git a/package/network/services/ppp/patches/330-retain_foreign_default_routes.patch b/package/network/services/ppp/patches/330-retain_foreign_default_routes.patch
new file mode 100644
index 0000000000..0d7fff9b4b
--- /dev/null
+++ b/package/network/services/ppp/patches/330-retain_foreign_default_routes.patch
@@ -0,0 +1,22 @@
+pppd: Retain foreign default routes on Linux
+
+On Linux, when pppd attempts to delete its default route it does not fill
+the rt_dev field of the struct rtentry used to match the system default route.
+As a consequence, pppd happily deletes any default route even if it belongs
+to another interface.
+
+This patch makes pppd fill out the rt_dev field so that only own default
+routes are ever matched.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -1743,6 +1743,7 @@ int cifdefaultroute (int unit, u_int32_t
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
++ rt.rt_dev = ifname;
+ rt.rt_flags = RTF_UP;
+ if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+ if (still_ppp()) {
diff --git a/package/network/services/ppp/patches/340-populate_default_gateway.patch b/package/network/services/ppp/patches/340-populate_default_gateway.patch
new file mode 100644
index 0000000000..9a0284eb23
--- /dev/null
+++ b/package/network/services/ppp/patches/340-populate_default_gateway.patch
@@ -0,0 +1,34 @@
+pppd: Fill in default gateway on Linux
+
+On Linux, when pppd creates the default route, it does not set the peer
+address as gateway, leading to a default route without gateway address.
+
+This behaviour breaks various downstream programs which attempt to infer
+the default gateway IP address from the system default route entry.
+
+This patch addresses the issue by filling in the peer address as gateway
+when generating the default route entry.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -1697,6 +1697,9 @@ int sifdefaultroute (int unit, u_int32_t
+ memset (&rt, 0, sizeof (rt));
+ SET_SA_FAMILY (rt.rt_dst, AF_INET);
+
++ SET_SA_FAMILY(rt.rt_gateway, AF_INET);
++ SIN_ADDR(rt.rt_gateway) = gateway;
++
+ rt.rt_dev = ifname;
+
+ if (kernel_version > KVERSION(2,1,0)) {
+@@ -1704,7 +1707,7 @@ int sifdefaultroute (int unit, u_int32_t
+ SIN_ADDR(rt.rt_genmask) = 0L;
+ }
+
+- rt.rt_flags = RTF_UP;
++ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
+ if (!ok_error(errno))
+ error("default route ioctl(SIOCADDRT): %m");
diff --git a/package/network/services/ppp/patches/400-simplify_kernel_checks.patch b/package/network/services/ppp/patches/400-simplify_kernel_checks.patch
new file mode 100644
index 0000000000..ec82576cbc
--- /dev/null
+++ b/package/network/services/ppp/patches/400-simplify_kernel_checks.patch
@@ -0,0 +1,154 @@
+pppd: Remove runtime kernel checks
+
+On embedded system distributions the required kernel features for pppd are
+more or less guaranteed to be present, so there is not much point in
+performing runtime checks, it just increases the binary size.
+
+This patch removes the runtime kernel feature checks.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -203,7 +203,7 @@ static int driver_is_old = 0;
+ static int restore_term = 0; /* 1 => we've munged the terminal */
+ static struct termios inittermios; /* Initial TTY termios */
+
+-int new_style_driver = 0;
++static const int new_style_driver = 1;
+
+ static char loop_name[20];
+ static unsigned char inbuf[512]; /* buffer for chars read from loopback */
+@@ -220,8 +220,8 @@ static int looped; /* 1 if using loop
+ static int link_mtu; /* mtu for the link (not bundle) */
+
+ static struct utsname utsname; /* for the kernel version */
+-static int kernel_version;
+ #define KVERSION(j,n,p) ((j)*1000000 + (n)*1000 + (p))
++static const int kernel_version = KVERSION(2,6,37);
+
+ #define MAX_IFS 100
+
+@@ -1438,11 +1438,12 @@ int ccp_fatal_error (int unit)
+ *
+ * path_to_procfs - find the path to the proc file system mount point
+ */
+-static char proc_path[MAXPATHLEN];
+-static int proc_path_len;
++static char proc_path[MAXPATHLEN] = "/proc";
++static int proc_path_len = 5;
+
+ static char *path_to_procfs(const char *tail)
+ {
++#if 0
+ struct mntent *mntent;
+ FILE *fp;
+
+@@ -1464,6 +1465,7 @@ static char *path_to_procfs(const char *
+ fclose (fp);
+ }
+ }
++#endif
+
+ strlcpy(proc_path + proc_path_len, tail,
+ sizeof(proc_path) - proc_path_len);
+@@ -2116,15 +2118,19 @@ int ppp_available(void)
+ int my_version, my_modification, my_patch;
+ int osmaj, osmin, ospatch;
+
++#if 0
+ /* get the kernel version now, since we are called before sys_init */
+ uname(&utsname);
+ osmaj = osmin = ospatch = 0;
+ sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
+ kernel_version = KVERSION(osmaj, osmin, ospatch);
++#endif
+
+ fd = open("/dev/ppp", O_RDWR);
+ if (fd >= 0) {
++#if 0
+ new_style_driver = 1;
++#endif
+
+ /* XXX should get from driver */
+ driver_version = 2;
+@@ -2185,6 +2191,7 @@ int ppp_available(void)
+
+ if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP))
+ ok = 0;
++ return ok;
+
+ /*
+ * This is the PPP device. Validate the version of the driver at this
+@@ -2678,6 +2685,7 @@ get_pty(master_fdp, slave_fdp, slave_nam
+ }
+ #endif /* TIOCGPTN */
+
++#if 0
+ if (sfd < 0) {
+ /* the old way - scan through the pty name space */
+ for (i = 0; i < 64; ++i) {
+@@ -2696,6 +2704,7 @@ get_pty(master_fdp, slave_fdp, slave_nam
+ }
+ }
+ }
++#endif
+
+ if (sfd < 0)
+ return 0;
+--- a/pppd/plugins/pppoatm/pppoatm.c
++++ b/pppd/plugins/pppoatm/pppoatm.c
+@@ -170,14 +170,6 @@ static void disconnect_pppoatm(void)
+
+ 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);
+ }
+--- a/pppd/plugins/rp-pppoe/plugin.c
++++ b/pppd/plugins/rp-pppoe/plugin.c
+@@ -60,9 +60,6 @@ static char const RCSID[] =
+
+ 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;
+@@ -340,10 +337,6 @@ PPPoEDevnameHook(char *cmd, char **argv,
+ 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",
+--- a/pppd/plugins/pppol2tp/pppol2tp.c
++++ b/pppd/plugins/pppol2tp/pppol2tp.c
+@@ -500,12 +500,7 @@ static void pppol2tp_cleanup(void)
+
+ 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 PPPoL2TP");
+-#else
++#if !defined(__linux__)
+ fatal("No PPPoL2TP support on this OS");
+ #endif
+ add_options(pppol2tp_options);
diff --git a/package/network/services/ppp/patches/401-no_record_file.patch b/package/network/services/ppp/patches/401-no_record_file.patch
new file mode 100644
index 0000000000..94c0263eaa
--- /dev/null
+++ b/package/network/services/ppp/patches/401-no_record_file.patch
@@ -0,0 +1,39 @@
+pppd: Remove the "record" option
+
+On many embedded systems there is not enough space to record PPP session
+information to the permanent storage, therfore remove this option.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/pppd.h
++++ b/pppd/pppd.h
+@@ -309,7 +309,6 @@ extern int holdoff; /* Dead time before
+ extern bool holdoff_specified; /* true if user gave a holdoff value */
+ extern bool notty; /* Stdin/out is not a tty */
+ extern char *pty_socket; /* Socket to connect to pty */
+-extern char *record_file; /* File to record chars sent/received */
+ extern bool sync_serial; /* Device is synchronous serial device */
+ extern int maxfail; /* Max # of unsuccessful connection attempts */
+ extern char linkname[MAXPATHLEN]; /* logical name for link */
+--- a/pppd/tty.c
++++ b/pppd/tty.c
+@@ -145,7 +145,7 @@ char *disconnect_script = NULL; /* Scrip
+ char *welcomer = NULL; /* Script to run after phys link estab. */
+ char *ptycommand = NULL; /* Command to run on other side of pty */
+ bool notty = 0; /* Stdin/out is not a tty */
+-char *record_file = NULL; /* File to record chars sent/received */
++static char *const record_file = NULL; /* File to record chars sent/received */
+ int max_data_rate; /* max bytes/sec through charshunt */
+ bool sync_serial = 0; /* Device is synchronous serial device */
+ char *pty_socket = NULL; /* Socket to connect to pty */
+@@ -201,8 +201,10 @@ option_t tty_options[] = {
+ "Send and receive over socket, arg is host:port",
+ OPT_PRIO | OPT_DEVNAM },
+
++#if 0
+ { "record", o_string, &record_file,
+ "Record characters sent/received to file", OPT_PRIO },
++#endif
+
+ { "crtscts", o_int, &crtscts,
+ "Set hardware (RTS/CTS) flow control",
diff --git a/package/network/services/ppp/patches/403-no_wtmp.patch b/package/network/services/ppp/patches/403-no_wtmp.patch
new file mode 100644
index 0000000000..71233200e7
--- /dev/null
+++ b/package/network/services/ppp/patches/403-no_wtmp.patch
@@ -0,0 +1,25 @@
+pppd: Disable wtmp support
+
+Many uClibc based environments lack wtmp and utmp support, therfore remove
+the code updating the wtmp information.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/sys-linux.c
++++ b/pppd/sys-linux.c
+@@ -2254,6 +2254,7 @@ int ppp_available(void)
+
+ void logwtmp (const char *line, const char *name, const char *host)
+ {
++#if 0
+ struct utmp ut, *utp;
+ pid_t mypid = getpid();
+ #if __GLIBC__ < 2
+@@ -2319,6 +2320,7 @@ void logwtmp (const char *line, const ch
+ close (wtmp);
+ }
+ #endif
++#endif
+ }
+
+
diff --git a/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch b/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch
new file mode 100644
index 0000000000..edbca7603c
--- /dev/null
+++ b/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch
@@ -0,0 +1,151 @@
+pppd: Remove historical protocol names
+
+Remove a number of historical protocol entries from pppd's builtin list, this
+reduced the binary size without loss of features.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/main.c
++++ b/pppd/main.c
+@@ -882,14 +882,17 @@ struct protocol_list {
+ const char *name;
+ } protocol_list[] = {
+ { 0x21, "IP" },
++#if 0
+ { 0x23, "OSI Network Layer" },
+ { 0x25, "Xerox NS IDP" },
+ { 0x27, "DECnet Phase IV" },
+ { 0x29, "Appletalk" },
+ { 0x2b, "Novell IPX" },
++#endif
+ { 0x2d, "VJ compressed TCP/IP" },
+ { 0x2f, "VJ uncompressed TCP/IP" },
+ { 0x31, "Bridging PDU" },
++#if 0
+ { 0x33, "Stream Protocol ST-II" },
+ { 0x35, "Banyan Vines" },
+ { 0x39, "AppleTalk EDDP" },
+@@ -903,8 +906,11 @@ struct protocol_list {
+ { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
+ { 0x4b, "SNA over 802.2" },
+ { 0x4d, "SNA" },
++#endif
+ { 0x4f, "IP6 Header Compression" },
++#if 0
+ { 0x51, "KNX Bridging Data" },
++#endif
+ { 0x53, "Encryption" },
+ { 0x55, "Individual Link Encryption" },
+ { 0x57, "IPv6" },
+@@ -915,12 +921,15 @@ struct protocol_list {
+ { 0x65, "RTP IPHC Compressed non-TCP" },
+ { 0x67, "RTP IPHC Compressed UDP 8" },
+ { 0x69, "RTP IPHC Compressed RTP 8" },
++#if 0
+ { 0x6f, "Stampede Bridging" },
+ { 0x73, "MP+" },
+ { 0xc1, "NTCITS IPI" },
++#endif
+ { 0xfb, "single-link compression" },
+ { 0xfd, "Compressed Datagram" },
+ { 0x0201, "802.1d Hello Packets" },
++#if 0
+ { 0x0203, "IBM Source Routing BPDU" },
+ { 0x0205, "DEC LANBridge100 Spanning Tree" },
+ { 0x0207, "Cisco Discovery Protocol" },
+@@ -932,15 +941,19 @@ struct protocol_list {
+ { 0x0231, "Luxcom" },
+ { 0x0233, "Sigma Network Systems" },
+ { 0x0235, "Apple Client Server Protocol" },
++#endif
+ { 0x0281, "MPLS Unicast" },
+ { 0x0283, "MPLS Multicast" },
++#if 0
+ { 0x0285, "IEEE p1284.4 standard - data packets" },
+ { 0x0287, "ETSI TETRA Network Protocol Type 1" },
++#endif
+ { 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" },
++#if 0
+ { 0x4001, "Cray Communications Control Protocol" },
+ { 0x4003, "CDPD Mobile Network Registration Protocol" },
+ { 0x4005, "Expand accelerator protocol" },
+@@ -951,8 +964,10 @@ struct protocol_list {
+ { 0x4023, "RefTek Protocol" },
+ { 0x4025, "Fibre Channel" },
+ { 0x4027, "EMIT Protocols" },
++#endif
+ { 0x405b, "Vendor-Specific Protocol (VSP)" },
+ { 0x8021, "Internet Protocol Control Protocol" },
++#if 0
+ { 0x8023, "OSI Network Layer Control Protocol" },
+ { 0x8025, "Xerox NS IDP Control Protocol" },
+ { 0x8027, "DECnet Phase IV Control Protocol" },
+@@ -961,7 +976,9 @@ struct protocol_list {
+ { 0x8031, "Bridging NCP" },
+ { 0x8033, "Stream Protocol Control Protocol" },
+ { 0x8035, "Banyan Vines Control Protocol" },
++#endif
+ { 0x803d, "Multi-Link Control Protocol" },
++#if 0
+ { 0x803f, "NETBIOS Framing Control Protocol" },
+ { 0x8041, "Cisco Systems Control Protocol" },
+ { 0x8043, "Ascom Timeplex" },
+@@ -970,18 +987,24 @@ struct protocol_list {
+ { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+ { 0x804b, "SNA over 802.2 Control Protocol" },
+ { 0x804d, "SNA Control Protocol" },
++#endif
+ { 0x804f, "IP6 Header Compression Control Protocol" },
++#if 0
+ { 0x8051, "KNX Bridging Control Protocol" },
++#endif
+ { 0x8053, "Encryption Control Protocol" },
+ { 0x8055, "Individual Link Encryption Control Protocol" },
+ { 0x8057, "IPv6 Control Protocol" },
+ { 0x8059, "PPP Muxing Control Protocol" },
+ { 0x805b, "Vendor-Specific Network Control Protocol (VSNCP)" },
++#if 0
+ { 0x806f, "Stampede Bridging Control Protocol" },
+ { 0x8073, "MP+ Control Protocol" },
+ { 0x80c1, "NTCITS IPI Control Protocol" },
++#endif
+ { 0x80fb, "Single Link Compression Control Protocol" },
+ { 0x80fd, "Compression Control Protocol" },
++#if 0
+ { 0x8207, "Cisco Discovery Protocol Control" },
+ { 0x8209, "Netcs Twin Routing" },
+ { 0x820b, "STP - Control Protocol" },
+@@ -990,24 +1013,29 @@ struct protocol_list {
+ { 0x8281, "MPLSCP" },
+ { 0x8285, "IEEE p1284.4 standard - Protocol Control" },
+ { 0x8287, "ETSI TETRA TNP1 Control Protocol" },
++#endif
+ { 0x8289, "Multichannel Flow Treatment Protocol" },
+ { 0xc021, "Link Control Protocol" },
+ { 0xc023, "Password Authentication Protocol" },
+ { 0xc025, "Link Quality Report" },
++#if 0
+ { 0xc027, "Shiva Password Authentication Protocol" },
+ { 0xc029, "CallBack Control Protocol (CBCP)" },
+ { 0xc02b, "BACP Bandwidth Allocation Control Protocol" },
+ { 0xc02d, "BAP" },
++#endif
+ { 0xc05b, "Vendor-Specific Authentication Protocol (VSAP)" },
+ { 0xc081, "Container Control Protocol" },
+ { 0xc223, "Challenge Handshake Authentication Protocol" },
+ { 0xc225, "RSA Authentication Protocol" },
+ { 0xc227, "Extensible Authentication Protocol" },
++#if 0
+ { 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" },
++#endif
+ { 0, NULL },
+ };
+
diff --git a/package/network/services/ppp/patches/405-no_multilink_option.patch b/package/network/services/ppp/patches/405-no_multilink_option.patch
new file mode 100644
index 0000000000..97a79c474a
--- /dev/null
+++ b/package/network/services/ppp/patches/405-no_multilink_option.patch
@@ -0,0 +1,28 @@
+pppd: Support "nomp" option even if multilink support is off
+
+This patch moves the "nomp" option entry outside of the defines protecting
+the multilink specific code. The motivation is to allow "nomp" even if pppd
+does not support multilink, so that controlling programs can unconditionally
+pass it to pppd regardless of the compile time features.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/pppd/options.c
++++ b/pppd/options.c
+@@ -318,13 +318,14 @@ option_t general_options[] = {
+ "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 */
+
++ { "nomp", o_bool, &multilink,
++ "Disable multilink operation", OPT_PRIOSUB | OPT_ALIAS | 0 },
++
+ #ifdef PLUGIN
+ { "plugin", o_special, (void *)loadplugin,
+ "Load a plug-in module into pppd", OPT_PRIV | OPT_A2LIST },
diff --git a/package/network/services/ppp/patches/430-pppol2tpv3-struct.patch b/package/network/services/ppp/patches/430-pppol2tpv3-struct.patch
new file mode 100644
index 0000000000..4f517df764
--- /dev/null
+++ b/package/network/services/ppp/patches/430-pppol2tpv3-struct.patch
@@ -0,0 +1,30 @@
+pppol2tp: Provide struct pppol2tpv3_addr to align with Linux
+
+The struct pppol2tpv3_addr is referenced in the current Linux kernel sources
+but not provided by the shipped kernel headers, add it.
+
+Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
+
+--- a/include/linux/if_pppol2tp.h
++++ b/include/linux/if_pppol2tp.h
+@@ -32,6 +32,20 @@ struct pppol2tp_addr
+ __u16 d_tunnel, d_session; /* For sending outgoing packets */
+ };
+
++/* The L2TPv3 protocol changes tunnel and session ids from 16 to 32
++ * bits. So we need a different sockaddr structure.
++ */
++struct pppol2tpv3_addr {
++ pid_t pid; /* pid that owns the fd.
++ * 0 => current */
++ int fd; /* FD of UDP or IP socket to use */
++
++ struct sockaddr_in addr; /* IP address and port to send to */
++
++ __u32 s_tunnel, s_session; /* For matching incoming packets */
++ __u32 d_tunnel, d_session; /* For sending outgoing packets */
++};
++
+ /* Socket options:
+ * DEBUG - bitmask of debug message categories
+ * SENDSEQ - 0 => don't send packets with sequence numbers
diff --git a/package/network/services/ppp/patches/500-add-pptp-plugin.patch b/package/network/services/ppp/patches/500-add-pptp-plugin.patch
new file mode 100644
index 0000000000..d984e1b162
--- /dev/null
+++ b/package/network/services/ppp/patches/500-add-pptp-plugin.patch
@@ -0,0 +1,3065 @@
+--- a/configure
++++ b/configure
+@@ -195,7 +195,7 @@ if [ -d "$ksrc" ]; then
+ 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 \
+- pppd/plugins/pppol2tp; do
++ pppd/plugins/pppol2tp pppd/plugins/pptp ; do
+ mkmkf $dir/Makefile.$makext $dir/Makefile
+ done
+ if [ -f $ksrc/Makefile.$makext$archvariant ]; then
+--- a/pppd/plugins/Makefile.linux
++++ b/pppd/plugins/Makefile.linux
+@@ -9,7 +9,7 @@ BINDIR = $(DESTDIR)/sbin
+ MANDIR = $(DESTDIR)/share/man/man8
+ LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
+
+-SUBDIRS := rp-pppoe pppoatm pppol2tp
++SUBDIRS := rp-pppoe pppoatm pppol2tp pptp
+ # Uncomment the next line to include the radius authentication plugin
+ SUBDIRS += radius
+ PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so
+--- /dev/null
++++ b/pppd/plugins/pptp/Makefile.linux
+@@ -0,0 +1,31 @@
++#
++# 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.9 2012/05/04 21:48:00 dgolle Exp $
++#***********************************************************************
++
++DESTDIR = $(INSTROOT)@DESTDIR@
++LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION)
++
++PPPDVERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h)
++
++INSTALL = install
++
++COPTS=-O2 -g
++CFLAGS = $(COPTS) -I. -I../.. -I../../../include -fPIC -DPPPD_VERSION=\"$(PPPDVERSION)\"
++all: pptp.so
++
++%.o: %.c
++ $(CC) $(CFLAGS) -c -o $@ $<
++
++pptp.so: dirutil.o orckit_quirks.o pptp.o pptp_callmgr.o pptp_ctrl.o pptp_quirks.o util.o vector.o
++ $(CC) -o pptp.so -shared dirutil.o orckit_quirks.o pptp.o pptp_callmgr.o pptp_ctrl.o pptp_quirks.o util.o vector.o
++
++install: all
++ $(INSTALL) -d -m 755 $(LIBDIR)
++ $(INSTALL) -c -m 4550 pptp.so $(LIBDIR)
++
++clean:
++ rm -f *.o *.so
++
+--- /dev/null
++++ b/pppd/plugins/pptp/dirutil.c
+@@ -0,0 +1,68 @@
++/* dirutil.c ... directory utilities.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: dirutil.c,v 1.2 2003/06/17 17:25:47 reink Exp $
++ */
++
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdlib.h>
++#include "dirutil.h"
++
++/* Returned malloc'ed string representing basename */
++char *basenamex(char *pathname)
++{
++ char *dup = strdup(pathname);
++ char *ptr = strrchr(stripslash(dup), '/');
++ if (ptr == NULL) return dup;
++ ptr = strdup(ptr+1);
++ free(dup);
++ return ptr;
++}
++
++/* Return malloc'ed string representing directory name (no trailing slash) */
++char *dirnamex(char *pathname)
++{
++ char *dup = strdup(pathname);
++ char *ptr = strrchr(stripslash(dup), '/');
++ if (ptr == NULL) { free(dup); return strdup("."); }
++ if (ptr == dup && dup[0] == '/') ptr++;
++ *ptr = '\0';
++ return dup;
++}
++
++/* In-place modify a string to remove trailing slashes. Returns arg.
++ * stripslash("/") returns "/";
++ */
++char *stripslash(char *pathname) {
++ int len = strlen(pathname);
++ while (len > 1 && pathname[len - 1] == '/')
++ pathname[--len] = '\0';
++ return pathname;
++}
++
++/* ensure dirname exists, creating it if necessary. */
++int make_valid_path(char *dir, mode_t mode)
++{
++ struct stat st;
++ char *tmp = NULL, *path = stripslash(strdup(dir));
++ int retval;
++ if (stat(path, &st) == 0) { /* file exists */
++ if (S_ISDIR(st.st_mode)) { retval = 1; goto end; }
++ else { retval = 0; goto end; } /* not a directory. Oops. */
++ }
++ /* Directory doesn't exist. Let's make it. */
++ /* Make parent first. */
++ if (!make_valid_path(tmp = dirnamex(path), mode)) { retval = 0; goto end; }
++ /* Now make this 'un. */
++ if (mkdir(path, mode) < 0) { retval = 0; goto end; }
++ /* Success. */
++ retval = 1;
++
++end:
++ if (tmp != NULL) free(tmp);
++ if (path != NULL) free(path);
++ return retval;
++}
+--- /dev/null
++++ b/pppd/plugins/pptp/dirutil.h
+@@ -0,0 +1,14 @@
++/* dirutil.h ... directory utilities.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: dirutil.h,v 1.1.1.1 2000/12/23 08:19:51 scott Exp $
++ */
++
++/* Returned malloc'ed string representing basename */
++char *basenamex(char *pathname);
++/* Return malloc'ed string representing directory name (no trailing slash) */
++char *dirnamex(char *pathname);
++/* In-place modify a string to remove trailing slashes. Returns arg. */
++char *stripslash(char *pathname);
++/* ensure dirname exists, creating it if necessary. */
++int make_valid_path(char *dirname, mode_t mode);
+--- /dev/null
++++ b/pppd/plugins/pptp/orckit_quirks.c
+@@ -0,0 +1,86 @@
++/* orckit_quirks.c ...... fix quirks in orckit adsl modems
++ * mulix <mulix@actcom.co.il>
++ *
++ * $Id: orckit_quirks.c,v 1.3 2002/03/01 01:23:36 quozl Exp $
++ */
++
++#include <string.h>
++#include <sys/types.h>
++#include <netinet/in.h>
++#include "pptp_msg.h"
++#include "pptp_options.h"
++#include "pptp_ctrl.h"
++#include "util.h"
++
++
++
++/* return 0 on success, non zero otherwise */
++int
++orckit_atur3_build_hook(struct pptp_out_call_rqst* packet)
++{
++ unsigned int name_length = 10;
++
++ struct pptp_out_call_rqst fixed_packet = {
++ PPTP_HEADER_CTRL(PPTP_OUT_CALL_RQST),
++ 0, /* hton16(call->callid) */
++ 0, /* hton16(call->sernum) */
++ hton32(PPTP_BPS_MIN), hton32(PPTP_BPS_MAX),
++ hton32(PPTP_BEARER_DIGITAL), hton32(PPTP_FRAME_ANY),
++ hton16(PPTP_WINDOW), 0, hton16(name_length), 0,
++ {'R','E','L','A','Y','_','P','P','P','1',0}, {0}
++ };
++
++ if (!packet)
++ return -1;
++
++ memcpy(packet, &fixed_packet, sizeof(*packet));
++
++ return 0;
++}
++
++/* return 0 on success, non zero otherwise */
++int
++orckit_atur3_set_link_hook(struct pptp_set_link_info* packet,
++ int peer_call_id)
++{
++ struct pptp_set_link_info fixed_packet = {
++ PPTP_HEADER_CTRL(PPTP_SET_LINK_INFO),
++ hton16(peer_call_id),
++ 0,
++ 0xffffffff,
++ 0xffffffff};
++
++ if (!packet)
++ return -1;
++
++ memcpy(packet, &fixed_packet, sizeof(*packet));
++ return 0;
++}
++
++/* return 0 on success, non 0 otherwise */
++int
++orckit_atur3_start_ctrl_conn_hook(struct pptp_start_ctrl_conn* packet)
++{
++ struct pptp_start_ctrl_conn fixed_packet = {
++ {0}, /* we'll set the header later */
++ hton16(PPTP_VERSION), 0, 0,
++ hton32(PPTP_FRAME_ASYNC), hton32(PPTP_BEARER_ANALOG),
++ hton16(0) /* max channels */,
++ hton16(0x6021),
++ {'R','E','L','A','Y','_','P','P','P','1',0}, /* hostname */
++ {'M','S',' ','W','i','n',' ','N','T',0} /* vendor */
++ };
++
++ if (!packet)
++ return -1;
++
++ /* grab the header from the original packet, since we dont
++ know if this is a request or a reply */
++ memcpy(&fixed_packet.header, &packet->header, sizeof(struct pptp_header));
++
++ /* and now overwrite the full packet, effectively preserving the header */
++ memcpy(packet, &fixed_packet, sizeof(*packet));
++ return 0;
++}
++
++
+--- /dev/null
++++ b/pppd/plugins/pptp/orckit_quirks.h
+@@ -0,0 +1,27 @@
++/* orckit_quirks.h ...... fix quirks in orckit adsl modems
++ * mulix <mulix@actcom.co.il>
++ *
++ * $Id: orckit_quirks.h,v 1.2 2001/11/23 03:42:51 quozl Exp $
++ */
++
++#ifndef INC_ORCKIT_QUIRKS_H_
++#define INC_ORCKIT_QUIRKS_H_
++
++#include "pptp_options.h"
++#include "pptp_ctrl.h"
++#include "pptp_msg.h"
++
++/* return 0 on success, non zero otherwise */
++int
++orckit_atur3_build_hook(struct pptp_out_call_rqst* packt);
++
++/* return 0 on success, non zero otherwise */
++int
++orckit_atur3_set_link_hook(struct pptp_set_link_info* packet,
++ int peer_call_id);
++
++/* return 0 on success, non zero otherwise */
++int
++orckit_atur3_start_ctrl_conn_hook(struct pptp_start_ctrl_conn* packet);
++
++#endif /* INC_ORCKIT_QUIRKS_H_ */
+--- /dev/null
++++ b/pppd/plugins/pptp/pppd-pptp.8
+@@ -0,0 +1,68 @@
++.\" manual page [] for PPTP plugin for pppd 2.4
++.\" $Id: pppd-pptp.8,v 1.0 2007/10/17 13:27:17 kad Exp $
++.\" SH section heading
++.\" SS subsection heading
++.\" LP paragraph
++.\" IP indented paragraph
++.\" TP hanging label
++.TH PPPD-PPTP 8
++.SH NAME
++pptp.so \- PPTP VPN plugin for
++.BR pppd (8)
++.SH SYNOPSIS
++.B pppd
++[
++.I options
++]
++plugin pptp.so
++.SH DESCRIPTION
++.LP
++The PPTP plugin for pppd performs interaction with pptp kernel module
++and has built-in call manager (client part of PPTP).
++It pasees necessary paremeters from \fIoptions\fR into kernel module
++to configure ppp-pptp channel. If it runs in client mode, then additionally
++call manager starts up. PPTPD daemon automaticaly invokes this plugin
++in server mode and passes necessary options, so additional configuration
++is not needed.
++
++.SH OPTIONS for client mode
++The PPTP plugin introduces one additional pppd option:
++.TP
++.BI "pptp_server " server " (required)"
++Specifies ip address or hostname of pptp server.
++.TP
++.BI "pptp_window " packets " (optional)"
++The amount of sliding window size.
++Set to 0 to turn off sliding window.
++ to 3-10 for low speed connections.
++ to >10 for hi speed connections.
++Default is 50
++.TP
++.BI "pptp_phone " phone " (optional)"
++The phone string that sended to pptp server.
++.SH USAGE
++Sample configuration file:
++.nf
++plugin "pptp.so"
++pptp_server 192.168.0.1
++pptp_window 100
++name myname
++remotename pptp
++noauth
++refuse-eap
++refuse-chap
++refuse-mschap
++nobsdcomp
++nodeflate
++novj
++novjccomp
++require-mppe-128
++lcp-echo-interval 20
++lcp-echo-failure 3
++.fi
++
++.SH SEE ALSO
++.BR pppd (8) " " pptpd (8) " " pptpd.conf (5)
++
++.SH AUTHOR
++xeb xeb@mail.ru
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp.c
+@@ -0,0 +1,323 @@
++/***************************************************************************
++ * Copyright (C) 2006 by Kozlov D. <xeb@mail.ru> *
++ * some cleanup done (C) 2012 by Daniel Golle <dgolle@allnet.de> *
++ * *
++ * 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., *
++ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
++ ***************************************************************************/
++
++#define PPTP_VERSION "1.00"
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <sys/un.h>
++#include <netdb.h>
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <syslog.h>
++#include <unistd.h>
++#include <signal.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <sys/wait.h>
++#include <sys/ioctl.h>
++
++#include "pppd.h"
++#include "fsm.h"
++#include "lcp.h"
++#include "ipcp.h"
++#include "ccp.h"
++#include "pathnames.h"
++
++#include "pptp_callmgr.h"
++#include <net/if.h>
++#include <net/ethernet.h>
++#include <linux/if_pppox.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++
++
++
++extern char** environ;
++
++char pppd_version[] = PPPD_VERSION;
++extern int new_style_driver;
++
++
++char *pptp_server = NULL;
++char *pptp_client = NULL;
++char *pptp_phone = NULL;
++int pptp_window=50;
++int pptp_sock=-1;
++struct in_addr localbind = { INADDR_NONE };
++
++static int callmgr_sock;
++static int pptp_fd;
++int call_ID;
++
++static int open_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window);
++static void launch_callmgr(int call_is,struct in_addr inetaddr, char *phonenr,int window);
++static int get_call_id(int sock, pid_t gre, pid_t pppd, u_int16_t *peer_call_id);
++
++static option_t Options[] =
++{
++ { "pptp_server", o_string, &pptp_server,
++ "PPTP Server" },
++ { "pptp_client", o_string, &pptp_client,
++ "PPTP Client" },
++ { "pptp_sock",o_int, &pptp_sock,
++ "PPTP socket" },
++ { "pptp_phone", o_string, &pptp_phone,
++ "PPTP Phone number" },
++ { "pptp_window",o_int, &pptp_window,
++ "PPTP window" },
++ { NULL }
++};
++
++static int pptp_connect(void);
++static void pptp_disconnect(void);
++
++struct channel pptp_channel = {
++ options: Options,
++ check_options: NULL,
++ connect: &pptp_connect,
++ disconnect: &pptp_disconnect,
++ establish_ppp: &generic_establish_ppp,
++ disestablish_ppp: &generic_disestablish_ppp,
++ close: NULL,
++ cleanup: NULL
++};
++
++static int pptp_start_server(void)
++{
++ pptp_fd=pptp_sock;
++ sprintf(ppp_devnam,"pptp (%s)",pptp_client);
++
++ return pptp_fd;
++}
++static int pptp_start_client(void)
++{
++ socklen_t len;
++ struct sockaddr_pppox src_addr,dst_addr;
++ struct hostent *hostinfo;
++
++ hostinfo=gethostbyname(pptp_server);
++ if (!hostinfo)
++ {
++ error("PPTP: Unknown host %s\n", pptp_server);
++ return -1;
++ }
++ dst_addr.sa_addr.pptp.sin_addr=*(struct in_addr*)hostinfo->h_addr;
++ {
++ int sock;
++ struct sockaddr_in addr;
++ len=sizeof(addr);
++ addr.sin_addr=dst_addr.sa_addr.pptp.sin_addr;
++ addr.sin_family=AF_INET;
++ addr.sin_port=htons(1700);
++ sock=socket(AF_INET,SOCK_DGRAM,0);
++ if (connect(sock,(struct sockaddr*)&addr,sizeof(addr)))
++ {
++ close(sock);
++ error("PPTP: connect failed (%s)\n",strerror(errno));
++ return -1;
++ }
++ getsockname(sock,(struct sockaddr*)&addr,&len);
++ src_addr.sa_addr.pptp.sin_addr=addr.sin_addr;
++ close(sock);
++ }
++
++ src_addr.sa_family=AF_PPPOX;
++ src_addr.sa_protocol=PX_PROTO_PPTP;
++ src_addr.sa_addr.pptp.call_id=0;
++
++ dst_addr.sa_family=AF_PPPOX;
++ dst_addr.sa_protocol=PX_PROTO_PPTP;
++ dst_addr.sa_addr.pptp.call_id=0;
++
++ pptp_fd=socket(AF_PPPOX,SOCK_STREAM,PX_PROTO_PPTP);
++ if (pptp_fd<0)
++ {
++ error("PPTP: failed to create PPTP socket (%s)\n",strerror(errno));
++ return -1;
++ }
++ if (bind(pptp_fd,(struct sockaddr*)&src_addr,sizeof(src_addr)))
++ {
++ close(pptp_fd);
++ error("PPTP: failed to bind PPTP socket (%s)\n",strerror(errno));
++ return -1;
++ }
++ len=sizeof(src_addr);
++ getsockname(pptp_fd,(struct sockaddr*)&src_addr,&len);
++ call_ID=src_addr.sa_addr.pptp.call_id;
++
++ do {
++ /*
++ * Open connection to call manager (Launch call manager if necessary.)
++ */
++ callmgr_sock = open_callmgr(src_addr.sa_addr.pptp.call_id,dst_addr.sa_addr.pptp.sin_addr, pptp_phone, pptp_window);
++ if (callmgr_sock<0)
++ {
++ close(pptp_fd);
++ return -1;
++ }
++ /* Exchange PIDs, get call ID */
++ } while (get_call_id(callmgr_sock, getpid(), getpid(), &dst_addr.sa_addr.pptp.call_id) < 0);
++
++ if (connect(pptp_fd,(struct sockaddr*)&dst_addr,sizeof(dst_addr)))
++ {
++ close(callmgr_sock);
++ close(pptp_fd);
++ error("PPTP: failed to connect PPTP socket (%s)\n",strerror(errno));
++ return -1;
++ }
++
++ sprintf(ppp_devnam,"pptp (%s)",pptp_server);
++
++ return pptp_fd;
++}
++static int pptp_connect(void)
++{
++ if ((!pptp_server && !pptp_client) || (pptp_server && pptp_client))
++ {
++ fatal("PPTP: unknown mode (you must specify pptp_server or pptp_client option)");
++ return -1;
++ }
++
++ if (pptp_server) return pptp_start_client();
++ return pptp_start_server();
++}
++
++static void pptp_disconnect(void)
++{
++ if (pptp_server) close(callmgr_sock);
++ close(pptp_fd);
++}
++
++static int open_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window)
++{
++ /* Try to open unix domain socket to call manager. */
++ struct sockaddr_un where;
++ const int NUM_TRIES = 3;
++ int i, fd;
++ pid_t pid;
++ int status;
++ /* Open socket */
++ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
++ {
++ fatal("Could not create unix domain socket: %s", strerror(errno));
++ }
++ /* Make address */
++ callmgr_name_unixsock(&where, inetaddr, localbind);
++ for (i = 0; i < NUM_TRIES; i++)
++ {
++ if (connect(fd, (struct sockaddr *) &where, sizeof(where)) < 0)
++ {
++ /* couldn't connect. We'll have to launch this guy. */
++
++ unlink (where.sun_path);
++
++ /* fork and launch call manager process */
++ switch (pid = fork())
++ {
++ case -1: /* failure */
++ fatal("fork() to launch call manager failed.");
++ case 0: /* child */
++ {
++ /* close the pty and gre in the call manager */
++ close(fd);
++ close(pptp_fd);
++ launch_callmgr(call_id,inetaddr,phonenr,window);
++ }
++ default: /* parent */
++ waitpid(pid, &status, 0);
++ if (status!= 0)
++ {
++ close(fd);
++ error("Call manager exited with error %d", status);
++ return -1;
++ }
++ break;
++ }
++ sleep(1);
++ }
++ else return fd;
++ }
++ close(fd);
++ error("Could not launch call manager after %d tries.", i);
++ return -1; /* make gcc happy */
++}
++
++/*** call the call manager main ***********************************************/
++static void launch_callmgr(int call_id,struct in_addr inetaddr, char *phonenr,int window)
++{
++ dbglog("pptp: call manager for %s\n", inet_ntoa(inetaddr));
++ dbglog("window size:\t%d\n",window);
++ if (phonenr) dbglog("phone number:\t'%s'\n",phonenr);
++ dbglog("call id:\t%d\n",call_id);
++ exit(callmgr_main(inetaddr, phonenr, window, call_id));
++}
++
++/*** exchange data with the call manager *************************************/
++/* XXX need better error checking XXX */
++static int get_call_id(int sock, pid_t gre, pid_t pppd,
++ u_int16_t *peer_call_id)
++{
++ u_int16_t m_call_id, m_peer_call_id;
++ /* write pid's to socket */
++ /* don't bother with network byte order, because pid's are meaningless
++ * outside the local host.
++ */
++ int rc;
++ rc = write(sock, &gre, sizeof(gre));
++ if (rc != sizeof(gre))
++ return -1;
++ rc = write(sock, &pppd, sizeof(pppd));
++ if (rc != sizeof(pppd))
++ return -1;
++ rc = read(sock, &m_call_id, sizeof(m_call_id));
++ if (rc != sizeof(m_call_id))
++ return -1;
++ rc = read(sock, &m_peer_call_id, sizeof(m_peer_call_id));
++ if (rc != sizeof(m_peer_call_id))
++ return -1;
++ /*
++ * XXX FIXME ... DO ERROR CHECKING & TIME-OUTS XXX
++ * (Rhialto: I am assuming for now that timeouts are not relevant
++ * here, because the read and write calls would return -1 (fail) when
++ * the peer goes away during the process. We know it is (or was)
++ * running because the connect() call succeeded.)
++ * (James: on the other hand, if the route to the peer goes away, we
++ * wouldn't get told by read() or write() for quite some time.)
++ */
++ *peer_call_id = m_peer_call_id;
++ return 0;
++}
++
++void plugin_init(void)
++{
++ add_options(Options);
++
++ info("PPTP plugin version %s", PPTP_VERSION);
++
++ the_channel = &pptp_channel;
++ modem = 0;
++}
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_callmgr.c
+@@ -0,0 +1,381 @@
++/* pptp_callmgr.c ... Call manager for PPTP connections.
++ * Handles TCP port 1723 protocol.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: pptp_callmgr.c,v 1.20 2005/03/31 07:42:39 quozl Exp $
++ */
++#include <signal.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <sys/un.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++#include <setjmp.h>
++#include <stdio.h>
++#include <errno.h>
++#include "pptp_callmgr.h"
++#include "pptp_ctrl.h"
++#include "pptp_msg.h"
++#include "dirutil.h"
++#include "vector.h"
++#include "util.h"
++#include "pppd.h"
++
++extern struct in_addr localbind; /* from pptp.c */
++extern int call_ID;
++
++int open_inetsock(struct in_addr inetaddr);
++int open_unixsock(struct in_addr inetaddr);
++void close_inetsock(int fd, struct in_addr inetaddr);
++void close_unixsock(int fd, struct in_addr inetaddr);
++
++sigjmp_buf callmgr_env;
++
++void callmgr_sighandler(int sig) {
++ /* TODO: according to signal(2), siglongjmp() is unsafe used here */
++ siglongjmp (callmgr_env, 1);
++}
++
++void callmgr_do_nothing(int sig) {
++ /* do nothing signal handler */
++}
++
++struct local_callinfo {
++ int unix_sock;
++ pid_t pid[2];
++};
++
++struct local_conninfo {
++ VECTOR * call_list;
++ fd_set * call_set;
++};
++
++/* Call callback */
++void call_callback(PPTP_CONN *conn, PPTP_CALL *call, enum call_state state)
++{
++ struct local_callinfo *lci;
++ struct local_conninfo *conninfo;
++ u_int16_t call_id[2];
++ switch(state) {
++ case CALL_OPEN_DONE:
++ /* okey dokey. This means that the call_id and peer_call_id are
++ * now valid, so lets send them on to our friends who requested
++ * this call. */
++ lci = pptp_call_closure_get(conn, call); assert(lci != NULL);
++ pptp_call_get_ids(conn, call, &call_id[0], &call_id[1]);
++ write(lci->unix_sock, &call_id, sizeof(call_id));
++ /* Our duty to the fatherland is now complete. */
++ break;
++ case CALL_OPEN_FAIL:
++ case CALL_CLOSE_RQST:
++ case CALL_CLOSE_DONE:
++ /* don't need to do anything here, except make sure tables
++ * are sync'ed */
++ dbglog("Closing connection (call state)");
++ conninfo = pptp_conn_closure_get(conn);
++ lci = pptp_call_closure_get(conn, call);
++ assert(lci != NULL && conninfo != NULL);
++ if (vector_contains(conninfo->call_list, lci->unix_sock)) {
++ vector_remove(conninfo->call_list, lci->unix_sock);
++ close(lci->unix_sock);
++ FD_CLR(lci->unix_sock, conninfo->call_set);
++ }
++ break;
++ default:
++ dbglog("Unhandled call callback state [%d].", (int) state);
++ break;
++ }
++}
++
++/******************************************************************************
++ * NOTE ABOUT 'VOLATILE':
++ * several variables here get a volatile qualifier to silence warnings
++ * from older (before 3.0) gccs. if the longjmp stuff is removed,
++ * the volatile qualifiers should be removed as well.
++ *****************************************************************************/
++
++/*** Call Manager *************************************************************/
++int callmgr_main(struct in_addr inetaddr, char phonenr[], int window, int pcallid)
++{
++ int inet_sock, unix_sock;
++ fd_set call_set;
++ PPTP_CONN * conn;
++ VECTOR * call_list;
++ int max_fd = 0;
++ volatile int first = 1;
++ int retval;
++ int i;
++ if (pcallid>0) call_ID=pcallid;
++
++ /* Step 1: Open sockets. */
++ if ((inet_sock = open_inetsock(inetaddr)) < 0)
++ fatal("Could not open control connection to %s", inet_ntoa(inetaddr));
++ dbglog("control connection");
++ if ((unix_sock = open_unixsock(inetaddr)) < 0)
++ fatal("Could not open unix socket for %s", inet_ntoa(inetaddr));
++ /* Step 1b: FORK and return status to calling process. */
++ dbglog("unix_sock");
++
++ switch (fork()) {
++ case 0: /* child. stick around. */
++ break;
++ case -1: /* failure. Fatal. */
++ fatal("Could not fork.");
++ default: /* Parent. Return status to caller. */
++ exit(0);
++ }
++ /* re-open stderr as /dev/null to release it */
++ file2fd("/dev/null", "wb", STDERR_FILENO);
++ /* Step 1c: Clean up unix socket on TERM */
++ if (sigsetjmp(callmgr_env, 1) != 0)
++ goto cleanup;
++ signal(SIGINT, callmgr_sighandler);
++ signal(SIGTERM, callmgr_sighandler);
++ signal(SIGPIPE, callmgr_do_nothing);
++ signal(SIGUSR1, callmgr_do_nothing); /* signal state change
++ wake up accept */
++ /* Step 2: Open control connection and register callback */
++ if ((conn = pptp_conn_open(inet_sock, 1, NULL/* callback */)) == NULL) {
++ close(unix_sock); close(inet_sock); fatal("Could not open connection.");
++ }
++ FD_ZERO(&call_set);
++ call_list = vector_create();
++ {
++ struct local_conninfo *conninfo = malloc(sizeof(*conninfo));
++ if (conninfo == NULL) {
++ close(unix_sock); close(inet_sock); fatal("No memory.");
++ }
++ conninfo->call_list = call_list;
++ conninfo->call_set = &call_set;
++ pptp_conn_closure_put(conn, conninfo);
++ }
++ if (sigsetjmp(callmgr_env, 1) != 0) goto shutdown;
++ /* Step 3: Get FD_SETs */
++ max_fd = unix_sock;
++ do {
++ int rc;
++ fd_set read_set = call_set, write_set;
++ FD_ZERO (&write_set);
++ if (pptp_conn_established(conn)) {
++ FD_SET (unix_sock, &read_set);
++ if (unix_sock > max_fd) max_fd = unix_sock;
++ }
++ pptp_fd_set(conn, &read_set, &write_set, &max_fd);
++ for (; max_fd > 0 ; max_fd--) {
++ if (FD_ISSET (max_fd, &read_set) ||
++ FD_ISSET (max_fd, &write_set))
++ break;
++ }
++ /* Step 4: Wait on INET or UNIX event */
++ if ((rc = select(max_fd + 1, &read_set, &write_set, NULL, NULL)) <0) {
++ if (errno == EBADF) break;
++ /* a signal or somesuch. */
++ continue;
++ }
++ /* Step 5a: Handle INET events */
++ rc = pptp_dispatch(conn, &read_set, &write_set);
++ if (rc < 0)
++ break;
++ /* Step 5b: Handle new connection to UNIX socket */
++ if (FD_ISSET(unix_sock, &read_set)) {
++ /* New call! */
++ struct sockaddr_un from;
++ int len = sizeof(from);
++ PPTP_CALL * call;
++ struct local_callinfo *lci;
++ int s;
++ /* Accept the socket */
++ FD_CLR (unix_sock, &read_set);
++ if ((s = accept(unix_sock, (struct sockaddr *) &from, &len)) < 0) {
++ warn("Socket not accepted: %s", strerror(errno));
++ goto skip_accept;
++ }
++ /* Allocate memory for local call information structure. */
++ if ((lci = malloc(sizeof(*lci))) == NULL) {
++ warn("Out of memory."); close(s); goto skip_accept;
++ }
++ lci->unix_sock = s;
++ /* Give the initiator time to write the PIDs while we open
++ * the call */
++ call = pptp_call_open(conn, call_ID,call_callback, phonenr,window);
++ /* Read and store the associated pids */
++ read(s, &lci->pid[0], sizeof(lci->pid[0]));
++ read(s, &lci->pid[1], sizeof(lci->pid[1]));
++ /* associate the local information with the call */
++ pptp_call_closure_put(conn, call, (void *) lci);
++ /* The rest is done on callback. */
++ /* Keep alive; wait for close */
++ retval = vector_insert(call_list, s, call); assert(retval);
++ if (s > max_fd) max_fd = s;
++ FD_SET(s, &call_set);
++ first = 0;
++ }
++skip_accept: /* Step 5c: Handle socket close */
++ for (i = 0; i < max_fd + 1; i++)
++ if (FD_ISSET(i, &read_set)) {
++ /* close it */
++ PPTP_CALL * call;
++ retval = vector_search(call_list, i, &call);
++ if (retval) {
++ struct local_callinfo *lci =
++ pptp_call_closure_get(conn, call);
++ dbglog("Closing connection (unhandled)");
++ free(lci);
++ /* soft shutdown. Callback will do hard shutdown later */
++ pptp_call_close(conn, call);
++ vector_remove(call_list, i);
++ }
++ FD_CLR(i, &call_set);
++ close(i);
++ }
++ } while (vector_size(call_list) > 0 || first);
++shutdown:
++ {
++ int rc;
++ fd_set read_set, write_set;
++ struct timeval tv;
++ signal(SIGINT, callmgr_do_nothing);
++ signal(SIGTERM, callmgr_do_nothing);
++ /* warn("Shutdown"); */
++ /* kill all open calls */
++ for (i = 0; i < vector_size(call_list); i++) {
++ PPTP_CALL *call = vector_get_Nth(call_list, i);
++ dbglog("Closing connection (shutdown)");
++ pptp_call_close(conn, call);
++ }
++ /* attempt to dispatch these messages */
++ FD_ZERO(&read_set);
++ FD_ZERO(&write_set);
++ pptp_fd_set(conn, &read_set, &write_set, &max_fd);
++ tv.tv_sec = 0;
++ tv.tv_usec = 0;
++ select(max_fd + 1, &read_set, &write_set, NULL, &tv);
++ rc = pptp_dispatch(conn, &read_set, &write_set);
++ if (rc > 0) {
++ /* wait for a respond, a timeout because there might not be one */
++ FD_ZERO(&read_set);
++ FD_ZERO(&write_set);
++ pptp_fd_set(conn, &read_set, &write_set, &max_fd);
++ tv.tv_sec = 2;
++ tv.tv_usec = 0;
++ select(max_fd + 1, &read_set, &write_set, NULL, &tv);
++ rc = pptp_dispatch(conn, &read_set, &write_set);
++ if (rc > 0) {
++ if (i > 0) sleep(2);
++ /* no more open calls. Close the connection. */
++ pptp_conn_close(conn, PPTP_STOP_LOCAL_SHUTDOWN);
++ /* wait for a respond, a timeout because there might not be one */
++ FD_ZERO(&read_set);
++ FD_ZERO(&write_set);
++ pptp_fd_set(conn, &read_set, &write_set, &max_fd);
++ tv.tv_sec = 2;
++ tv.tv_usec = 0;
++ select(max_fd + 1, &read_set, &write_set, NULL, &tv);
++ pptp_dispatch(conn, &read_set, &write_set);
++ if (rc > 0) sleep(2);
++ }
++ }
++ /* with extreme prejudice */
++ pptp_conn_destroy(conn);
++ vector_destroy(call_list);
++ }
++cleanup:
++ signal(SIGINT, callmgr_do_nothing);
++ signal(SIGTERM, callmgr_do_nothing);
++ close_inetsock(inet_sock, inetaddr);
++ close_unixsock(unix_sock, inetaddr);
++ return 0;
++}
++
++/*** open_inetsock ************************************************************/
++int open_inetsock(struct in_addr inetaddr)
++{
++ struct sockaddr_in dest, src;
++ int s;
++ dest.sin_family = AF_INET;
++ dest.sin_port = htons(PPTP_PORT);
++ dest.sin_addr = inetaddr;
++ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ warn("socket: %s", strerror(errno));
++ return s;
++ }
++ if (localbind.s_addr != INADDR_NONE) {
++ bzero(&src, sizeof(src));
++ src.sin_family = AF_INET;
++ src.sin_addr = localbind;
++ if (bind(s, (struct sockaddr *) &src, sizeof(src)) != 0) {
++ warn("bind: %s", strerror(errno));
++ close(s); return -1;
++ }
++ }
++ if (connect(s, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
++ warn("connect: %s", strerror(errno));
++ close(s); return -1;
++ }
++ return s;
++}
++
++/*** open_unixsock ************************************************************/
++int open_unixsock(struct in_addr inetaddr)
++{
++ struct sockaddr_un where;
++ struct stat st;
++ char *dir;
++ int s;
++ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
++ warn("socket: %s", strerror(errno));
++ return s;
++ }
++ callmgr_name_unixsock( &where, inetaddr, localbind);
++ if (stat(where.sun_path, &st) >= 0)
++ {
++ warn("Call manager for %s is already running.", inet_ntoa(inetaddr));
++ close(s); return -1;
++ }
++ /* Make sure path is valid. */
++ dir = dirnamex(where.sun_path);
++ if (!make_valid_path(dir, 0770))
++ fatal("Could not make path to %s: %s", where.sun_path, strerror(errno));
++ free(dir);
++ if (bind(s, (struct sockaddr *) &where, sizeof(where)) < 0) {
++ warn("bind: %s", strerror(errno));
++ close(s); return -1;
++ }
++ chmod(where.sun_path, 0777);
++ listen(s, 127);
++ return s;
++}
++
++/*** close_inetsock ***********************************************************/
++void close_inetsock(int fd, struct in_addr inetaddr)
++{
++ close(fd);
++}
++
++/*** close_unixsock ***********************************************************/
++void close_unixsock(int fd, struct in_addr inetaddr)
++{
++ struct sockaddr_un where;
++ close(fd);
++ callmgr_name_unixsock(&where, inetaddr, localbind);
++ unlink(where.sun_path);
++}
++
++/*** make a unix socket address ***********************************************/
++void callmgr_name_unixsock(struct sockaddr_un *where,
++ struct in_addr inetaddr,
++ struct in_addr localbind)
++{
++ char localaddr[16], remoteaddr[16];
++ where->sun_family = AF_UNIX;
++ strncpy(localaddr, inet_ntoa(localbind), 16);
++ strncpy(remoteaddr, inet_ntoa(inetaddr), 16);
++ snprintf(where->sun_path, sizeof(where->sun_path),
++ PPTP_SOCKET_PREFIX "%s:%i", remoteaddr,call_ID);
++}
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_callmgr.h
+@@ -0,0 +1,17 @@
++/* pptp_callmgr.h ... Call manager for PPTP connections.
++ * Handles TCP port 1723 protocol.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: pptp_callmgr.h,v 1.3 2003/02/17 00:22:17 quozl Exp $
++ */
++
++#define PPTP_SOCKET_PREFIX "/var/run/pptp/"
++
++int callmgr_main(struct in_addr inetaddr,
++ char phonenr[],
++ int window,
++ int pcallid);
++
++void callmgr_name_unixsock(struct sockaddr_un *where,
++ struct in_addr inetaddr,
++ struct in_addr localbind);
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_ctrl.c
+@@ -0,0 +1,1077 @@
++/* pptp_ctrl.c ... handle PPTP control connection.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: pptp_ctrl.c,v 1.31 2005/03/31 07:42:39 quozl Exp $
++ */
++
++#include <errno.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <assert.h>
++#include <signal.h>
++#include <string.h>
++#include <ctype.h>
++#include <fcntl.h>
++#include "pptp_msg.h"
++#include "pptp_ctrl.h"
++#include "pptp_options.h"
++#include "vector.h"
++#include "util.h"
++#include "pptp_quirks.h"
++
++/* BECAUSE OF SIGNAL LIMITATIONS, EACH PROCESS CAN ONLY MANAGE ONE
++ * CONNECTION. SO THIS 'PPTP_CONN' STRUCTURE IS A BIT MISLEADING.
++ * WE'LL KEEP CONNECTION-SPECIFIC INFORMATION IN THERE ANYWAY (AS
++ * OPPOSED TO USING GLOBAL VARIABLES), BUT BEWARE THAT THE ENTIRE
++ * UNIX SIGNAL-HANDLING SEMANTICS WOULD HAVE TO CHANGE (OR THE
++ * TIME-OUT CODE DRASTICALLY REWRITTEN) BEFORE YOU COULD DO A
++ * PPTP_CONN_OPEN MORE THAN ONCE PER PROCESS AND GET AWAY WITH IT.
++ */
++
++/* This structure contains connection-specific information that the
++ * signal handler needs to see. Thus, it needs to be in a global
++ * variable. If you end up using pthreads or something (why not
++ * just processes?), this would have to be placed in a thread-specific
++ * data area, using pthread_get|set_specific, etc., so I've
++ * conveniently encapsulated it for you.
++ * [linux threads will have to support thread-specific signals
++ * before this would work at all, which, as of this writing
++ * (linux-threads v0.6, linux kernel 2.1.72), it does not.]
++ */
++
++/* Globals */
++
++/* control the number of times echo packets will be logged */
++static int nlogecho = 10;
++
++static struct thread_specific {
++ struct sigaction old_sigaction; /* evil signals */
++ PPTP_CONN * conn;
++} global;
++
++#define INITIAL_BUFSIZE 512 /* initial i/o buffer size. */
++
++struct PPTP_CONN {
++ int inet_sock;
++ /* Connection States */
++ enum {
++ CONN_IDLE, CONN_WAIT_CTL_REPLY, CONN_WAIT_STOP_REPLY, CONN_ESTABLISHED
++ } conn_state; /* on startup: CONN_IDLE */
++ /* Keep-alive states */
++ enum {
++ KA_NONE, KA_OUTSTANDING
++ } ka_state; /* on startup: KA_NONE */
++ /* Keep-alive ID; monotonically increasing (watch wrap-around!) */
++ u_int32_t ka_id; /* on startup: 1 */
++ /* Other properties. */
++ u_int16_t version;
++ u_int16_t firmware_rev;
++ u_int8_t hostname[64], vendor[64];
++ /* XXX these are only PNS properties, currently XXX */
++ /* Call assignment information. */
++ u_int16_t call_serial_number;
++ VECTOR *call;
++ void * closure;
++ pptp_conn_cb callback;
++ /******* IO buffers ******/
++ char * read_buffer, *write_buffer;
++ size_t read_alloc, write_alloc;
++ size_t read_size, write_size;
++};
++
++struct PPTP_CALL {
++ /* Call properties */
++ enum {
++ PPTP_CALL_PAC, PPTP_CALL_PNS
++ } call_type;
++ union {
++ enum pptp_pac_state {
++ PAC_IDLE, PAC_WAIT_REPLY, PAC_ESTABLISHED, PAC_WAIT_CS_ANS
++ } pac;
++ enum pptp_pns_state {
++ PNS_IDLE, PNS_WAIT_REPLY, PNS_ESTABLISHED, PNS_WAIT_DISCONNECT
++ } pns;
++ } state;
++ u_int16_t call_id, peer_call_id;
++ u_int16_t sernum;
++ u_int32_t speed;
++ /* For user data: */
++ pptp_call_cb callback;
++ void * closure;
++};
++
++
++/* PPTP error codes: ----------------------------------------------*/
++
++/* (General Error Codes) */
++static const struct {
++ const char *name, *desc;
++} pptp_general_errors[] = {
++#define PPTP_GENERAL_ERROR_NONE 0
++ { "(None)", "No general error" },
++#define PPTP_GENERAL_ERROR_NOT_CONNECTED 1
++ { "(Not-Connected)", "No control connection exists yet for this "
++ "PAC-PNS pair" },
++#define PPTP_GENERAL_ERROR_BAD_FORMAT 2
++ { "(Bad-Format)", "Length is wrong or Magic Cookie value is incorrect" },
++#define PPTP_GENERAL_ERROR_BAD_VALUE 3
++ { "(Bad-Value)", "One of the field values was out of range or "
++ "reserved field was non-zero" },
++#define PPTP_GENERAL_ERROR_NO_RESOURCE 4
++ { "(No-Resource)", "Insufficient resources to handle this command now" },
++#define PPTP_GENERAL_ERROR_BAD_CALLID 5
++ { "(Bad-Call ID)", "The Call ID is invalid in this context" },
++#define PPTP_GENERAL_ERROR_PAC_ERROR 6
++ { "(PAC-Error)", "A generic vendor-specific error occured in the PAC" }
++};
++
++#define MAX_GENERAL_ERROR ( sizeof(pptp_general_errors) / \
++ sizeof(pptp_general_errors[0]) - 1)
++
++/* Outgoing Call Reply Result Codes */
++static const char *pptp_out_call_reply_result[] = {
++/* 0 */ "Unknown Result Code",
++/* 1 */ "Connected",
++/* 2 */ "General Error",
++/* 3 */ "No Carrier Detected",
++/* 4 */ "Busy Signal",
++/* 5 */ "No Dial Tone",
++/* 6 */ "Time Out",
++/* 7 */ "Not Accepted, Call is administratively prohibited" };
++
++#define MAX_OUT_CALL_REPLY_RESULT 7
++
++/* Call Disconnect Notify Result Codes */
++static const char *pptp_call_disc_ntfy[] = {
++/* 0 */ "Unknown Result Code",
++/* 1 */ "Lost Carrier",
++/* 2 */ "General Error",
++/* 3 */ "Administrative Shutdown",
++/* 4 */ "(your) Request" };
++
++#define MAX_CALL_DISC_NTFY 4
++
++/* Call Disconnect Notify Result Codes */
++static const char *pptp_start_ctrl_conn_rply[] = {
++/* 0 */ "Unknown Result Code",
++/* 1 */ "Successful Channel Establishment",
++/* 2 */ "General Error",
++/* 3 */ "Command Channel Already Exists",
++/* 4 */ "Requester is not Authorized" };
++
++#define MAX_START_CTRL_CONN_REPLY 4
++
++/* timing options */
++int idle_wait = PPTP_TIMEOUT;
++int max_echo_wait = PPTP_TIMEOUT;
++
++/* Local prototypes */
++static void pptp_reset_timer(void);
++static void pptp_handle_timer();
++/* Write/read as much as we can without blocking. */
++int pptp_write_some(PPTP_CONN * conn);
++int pptp_read_some(PPTP_CONN * conn);
++/* Make valid packets from read_buffer */
++int pptp_make_packet(PPTP_CONN * conn, void **buf, size_t *size);
++/* Add packet to write_buffer */
++int pptp_send_ctrl_packet(PPTP_CONN * conn, void * buffer, size_t size);
++/* Dispatch packets (general) */
++int pptp_dispatch_packet(PPTP_CONN * conn, void * buffer, size_t size);
++/* Dispatch packets (control messages) */
++int ctrlp_disp(PPTP_CONN * conn, void * buffer, size_t size);
++/* Set link info, for pptp servers that need it.
++ this is a noop, unless the user specified a quirk and
++ there's a set_link hook defined in the quirks table
++ for that quirk */
++void pptp_set_link(PPTP_CONN * conn, int peer_call_id);
++
++/*** log error information in control packets *********************************/
++static void ctrlp_error( int result, int error, int cause,
++ const char *result_text[], int max_result)
++{
++ if( cause >= 0)
++ warn("Result code is %d '%s'. Error code is %d, Cause code is %d",
++ result, result_text[result <= max_result ? result : 0], error,
++ cause );
++ else
++ warn("Reply result code is %d '%s'. Error code is %d",
++ result, result_text[result <= max_result ? result : 0], error);
++ if ((error > 0) && (error <= MAX_GENERAL_ERROR)){
++ if( result != PPTP_RESULT_GENERAL_ERROR )
++ warn("Result code is something else then \"general error\", "
++ "so the following error is probably bogus.");
++ warn("Error is '%s', Error message: '%s'",
++ pptp_general_errors[error].name,
++ pptp_general_errors[error].desc);
++ }
++}
++
++static const char *ctrl_msg_types[] = {
++ "invalid control message type",
++/* (Control Connection Management) */
++ "Start-Control-Connection-Request", /* 1 */
++ "Start-Control-Connection-Reply", /* 2 */
++ "Stop-Control-Connection-Request", /* 3 */
++ "Stop-Control-Connection-Reply", /* 4 */
++ "Echo-Request", /* 5 */
++ "Echo-Reply", /* 6 */
++/* (Call Management) */
++ "Outgoing-Call-Request", /* 7 */
++ "Outgoing-Call-Reply", /* 8 */
++ "Incoming-Call-Request", /* 9 */
++ "Incoming-Call-Reply", /* 10 */
++ "Incoming-Call-Connected", /* 11 */
++ "Call-Clear-Request", /* 12 */
++ "Call-Disconnect-Notify", /* 13 */
++/* (Error Reporting) */
++ "WAN-Error-Notify", /* 14 */
++/* (PPP Session Control) */
++ "Set-Link-Info" /* 15 */
++};
++#define MAX_CTRLMSG_TYPE 15
++
++/*** report a sent packet ****************************************************/
++static void ctrlp_rep( void * buffer, int size, int isbuff)
++{
++ struct pptp_header *packet = buffer;
++ unsigned int type;
++ if(size < sizeof(struct pptp_header)) return;
++ type = ntoh16(packet->ctrl_type);
++ /* FIXME: do not report sending echo requests as long as they are
++ * sent in a signal handler. This may dead lock as the syslog call
++ * is not reentrant */
++ if( type == PPTP_ECHO_RQST ) return;
++ /* don't keep reporting sending of echo's */
++ if( (type == PPTP_ECHO_RQST || type == PPTP_ECHO_RPLY) && nlogecho <= 0 ) return;
++ dbglog("%s control packet type is %d '%s'\n",isbuff ? "Buffered" : "Sent",
++ type, ctrl_msg_types[type <= MAX_CTRLMSG_TYPE ? type : 0]);
++
++}
++
++
++
++/* Open new pptp_connection. Returns NULL on failure. */
++PPTP_CONN * pptp_conn_open(int inet_sock, int isclient, pptp_conn_cb callback)
++{
++ PPTP_CONN *conn;
++ /* Allocate structure */
++ if ((conn = malloc(sizeof(*conn))) == NULL) return NULL;
++ if ((conn->call = vector_create()) == NULL) { free(conn); return NULL; }
++ /* Initialize */
++ conn->inet_sock = inet_sock;
++ conn->conn_state = CONN_IDLE;
++ conn->ka_state = KA_NONE;
++ conn->ka_id = 1;
++ conn->call_serial_number = 0;
++ conn->callback = callback;
++ /* Create I/O buffers */
++ conn->read_size = conn->write_size = 0;
++ conn->read_alloc = conn->write_alloc = INITIAL_BUFSIZE;
++ conn->read_buffer =
++ malloc(sizeof(*(conn->read_buffer)) * conn->read_alloc);
++ conn->write_buffer =
++ malloc(sizeof(*(conn->write_buffer)) * conn->write_alloc);
++ if (conn->read_buffer == NULL || conn->write_buffer == NULL) {
++ if (conn->read_buffer != NULL) free(conn->read_buffer);
++ if (conn->write_buffer != NULL) free(conn->write_buffer);
++ vector_destroy(conn->call); free(conn); return NULL;
++ }
++ /* Make this socket non-blocking. */
++ fcntl(conn->inet_sock, F_SETFL, O_NONBLOCK);
++ /* Request connection from server, if this is a client */
++ if (isclient) {
++ struct pptp_start_ctrl_conn packet = {
++ PPTP_HEADER_CTRL(PPTP_START_CTRL_CONN_RQST),
++ hton16(PPTP_VERSION), 0, 0,
++ hton32(PPTP_FRAME_CAP), hton32(PPTP_BEARER_CAP),
++ hton16(PPTP_MAX_CHANNELS), hton16(PPTP_FIRMWARE_VERSION),
++ PPTP_HOSTNAME, PPTP_VENDOR
++ };
++ /* fix this packet, if necessary */
++ int idx, rc;
++ idx = get_quirk_index();
++ if (idx != -1 && pptp_fixups[idx].start_ctrl_conn) {
++ if ((rc = pptp_fixups[idx].start_ctrl_conn(&packet)))
++ warn("calling the start_ctrl_conn hook failed (%d)", rc);
++ }
++ if (pptp_send_ctrl_packet(conn, &packet, sizeof(packet)))
++ conn->conn_state = CONN_WAIT_CTL_REPLY;
++ else
++ return NULL; /* could not send initial start request. */
++ }
++ /* Set up interval/keep-alive timer */
++ /* First, register handler for SIGALRM */
++ sigpipe_create();
++ sigpipe_assign(SIGALRM);
++ global.conn = conn;
++ /* Reset event timer */
++ pptp_reset_timer();
++ /* all done. */
++ return conn;
++}
++
++int pptp_conn_established(PPTP_CONN *conn) {
++ return (conn->conn_state == CONN_ESTABLISHED);
++}
++
++/* This currently *only* works for client call requests.
++ * We need to do something else to allocate calls for incoming requests.
++ */
++PPTP_CALL * pptp_call_open(PPTP_CONN * conn, int call_id,pptp_call_cb callback,
++ char *phonenr,int window)
++{
++ PPTP_CALL * call;
++ int idx, rc;
++ /* Send off the call request */
++ struct pptp_out_call_rqst packet = {
++ PPTP_HEADER_CTRL(PPTP_OUT_CALL_RQST),
++ 0,0, /*call_id, sernum */
++ hton32(PPTP_BPS_MIN), hton32(PPTP_BPS_MAX),
++ hton32(PPTP_BEARER_CAP), hton32(PPTP_FRAME_CAP),
++ hton16(window), 0, 0, 0, {0}, {0}
++ };
++ assert(conn && conn->call);
++ assert(conn->conn_state == CONN_ESTABLISHED);
++ /* Assign call id */
++ if (!call_id && !vector_scan(conn->call, 0, PPTP_MAX_CHANNELS - 1, &call_id))
++ /* no more calls available! */
++ return NULL;
++ /* allocate structure. */
++ if ((call = malloc(sizeof(*call))) == NULL) return NULL;
++ /* Initialize call structure */
++ call->call_type = PPTP_CALL_PNS;
++ call->state.pns = PNS_IDLE;
++ call->call_id = (u_int16_t) call_id;
++ call->sernum = conn->call_serial_number++;
++ call->callback = callback;
++ call->closure = NULL;
++ packet.call_id = htons(call->call_id);
++ packet.call_sernum = htons(call->sernum);
++ /* if we have a quirk, build a new packet to fit it */
++ idx = get_quirk_index();
++ if (idx != -1 && pptp_fixups[idx].out_call_rqst_hook) {
++ if ((rc = pptp_fixups[idx].out_call_rqst_hook(&packet)))
++ warn("calling the out_call_rqst hook failed (%d)", rc);
++ }
++ /* fill in the phone number if it was specified */
++ if (phonenr) {
++ strncpy(packet.phone_num, phonenr, sizeof(packet.phone_num));
++ packet.phone_len = strlen(phonenr);
++ if( packet.phone_len > sizeof(packet.phone_num))
++ packet.phone_len = sizeof(packet.phone_num);
++ packet.phone_len = hton16 (packet.phone_len);
++ }
++ if (pptp_send_ctrl_packet(conn, &packet, sizeof(packet))) {
++ pptp_reset_timer();
++ call->state.pns = PNS_WAIT_REPLY;
++ /* and add it to the call vector */
++ vector_insert(conn->call, call_id, call);
++ return call;
++ } else { /* oops, unsuccessful. Deallocate. */
++ free(call);
++ return NULL;
++ }
++}
++
++/*** pptp_call_close **********************************************************/
++void pptp_call_close(PPTP_CONN * conn, PPTP_CALL * call)
++{
++ struct pptp_call_clear_rqst rqst = {
++ PPTP_HEADER_CTRL(PPTP_CALL_CLEAR_RQST), 0, 0
++ };
++ assert(conn && conn->call); assert(call);
++ assert(vector_contains(conn->call, call->call_id));
++ /* haven't thought about PAC yet */
++ assert(call->call_type == PPTP_CALL_PNS);
++ assert(call->state.pns != PNS_IDLE);
++ rqst.call_id = hton16(call->call_id);
++ /* don't check state against WAIT_DISCONNECT... allow multiple disconnect
++ * requests to be made.
++ */
++ pptp_send_ctrl_packet(conn, &rqst, sizeof(rqst));
++ pptp_reset_timer();
++ call->state.pns = PNS_WAIT_DISCONNECT;
++ /* call structure will be freed when we have confirmation of disconnect. */
++}
++
++/*** hard close ***************************************************************/
++void pptp_call_destroy(PPTP_CONN *conn, PPTP_CALL *call)
++{
++ assert(conn && conn->call); assert(call);
++ assert(vector_contains(conn->call, call->call_id));
++ /* notify */
++ if (call->callback != NULL) call->callback(conn, call, CALL_CLOSE_DONE);
++ /* deallocate */
++ vector_remove(conn->call, call->call_id);
++ free(call);
++}
++
++/*** this is a soft close *****************************************************/
++void pptp_conn_close(PPTP_CONN * conn, u_int8_t close_reason)
++{
++ struct pptp_stop_ctrl_conn rqst = {
++ PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RQST),
++ hton8(close_reason), 0, 0
++ };
++ int i;
++ assert(conn && conn->call);
++ /* avoid repeated close attempts */
++ if (conn->conn_state == CONN_IDLE || conn->conn_state == CONN_WAIT_STOP_REPLY)
++ return;
++ /* close open calls, if any */
++ for (i = 0; i < vector_size(conn->call); i++)
++ pptp_call_close(conn, vector_get_Nth(conn->call, i));
++ /* now close connection */
++ info("Closing PPTP connection");
++ pptp_send_ctrl_packet(conn, &rqst, sizeof(rqst));
++ pptp_reset_timer(); /* wait 60 seconds for reply */
++ conn->conn_state = CONN_WAIT_STOP_REPLY;
++ return;
++}
++
++/*** this is a hard close *****************************************************/
++void pptp_conn_destroy(PPTP_CONN * conn)
++{
++ int i;
++ assert(conn != NULL); assert(conn->call != NULL);
++ /* destroy all open calls */
++ for (i = 0; i < vector_size(conn->call); i++)
++ pptp_call_destroy(conn, vector_get_Nth(conn->call, i));
++ /* notify */
++ if (conn->callback != NULL) conn->callback(conn, CONN_CLOSE_DONE);
++ sigpipe_close();
++ close(conn->inet_sock);
++ /* deallocate */
++ vector_destroy(conn->call);
++ free(conn);
++}
++
++/*** Deal with messages, in a non-blocking manner
++ * Add file descriptors used by pptp to fd_set.
++ */
++void pptp_fd_set(PPTP_CONN * conn, fd_set * read_set, fd_set * write_set,
++ int * max_fd)
++{
++ assert(conn && conn->call);
++ /* Add fd to write_set if there are outstanding writes. */
++ if (conn->write_size > 0)
++ FD_SET(conn->inet_sock, write_set);
++ /* Always add fd to read_set. (always want something to read) */
++ FD_SET(conn->inet_sock, read_set);
++ if (*max_fd < conn->inet_sock) *max_fd = conn->inet_sock;
++ /* Add signal pipe file descriptor to set */
++ int sig_fd = sigpipe_fd();
++ FD_SET(sig_fd, read_set);
++ if (*max_fd < sig_fd) *max_fd = sig_fd;
++}
++
++/*** handle any pptp file descriptors set in fd_set, and clear them ***********/
++int pptp_dispatch(PPTP_CONN * conn, fd_set * read_set, fd_set * write_set)
++{
++ int r = 0;
++ assert(conn && conn->call);
++ /* Check for signals */
++ if (FD_ISSET(sigpipe_fd(), read_set)) {
++ if (sigpipe_read() == SIGALRM) pptp_handle_timer();
++ FD_CLR(sigpipe_fd(), read_set);
++ }
++ /* Check write_set could be set. */
++ if (FD_ISSET(conn->inet_sock, write_set)) {
++ FD_CLR(conn->inet_sock, write_set);
++ if (conn->write_size > 0)
++ r = pptp_write_some(conn);/* write as much as we can without blocking */
++ }
++ /* Check read_set */
++ if (r >= 0 && FD_ISSET(conn->inet_sock, read_set)) {
++ void *buffer; size_t size;
++ FD_CLR(conn->inet_sock, read_set);
++ r = pptp_read_some(conn); /* read as much as we can without blocking */
++ if (r < 0)
++ return r;
++ /* make packets of the buffer, while we can. */
++ while (r >= 0 && pptp_make_packet(conn, &buffer, &size)) {
++ r = pptp_dispatch_packet(conn, buffer, size);
++ free(buffer);
++ }
++ }
++ /* That's all, folks. Simple, eh? */
++ return r;
++}
++
++/*** Non-blocking write *******************************************************/
++int pptp_write_some(PPTP_CONN * conn) {
++ ssize_t retval;
++ assert(conn && conn->call);
++ retval = write(conn->inet_sock, conn->write_buffer, conn->write_size);
++ if (retval < 0) { /* error. */
++ if (errno == EAGAIN || errno == EINTR) {
++ return 0;
++ } else { /* a real error */
++ warn("write error: %s", strerror(errno));
++ return -1;
++ }
++ }
++ assert(retval <= conn->write_size);
++ conn->write_size -= retval;
++ memmove(conn->write_buffer, conn->write_buffer + retval, conn->write_size);
++ ctrlp_rep(conn->write_buffer, retval, 0);
++ return 0;
++}
++
++/*** Non-blocking read ********************************************************/
++int pptp_read_some(PPTP_CONN * conn)
++{
++ ssize_t retval;
++ assert(conn && conn->call);
++ if (conn->read_size == conn->read_alloc) { /* need to alloc more memory */
++ char *new_buffer = realloc(conn->read_buffer,
++ sizeof(*(conn->read_buffer)) * conn->read_alloc * 2);
++ if (new_buffer == NULL) {
++ warn("Out of memory"); return -1;
++ }
++ conn->read_alloc *= 2;
++ conn->read_buffer = new_buffer;
++ }
++ retval = read(conn->inet_sock, conn->read_buffer + conn->read_size,
++ conn->read_alloc - conn->read_size);
++ if (retval == 0) {
++ warn("read returned zero, peer has closed");
++ return -1;
++ }
++ if (retval < 0) {
++ if (errno == EINTR || errno == EAGAIN)
++ return 0;
++ else { /* a real error */
++ warn("read error: %s", strerror(errno));
++ return -1;
++ }
++ }
++ conn->read_size += retval;
++ assert(conn->read_size <= conn->read_alloc);
++ return 0;
++}
++
++/*** Packet formation *********************************************************/
++int pptp_make_packet(PPTP_CONN * conn, void **buf, size_t *size)
++{
++ struct pptp_header *header;
++ size_t bad_bytes = 0;
++ assert(conn && conn->call); assert(buf != NULL); assert(size != NULL);
++ /* Give up unless there are at least sizeof(pptp_header) bytes */
++ while ((conn->read_size-bad_bytes) >= sizeof(struct pptp_header)) {
++ /* Throw out bytes until we have a valid header. */
++ header = (struct pptp_header *) (conn->read_buffer + bad_bytes);
++ if (ntoh32(header->magic) != PPTP_MAGIC) goto throwitout;
++ if (ntoh16(header->reserved0) != 0)
++ warn("reserved0 field is not zero! (0x%x) Cisco feature? \n",
++ ntoh16(header->reserved0));
++ if (ntoh16(header->length) < sizeof(struct pptp_header)) goto throwitout;
++ if (ntoh16(header->length) > PPTP_CTRL_SIZE_MAX) goto throwitout;
++ /* well. I guess it's good. Let's see if we've got it all. */
++ if (ntoh16(header->length) > (conn->read_size-bad_bytes))
++ /* nope. Let's wait until we've got it, then. */
++ goto flushbadbytes;
++ /* One last check: */
++ if ((ntoh16(header->pptp_type) == PPTP_MESSAGE_CONTROL) &&
++ (ntoh16(header->length) !=
++ PPTP_CTRL_SIZE(ntoh16(header->ctrl_type))))
++ goto throwitout;
++ /* well, I guess we've got it. */
++ *size = ntoh16(header->length);
++ *buf = malloc(*size);
++ if (*buf == NULL) { warn("Out of memory."); return 0; /* ack! */ }
++ memcpy(*buf, conn->read_buffer + bad_bytes, *size);
++ /* Delete this packet from the read_buffer. */
++ conn->read_size -= (bad_bytes + *size);
++ memmove(conn->read_buffer, conn->read_buffer + bad_bytes + *size,
++ conn->read_size);
++ if (bad_bytes > 0)
++ warn("%lu bad bytes thrown away.", (unsigned long) bad_bytes);
++ return 1;
++throwitout:
++ bad_bytes++;
++ }
++flushbadbytes:
++ /* no more packets. Let's get rid of those bad bytes */
++ conn->read_size -= bad_bytes;
++ memmove(conn->read_buffer, conn->read_buffer + bad_bytes, conn->read_size);
++ if (bad_bytes > 0)
++ warn("%lu bad bytes thrown away.", (unsigned long) bad_bytes);
++ return 0;
++}
++
++/*** pptp_send_ctrl_packet ****************************************************/
++int pptp_send_ctrl_packet(PPTP_CONN * conn, void * buffer, size_t size)
++{
++ assert(conn && conn->call); assert(buffer);
++ if( conn->write_size > 0) pptp_write_some( conn);
++ if( conn->write_size == 0) {
++ ssize_t retval;
++ retval = write(conn->inet_sock, buffer, size);
++ if (retval < 0) { /* error. */
++ if (errno == EAGAIN || errno == EINTR) {
++ /* ignore */;
++ retval = 0;
++ } else { /* a real error */
++ warn("write error: %s", strerror(errno));
++ pptp_conn_destroy(conn); /* shut down fast. */
++ return 0;
++ }
++ }
++ ctrlp_rep( buffer, retval, 0);
++ size -= retval;
++ if( size <= 0) return 1;
++ }
++ /* Shove anything not written into the write buffer */
++ if (conn->write_size + size > conn->write_alloc) { /* need more memory */
++ char *new_buffer = realloc(conn->write_buffer,
++ sizeof(*(conn->write_buffer)) * conn->write_alloc * 2);
++ if (new_buffer == NULL) {
++ warn("Out of memory"); return 0;
++ }
++ conn->write_alloc *= 2;
++ conn->write_buffer = new_buffer;
++ }
++ memcpy(conn->write_buffer + conn->write_size, buffer, size);
++ conn->write_size += size;
++ ctrlp_rep( buffer,size,1);
++ return 1;
++}
++
++/*** Packet Dispatch **********************************************************/
++int pptp_dispatch_packet(PPTP_CONN * conn, void * buffer, size_t size)
++{
++ int r = 0;
++ struct pptp_header *header = (struct pptp_header *)buffer;
++ assert(conn && conn->call); assert(buffer);
++ assert(ntoh32(header->magic) == PPTP_MAGIC);
++ assert(ntoh16(header->length) == size);
++ switch (ntoh16(header->pptp_type)) {
++ case PPTP_MESSAGE_CONTROL:
++ r = ctrlp_disp(conn, buffer, size);
++ break;
++ case PPTP_MESSAGE_MANAGE:
++ /* MANAGEMENT messages aren't even part of the spec right now. */
++ dbglog("PPTP management message received, but not understood.");
++ break;
++ default:
++ dbglog("Unknown PPTP control message type received: %u",
++ (unsigned int) ntoh16(header->pptp_type));
++ break;
++ }
++ return r;
++}
++
++/*** log echo request/replies *************************************************/
++static void logecho( int type)
++{
++ /* hack to stop flooding the log files (the most interesting part is right
++ * after the connection built-up) */
++ if( nlogecho > 0) {
++ dbglog("Echo Re%s received.", type == PPTP_ECHO_RQST ? "quest" :"ply");
++ if( --nlogecho == 0)
++ dbglog("no more Echo Reply/Request packets will be reported.");
++ }
++}
++
++/*** pptp_dispatch_ctrl_packet ************************************************/
++int ctrlp_disp(PPTP_CONN * conn, void * buffer, size_t size)
++{
++ struct pptp_header *header = (struct pptp_header *)buffer;
++ u_int8_t close_reason = PPTP_STOP_NONE;
++ assert(conn && conn->call); assert(buffer);
++ assert(ntoh32(header->magic) == PPTP_MAGIC);
++ assert(ntoh16(header->length) == size);
++ assert(ntoh16(header->pptp_type) == PPTP_MESSAGE_CONTROL);
++ if (size < PPTP_CTRL_SIZE(ntoh16(header->ctrl_type))) {
++ warn("Invalid packet received [type: %d; length: %d].",
++ (int) ntoh16(header->ctrl_type), (int) size);
++ return 0;
++ }
++ switch (ntoh16(header->ctrl_type)) {
++ /* ----------- STANDARD Start-Session MESSAGES ------------ */
++ case PPTP_START_CTRL_CONN_RQST:
++ {
++ struct pptp_start_ctrl_conn *packet =
++ (struct pptp_start_ctrl_conn *) buffer;
++ struct pptp_start_ctrl_conn reply = {
++ PPTP_HEADER_CTRL(PPTP_START_CTRL_CONN_RPLY),
++ hton16(PPTP_VERSION), 0, 0,
++ hton32(PPTP_FRAME_CAP), hton32(PPTP_BEARER_CAP),
++ hton16(PPTP_MAX_CHANNELS), hton16(PPTP_FIRMWARE_VERSION),
++ PPTP_HOSTNAME, PPTP_VENDOR };
++ int idx, rc;
++ dbglog("Received Start Control Connection Request");
++ /* fix this packet, if necessary */
++ idx = get_quirk_index();
++ if (idx != -1 && pptp_fixups[idx].start_ctrl_conn) {
++ if ((rc = pptp_fixups[idx].start_ctrl_conn(&reply)))
++ warn("calling the start_ctrl_conn hook failed (%d)", rc);
++ }
++ if (conn->conn_state == CONN_IDLE) {
++ if (ntoh16(packet->version) < PPTP_VERSION) {
++ /* Can't support this (earlier) PPTP_VERSION */
++ reply.version = packet->version;
++ /* protocol version not supported */
++ reply.result_code = hton8(5);
++ pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
++ pptp_reset_timer(); /* give sender a chance for a retry */
++ } else { /* same or greater version */
++ if (pptp_send_ctrl_packet(conn, &reply, sizeof(reply))) {
++ conn->conn_state = CONN_ESTABLISHED;
++ dbglog("server connection ESTABLISHED.");
++ pptp_reset_timer();
++ }
++ }
++ }
++ break;
++ }
++ case PPTP_START_CTRL_CONN_RPLY:
++ {
++ struct pptp_start_ctrl_conn *packet =
++ (struct pptp_start_ctrl_conn *) buffer;
++ dbglog("Received Start Control Connection Reply");
++ if (conn->conn_state == CONN_WAIT_CTL_REPLY) {
++ /* XXX handle collision XXX [see rfc] */
++ if (ntoh16(packet->version) != PPTP_VERSION) {
++ if (conn->callback != NULL)
++ conn->callback(conn, CONN_OPEN_FAIL);
++ close_reason = PPTP_STOP_PROTOCOL;
++ goto pptp_conn_close;
++ }
++ if (ntoh8(packet->result_code) != 1 &&
++ /* J'ai change le if () afin que la connection ne se ferme
++ * pas pour un "rien" :p adel@cybercable.fr -
++ *
++ * Don't close the connection if the result code is zero
++ * (feature found in certain ADSL modems)
++ */
++ ntoh8(packet->result_code) != 0) {
++ dbglog("Negative reply received to our Start Control "
++ "Connection Request");
++ ctrlp_error(packet->result_code, packet->error_code,
++ -1, pptp_start_ctrl_conn_rply,
++ MAX_START_CTRL_CONN_REPLY);
++ if (conn->callback != NULL)
++ conn->callback(conn, CONN_OPEN_FAIL);
++ close_reason = PPTP_STOP_PROTOCOL;
++ goto pptp_conn_close;
++ }
++ conn->conn_state = CONN_ESTABLISHED;
++ /* log session properties */
++ conn->version = ntoh16(packet->version);
++ conn->firmware_rev = ntoh16(packet->firmware_rev);
++ memcpy(conn->hostname, packet->hostname, sizeof(conn->hostname));
++ memcpy(conn->vendor, packet->vendor, sizeof(conn->vendor));
++ pptp_reset_timer(); /* 60 seconds until keep-alive */
++ dbglog("Client connection established.");
++ if (conn->callback != NULL)
++ conn->callback(conn, CONN_OPEN_DONE);
++ } /* else goto pptp_conn_close; */
++ break;
++ }
++ /* ----------- STANDARD Stop-Session MESSAGES ------------ */
++ case PPTP_STOP_CTRL_CONN_RQST:
++ {
++ /* conn_state should be CONN_ESTABLISHED, but it could be
++ * something else */
++ struct pptp_stop_ctrl_conn reply = {
++ PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RPLY),
++ hton8(1), hton8(PPTP_GENERAL_ERROR_NONE), 0
++ };
++ dbglog("Received Stop Control Connection Request.");
++ if (conn->conn_state == CONN_IDLE) break;
++ if (pptp_send_ctrl_packet(conn, &reply, sizeof(reply))) {
++ if (conn->callback != NULL)
++ conn->callback(conn, CONN_CLOSE_RQST);
++ conn->conn_state = CONN_IDLE;
++ return -1;
++ }
++ break;
++ }
++ case PPTP_STOP_CTRL_CONN_RPLY:
++ {
++ dbglog("Received Stop Control Connection Reply.");
++ /* conn_state should be CONN_WAIT_STOP_REPLY, but it
++ * could be something else */
++ if (conn->conn_state == CONN_IDLE) break;
++ conn->conn_state = CONN_IDLE;
++ return -1;
++ }
++ /* ----------- STANDARD Echo/Keepalive MESSAGES ------------ */
++ case PPTP_ECHO_RPLY:
++ {
++ struct pptp_echo_rply *packet =
++ (struct pptp_echo_rply *) buffer;
++ logecho( PPTP_ECHO_RPLY);
++ if ((conn->ka_state == KA_OUTSTANDING) &&
++ (ntoh32(packet->identifier) == conn->ka_id)) {
++ conn->ka_id++;
++ conn->ka_state = KA_NONE;
++ pptp_reset_timer();
++ }
++ break;
++ }
++ case PPTP_ECHO_RQST:
++ {
++ struct pptp_echo_rqst *packet =
++ (struct pptp_echo_rqst *) buffer;
++ struct pptp_echo_rply reply = {
++ PPTP_HEADER_CTRL(PPTP_ECHO_RPLY),
++ packet->identifier, /* skip hton32(ntoh32(id)) */
++ hton8(1), hton8(PPTP_GENERAL_ERROR_NONE), 0
++ };
++ logecho( PPTP_ECHO_RQST);
++ pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
++ pptp_reset_timer();
++ break;
++ }
++ /* ----------- OUTGOING CALL MESSAGES ------------ */
++ case PPTP_OUT_CALL_RQST:
++ {
++ struct pptp_out_call_rqst *packet =
++ (struct pptp_out_call_rqst *)buffer;
++ struct pptp_out_call_rply reply = {
++ PPTP_HEADER_CTRL(PPTP_OUT_CALL_RPLY),
++ 0 /* callid */, packet->call_id, 1, PPTP_GENERAL_ERROR_NONE, 0,
++ hton32(PPTP_CONNECT_SPEED),
++ hton16(PPTP_WINDOW), hton16(PPTP_DELAY), 0
++ };
++ dbglog("Received Outgoing Call Request.");
++ /* XXX PAC: eventually this should make an outgoing call. XXX */
++ reply.result_code = hton8(7); /* outgoing calls verboten */
++ pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
++ break;
++ }
++ case PPTP_OUT_CALL_RPLY:
++ {
++ struct pptp_out_call_rply *packet =
++ (struct pptp_out_call_rply *)buffer;
++ PPTP_CALL * call;
++ u_int16_t callid = ntoh16(packet->call_id_peer);
++ dbglog("Received Outgoing Call Reply.");
++ if (!vector_search(conn->call, (int) callid, &call)) {
++ dbglog("PPTP_OUT_CALL_RPLY received for non-existant call: "
++ "peer call ID (us) %d call ID (them) %d.",
++ callid, ntoh16(packet->call_id));
++ break;
++ }
++ if (call->call_type != PPTP_CALL_PNS) {
++ dbglog("Ack! How did this call_type get here?"); /* XXX? */
++ break;
++ }
++ if (call->state.pns != PNS_WAIT_REPLY) {
++ warn("Unexpected(?) Outgoing Call Reply will be ignored.");
++ break;
++ }
++ /* check for errors */
++ if (packet->result_code != 1) {
++ /* An error. Log it verbosely. */
++ dbglog("Our outgoing call request [callid %d] has not been "
++ "accepted.", (int) callid);
++ ctrlp_error(packet->result_code, packet->error_code,
++ packet->cause_code, pptp_out_call_reply_result,
++ MAX_OUT_CALL_REPLY_RESULT);
++ call->state.pns = PNS_IDLE;
++ if (call->callback != NULL)
++ call->callback(conn, call, CALL_OPEN_FAIL);
++ pptp_call_destroy(conn, call);
++ } else {
++ /* connection established */
++ call->state.pns = PNS_ESTABLISHED;
++ call->peer_call_id = ntoh16(packet->call_id);
++ call->speed = ntoh32(packet->speed);
++ pptp_reset_timer();
++ /* call pptp_set_link. unless the user specified a quirk
++ and this quirk has a set_link hook, this is a noop */
++ pptp_set_link(conn, call->peer_call_id);
++ if (call->callback != NULL)
++ call->callback(conn, call, CALL_OPEN_DONE);
++ dbglog("Outgoing call established (call ID %u, peer's "
++ "call ID %u).\n", call->call_id, call->peer_call_id);
++ }
++ break;
++ }
++ /* ----------- INCOMING CALL MESSAGES ------------ */
++ /* XXX write me XXX */
++ /* ----------- CALL CONTROL MESSAGES ------------ */
++ case PPTP_CALL_CLEAR_RQST:
++ {
++ struct pptp_call_clear_rqst *packet =
++ (struct pptp_call_clear_rqst *)buffer;
++ struct pptp_call_clear_ntfy reply = {
++ PPTP_HEADER_CTRL(PPTP_CALL_CLEAR_NTFY), packet->call_id,
++ 1, PPTP_GENERAL_ERROR_NONE, 0, 0, {0}
++ };
++ dbglog("Received Call Clear Request.");
++ if (vector_contains(conn->call, ntoh16(packet->call_id))) {
++ PPTP_CALL * call;
++ vector_search(conn->call, ntoh16(packet->call_id), &call);
++ if (call->callback != NULL)
++ call->callback(conn, call, CALL_CLOSE_RQST);
++ pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
++ pptp_call_destroy(conn, call);
++ dbglog("Call closed (RQST) (call id %d)", (int) call->call_id);
++ }
++ break;
++ }
++ case PPTP_CALL_CLEAR_NTFY:
++ {
++ struct pptp_call_clear_ntfy *packet =
++ (struct pptp_call_clear_ntfy *)buffer;
++ dbglog("Call disconnect notification received (call id %d)",
++ ntoh16(packet->call_id));
++ if (vector_contains(conn->call, ntoh16(packet->call_id))) {
++ PPTP_CALL * call;
++ ctrlp_error(packet->result_code, packet->error_code,
++ packet->cause_code, pptp_call_disc_ntfy,
++ MAX_CALL_DISC_NTFY);
++ vector_search(conn->call, ntoh16(packet->call_id), &call);
++ pptp_call_destroy(conn, call);
++ }
++ /* XXX we could log call stats here XXX */
++ /* XXX not all servers send this XXX */
++ break;
++ }
++ case PPTP_SET_LINK_INFO:
++ {
++ /* I HAVE NO CLUE WHAT TO DO IF send_accm IS NOT 0! */
++ /* this is really dealt with in the HDLC deencapsulation, anyway. */
++ struct pptp_set_link_info *packet =
++ (struct pptp_set_link_info *)buffer;
++ /* log it. */
++ dbglog("PPTP_SET_LINK_INFO received from peer_callid %u",
++ (unsigned int) ntoh16(packet->call_id_peer));
++ dbglog(" send_accm is %08lX, recv_accm is %08lX",
++ (unsigned long) ntoh32(packet->send_accm),
++ (unsigned long) ntoh32(packet->recv_accm));
++ if (!(ntoh32(packet->send_accm) == 0 &&
++ ntoh32(packet->recv_accm) == 0))
++ warn("Non-zero Async Control Character Maps are not supported!");
++ break;
++ }
++ default:
++ dbglog("Unrecognized Packet %d received.",
++ (int) ntoh16(((struct pptp_header *)buffer)->ctrl_type));
++ /* goto pptp_conn_close; */
++ break;
++ }
++ return 0;
++pptp_conn_close:
++ warn("pptp_conn_close(%d)", (int) close_reason);
++ pptp_conn_close(conn, close_reason);
++ return 0;
++}
++
++/*** pptp_set_link **************************************************************/
++void pptp_set_link(PPTP_CONN* conn, int peer_call_id)
++{
++ int idx, rc;
++ /* if we need to send a set_link packet because of buggy
++ hardware or pptp server, do it now */
++ if ((idx = get_quirk_index()) != -1 && pptp_fixups[idx].set_link_hook) {
++ struct pptp_set_link_info packet;
++ if ((rc = pptp_fixups[idx].set_link_hook(&packet, peer_call_id)))
++ warn("calling the set_link hook failed (%d)", rc);
++ if (pptp_send_ctrl_packet(conn, &packet, sizeof(packet))) {
++ pptp_reset_timer();
++ }
++ }
++}
++
++/*** Get info from call structure *********************************************/
++/* NOTE: The peer_call_id is undefined until we get a server response. */
++void pptp_call_get_ids(PPTP_CONN * conn, PPTP_CALL * call,
++ u_int16_t * call_id, u_int16_t * peer_call_id)
++{
++ assert(conn != NULL); assert(call != NULL);
++ *call_id = call->call_id;
++ *peer_call_id = call->peer_call_id;
++}
++
++/*** pptp_call_closure_put ****************************************************/
++void pptp_call_closure_put(PPTP_CONN * conn, PPTP_CALL * call, void *cl)
++{
++ assert(conn != NULL); assert(call != NULL);
++ call->closure = cl;
++}
++
++/*** pptp_call_closure_get ****************************************************/
++void * pptp_call_closure_get(PPTP_CONN * conn, PPTP_CALL * call)
++{
++ assert(conn != NULL); assert(call != NULL);
++ return call->closure;
++}
++
++/*** pptp_conn_closure_put ****************************************************/
++void pptp_conn_closure_put(PPTP_CONN * conn, void *cl)
++{
++ assert(conn != NULL);
++ conn->closure = cl;
++}
++
++/*** pptp_conn_closure_get ****************************************************/
++void * pptp_conn_closure_get(PPTP_CONN * conn)
++{
++ assert(conn != NULL);
++ return conn->closure;
++}
++
++/*** Reset keep-alive timer ***************************************************/
++static void pptp_reset_timer(void)
++{
++ const struct itimerval tv = { { 0, 0 }, /* stop on time-out */
++ { idle_wait, 0 } };
++ if (idle_wait) setitimer(ITIMER_REAL, &tv, NULL);
++}
++
++
++/*** Handle keep-alive timer **************************************************/
++static void pptp_handle_timer()
++{
++ int i;
++ /* "Keep Alives and Timers, 1": check connection state */
++ if (global.conn->conn_state != CONN_ESTABLISHED) {
++ if (global.conn->conn_state == CONN_WAIT_STOP_REPLY)
++ /* hard close. */
++ pptp_conn_destroy(global.conn);
++ else /* soft close */
++ pptp_conn_close(global.conn, PPTP_STOP_NONE);
++ }
++ /* "Keep Alives and Timers, 2": check echo status */
++ if (global.conn->ka_state == KA_OUTSTANDING) {
++ /* no response to keep-alive */
++ info("closing control connection due to missing echo reply");
++ pptp_conn_close(global.conn, PPTP_STOP_NONE);
++ } else { /* ka_state == NONE */ /* send keep-alive */
++ struct pptp_echo_rqst rqst = {
++ PPTP_HEADER_CTRL(PPTP_ECHO_RQST), hton32(global.conn->ka_id) };
++ pptp_send_ctrl_packet(global.conn, &rqst, sizeof(rqst));
++ global.conn->ka_state = KA_OUTSTANDING;
++ }
++ /* check incoming/outgoing call states for !IDLE && !ESTABLISHED */
++ for (i = 0; i < vector_size(global.conn->call); i++) {
++ PPTP_CALL * call = vector_get_Nth(global.conn->call, i);
++ if (call->call_type == PPTP_CALL_PNS) {
++ if (call->state.pns == PNS_WAIT_REPLY) {
++ /* send close request */
++ pptp_call_close(global.conn, call);
++ assert(call->state.pns == PNS_WAIT_DISCONNECT);
++ } else if (call->state.pns == PNS_WAIT_DISCONNECT) {
++ /* hard-close the call */
++ pptp_call_destroy(global.conn, call);
++ }
++ } else if (call->call_type == PPTP_CALL_PAC) {
++ if (call->state.pac == PAC_WAIT_REPLY) {
++ /* XXX FIXME -- drop the PAC connection XXX */
++ } else if (call->state.pac == PAC_WAIT_CS_ANS) {
++ /* XXX FIXME -- drop the PAC connection XXX */
++ }
++ }
++ }
++ pptp_reset_timer();
++}
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_ctrl.h
+@@ -0,0 +1,57 @@
++/* pptp_ctrl.h ... handle PPTP control connection.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: pptp_ctrl.h,v 1.5 2004/11/09 01:42:32 quozl Exp $
++ */
++
++#ifndef INC_PPTP_CTRL_H
++#define INC_PPTP_CTRL_H
++#include <sys/types.h>
++
++typedef struct PPTP_CONN PPTP_CONN;
++typedef struct PPTP_CALL PPTP_CALL;
++
++enum call_state { CALL_OPEN_RQST, CALL_OPEN_DONE, CALL_OPEN_FAIL,
++ CALL_CLOSE_RQST, CALL_CLOSE_DONE };
++enum conn_state { CONN_OPEN_RQST, CONN_OPEN_DONE, CONN_OPEN_FAIL,
++ CONN_CLOSE_RQST, CONN_CLOSE_DONE };
++
++typedef void (*pptp_call_cb)(PPTP_CONN*, PPTP_CALL*, enum call_state);
++typedef void (*pptp_conn_cb)(PPTP_CONN*, enum conn_state);
++
++/* if 'isclient' is true, then will send 'conn open' packet to other host.
++ * not necessary if this is being opened by a server process after
++ * receiving a conn_open packet from client.
++ */
++PPTP_CONN * pptp_conn_open(int inet_sock, int isclient,
++ pptp_conn_cb callback);
++PPTP_CALL * pptp_call_open(PPTP_CONN * conn, int call_id,
++ pptp_call_cb callback, char *phonenr,int window);
++int pptp_conn_established(PPTP_CONN * conn);
++/* soft close. Will callback on completion. */
++void pptp_call_close(PPTP_CONN * conn, PPTP_CALL * call);
++/* hard close. */
++void pptp_call_destroy(PPTP_CONN *conn, PPTP_CALL *call);
++/* soft close. Will callback on completion. */
++void pptp_conn_close(PPTP_CONN * conn, u_int8_t close_reason);
++/* hard close */
++void pptp_conn_destroy(PPTP_CONN * conn);
++
++/* Add file descriptors used by pptp to fd_set. */
++void pptp_fd_set(PPTP_CONN * conn, fd_set * read_set, fd_set * write_set, int *max_fd);
++/* handle any pptp file descriptors set in fd_set, and clear them */
++int pptp_dispatch(PPTP_CONN * conn, fd_set * read_set, fd_set * write_set);
++
++/* Get info about connection, call */
++void pptp_call_get_ids(PPTP_CONN * conn, PPTP_CALL * call,
++ u_int16_t * call_id, u_int16_t * peer_call_id);
++/* Arbitrary user data about this call/connection.
++ * It is the caller's responsibility to free this data before calling
++ * pptp_call|conn_close()
++ */
++void * pptp_conn_closure_get(PPTP_CONN * conn);
++void pptp_conn_closure_put(PPTP_CONN * conn, void *cl);
++void * pptp_call_closure_get(PPTP_CONN * conn, PPTP_CALL * call);
++void pptp_call_closure_put(PPTP_CONN * conn, PPTP_CALL * call, void *cl);
++
++#endif /* INC_PPTP_CTRL_H */
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_msg.h
+@@ -0,0 +1,303 @@
++/* pptp.h: packet structures and magic constants for the PPTP protocol
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: pptp_msg.h,v 1.3 2003/02/15 10:37:21 quozl Exp $
++ */
++
++#ifndef INC_PPTP_H
++#define INC_PPTP_H
++
++/* Grab definitions of int16, int32, etc. */
++#include <sys/types.h>
++/* define "portable" htons, etc. */
++#define hton8(x) (x)
++#define ntoh8(x) (x)
++#define hton16(x) htons(x)
++#define ntoh16(x) ntohs(x)
++#define hton32(x) htonl(x)
++#define ntoh32(x) ntohl(x)
++
++/* PPTP magic numbers: ----------------------------------------- */
++
++#define PPTP_MAGIC 0x1A2B3C4D /* Magic cookie for PPTP datagrams */
++#define PPTP_PORT 1723 /* PPTP TCP port number */
++#define PPTP_PROTO 47 /* PPTP IP protocol number */
++
++/* Control Connection Message Types: --------------------------- */
++
++#define PPTP_MESSAGE_CONTROL 1
++#define PPTP_MESSAGE_MANAGE 2
++
++/* Control Message Types: -------------------------------------- */
++
++/* (Control Connection Management) */
++#define PPTP_START_CTRL_CONN_RQST 1
++#define PPTP_START_CTRL_CONN_RPLY 2
++#define PPTP_STOP_CTRL_CONN_RQST 3
++#define PPTP_STOP_CTRL_CONN_RPLY 4
++#define PPTP_ECHO_RQST 5
++#define PPTP_ECHO_RPLY 6
++
++/* (Call Management) */
++#define PPTP_OUT_CALL_RQST 7
++#define PPTP_OUT_CALL_RPLY 8
++#define PPTP_IN_CALL_RQST 9
++#define PPTP_IN_CALL_RPLY 10
++#define PPTP_IN_CALL_CONNECT 11
++#define PPTP_CALL_CLEAR_RQST 12
++#define PPTP_CALL_CLEAR_NTFY 13
++
++/* (Error Reporting) */
++#define PPTP_WAN_ERR_NTFY 14
++
++/* (PPP Session Control) */
++#define PPTP_SET_LINK_INFO 15
++
++/* PPTP version information: --------------------------------------*/
++#define PPTP_VERSION_STRING "1.00"
++#define PPTP_VERSION 0x100
++#define PPTP_FIRMWARE_STRING "0.01"
++#define PPTP_FIRMWARE_VERSION 0x001
++
++/* PPTP capabilities: ---------------------------------------------*/
++
++/* (Framing capabilities for msg sender) */
++#define PPTP_FRAME_ASYNC 1
++#define PPTP_FRAME_SYNC 2
++#define PPTP_FRAME_ANY 3
++
++/* (Bearer capabilities for msg sender) */
++#define PPTP_BEARER_ANALOG 1
++#define PPTP_BEARER_DIGITAL 2
++#define PPTP_BEARER_ANY 3
++
++#define PPTP_RESULT_GENERAL_ERROR 2
++
++/* (Reasons to close a connection) */
++#define PPTP_STOP_NONE 1 /* no good reason */
++#define PPTP_STOP_PROTOCOL 2 /* can't support peer's protocol version */
++#define PPTP_STOP_LOCAL_SHUTDOWN 3 /* requester is being shut down */
++
++/* PPTP datagram structures (all data in network byte order): ----------*/
++
++struct pptp_header {
++ u_int16_t length; /* message length in octets, including header */
++ u_int16_t pptp_type; /* PPTP message type. 1 for control message. */
++ u_int32_t magic; /* this should be PPTP_MAGIC. */
++ u_int16_t ctrl_type; /* Control message type (0-15) */
++ u_int16_t reserved0; /* reserved. MUST BE ZERO. */
++};
++
++struct pptp_start_ctrl_conn { /* for control message types 1 and 2 */
++ struct pptp_header header;
++
++ u_int16_t version; /* PPTP protocol version. = PPTP_VERSION */
++ u_int8_t result_code; /* these two fields should be zero on rqst msg*/
++ u_int8_t error_code; /* 0 unless result_code==2 (General Error) */
++ u_int32_t framing_cap; /* Framing capabilities */
++ u_int32_t bearer_cap; /* Bearer Capabilities */
++ u_int16_t max_channels; /* Maximum Channels (=0 for PNS, PAC ignores) */
++ u_int16_t firmware_rev; /* Firmware or Software Revision */
++ u_int8_t hostname[64]; /* Host Name (64 octets, zero terminated) */
++ u_int8_t vendor[64]; /* Vendor string (64 octets, zero term.) */
++ /* MS says that end of hostname/vendor fields should be filled with */
++ /* octets of value 0, but Win95 PPTP driver doesn't do this. */
++};
++
++struct pptp_stop_ctrl_conn { /* for control message types 3 and 4 */
++ struct pptp_header header;
++
++ u_int8_t reason_result; /* reason for rqst, result for rply */
++ u_int8_t error_code; /* MUST be 0, unless rply result==2 (general err)*/
++ u_int16_t reserved1; /* MUST be 0 */
++};
++
++struct pptp_echo_rqst { /* for control message type 5 */
++ struct pptp_header header;
++ u_int32_t identifier; /* arbitrary value set by sender which is used */
++ /* to match up reply and request */
++};
++
++struct pptp_echo_rply { /* for control message type 6 */
++ struct pptp_header header;
++ u_int32_t identifier; /* should correspond to id of rqst */
++ u_int8_t result_code;
++ u_int8_t error_code; /* =0, unless result_code==2 (general error) */
++ u_int16_t reserved1; /* MUST BE ZERO */
++};
++
++struct pptp_out_call_rqst { /* for control message type 7 */
++ struct pptp_header header;
++ u_int16_t call_id; /* Call ID (unique id used to multiplex data) */
++ u_int16_t call_sernum; /* Call Serial Number (used for logging) */
++ u_int32_t bps_min; /* Minimum BPS (lowest acceptable line speed) */
++ u_int32_t bps_max; /* Maximum BPS (highest acceptable line speed) */
++ u_int32_t bearer; /* Bearer type */
++ u_int32_t framing; /* Framing type */
++ u_int16_t recv_size; /* Recv. Window Size (no. of buffered packets) */
++ u_int16_t delay; /* Packet Processing Delay (in 1/10 sec) */
++ u_int16_t phone_len; /* Phone Number Length (num. of valid digits) */
++ u_int16_t reserved1; /* MUST BE ZERO */
++ u_int8_t phone_num[64]; /* Phone Number (64 octets, null term.) */
++ u_int8_t subaddress[64]; /* Subaddress (64 octets, null term.) */
++};
++
++struct pptp_out_call_rply { /* for control message type 8 */
++ struct pptp_header header;
++ u_int16_t call_id; /* Call ID (used to multiplex data over tunnel)*/
++ u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst)*/
++ u_int8_t result_code; /* Result Code (1 is no errors) */
++ u_int8_t error_code; /* Error Code (=0 unless result_code==2) */
++ u_int16_t cause_code; /* Cause Code (addt'l failure information) */
++ u_int32_t speed; /* Connect Speed (in BPS) */
++ u_int16_t recv_size; /* Recv. Window Size (no. of buffered packets) */
++ u_int16_t delay; /* Packet Processing Delay (in 1/10 sec) */
++ u_int32_t channel; /* Physical Channel ID (for logging) */
++};
++
++struct pptp_in_call_rqst { /* for control message type 9 */
++ struct pptp_header header;
++ u_int16_t call_id; /* Call ID (unique id used to multiplex data) */
++ u_int16_t call_sernum; /* Call Serial Number (used for logging) */
++ u_int32_t bearer; /* Bearer type */
++ u_int32_t channel; /* Physical Channel ID (for logging) */
++ u_int16_t dialed_len; /* Dialed Number Length (# of valid digits) */
++ u_int16_t dialing_len; /* Dialing Number Length (# of valid digits) */
++ u_int8_t dialed_num[64]; /* Dialed Number (64 octets, zero term.) */
++ u_int8_t dialing_num[64]; /* Dialing Number (64 octets, zero term.) */
++ u_int8_t subaddress[64]; /* Subaddress (64 octets, zero term.) */
++};
++
++struct pptp_in_call_rply { /* for control message type 10 */
++ struct pptp_header header;
++ u_int16_t call_id; /* Call ID (used to multiplex data over tunnel)*/
++ u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst)*/
++ u_int8_t result_code; /* Result Code (1 is no errors) */
++ u_int8_t error_code; /* Error Code (=0 unless result_code==2) */
++ u_int16_t recv_size; /* Recv. Window Size (no. of buffered packets) */
++ u_int16_t delay; /* Packet Processing Delay (in 1/10 sec) */
++ u_int16_t reserved1; /* MUST BE ZERO */
++};
++
++struct pptp_in_call_connect { /* for control message type 11 */
++ struct pptp_header header;
++ u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst)*/
++ u_int16_t reserved1; /* MUST BE ZERO */
++ u_int32_t speed; /* Connect Speed (in BPS) */
++ u_int16_t recv_size; /* Recv. Window Size (no. of buffered packets) */
++ u_int16_t delay; /* Packet Processing Delay (in 1/10 sec) */
++ u_int32_t framing; /* Framing type */
++};
++
++struct pptp_call_clear_rqst { /* for control message type 12 */
++ struct pptp_header header;
++ u_int16_t call_id; /* Call ID (used to multiplex data over tunnel)*/
++ u_int16_t reserved1; /* MUST BE ZERO */
++};
++
++struct pptp_call_clear_ntfy { /* for control message type 13 */
++ struct pptp_header header;
++ u_int16_t call_id; /* Call ID (used to multiplex data over tunnel)*/
++ u_int8_t result_code; /* Result Code */
++ u_int8_t error_code; /* Error Code (=0 unless result_code==2) */
++ u_int16_t cause_code; /* Cause Code (for ISDN, is Q.931 cause code) */
++ u_int16_t reserved1; /* MUST BE ZERO */
++ u_int8_t call_stats[128]; /* Call Statistics: 128 octets, ascii, 0-term */
++};
++
++struct pptp_wan_err_ntfy { /* for control message type 14 */
++ struct pptp_header header;
++ u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst)*/
++ u_int16_t reserved1; /* MUST BE ZERO */
++ u_int32_t crc_errors; /* CRC errors */
++ u_int32_t frame_errors; /* Framing errors */
++ u_int32_t hard_errors; /* Hardware overruns */
++ u_int32_t buff_errors; /* Buffer overruns */
++ u_int32_t time_errors; /* Time-out errors */
++ u_int32_t align_errors; /* Alignment errors */
++};
++
++struct pptp_set_link_info { /* for control message type 15 */
++ struct pptp_header header;
++ u_int16_t call_id_peer; /* Peer's Call ID (call_id of pptp_out_call_rqst) */
++ u_int16_t reserved1; /* MUST BE ZERO */
++ u_int32_t send_accm; /* Send ACCM (for PPP packets; default 0xFFFFFFFF)*/
++ u_int32_t recv_accm; /* Receive ACCM (for PPP pack.;default 0xFFFFFFFF)*/
++};
++
++/* helpful #defines: -------------------------------------------- */
++#define pptp_isvalid_ctrl(header, type, length) \
++ (!( ( ntoh16(((struct pptp_header *)header)->length) < (length) ) || \
++ ( ntoh16(((struct pptp_header *)header)->pptp_type) !=(type) ) || \
++ ( ntoh32(((struct pptp_header *)header)->magic) !=PPTP_MAGIC) || \
++ ( ntoh16(((struct pptp_header *)header)->ctrl_type) > PPTP_SET_LINK_INFO) || \
++ ( ntoh16(((struct pptp_header *)header)->reserved0) !=0 ) ))
++
++#define PPTP_HEADER_CTRL(type) \
++{ hton16(PPTP_CTRL_SIZE(type)), \
++ hton16(PPTP_MESSAGE_CONTROL), \
++ hton32(PPTP_MAGIC), \
++ hton16(type), 0 }
++
++#define PPTP_CTRL_SIZE(type) ( \
++(type==PPTP_START_CTRL_CONN_RQST)?sizeof(struct pptp_start_ctrl_conn): \
++(type==PPTP_START_CTRL_CONN_RPLY)?sizeof(struct pptp_start_ctrl_conn): \
++(type==PPTP_STOP_CTRL_CONN_RQST )?sizeof(struct pptp_stop_ctrl_conn): \
++(type==PPTP_STOP_CTRL_CONN_RPLY )?sizeof(struct pptp_stop_ctrl_conn): \
++(type==PPTP_ECHO_RQST )?sizeof(struct pptp_echo_rqst): \
++(type==PPTP_ECHO_RPLY )?sizeof(struct pptp_echo_rply): \
++(type==PPTP_OUT_CALL_RQST )?sizeof(struct pptp_out_call_rqst): \
++(type==PPTP_OUT_CALL_RPLY )?sizeof(struct pptp_out_call_rply): \
++(type==PPTP_IN_CALL_RQST )?sizeof(struct pptp_in_call_rqst): \
++(type==PPTP_IN_CALL_RPLY )?sizeof(struct pptp_in_call_rply): \
++(type==PPTP_IN_CALL_CONNECT )?sizeof(struct pptp_in_call_connect): \
++(type==PPTP_CALL_CLEAR_RQST )?sizeof(struct pptp_call_clear_rqst): \
++(type==PPTP_CALL_CLEAR_NTFY )?sizeof(struct pptp_call_clear_ntfy): \
++(type==PPTP_WAN_ERR_NTFY )?sizeof(struct pptp_wan_err_ntfy): \
++(type==PPTP_SET_LINK_INFO )?sizeof(struct pptp_set_link_info): \
++0)
++#define max(a,b) (((a)>(b))?(a):(b))
++#define PPTP_CTRL_SIZE_MAX ( \
++max(sizeof(struct pptp_start_ctrl_conn), \
++max(sizeof(struct pptp_echo_rqst), \
++max(sizeof(struct pptp_echo_rply), \
++max(sizeof(struct pptp_out_call_rqst), \
++max(sizeof(struct pptp_out_call_rply), \
++max(sizeof(struct pptp_in_call_rqst), \
++max(sizeof(struct pptp_in_call_rply), \
++max(sizeof(struct pptp_in_call_connect), \
++max(sizeof(struct pptp_call_clear_rqst), \
++max(sizeof(struct pptp_call_clear_ntfy), \
++max(sizeof(struct pptp_wan_err_ntfy), \
++max(sizeof(struct pptp_set_link_info), 0)))))))))))))
++
++
++/* gre header structure: -------------------------------------------- */
++
++#define PPTP_GRE_PROTO 0x880B
++#define PPTP_GRE_VER 0x1
++
++#define PPTP_GRE_FLAG_C 0x80
++#define PPTP_GRE_FLAG_R 0x40
++#define PPTP_GRE_FLAG_K 0x20
++#define PPTP_GRE_FLAG_S 0x10
++#define PPTP_GRE_FLAG_A 0x80
++
++#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
++#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
++#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
++#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
++#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)
++
++struct pptp_gre_header {
++ u_int8_t flags; /* bitfield */
++ u_int8_t ver; /* should be PPTP_GRE_VER (enhanced GRE) */
++ u_int16_t protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */
++ u_int16_t payload_len; /* size of ppp payload, not inc. gre header */
++ u_int16_t call_id; /* peer's call_id for this session */
++ u_int32_t seq; /* sequence number. Present if S==1 */
++ u_int32_t ack; /* seq number of highest packet recieved by */
++ /* sender in this session */
++};
++
++#endif /* INC_PPTP_H */
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_options.h
+@@ -0,0 +1,41 @@
++/* pptp_options.h ...... various constants used in the PPTP protocol.
++ * #define STANDARD to emulate NT 4.0 exactly.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: pptp_options.h,v 1.3 2004/11/09 01:42:32 quozl Exp $
++ */
++
++#ifndef INC_PPTP_OPTIONS_H
++#define INC_PPTP_OPTIONS_H
++
++#undef PPTP_FIRMWARE_STRING
++#undef PPTP_FIRMWARE_VERSION
++#define PPTP_BUF_MAX 65536
++#define PPTP_TIMEOUT 60 /* seconds */
++extern int idle_wait;
++extern int max_echo_wait;
++#define PPTP_CONNECT_SPEED 1000000000
++#define PPTP_WINDOW 3
++#define PPTP_DELAY 0
++#define PPTP_BPS_MIN 2400
++#define PPTP_BPS_MAX 1000000000
++
++#ifndef STANDARD
++#define PPTP_MAX_CHANNELS 65535
++#define PPTP_FIRMWARE_STRING "0.01"
++#define PPTP_FIRMWARE_VERSION 0x001
++#define PPTP_HOSTNAME {'l','o','c','a','l',0}
++#define PPTP_VENDOR {'c','a','n','a','n','i','a','n',0}
++#define PPTP_FRAME_CAP PPTP_FRAME_ANY
++#define PPTP_BEARER_CAP PPTP_BEARER_ANY
++#else
++#define PPTP_MAX_CHANNELS 5
++#define PPTP_FIRMWARE_STRING "0.01"
++#define PPTP_FIRMWARE_VERSION 0
++#define PPTP_HOSTNAME {'l','o','c','a','l',0}
++#define PPTP_VENDOR {'N','T',0}
++#define PPTP_FRAME_CAP 2
++#define PPTP_BEARER_CAP 1
++#endif
++
++#endif /* INC_PPTP_OPTIONS_H */
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_quirks.c
+@@ -0,0 +1,54 @@
++/* pptp_quirks.c ...... various options to fix quirks found in buggy adsl modems
++ * mulix <mulix@actcom.co.il>
++ *
++ * $Id: pptp_quirks.c,v 1.2 2001/11/23 03:42:51 quozl Exp $
++ */
++
++#include <string.h>
++#include "orckit_quirks.h"
++#include "pptp_quirks.h"
++
++static int quirk_index = -1;
++
++struct pptp_fixup pptp_fixups[] = {
++ {BEZEQ_ISRAEL, ORCKIT, ORCKIT_ATUR3,
++ orckit_atur3_build_hook,
++ orckit_atur3_start_ctrl_conn_hook,
++ orckit_atur3_set_link_hook}
++};
++
++static int fixups_sz = sizeof(pptp_fixups)/sizeof(pptp_fixups[0]);
++
++/* return 0 on success, non 0 otherwise */
++int set_quirk_index(int index)
++{
++ if (index >= 0 && index < fixups_sz) {
++ quirk_index = index;
++ return 0;
++ }
++
++ return -1;
++}
++
++int get_quirk_index()
++{
++ return quirk_index;
++}
++
++/* return the index for this isp in the quirks table, -1 if not found */
++int find_quirk(const char* isp_name)
++{
++ int i = 0;
++ if (isp_name) {
++ while (i < fixups_sz && pptp_fixups[i].isp) {
++ if (!strcmp(pptp_fixups[i].isp, isp_name)) {
++ return i;
++ }
++ ++i;
++ }
++ }
++
++ return -1;
++}
++
++
+--- /dev/null
++++ b/pppd/plugins/pptp/pptp_quirks.h
+@@ -0,0 +1,59 @@
++/* pptp_quirks.h ...... various options to fix quirks found in buggy adsl modems
++ * mulix <mulix@actcom.co.il>
++ *
++ * $Id: pptp_quirks.h,v 1.1 2001/11/20 06:30:10 quozl Exp $
++ */
++
++#ifndef INC_PPTP_QUIRKS_H
++#define INC_PPTP_QUIRKS_H
++
++/* isp defs - correspond to slots in the fixups table */
++#define BEZEQ_ISRAEL "BEZEQ_ISRAEL"
++
++/* vendor defs */
++
++#define ORCKIT 1
++#define ALCATEL 2
++
++/* device defs */
++
++#define ORCKIT_ATUR2 1
++#define ORCKIT_ATUR3 2
++
++#include "pptp_msg.h"
++#include "pptp_ctrl.h"
++
++struct pptp_fixup {
++ const char* isp; /* which isp? e.g. Bezeq in Israel */
++ int vendor; /* which vendor? e.g. Orckit */
++ int device; /* which device? e.g. Orckit Atur3 */
++
++ /* use this hook to build your own out call request packet */
++ int (*out_call_rqst_hook)(struct pptp_out_call_rqst* packet);
++
++ /* use this hook to build your own start control connection packet */
++ /* note that this hook is called from two different places, depending
++ on whether this is a request or reply */
++ int (*start_ctrl_conn)(struct pptp_start_ctrl_conn* packet);
++
++ /* use this hook if you need to send a 'set_link' packet once
++ the connection is established */
++ int (*set_link_hook)(struct pptp_set_link_info* packet,
++ int peer_call_id);
++};
++
++extern struct pptp_fixup pptp_fixups[];
++
++/* find the index for this isp in the quirks table */
++/* return the index on success, -1 if not found */
++int find_quirk(const char* isp_name);
++
++/* set the global quirk index. return 0 on success, non 0 otherwise */
++int set_quirk_index(int index);
++
++/* get the global quirk index. return the index on success,
++ -1 if no quirk is defined */
++int get_quirk_index();
++
++
++#endif /* INC_PPTP_QUIRKS_H */
+--- /dev/null
++++ b/pppd/plugins/pptp/util.c
+@@ -0,0 +1,109 @@
++/* util.c ....... error message utilities.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: util.c,v 1.11 2005/08/22 00:49:48 quozl Exp $
++ */
++
++#include <stdio.h>
++#include <stdarg.h>
++#include <syslog.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include "util.h"
++
++#define MAKE_STRING(label) \
++va_list ap; \
++char buf[256], string[256]; \
++va_start(ap, format); \
++vsnprintf(buf, sizeof(buf), format, ap); \
++snprintf(string, sizeof(string), "%s %s[%s:%s:%d]: %s", \
++ log_string, label, func, file, line, buf); \
++va_end(ap)
++
++/*** connect a file to a file descriptor **************************************/
++int file2fd(const char *path, const char *mode, int fd)
++{
++ int ok = 0;
++ FILE *file = NULL;
++ file = fopen(path, mode);
++ if (file != NULL && dup2(fileno(file), fd) != -1)
++ ok = 1;
++ if (file) fclose(file);
++ return ok;
++}
++
++/* signal to pipe delivery implementation */
++#include <unistd.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <string.h>
++
++/* pipe private to process */
++static int sigpipe[2];
++
++/* create a signal pipe, returns 0 for success, -1 with errno for failure */
++int sigpipe_create()
++{
++ int rc;
++
++ rc = pipe(sigpipe);
++ if (rc < 0) return rc;
++
++ fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC);
++ fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC);
++
++#ifdef O_NONBLOCK
++#define FLAG_TO_SET O_NONBLOCK
++#else
++#ifdef SYSV
++#define FLAG_TO_SET O_NDELAY
++#else /* BSD */
++#define FLAG_TO_SET FNDELAY
++#endif
++#endif
++
++ rc = fcntl(sigpipe[1], F_GETFL);
++ if (rc != -1)
++ rc = fcntl(sigpipe[1], F_SETFL, rc | FLAG_TO_SET);
++ if (rc < 0) return rc;
++ return 0;
++#undef FLAG_TO_SET
++}
++
++/* generic handler for signals, writes signal number to pipe */
++void sigpipe_handler(int signum)
++{
++ write(sigpipe[1], &signum, sizeof(signum));
++ signal(signum, sigpipe_handler);
++}
++
++/* assign a signal number to the pipe */
++void sigpipe_assign(int signum)
++{
++ struct sigaction sa;
++
++ memset(&sa, 0, sizeof(sa));
++ sa.sa_handler = sigpipe_handler;
++ sigaction(signum, &sa, NULL);
++}
++
++/* return the signal pipe read file descriptor for select(2) */
++int sigpipe_fd()
++{
++ return sigpipe[0];
++}
++
++/* read and return the pending signal from the pipe */
++int sigpipe_read()
++{
++ int signum;
++ read(sigpipe[0], &signum, sizeof(signum));
++ return signum;
++}
++
++void sigpipe_close()
++{
++ close(sigpipe[0]);
++ close(sigpipe[1]);
++}
++
+--- /dev/null
++++ b/pppd/plugins/pptp/util.h
+@@ -0,0 +1,31 @@
++/* util.h ....... error message utilities.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: util.h,v 1.6 2005/03/10 01:18:20 quozl Exp $
++ */
++
++#ifndef INC_UTIL_H
++#define INC_UTIL_H
++
++int file2fd(const char *path, const char *mode, int fd);
++
++/* signal to pipe delivery implementation */
++
++/* create a signal pipe, returns 0 for success, -1 with errno for failure */
++int sigpipe_create();
++
++/* generic handler for signals, writes signal number to pipe */
++void sigpipe_handler(int signum);
++
++/* assign a signal number to the pipe */
++void sigpipe_assign(int signum);
++
++/* return the signal pipe read file descriptor for select(2) */
++int sigpipe_fd();
++
++/* read and return the pending signal from the pipe */
++int sigpipe_read();
++
++void sigpipe_close();
++
++#endif /* INC_UTIL_H */
+--- /dev/null
++++ b/pppd/plugins/pptp/vector.c
+@@ -0,0 +1,209 @@
++/* vector.c ..... store a vector of PPTP_CALL information and search it
++ * efficiently.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: vector.c,v 1.3 2003/06/17 10:12:55 reink Exp $
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++#include "pptp_ctrl.h"
++#include "vector.h"
++/* #define VECTOR_DEBUG */
++#ifndef TRUE
++#define TRUE 1
++#endif
++#ifndef FALSE
++#define FALSE 0
++#endif
++
++struct vector_item {
++ int key;
++ PPTP_CALL *call;
++};
++
++struct vector_struct {
++ struct vector_item *item;
++ int size;
++ int alloc;
++#ifdef VECTOR_DEBUG
++ int key_max;
++#endif
++};
++
++static struct vector_item *binary_search(VECTOR *v, int key);
++
++/*** vector_create ************************************************************/
++VECTOR *vector_create()
++{
++ const int INITIAL_SIZE = 4;
++
++ VECTOR *v = malloc(sizeof(*v));
++ if (v == NULL) return v;
++
++ v->size = 0;
++ v->alloc = INITIAL_SIZE;
++ v->item = malloc(sizeof(*(v->item)) * (v->alloc));
++#ifdef VECTOR_DEBUG
++ v->key_max = -1;
++#endif
++ if (v->item == NULL) { free(v); return NULL; }
++ else return v;
++}
++
++/*** vector_destroy ***********************************************************/
++void vector_destroy(VECTOR *v)
++{
++ free(v->item);
++#ifdef VECTOR_DEBUG
++ v->item = NULL;
++#endif
++ free(v);
++}
++
++/*** vector_size **************************************************************/
++int vector_size(VECTOR *v)
++{
++ assert(v != NULL);
++ return v->size;
++}
++
++/*** vector_insert*************************************************************
++ * nice thing about file descriptors is that we are assured by POSIX
++ * that they are monotonically increasing.
++ */
++int vector_insert(VECTOR *v, int key, PPTP_CALL * call)
++{
++ int i;
++ assert(v != NULL && call != NULL);
++ assert(!vector_contains(v, key));
++#ifdef VECTOR_DEBUG
++ assert(v->key_max < key);
++#endif
++ if (!(v->size < v->alloc)) {
++ void *tmp = realloc(v->item, sizeof(*(v->item)) * 2 * v->alloc);
++ if (tmp != NULL) {
++ v->alloc *= 2;
++ v->item = tmp;
++ } else return FALSE; /* failed to alloc memory. */
++ }
++ assert(v->size < v->alloc);
++ /* for safety, we make this work in the general case;
++ * but this is optimized for adding call to the end of the vector.
++ */
++ for(i = v->size - 1; i >= 0; i--)
++ if (v->item[i].key < key)
++ break;
++ /* insert after item i */
++ memmove(&v->item[i + 2], &v->item[i + 1],
++ (v->size - i - 1) * sizeof(*(v->item)));
++ v->item[i + 1].key = key;
++ v->item[i + 1].call = call;
++ v->size++;
++#ifdef VECTOR_DEBUG
++ if (v->key_max < key) /* ie, always. */
++ v->key_max = key;
++#endif
++ return TRUE;
++}
++
++/*** vector_remove ************************************************************/
++int vector_remove(VECTOR *v, int key)
++{
++ struct vector_item *tmp;
++ assert(v != NULL);
++ if ((tmp =binary_search(v,key)) == NULL) return FALSE;
++ assert(tmp >= v->item && tmp < v->item + v->size);
++ memmove(tmp, tmp + 1, (v->size - (v->item - tmp) - 1) * sizeof(*(v->item)));
++ v->size--;
++ return TRUE;
++}
++
++/*** vector_search ************************************************************/
++int vector_search(VECTOR *v, int key, PPTP_CALL **call)
++{
++ struct vector_item *tmp;
++ assert(v != NULL);
++ tmp = binary_search(v, key);
++ if (tmp ==NULL) return FALSE;
++ *call = tmp->call;
++ return TRUE;
++}
++
++/*** vector_contains **********************************************************/
++int vector_contains(VECTOR *v, int key)
++{
++ assert(v != NULL);
++ return (binary_search(v, key) != NULL);
++}
++
++/*** vector_item **************************************************************/
++static struct vector_item *binary_search(VECTOR *v, int key)
++{
++ int l,r,x;
++ l = 0;
++ r = v->size - 1;
++ while (r >= l) {
++ x = (l + r)/2;
++ if (key < v->item[x].key) r = x - 1; else l = x + 1;
++ if (key == v->item[x].key) return &(v->item[x]);
++ }
++ return NULL;
++}
++
++/*** vector_scan ***************************************************************
++ * Hmm. Let's be fancy and use a binary search for the first
++ * unused key, taking advantage of the list is stored sorted; ie
++ * we can look at pointers and keys at two different locations,
++ * and if (ptr1 - ptr2) = (key1 - key2) then all the slots
++ * between ptr1 and ptr2 are filled. Note that ptr1-ptr2 should
++ * never be greater than key1-key2 (no duplicate keys!)... we
++ * check for this.
++ */
++int vector_scan(VECTOR *v, int lo, int hi, int *key)
++{
++ int l,r,x;
++ assert(v != NULL);
++ assert(key != NULL);
++ if ((v->size<1) || (lo < v->item[0].key)) { *key = lo; return TRUE; }
++ /* our array bounds */
++ l = 0; r = v->size - 1;
++ while (r > l) {
++ /* check for a free spot right after l */
++ if (v->item[l].key + 1 < v->item[l + 1].key) { /* found it! */
++ *key = v->item[l].key + 1;
++ return TRUE;
++ }
++ /* no dice. Let's see if the free spot is before or after the midpoint */
++ x = (l + r)/2;
++ /* Okay, we have right (r), left (l) and the probe (x). */
++ assert(x - l <= v->item[x].key - v->item[l].key);
++ assert(r - x <= v->item[r].key - v->item[x].key);
++ if (x - l < v->item[x].key - v->item[l].key)
++ /* room between l and x */
++ r = x;
++ else /* no room between l and x */
++ if (r - x < v->item[r].key - v->item[x].key)
++ /* room between x and r */
++ l = x;
++ else /* no room between x and r, either */
++ break; /* game over, man. */
++ }
++ /* no room found in already allocated space. Check to see if
++ * there's free space above allocated entries. */
++ if (v->item[v->size - 1].key < hi) {
++ *key = v->item[v->size - 1].key + 1;
++ return TRUE;
++ }
++ /* outta luck */
++ return FALSE;
++}
++
++/*** vector_get_Nth ***********************************************************/
++PPTP_CALL * vector_get_Nth(VECTOR *v, int n)
++{
++ assert(v != NULL);
++ assert(0 <= n && n < vector_size(v));
++ return v->item[n].call;
++}
+--- /dev/null
++++ b/pppd/plugins/pptp/vector.h
+@@ -0,0 +1,31 @@
++/* vector.h ..... store a vector of PPTP_CALL information and search it
++ * efficiently.
++ * C. Scott Ananian <cananian@alumni.princeton.edu>
++ *
++ * $Id: vector.h,v 1.1.1.1 2000/12/23 08:19:51 scott Exp $
++ */
++
++#ifndef INC_VECTOR_H
++#define INC_VECTOR_H
++
++#include "pptp_ctrl.h" /* for definition of PPTP_CALL */
++
++typedef struct vector_struct VECTOR;
++
++VECTOR *vector_create();
++void vector_destroy(VECTOR *v);
++
++int vector_size(VECTOR *v);
++
++/* vector_insert and vector_search return TRUE on success, FALSE on failure. */
++int vector_insert(VECTOR *v, int key, PPTP_CALL * call);
++int vector_remove(VECTOR *v, int key);
++int vector_search(VECTOR *v, int key, PPTP_CALL ** call);
++/* vector_contains returns FALSE if not found, TRUE if found. */
++int vector_contains(VECTOR *v, int key);
++/* find first unused key. Returns TRUE on success, FALSE if no. */
++int vector_scan(VECTOR *v, int lo, int hi, int *key);
++/* get a specific PPTP_CALL ... useful only when iterating. */
++PPTP_CALL * vector_get_Nth(VECTOR *v, int n);
++
++#endif /* INC_VECTOR_H */