diff -urN ppp-2.4.4-org/pppd/plugins/Makefile.linux ppp-2.4.4/pppd/plugins/Makefile.linux
--- ppp-2.4.4-org/pppd/plugins/Makefile.linux	2006-06-04 13:04:14.000000000 +0800
+++ ppp-2.4.4/pppd/plugins/Makefile.linux	2009-03-18 17:43:48.000000000 +0800
@@ -9,9 +9,9 @@
 MANDIR = $(DESTDIR)/share/man/man8
 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION)
 
-SUBDIRS := rp-pppoe pppoatm
+SUBDIRS := rp-pppoe pppoatm radius pppol2tp
 # Uncomment the next line to include the radius authentication plugin
-SUBDIRS += radius
+# SUBDIRS += radius
 PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so
 
 # include dependencies if present
diff -urN ppp-2.4.4-org/pppd/plugins/pppol2tp/l2tp_event.h ppp-2.4.4/pppd/plugins/pppol2tp/l2tp_event.h
--- ppp-2.4.4-org/pppd/plugins/pppol2tp/l2tp_event.h	1970-01-01 08:00:00.000000000 +0800
+++ ppp-2.4.4/pppd/plugins/pppol2tp/l2tp_event.h	2009-03-18 17:45:48.000000000 +0800
@@ -0,0 +1,104 @@
+/***************************************************************************** 
+ * Copyright (C) 2008 Katalix Systems Ltd 
+ * 
+ * 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. 
+ * 
+ *****************************************************************************/ 
+ 
+/* 
+ * OpenL2TP application event interface definition. 
+ * 
+ * This plugin is used by OpenL2TP to receive events from pppd. 
+ * 
+ * Events are used as follows:- 
+ * PPP_UPDOWN_IND - tells OpenL2TP of PPP session state changes. 
+ * PPP_ACCM_IND - tells OpenL2TP of PPP ACCM negotiated options 
+ * 
+ * Non-GPL applications are permitted to use this API. 
+ */ 
+ 
+#ifndef L2TP_EVENT_H 
+#define L2TP_EVENT_H 
+ 
+#include <stdint.h> 
+ 
+/***************************************************************************** 
+ * API definition 
+ *****************************************************************************/ 
+ 
+#define OPENL2TP_EVENT_SOCKET_NAME "/tmp/openl2tp-event.sock" 
+ 
+#define OPENL2TP_MSG_TYPE_NULL 0 
+#define OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND 1 
+#define OPENL2TP_MSG_TYPE_PPP_ACCM_IND 2 
+#define OPENL2TP_MSG_TYPE_MAX 3 
+ 
+enum { 
+ OPENL2TP_TLV_TYPE_TUNNEL_ID, 
+ OPENL2TP_TLV_TYPE_SESSION_ID, 
+ OPENL2TP_TLV_TYPE_PPP_ACCM, 
+ OPENL2TP_TLV_TYPE_PPP_UNIT, 
+ OPENL2TP_TLV_TYPE_PPP_IFNAME, 
+ OPENL2TP_TLV_TYPE_PPP_USER_NAME, 
+ OPENL2TP_TLV_TYPE_PPP_STATE 
+}; 
+#define OPENL2TP_TLV_TYPE_MAX (OPENL2TP_TLV_TYPE_PPP_STATE + 1) 
+ 
+#define OPENL2TP_MSG_MAX_LEN 512 
+#define OPENL2TP_MSG_SIGNATURE 0x6b6c7831 
+ 
+#define ALIGN32(n) (((n) + 3) & ~3) 
+ 
+/* Each data field in a message is defined by a Type-Length-Value 
+ * (TLV) tuplet. 
+ */ 
+struct openl2tp_event_tlv { 
+ uint16_t tlv_type; 
+ uint16_t tlv_len; 
+ uint8_t tlv_value[0]; 
+}; 
+ 
+/* Messages contain a small header followed by a list of TLVs. Each 
+ * TLV starts on a 4-byte boundary. 
+ */ 
+struct openl2tp_event_msg { 
+ uint32_t msg_signature; /* OPENL2TP_MSG_SIGNATURE */ 
+ uint16_t msg_type; /* OPENL2TP_MSG_TYPE_* */ 
+ uint16_t msg_len; /* length of data that follows */ 
+ uint8_t msg_data[0]; /* list of TLVs, each always longword aligned */ 
+}; 
+ 
+/* These structs define the data field layout of each TLV. 
+ */ 
+struct openl2tp_tlv_tunnel_id { 
+ uint16_t tunnel_id; 
+}; 
+ 
+struct openl2tp_tlv_session_id { 
+ uint16_t session_id; 
+}; 
+ 
+struct openl2tp_tlv_ppp_accm { 
+ uint32_t send_accm; 
+ uint32_t recv_accm; 
+}; 
+ 
+struct openl2tp_tlv_ppp_unit { 
+ uint32_t unit; 
+}; 
+ 
+struct openl2tp_tlv_ppp_state { 
+ uint8_t up; /* 0=down, 1=up */ 
+}; 
+ 
+struct openl2tp_tlv_ppp_ifname { 
+ char ifname[0]; 
+}; 
+ 
+struct openl2tp_tlv_ppp_user_name { 
+ char user_name[0]; 
+}; 
+ 
+#endif /* L2TP_EVENT_H */
diff -urN ppp-2.4.4-org/pppd/plugins/pppol2tp/Makefile ppp-2.4.4/pppd/plugins/pppol2tp/Makefile
--- ppp-2.4.4-org/pppd/plugins/pppol2tp/Makefile	1970-01-01 08:00:00.000000000 +0800
+++ ppp-2.4.4/pppd/plugins/pppol2tp/Makefile	2009-03-18 17:46:44.000000000 +0800
@@ -0,0 +1,29 @@
+CC = gcc 
+COPTS = -O2 -g 
+CFLAGS = $(COPTS) -I. -I../.. -I../../../include -fPIC  
+LDFLAGS = -shared 
+INSTALL = install 
+
+#*********************************************************************** 
+
+DESTDIR = /usr 
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) 
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) 
+
+PLUGINS := pppol2tp.so openl2tp.so
+
+all: $(PLUGINS) 
+
+%.so: %.o 
+	$(CC) $(CFLAGS) -o $@ -shared $^ $(LIBS) 
+
+install: all 
+	$(INSTALL) -d -m 755 $(LIBDIR) 
+	$(INSTALL) -c -m 4550 $(PLUGINS) $(LIBDIR) 
+
+clean: 
+	rm -f *.o *.so 
+
+%.o: %.c 
+	$(CC) $(CFLAGS) -c -o $@ $< 
diff -urN ppp-2.4.4-org/pppd/plugins/pppol2tp/Makefile.linux ppp-2.4.4/pppd/plugins/pppol2tp/Makefile.linux
--- ppp-2.4.4-org/pppd/plugins/pppol2tp/Makefile.linux	1970-01-01 08:00:00.000000000 +0800
+++ ppp-2.4.4/pppd/plugins/pppol2tp/Makefile.linux	2009-03-18 17:47:48.000000000 +0800
@@ -0,0 +1,29 @@
+CC = gcc 
+COPTS = -O2 -g 
+CFLAGS = $(COPTS) -I. -I../.. -I../../../include -fPIC  
+LDFLAGS = -shared 
+INSTALL = install 
+
+#*********************************************************************** 
+
+DESTDIR = @DESTDIR@ 
+LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) 
+
+VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) 
+
+PLUGINS := pppol2tp.so openl2tp.so
+
+all: $(PLUGINS) 
+
+%.so: %.o 
+	$(CC) $(CFLAGS) -o $@ -shared $^ $(LIBS) 
+
+install: all 
+	$(INSTALL) -d -m 755 $(LIBDIR) 
+	$(INSTALL) -c -m 4550 $(PLUGINS) $(LIBDIR) 
+
+clean: 
+	rm -f *.o *.so 
+
+%.o: %.c 
+	$(CC) $(CFLAGS) -c -o $@ $< 
diff -urN ppp-2.4.4-org/pppd/plugins/pppol2tp/openl2tp.c ppp-2.4.4/pppd/plugins/pppol2tp/openl2tp.c
--- ppp-2.4.4-org/pppd/plugins/pppol2tp/openl2tp.c	1970-01-01 08:00:00.000000000 +0800
+++ ppp-2.4.4/pppd/plugins/pppol2tp/openl2tp.c	2009-03-18 17:51:02.000000000 +0800
@@ -0,0 +1,307 @@
+/***************************************************************************** 
+ * Copyright (C) 2006,2007,2008 Katalix Systems Ltd 
+ * 
+ * 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 
+ * 
+ *****************************************************************************/ 
+ 
+/* pppd plugin for interfacing to openl2tpd */ 
+ 
+#include <unistd.h> 
+#include <string.h> 
+#include <stdlib.h> 
+#include <errno.h> 
+#include "pppd.h" 
+#include "pathnames.h" 
+#include "fsm.h" 
+#include "lcp.h" 
+#include "ccp.h" 
+#include "ipcp.h" 
+#include <sys/stat.h> 
+#include <net/if.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h> 
+#include <sys/un.h> 
+#include <netinet/in.h> 
+#include <signal.h> 
+#include <linux/version.h> 
+#include <linux/sockios.h> 
+ 
+#ifndef aligned_u64 
+/* should be defined in sys/types.h */ 
+#define aligned_u64 unsigned long long __attribute__((aligned(8))) 
+#endif 
+#include <linux/types.h> 
+#include <linux/if_ether.h> 
+#include <linux/ppp_defs.h> 
+#include <linux/if_ppp.h> 
+#include <linux/if_pppox.h> 
+#include <linux/if_pppol2tp.h> 
+ 
+#include "l2tp_event.h" 
+ 
+extern int pppol2tp_tunnel_id; 
+extern int pppol2tp_session_id; 
+ 
+extern void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm); 
+extern void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up); 
+ 
+const char pppd_version[] = VERSION; 
+ 
+static int openl2tp_fd = -1; 
+ 
+static void (*old_pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm) = NULL; 
+static void (*old_pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up) = NULL; 
+static int (*old_new_phase_hook)(int phase) = NULL; 
+ 
+/***************************************************************************** 
+ * OpenL2TP RPC interface. 
+ * We send a PPP_ACCM_IND to openl2tpd to report ACCM values and 
+ * SESSION_PPP_UPDOWN_IND to indicate when the PPP link comes up or 
+ * goes down. 
+ * Async RPC is used so that we don't stall waiting for openl2tpd to send a 
+ * reply. Since we're running on the same host as openl2tpd, UDP is reliable, 
+ * hence there's no need to worry about whether our message arrives. 
+ *****************************************************************************/ 
+ 
+static int openl2tp_client_create(void) 
+{ 
+ struct sockaddr_un addr; 
+ int result; 
+ 
+ if (openl2tp_fd < 0) { 
+ openl2tp_fd = socket(PF_UNIX, SOCK_DGRAM, 0); 
+ if (openl2tp_fd < 0) { 
+ error("openl2tp connection create: %m"); 
+ return -ENOTCONN; 
+ } 
+ 
+ addr.sun_family = AF_UNIX; 
+ strcpy(&addr.sun_path[0], OPENL2TP_EVENT_SOCKET_NAME); 
+ 
+ result = connect(openl2tp_fd, (struct sockaddr *) &addr, sizeof(addr)); 
+ if (result < 0) { 
+ error("openl2tp connection connect: %m"); 
+ return -ENOTCONN; 
+ } 
+ } 
+ 
+ return 0; 
+} 
+ 
+static void openl2tp_client_delete(void) 
+{ 
+ if (openl2tp_fd >= 0) { 
+ close(openl2tp_fd); 
+ openl2tp_fd = -1; 
+ } 
+} 
+ 
+ 
+static void openl2tp_send_accm_ind(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm) 
+{ 
+ int result; 
+ uint8_t buf[OPENL2TP_MSG_MAX_LEN]; 
+ struct openl2tp_event_msg *msg = (void *) &buf[0]; 
+ struct openl2tp_event_tlv *tlv; 
+ uint16_t tid = tunnel_id; 
+ uint16_t sid = session_id; 
+ struct openl2tp_tlv_ppp_accm accm; 
+ 
+ if (openl2tp_fd < 0) { 
+ result = openl2tp_client_create(); 
+ if (result < 0) { 
+ goto out; 
+ } 
+ } 
+ 
+ accm.send_accm = send_accm; 
+ accm.recv_accm = recv_accm; 
+ 
+ msg->msg_signature = OPENL2TP_MSG_SIGNATURE; 
+ msg->msg_type = OPENL2TP_MSG_TYPE_PPP_ACCM_IND; 
+ msg->msg_len = 0; 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; 
+ tlv->tlv_len = sizeof(tid); 
+ memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; 
+ tlv->tlv_len = sizeof(sid); 
+ memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_ACCM; 
+ tlv->tlv_len = sizeof(accm); 
+ memcpy(&tlv->tlv_value[0], &accm, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, MSG_NOSIGNAL); 
+ if (result < 0) { 
+ error("openl2tp send: %m"); 
+ } 
+ if (result != (sizeof(*msg) + msg->msg_len)) { 
+ warn("openl2tp send: unexpected byte count %d, expected %d", result, sizeof(msg) + msg->msg_len); 
+ } 
+ dbglog("openl2tp send: sent PPP_ACCM_IND, %d bytes", result); 
+ 
+out: 
+ if (old_pppol2tp_send_accm_hook != NULL) { 
+ (*old_pppol2tp_send_accm_hook)(tunnel_id, session_id, send_accm, recv_accm); 
+ } 
+ return; 
+} 
+ 
+static void openl2tp_ppp_updown_ind(int tunnel_id, int session_id, int up) 
+{ 
+ int result; 
+ uint8_t buf[OPENL2TP_MSG_MAX_LEN]; 
+ struct openl2tp_event_msg *msg = (void *) &buf[0]; 
+ struct openl2tp_event_tlv *tlv; 
+ uint16_t tid = tunnel_id; 
+ uint16_t sid = session_id; 
+ uint8_t state = up; 
+ int unit = ifunit; 
+ char *user_name = NULL; 
+ 
+ if (openl2tp_fd < 0) { 
+ result = openl2tp_client_create(); 
+ if (result < 0) { 
+ goto out; 
+ } 
+ } 
+ 
+ if (peer_authname[0] != '\0') { 
+ user_name = strdup(peer_authname); 
+ } 
+ 
+ msg->msg_signature = OPENL2TP_MSG_SIGNATURE; 
+ msg->msg_type = OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND; 
+ msg->msg_len = 0; 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; 
+ tlv->tlv_len = sizeof(tid); 
+ memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; 
+ tlv->tlv_len = sizeof(sid); 
+ memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_STATE; 
+ tlv->tlv_len = sizeof(state); 
+ memcpy(&tlv->tlv_value[0], &state, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_UNIT; 
+ tlv->tlv_len = sizeof(unit); 
+ memcpy(&tlv->tlv_value[0], &unit, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_IFNAME; 
+ tlv->tlv_len = strlen(ifname) + 1; 
+ memcpy(&tlv->tlv_value[0], ifname, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ 
+ if (user_name != NULL) { 
+ tlv = (void *) &msg->msg_data[msg->msg_len]; 
+ tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_USER_NAME; 
+ tlv->tlv_len = strlen(user_name) + 1; 
+ memcpy(&tlv->tlv_value[0], user_name, tlv->tlv_len); 
+ msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 
+ } 
+ 
+ result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, MSG_NOSIGNAL); 
+ if (result < 0) { 
+ error("openl2tp send: %m"); 
+ } 
+ if (result != (sizeof(*msg) + msg->msg_len)) { 
+ warn("openl2tp send: unexpected byte count %d, expected %d", result, sizeof(msg) + msg->msg_len); 
+ } 
+ dbglog("openl2tp send: sent PPP_UPDOWN_IND, %d bytes", result); 
+ 
+out: 
+ if (old_pppol2tp_ip_updown_hook != NULL) { 
+ (*old_pppol2tp_ip_updown_hook)(tunnel_id, session_id, up); 
+ } 
+ 
+ return; 
+} 
+ 
+/***************************************************************************** 
+ * When a multilink interface is created, there are 2 cases to consider. 
+ * 
+ * 1. The new interface is the first of a multilink bundle (master). 
+ * 2. The new interface is being attached to an existing bundle. 
+ * 
+ * The first case is handled by existing code because the interface 
+ * generates ip-up events just like standard interfaces. But in the 
+ * second case, where the interface is added to an existing ppp 
+ * bundle, pppd does not do IP negotiation and so as a result, no 
+ * ip-up event is generated when the interface is created. Since 
+ * openl2tpd needs the SESSION_PPP_UPDOWN_IND for all interfaces of a 
+ * PPP bundle, we must fake the event. 
+ * 
+ * Unfortunately, pppd provides no easy way to tell that the new link 
+ * successfully attaches to the bundle. We use its new_phase_hook to 
+ * watch state changes. If the new interface reaches PHASE_NETWORK, 
+ * then it is up. However, pppd sets variables that we need to test 
+ * _after_ it reaches PHASE_NETWORK, so we use a timer to check the 
+ * variables. This is messy. It would be much easier if pppd exported 
+ * a hook for multilink interface up. 
+ *****************************************************************************/ 
+ 
+static void openl2tp_multilink_check(void *p) 
+{ 
+ if (doing_multilink && !multilink_master) { 
+ /* send event only if not master */ 
+ openl2tp_ppp_updown_ind(pppol2tp_tunnel_id, pppol2tp_session_id, 1); 
+ } 
+} 
+ 
+static int openl2tp_new_phase_hook(int phase) 
+{ 
+ if ((phase == PHASE_NETWORK) && multilink) { 
+ TIMEOUT(openl2tp_multilink_check, NULL, 1); 
+ } 
+ 
+ return 0; 
+} 
+ 
+/***************************************************************************** 
+ * Application init 
+ *****************************************************************************/ 
+ 
+void plugin_init(void) 
+{ 
+ old_pppol2tp_send_accm_hook = pppol2tp_send_accm_hook; 
+ pppol2tp_send_accm_hook = openl2tp_send_accm_ind; 
+ 
+ old_pppol2tp_ip_updown_hook = pppol2tp_ip_updown_hook; 
+ pppol2tp_ip_updown_hook = openl2tp_ppp_updown_ind; 
+ 
+ old_new_phase_hook = new_phase_hook; 
+ new_phase_hook = openl2tp_new_phase_hook; 
+} 
diff -urN ppp-2.4.4-org/pppd/plugins/pppol2tp/pppol2tp.c ppp-2.4.4/pppd/plugins/pppol2tp/pppol2tp.c
--- ppp-2.4.4-org/pppd/plugins/pppol2tp/pppol2tp.c	1970-01-01 08:00:00.000000000 +0800
+++ ppp-2.4.4/pppd/plugins/pppol2tp/pppol2tp.c	2009-03-18 17:55:10.000000000 +0800
@@ -0,0 +1,500 @@
+/* pppol2tp.c - pppd plugin to implement PPPoL2TP protocol 
+ *   for Linux using kernel pppol2tp support. 
+ * 
+ * Requires kernel pppol2tp driver which is integrated into the kernel 
+ * from 2.6.23 onwards. For earlier kernels, a version can be obtained 
+ * from the OpenL2TP project at 
+ * http://www.sourceforge.net/projects/openl2tp/
+ * 
+ * Original by Martijn van Oosterhout <kleptog@...> 
+ * Modified by jchapman@... 
+ * 
+ * Heavily based upon pppoatm.c: original notice follows 
+ * 
+ * Copyright 2000 Mitchell Blank Jr. 
+ * Based in part on work from Jens Axboe and Paul Mackerras. 
+ * Updated to ppp-2.4.1 by Bernhard Kaindl 
+ * 
+ *  This program is free software; you can redistribute it and/or 
+ *  modify it under the terms of the GNU General Public License 
+ *  as published by the Free Software Foundation; either version 
+ *  2 of the License, or (at your option) any later version. 
+ */ 
+#include <unistd.h> 
+#include <string.h> 
+#include <stdlib.h> 
+#include <errno.h> 
+#include "pppd.h" 
+#include "pathnames.h" 
+#include "fsm.h" 
+#include "lcp.h" 
+#include "ccp.h" 
+#include "ipcp.h" 
+#include <sys/stat.h> 
+#include <net/if.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h> 
+#include <netinet/in.h> 
+#include <signal.h> 
+#include <linux/version.h> 
+#include <linux/sockios.h> 
+#ifndef aligned_u64 
+/* should be defined in sys/types.h */ 
+#define aligned_u64 unsigned long long __attribute__((aligned(8))) 
+#endif 
+#include <linux/types.h> 
+#include <linux/if_ether.h> 
+#include <linux/ppp_defs.h> 
+#include <linux/if_ppp.h> 
+#include <linux/if_pppox.h> 
+#include <linux/if_pppol2tp.h> 
+ 
+/* should be added to system's socket.h... */ 
+#ifndef SOL_PPPOL2TP 
+#define SOL_PPPOL2TP 273 
+#endif 
+ 
+const char pppd_version[] = VERSION; 
+ 
+static int setdevname_pppol2tp(char **argv); 
+ 
+static int pppol2tp_fd = -1; 
+static char *pppol2tp_fd_str; 
+static bool pppol2tp_lns_mode = 0; 
+static bool pppol2tp_recv_seq = 0; 
+static bool pppol2tp_send_seq = 0; 
+static int pppol2tp_debug_mask = 0; 
+static int pppol2tp_reorder_timeout = 0; 
+static char pppol2tp_ifname[32] = { 0, }; 
+int pppol2tp_tunnel_id = 0; 
+int pppol2tp_session_id = 0; 
+ 
+static int device_got_set = 0; 
+struct channel pppol2tp_channel; 
+ 
+static void (*old_snoop_recv_hook)(unsigned char *p, int len) = NULL; 
+static void (*old_snoop_send_hook)(unsigned char *p, int len) = NULL; 
+static void (*old_ip_up_hook)(void) = NULL; 
+static void (*old_ip_down_hook)(void) = NULL; 
+ 
+/* Hook provided to allow other plugins to handle ACCM changes */ 
+void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, uint32_t send_accm, uint32_t recv_accm) = NULL; 
+ 
+/* Hook provided to allow other plugins to handle IP up/down */ 
+void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up) = NULL; 
+ 
+static option_t pppol2tp_options[] = { 
+ { "pppol2tp", o_special, &setdevname_pppol2tp, 
+  "FD for PPPoL2TP socket", OPT_DEVNAM | OPT_A2STRVAL, 
+          &pppol2tp_fd_str }, 
+ { "pppol2tp_lns_mode", o_bool, &pppol2tp_lns_mode, 
+  "PPPoL2TP LNS behavior. Default off.", 
+  OPT_PRIO | OPRIO_CFGFILE }, 
+ { "pppol2tp_send_seq", o_bool, &pppol2tp_send_seq, 
+  "PPPoL2TP enable sequence numbers in transmitted data packets. Default off.", 
+  OPT_PRIO | OPRIO_CFGFILE }, 
+ { "pppol2tp_recv_seq", o_bool, &pppol2tp_recv_seq, 
+  "PPPoL2TP enforce sequence numbers in received data packets. Default off.", 
+  OPT_PRIO | OPRIO_CFGFILE }, 
+ { "pppol2tp_reorderto", o_int, &pppol2tp_reorder_timeout, 
+  "PPPoL2TP data packet reorder timeout. Default 0 (no reordering).", 
+  OPT_PRIO }, 
+ { "pppol2tp_debug_mask", o_int, &pppol2tp_debug_mask, 
+  "PPPoL2TP debug mask. Default: no debug.", 
+  OPT_PRIO }, 
+ { "pppol2tp_ifname", o_string, &pppol2tp_ifname, 
+  "Set interface name of PPP interface", 
+  OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, 16 }, 
+ { "pppol2tp_tunnel_id", o_int, &pppol2tp_tunnel_id, 
+  "PPPoL2TP tunnel_id.", 
+  OPT_PRIO }, 
+ { "pppol2tp_session_id", o_int, &pppol2tp_session_id, 
+  "PPPoL2TP session_id.", 
+  OPT_PRIO }, 
+ { NULL } 
+}; 
+
+static int setdevname_pppol2tp(char **argv) 
+{ 
+ union { 
+ char buffer[128]; 
+ struct sockaddr pppol2tp; 
+ } s; 
+ int len = sizeof(s); 
+ char **a; 
+ int tmp; 
+ int tmp_len = sizeof(tmp); 
+ 
+ if (device_got_set) 
+ return 0; 
+ 
+ if (!int_option(*argv, &pppol2tp_fd)) 
+ return 0; 
+ 
+ if(getsockname(pppol2tp_fd, (struct sockaddr *)&s, &len) < 0) { 
+ fatal("Given FD for PPPoL2TP socket invalid (%s)", strerror(errno)); 
+ } 
+ if(s.pppol2tp.sa_family != AF_PPPOX) { 
+ fatal("Socket of not a PPPoX socket"); 
+ } 
+ 
+ /* Do a test getsockopt() to ensure that the kernel has the necessary 
+ * feature available. 
+ */ 
+ if (getsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_DEBUG, &tmp, &tmp_len) < 0) { 
+ fatal("PPPoL2TP kernel driver not installed"); 
+ } 
+ 
+ /* Setup option defaults. Compression options are disabled! */ 
+ 
+ modem = 0; 
+ 
+ lcp_allowoptions[0].neg_accompression = 1; 
+ lcp_wantoptions[0].neg_accompression = 0; 
+ 
+ lcp_allowoptions[0].neg_pcompression = 1; 
+ lcp_wantoptions[0].neg_pcompression = 0; 
+ 
+ ccp_allowoptions[0].deflate = 0; 
+ ccp_wantoptions[0].deflate = 0; 
+ 
+ ipcp_allowoptions[0].neg_vj = 0; 
+ ipcp_wantoptions[0].neg_vj = 0; 
+ 
+ ccp_allowoptions[0].bsd_compress = 0; 
+ ccp_wantoptions[0].bsd_compress = 0; 
+ 
+ the_channel = &pppol2tp_channel; 
+ device_got_set = 1; 
+ 
+ return 1; 
+} 
+ 
+static int connect_pppol2tp(void) 
+{ 
+ if(pppol2tp_fd == -1) { 
+ fatal("No PPPoL2TP FD specified"); 
+ } 
+ 
+ return pppol2tp_fd; 
+} 
+ 
+static void disconnect_pppol2tp(void) 
+{ 
+ if (pppol2tp_fd >= 0) { 
+ close(pppol2tp_fd); 
+ pppol2tp_fd = -1; 
+ } 
+} 
+ 
+static void send_config_pppol2tp(int mtu, 
+      u_int32_t asyncmap, 
+      int pcomp, 
+      int accomp) 
+{ 
+ struct ifreq ifr; 
+ int on = 1; 
+ int fd; 
+ char reorderto[16]; 
+ char tid[8]; 
+ char sid[8]; 
+ 
+ if (pppol2tp_ifname[0]) { 
+ struct ifreq ifr; 
+ int fd; 
+ 
+ fd = socket(AF_INET, SOCK_DGRAM, 0); 
+ if (fd >= 0) { 
+ memset (&ifr, '\0', sizeof (ifr)); 
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 
+ strlcpy(ifr.ifr_newname, pppol2tp_ifname, sizeof(ifr.ifr_name)); 
+ ioctl(fd, SIOCSIFNAME, (caddr_t) &ifr); 
+ strlcpy(ifname, pppol2tp_ifname, 32); 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) { 
+ dbglog("ppp%d: interface name %s", ifunit, ifname); 
+ } 
+ } 
+ close(fd); 
+ } 
+ 
+ if ((lcp_allowoptions[0].mru > 0) && (mtu > lcp_allowoptions[0].mru)) { 
+ warn("Overriding mtu %d to %d", mtu, lcp_allowoptions[0].mru); 
+ mtu = lcp_allowoptions[0].mru; 
+ } 
+ netif_set_mtu(ifunit, mtu); 
+ 
+ reorderto[0] = '\0'; 
+ if (pppol2tp_reorder_timeout > 0) 
+ sprintf(&reorderto[0], "%d ", pppol2tp_reorder_timeout); 
+ tid[0] = '\0'; 
+ if (pppol2tp_tunnel_id > 0) 
+ sprintf(&tid[0], "%hu ", pppol2tp_tunnel_id); 
+ sid[0] = '\0'; 
+ if (pppol2tp_session_id > 0) 
+ sprintf(&sid[0], "%hu ", pppol2tp_session_id); 
+ 
+ dbglog("PPPoL2TP options: %s%s%s%s%s%s%s%s%sdebugmask %d", 
+       pppol2tp_recv_seq ? "recvseq " : "", 
+       pppol2tp_send_seq ? "sendseq " : "", 
+       pppol2tp_lns_mode ? "lnsmode " : "", 
+       pppol2tp_reorder_timeout ? "reorderto " : "", reorderto, 
+       pppol2tp_tunnel_id ? "tid " : "", tid, 
+       pppol2tp_session_id ? "sid " : "", sid, 
+       pppol2tp_debug_mask); 
+ 
+ if (pppol2tp_recv_seq) 
+ if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_RECVSEQ, &on, sizeof(on)) < 0) 
+ fatal("setsockopt(PPPOL2TP_RECVSEQ): %m"); 
+ if (pppol2tp_send_seq) 
+ if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_SENDSEQ, &on, sizeof(on)) < 0) 
+ fatal("setsockopt(PPPOL2TP_SENDSEQ): %m"); 
+ if (pppol2tp_lns_mode) 
+ if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_LNSMODE, &on, sizeof(on)) < 0) 
+ fatal("setsockopt(PPPOL2TP_LNSMODE): %m"); 
+ if (pppol2tp_reorder_timeout) 
+ if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_REORDERTO, &pppol2tp_reorder_timeout, sizeof(pppol2tp_reorder_timeout)) < 0) 
+ fatal("setsockopt(PPPOL2TP_REORDERTO): %m"); 
+ if (pppol2tp_debug_mask) 
+ if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_DEBUG, &pppol2tp_debug_mask, sizeof(pppol2tp_debug_mask)) < 0) 
+ fatal("setsockopt(PPPOL2TP_DEBUG): %m"); 
+} 
+ 
+static void recv_config_pppol2tp(int mru, 
+      u_int32_t asyncmap, 
+      int pcomp, 
+      int accomp) 
+{ 
+ if ((lcp_allowoptions[0].mru > 0) && (mru > lcp_allowoptions[0].mru)) { 
+ warn("Overriding mru %d to mtu value %d", mru, lcp_allowoptions[0].mru); 
+ mru = lcp_allowoptions[0].mru; 
+ } 
+ if ((ifunit >= 0) && ioctl(pppol2tp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) 
+ error("Couldn't set PPP MRU: %m"); 
+} 
+ 
+/***************************************************************************** 
+ * Snoop LCP message exchanges to capture negotiated ACCM values. 
+ * When asyncmap values have been seen from both sides, give the values to 
+ * L2TP. 
+ * This code is derived from Roaring Penguin L2TP. 
+ *****************************************************************************/ 
+ 
+static void pppol2tp_lcp_snoop(unsigned char *buf, int len, int incoming) 
+{ 
+ static bool got_send_accm = 0; 
+ static bool got_recv_accm = 0; 
+ static uint32_t recv_accm = 0xffffffff; 
+ static uint32_t send_accm = 0xffffffff; 
+ static bool snooping = 1; 
+ 
+ uint16_t protocol; 
+ uint16_t lcp_pkt_len; 
+ int opt, opt_len; 
+ int reject; 
+ unsigned char const *opt_data; 
+ uint32_t accm; 
+ 
+ /* Skip HDLC header */ 
+ buf += 2; 
+ len -= 2; 
+
+ /* Unreasonably short frame?? */ 
+ if (len <= 0) return; 
+ 
+ /* Get protocol */ 
+ if (buf[0] & 0x01) { 
+ /* Compressed protcol field */ 
+ protocol = buf[0]; 
+ } else { 
+ protocol = ((unsigned int) buf[0]) * 256 + buf[1]; 
+ } 
+ 
+ /* If it's a network protocol, stop snooping */ 
+ if (protocol <= 0x3fff) { 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) { 
+ dbglog("Turning off snooping: Network protocol %04x found.", protocol); 
+ } 
+ snooping = 0; 
+ return; 
+ } 
+ 
+ /* If it's not LCP, do not snoop */ 
+ if (protocol != 0xc021) { 
+ return; 
+ } 
+ 
+ /* Skip protocol; go to packet data */ 
+ buf += 2; 
+ len -= 2; 
+ 
+ /* Unreasonably short frame?? */ 
+ if (len <= 0) return; 
+ 
+ /* Look for Configure-Ack or Configure-Reject code */ 
+ if (buf[0] != CONFACK && buf[0] != CONFREJ) return; 
+ 
+ reject = (buf[0] == CONFREJ); 
+ 
+ lcp_pkt_len = ((unsigned int) buf[2]) * 256 + buf[3]; 
+ 
+ /* Something fishy with length field? */ 
+ if (lcp_pkt_len > len) return; 
+ 
+ /* Skip to options */ 
+ len = lcp_pkt_len - 4; 
+ buf += 4; 
+ 
+ while (len > 0) { 
+ /* Pull off an option */ 
+ opt = buf[0]; 
+ opt_len = buf[1]; 
+ opt_data = &buf[2]; 
+ if (opt_len > len || opt_len < 2) break; 
+ len -= opt_len; 
+ buf += opt_len; 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) { 
+ dbglog("Found option type %02x; len %d", opt, opt_len); 
+ } 
+ 
+ /* We are specifically interested in ACCM */ 
+ if (opt == CI_ASYNCMAP && opt_len == 0x06) { 
+ if (reject) { 
+ /* ACCM negotiation REJECTED; use default */ 
+ accm = 0xffffffff; 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_DATA) { 
+ dbglog("Rejected ACCM negotiation; defaulting (%s)", incoming ? "incoming" : "outgoing"); 
+ } 
+ recv_accm = accm; 
+ send_accm = accm; 
+ got_recv_accm = 1; 
+ got_send_accm = 1; 
+ } else { 
+ memcpy(&accm, opt_data, sizeof(accm)); 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_DATA) { 
+ dbglog("Found ACCM of %08x (%s)", accm, incoming ? "incoming" : "outgoing"); 
+ } 
+ if (incoming) { 
+ recv_accm = accm; 
+ got_recv_accm = 1; 
+ } else { 
+ send_accm = accm; 
+ got_send_accm = 1; 
+ } 
+ } 
+ 
+ if (got_recv_accm && got_send_accm) { 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) { 
+ dbglog("Telling L2TP: Send ACCM = %08x; Receive ACCM = %08x", send_accm, recv_accm); 
+ } 
+ if (pppol2tp_send_accm_hook != NULL) { 
+ (*pppol2tp_send_accm_hook)(pppol2tp_tunnel_id, pppol2tp_session_id, send_accm, recv_accm); 
+ } 
+ got_recv_accm = 0; 
+ got_send_accm = 0; 
+ } 
+ } 
+ } 
+} 
+ 
+static void pppol2tp_lcp_snoop_recv(unsigned char *p, int len) 
+{ 
+ if (old_snoop_recv_hook != NULL) 
+ (*old_snoop_recv_hook)(p, len); 
+ pppol2tp_lcp_snoop(p, len, 1); 
+} 
+ 
+static void pppol2tp_lcp_snoop_send(unsigned char *p, int len) 
+{ 
+ if (old_snoop_send_hook != NULL) 
+ (*old_snoop_send_hook)(p, len); 
+ pppol2tp_lcp_snoop(p, len, 0); 
+} 
+ 
+/***************************************************************************** 
+ * Interface up/down events 
+ *****************************************************************************/ 
+ 
+static void pppol2tp_ip_up_hook(void) 
+{ 
+ if (old_ip_up_hook != NULL) 
+ (*old_ip_up_hook)(); 
+ 
+ if (pppol2tp_ip_updown_hook != NULL) { 
+ (*pppol2tp_ip_updown_hook)(pppol2tp_tunnel_id, pppol2tp_session_id, 1); 
+ } 
+} 
+ 
+static void pppol2tp_ip_down_hook(void) 
+{ 
+ if (old_ip_down_hook != NULL) 
+ (*old_ip_down_hook)(); 
+ 
+ if (pppol2tp_ip_updown_hook != NULL) { 
+ (*pppol2tp_ip_updown_hook)(pppol2tp_tunnel_id, pppol2tp_session_id, 0); 
+ } 
+} 
+ 
+/***************************************************************************** 
+ * Application init 
+ *****************************************************************************/ 
+ 
+static void pppol2tp_check_options(void) 
+{ 
+ /* Enable LCP snooping for ACCM options only for LNS */ 
+ if (pppol2tp_lns_mode) { 
+ if ((pppol2tp_tunnel_id == 0) || (pppol2tp_session_id == 0)) { 
+ fatal("tunnel_id/session_id values not specified"); 
+ } 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) { 
+ dbglog("Enabling LCP snooping"); 
+ } 
+ old_snoop_recv_hook = snoop_recv_hook; 
+ old_snoop_send_hook = snoop_send_hook; 
+ 
+ snoop_recv_hook = pppol2tp_lcp_snoop_recv; 
+ snoop_send_hook = pppol2tp_lcp_snoop_send; 
+ } 
+ 
+ /* Hook up ip up/down hooks to send indicator to openl2tpd that the link is up */ 
+ old_ip_up_hook = ip_up_hook; 
+ ip_up_hook = pppol2tp_ip_up_hook; 
+ old_ip_down_hook = ip_down_hook; 
+ ip_down_hook = pppol2tp_ip_down_hook; 
+} 
+ 
+/* Called just before pppd exits. 
+ */ 
+static void pppol2tp_cleanup(void) 
+{ 
+ if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) { 
+ dbglog("pppol2tp: exiting."); 
+ } 
+ disconnect_pppol2tp(); 
+} 
+ 
+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 
+ fatal("No PPPoL2TP support on this OS"); 
+#endif 
+ add_options(pppol2tp_options); 
+} 
+ 
+struct channel pppol2tp_channel = { 
+    options: pppol2tp_options, 
+    process_extra_options: NULL, 
+    check_options: &pppol2tp_check_options, 
+    connect: &connect_pppol2tp, 
+    disconnect: &disconnect_pppol2tp, 
+    establish_ppp: &generic_establish_ppp, 
+    disestablish_ppp: &generic_disestablish_ppp, 
+    send_config: &send_config_pppol2tp, 
+    recv_config: &recv_config_pppol2tp, 
+    close: NULL, 
+    cleanup: NULL 
+}; 
