diff options
author | Felix Fietkau <nbd@openwrt.org> | 2012-10-10 12:32:29 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2012-10-10 12:32:29 +0000 |
commit | 405e21d16731b2764ab82aaaadcf36a813b105f7 (patch) | |
tree | c6f9a8402389a5081519b91ce62c4a8cafcb8917 /package/network/services/ppp/patches | |
parent | d0ec348ded6f715b43b396b06ccb10599b37969d (diff) | |
download | openwrt-405e21d16731b2764ab82aaaadcf36a813b105f7.tar.gz openwrt-405e21d16731b2764ab82aaaadcf36a813b105f7.tar.bz2 openwrt-405e21d16731b2764ab82aaaadcf36a813b105f7.zip |
packages: sort network related packages into package/network/
SVN-Revision: 33688
Diffstat (limited to 'package/network/services/ppp/patches')
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 */ |