--- /dev/null
+find_route() {
+ ip route get $1 | sed -e 's/ /\n/g' | \
+ sed -ne '1p;/via/{N;p};/dev/{N;p};/src/{N;p};/mtu/{N;p}'
+}
+
+scan_l2tp() {
+ config_set "$1" device "l2tp-$1"
+}
+
+stop_interface_l2tp() {
+ local config="$1"
+ local lock="/var/lock/l2tp-${config}"
+ local optfile="/tmp/l2tp/options.${config}"
+ local l2tpcontrol=/var/run/xl2tpd/l2tp-control
+
+ lock "$lock"
+
+ [ -p ${l2tpcontrol} ] && echo "r l2tp-${config}" > ${l2tpcontrol}
+ rm -f ${optfile}
+
+ for ip in $(uci_get_state network "$1" serv_addrs); do
+ ip route del "$ip" 2>/dev/null
+ done
+
+ lock -u "$lock"
+}
+
+setup_interface_l2tp() {
+ local config="$2"
+ local lock="/var/lock/l2tp-${config}"
+ local optfile="/tmp/l2tp/options.${config}"
+
+ lock "$lock"
+
+ if [ ! -p /var/run/xl2tpd/l2tp-control ]; then
+ /etc/init.d/xl2tpd start
+ fi
+
+ local device
+ config_get device "$config" device "l2tp-$config"
+
+ local server
+ config_get server "$config" server
+
+ local username
+ config_get username "$config" username
+
+ local password
+ config_get password "$config" password
+
+ local keepalive
+ config_get keepalive "$config" keepalive
+
+ local pppd_options
+ config_get pppd_options "$config" pppd_options
+
+ local defaultroute
+ config_get_bool defaultroute "$config" defaultroute 1
+ [ "$defaultroute" -eq 1 ] && \
+ defaultroute="defaultroute replacedefaultroute" || defaultroute="nodefaultroute"
+
+ local interval="${keepalive##*[, ]}"
+ [ "$interval" != "$keepalive" ] || interval=5
+
+ local dns
+ config_get dns "$config" dns
+
+ local has_dns=0
+ local peer_default=1
+ [ -n "$dns" ] && {
+ has_dns=1
+ peer_default=0
+ }
+
+ local peerdns
+ config_get_bool peerdns "$config" peerdns $peer_default
+
+ [ "$peerdns" -eq 1 ] && {
+ peerdns="usepeerdns"
+ } || {
+ peerdns=""
+ add_dns "$config" $dns
+ }
+
+ local ipv6
+ config_get ipv6 "$config" ipv6 1
+ [ "$ipv6" -eq 1 ] && ipv6="+ipv6" || ipv6=""
+
+ local serv_addrs=""
+ for ip in $(resolveip -t 3 "$server"); do
+ append serv_addrs "$ip"
+ ip route replace $(find_route $ip)
+ done
+ uci_toggle_state network "$config" serv_addrs "$serv_addrs"
+
+ # fix up the netmask
+ config_get netmask "$config" netmask
+ [ -z "$netmask" -o -z "$device" ] || ifconfig $device netmask $netmask
+
+ config_get mtu "$config" mtu
+
+ mkdir -p /tmp/l2tp
+
+ echo ${keepalive:+lcp-echo-interval $interval lcp-echo-failure ${keepalive%%[, ]*}} > "${optfile}"
+ echo "$peerdns" >> "${optfile}"
+ echo "$defaultroute" >> "${optfile}"
+ echo "${username:+user \"$username\" password \"$password\"}" >> "${optfile}"
+ echo "ipparam \"$config\"" >> "${optfile}"
+ echo "ifname \"l2tp-$config\"" >> "${optfile}"
+ # Don't wait for LCP term responses; exit immediately when killed.
+ echo "lcp-max-terminate 0" >> "${optfile}"
+ echo "${ipv6} ${pppd_options}" >> "${optfile}"
+
+ xl2tpd-control remove l2tp-${config}
+ # Wait and ensure pppd has died.
+ while [ -d /sys/class/net/l2tp-${config} ]; do
+ sleep 1
+ done
+
+ xl2tpd-control add l2tp-${config} pppoptfile=${optfile} lns=${server} redial=yes redial timeout=20
+ xl2tpd-control connect l2tp-${config}
+
+ lock -u "${lock}"
+}
--- /dev/null
+diff --git a/Makefile b/Makefile
+index 6f6481f..778f38d 100644
+--- a/Makefile
++++ b/Makefile
+@@ -62,8 +62,8 @@
+ # are packages seperately (eg kernel-headers on Fedora)
+ # Note: 2.6.23+ support still needs some changes in the xl2tpd source
+ #
+-#OSFLAGS+= -DUSE_KERNEL
+-#
++# Kernel mode fixed by sigwall <fionov@gmail.com>
++OSFLAGS+= -DUSE_KERNEL
+ #
+ # Uncomment the next line for FreeBSD
+ #
+diff --git a/call.c b/call.c
+index d1b1858..b672f91 100644
+--- a/call.c
++++ b/call.c
+@@ -680,6 +680,8 @@ struct call *get_call (int tunnel, int call, struct in_addr addr, int port,
+ st->peer.sin_port = port;
+ st->refme = refme;
+ st->refhim = refhim;
++ st->udp_fd = -1;
++ st->pppox_fd = -1;
+ bcopy (&addr, &st->peer.sin_addr, sizeof (addr));
+ st->next = tunnels.head;
+ tunnels.head = st;
+diff --git a/control.c b/control.c
+index 0892df9..9362ffd 100644
+--- a/control.c
++++ b/control.c
+@@ -596,6 +596,9 @@ int control_finish (struct tunnel *t, struct call *c)
+ if (gconfig.debug_state)
+ l2tp_log (LOG_DEBUG, "%s: sending SCCCN\n", __FUNCTION__);
+ control_xmit (buf);
++
++ connect_pppol2tp(t);
++
+ /* Schedule a HELLO */
+ tv.tv_sec = HELLO_DELAY;
+ tv.tv_usec = 0;
+@@ -608,6 +611,7 @@ int control_finish (struct tunnel *t, struct call *c)
+ "Connection established to %s, %d. Local: %d, Remote: %d (ref=%u/%u).\n",
+ IPADDY (t->peer.sin_addr),
+ ntohs (t->peer.sin_port), t->ourtid, t->tid, t->refme, t->refhim);
++
+ if (t->lac)
+ {
+ /* This is part of a LAC, so we want to go ahead
+@@ -635,6 +639,9 @@ int control_finish (struct tunnel *t, struct call *c)
+ IPADDY (t->peer.sin_addr),
+ ntohs (t->peer.sin_port), t->ourtid, t->tid, t->refme, t->refhim,
+ t->lns->entname);
++
++ connect_pppol2tp(t);
++
+ /* Schedule a HELLO */
+ tv.tv_sec = HELLO_DELAY;
+ tv.tv_usec = 0;
+diff --git a/l2tp.h b/l2tp.h
+index 2724fff..856423f 100644
+--- a/l2tp.h
++++ b/l2tp.h
+@@ -167,6 +167,8 @@ struct tunnel
+ int ourrws; /* Receive Window Size */
+ int rxspeed; /* Receive bps */
+ int txspeed; /* Transmit bps */
++ int udp_fd; /* UDP fd */
++ int pppox_fd; /* PPPOX tunnel fd */
+ struct call *self;
+ struct lns *lns; /* LNS that owns us */
+ struct lac *lac; /* LAC that owns us */
+@@ -220,6 +222,7 @@ extern void control_xmit (void *);
+ extern int ppd;
+ extern int switch_io; /* jz */
+ extern int control_fd;
++extern int connect_pppol2tp(struct tunnel *t);
+ extern int start_pppd (struct call *c, struct ppp_opts *);
+ extern void magic_lac_dial (void *);
+ extern int get_entropy (unsigned char *, int);
+diff --git a/linux/include/linux/if_pppol2tp.h b/linux/include/linux/if_pppol2tp.h
+index a7d6a22..0795e4a 100644
+--- a/linux/include/linux/if_pppol2tp.h
++++ b/linux/include/linux/if_pppol2tp.h
+@@ -36,6 +36,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/network.c b/network.c
+index 241bd82..fde250e 100644
+--- a/network.c
++++ b/network.c
+@@ -22,6 +22,7 @@
+ #include <unistd.h>
+ #include <stdlib.h>
+ #include <sys/ioctl.h>
++#include <sys/wait.h>
+ #ifndef LINUX
+ # include <sys/uio.h>
+ #endif
+@@ -36,6 +37,51 @@ int server_socket; /* Server socket */
+ int kernel_support; /* Kernel Support there or not? */
+ #endif
+
++#ifdef USE_KERNEL
++void modprobe() {
++ char * modules[] = { "l2tp_ppp", "pppol2tp", NULL };
++ char ** module;
++ char buf[256], *tok;
++ int pid, exit_status, fd;
++
++ FILE * fmod = fopen("/proc/modules", "r");
++
++ if (fmod == NULL)
++ return;
++
++ while (fgets(buf, 255, fmod) != NULL) {
++ if ((tok = strtok(buf, " ")) != NULL) {
++ for (module = modules; *module != NULL; ++module) {
++ if (!strcmp(*module, tok)) {
++ fclose(fmod);
++ return;
++ }
++ }
++ }
++ }
++
++ fclose(fmod);
++
++ for (module = modules; *module != NULL; ++module) {
++ if ((pid = fork()) >= 0) {
++ if (pid == 0) {
++ setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1);
++ if ((fd = open("/dev/null", O_RDWR)) > -1) {
++ dup2(fd, 1);
++ dup2(fd, 2);
++ }
++ execlp("modprobe", "modprobe", "-q", *module, (char *)NULL);
++ exit(1);
++ } else {
++ if ((pid = waitpid(pid, &exit_status, 0)) != -1 && WIFEXITED(exit_status)) {
++ if (WEXITSTATUS(exit_status) == 0)
++ return;
++ }
++ }
++ }
++ }
++}
++#endif
+
+ int init_network (void)
+ {
+@@ -45,6 +91,7 @@ int init_network (void)
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = gconfig.listenaddr;
+ server.sin_port = htons (gconfig.port);
++ int flags;
+ if ((server_socket = socket (PF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ l2tp_log (LOG_CRIT, "%s: Unable to allocate socket. Terminating.\n",
+@@ -52,6 +99,10 @@ int init_network (void)
+ return -EINVAL;
+ };
+
++ flags = 1;
++ setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
++ setsockopt(server_socket, SOL_SOCKET, SO_NO_CHECK, &flags, sizeof(flags));
++
+ if (bind (server_socket, (struct sockaddr *) &server, sizeof (server)))
+ {
+ close (server_socket);
+@@ -91,6 +142,7 @@ int init_network (void)
+ }
+ else
+ {
++ modprobe();
+ int kernel_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
+ if (kernel_fd < 0)
+ {
+@@ -321,6 +373,11 @@ int build_fdset (fd_set *readfds)
+
+ while (tun)
+ {
++ if (tun->udp_fd > -1) {
++ if (tun->udp_fd > max)
++ max = tun->udp_fd;
++ FD_SET (tun->udp_fd, readfds);
++ }
+ call = tun->call_head;
+ while (call)
+ {
+@@ -390,6 +447,8 @@ void network_thread ()
+ struct iovec iov;
+ char cbuf[256];
+ unsigned int refme, refhim;
++ int * currentfd;
++ int server_socket_processed;
+
+ /* This one buffer can be recycled for everything except control packets */
+ buf = new_buf (MAX_RECV_SIZE);
+@@ -428,7 +487,21 @@ void network_thread ()
+ {
+ do_control ();
+ }
+- if (FD_ISSET (server_socket, &readfds))
++ server_socket_processed = 0;
++ currentfd = NULL;
++ st = tunnels.head;
++ while (st || !server_socket_processed) {
++ if (st && (st->udp_fd == -1)) {
++ st=st->next;
++ continue;
++ }
++ if (st) {
++ currentfd = &st->udp_fd;
++ } else {
++ currentfd = &server_socket;
++ server_socket_processed = 1;
++ }
++ if (FD_ISSET (*currentfd, &readfds))
+ {
+ /*
+ * Okay, now we're ready for reading and processing new data.
+@@ -457,12 +530,19 @@ void network_thread ()
+ msgh.msg_flags = 0;
+
+ /* Receive one packet. */
+- recvsize = recvmsg(server_socket, &msgh, 0);
++ recvsize = recvmsg(*currentfd, &msgh, 0);
+
+ if (recvsize < MIN_PAYLOAD_HDR_LEN)
+ {
+ if (recvsize < 0)
+ {
++ if (errno == ECONNREFUSED) {
++ close(*currentfd);
++ }
++ if ((errno == ECONNREFUSED) ||
++ (errno == EBADF)) {
++ *currentfd = -1;
++ }
+ if (errno != EAGAIN)
+ l2tp_log (LOG_WARNING,
+ "%s: recvfrom returned error %d (%s)\n",
+@@ -567,6 +647,8 @@ void network_thread ()
+ }
+ };
+ }
++ if (st) st=st->next;
++ }
+
+ /*
+ * finished obvious sources, look for data from PPP connections.
+@@ -639,3 +721,82 @@ void network_thread ()
+ }
+
+ }
++
++int connect_pppol2tp(struct tunnel *t) {
++#ifdef USE_KERNEL
++ if (kernel_support) {
++ int ufd = -1, fd2 = -1;
++ int flags;
++ struct sockaddr_pppol2tp sax;
++
++ struct sockaddr_in server;
++ server.sin_family = AF_INET;
++ server.sin_addr.s_addr = gconfig.listenaddr;
++ server.sin_port = htons (gconfig.port);
++ if ((ufd = socket (PF_INET, SOCK_DGRAM, 0)) < 0)
++ {
++ l2tp_log (LOG_CRIT, "%s: Unable to allocate UDP socket. Terminating.\n",
++ __FUNCTION__);
++ return -EINVAL;
++ };
++
++ flags=1;
++ setsockopt(ufd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
++ setsockopt(ufd, SOL_SOCKET, SO_NO_CHECK, &flags, sizeof(flags));
++
++ if (bind (ufd, (struct sockaddr *) &server, sizeof (server)))
++ {
++ close (ufd);
++ l2tp_log (LOG_CRIT, "%s: Unable to bind UDP socket: %s. Terminating.\n",
++ __FUNCTION__, strerror(errno), errno);
++ return -EINVAL;
++ };
++ server = t->peer;
++ flags = fcntl(ufd, F_GETFL);
++ if (flags == -1 || fcntl(ufd, F_SETFL, flags | O_NONBLOCK) == -1) {
++ l2tp_log (LOG_WARNING, "%s: Unable to set UDP socket nonblock.\n",
++ __FUNCTION__);
++ return -EINVAL;
++ }
++ if (connect (ufd, (struct sockaddr *) &server, sizeof(server)) < 0) {
++ l2tp_log (LOG_CRIT, "%s: Unable to connect UDP peer. Terminating.\n",
++ __FUNCTION__);
++ return -EINVAL;
++ }
++
++ t->udp_fd=ufd;
++
++ fd2 = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
++ if (fd2 < 0) {
++ l2tp_log (LOG_WARNING, "%s: Unable to allocate PPPoL2TP socket.\n",
++ __FUNCTION__);
++ return -EINVAL;
++ }
++ flags = fcntl(fd2, F_GETFL);
++ if (flags == -1 || fcntl(fd2, F_SETFL, flags | O_NONBLOCK) == -1) {
++ l2tp_log (LOG_WARNING, "%s: Unable to set PPPoL2TP socket nonblock.\n",
++ __FUNCTION__);
++ return -EINVAL;
++ }
++ sax.sa_family = AF_PPPOX;
++ sax.sa_protocol = PX_PROTO_OL2TP;
++ sax.pppol2tp.pid = 0;
++ sax.pppol2tp.fd = t->udp_fd;
++ sax.pppol2tp.addr.sin_addr.s_addr = t->peer.sin_addr.s_addr;
++ sax.pppol2tp.addr.sin_port = t->peer.sin_port;
++ sax.pppol2tp.addr.sin_family = AF_INET;
++ sax.pppol2tp.s_tunnel = t->ourtid;
++ sax.pppol2tp.s_session = 0;
++ sax.pppol2tp.d_tunnel = t->tid;
++ sax.pppol2tp.d_session = 0;
++ if ((connect(fd2, (struct sockaddr *)&sax, sizeof(sax))) < 0) {
++ l2tp_log (LOG_WARNING, "%s: Unable to connect PPPoL2TP socket. %d %s\n",
++ __FUNCTION__, errno, strerror(errno));
++ close(fd2);
++ return -EINVAL;
++ }
++ t->pppox_fd = fd2;
++ }
++#endif
++ return 0;
++}
+diff --git a/xl2tpd.c b/xl2tpd.c
+index 307ac2e..3fb6dd7 100644
+--- a/xl2tpd.c
++++ b/xl2tpd.c
+@@ -278,7 +278,11 @@ void death_handler (int signal)
+ struct tunnel *st, *st2;
+ int sec;
+ l2tp_log (LOG_CRIT, "%s: Fatal signal %d received\n", __FUNCTION__, signal);
++#ifdef USE_KERNEL
++ if (kernel_support || signal != SIGTERM) {
++#else
+ if (signal != SIGTERM) {
++#endif
+ st = tunnels.head;
+ while (st)
+ {
+@@ -349,7 +353,7 @@ int start_pppd (struct call *c, struct ppp_opts *opts)
+ int flags;
+ #endif
+ int pos = 1;
+- int fd2;
++ int fd2 = -1;
+ #ifdef DEBUG_PPPD
+ int x;
+ #endif
+@@ -397,7 +401,7 @@ int start_pppd (struct call *c, struct ppp_opts *opts)
+ sax.sa_family = AF_PPPOX;
+ sax.sa_protocol = PX_PROTO_OL2TP;
+ sax.pppol2tp.pid = 0;
+- sax.pppol2tp.fd = server_socket;
++ sax.pppol2tp.fd = c->container->udp_fd;
+ sax.pppol2tp.addr.sin_addr.s_addr = c->container->peer.sin_addr.s_addr;
+ sax.pppol2tp.addr.sin_port = c->container->peer.sin_port;
+ sax.pppol2tp.addr.sin_family = AF_INET;
+@@ -408,6 +412,7 @@ int start_pppd (struct call *c, struct ppp_opts *opts)
+ if (connect(fd2, (struct sockaddr *)&sax, sizeof(sax)) < 0) {
+ l2tp_log (LOG_WARNING, "%s: Unable to connect PPPoL2TP socket.\n",
+ __FUNCTION__);
++ close(fd2);
+ return -EINVAL;
+ }
+ stropt[pos++] = strdup ("plugin");
+@@ -484,7 +489,7 @@ int start_pppd (struct call *c, struct ppp_opts *opts)
+ dup2 (fd2, 0);
+ dup2 (fd2, 1);
+ close(fd2);
+-
++ }
+ /* close all the calls pty fds */
+ st = tunnels.head;
+ while (st)
+@@ -492,12 +497,17 @@ int start_pppd (struct call *c, struct ppp_opts *opts)
+ sc = st->call_head;
+ while (sc)
+ {
+- close (sc->fd);
++#ifdef USE_KERNEL
++ if (kernel_support) {
++ close(st->udp_fd); /* tunnel UDP fd */
++ close(st->pppox_fd); /* tunnel PPPoX fd */
++ } else
++#endif
++ close (sc->fd); /* call pty fd */
+ sc = sc->next;
+ }
+ st = st->next;
+ }
+- }
+
+ /* close the UDP socket fd */
+ close (server_socket);
+@@ -615,6 +625,10 @@ void destroy_tunnel (struct tunnel *t)
+ the memory pointed to by t->chal_us.vector at some other place */
+ if (t->chal_them.vector)
+ free (t->chal_them.vector);
++ if (t->pppox_fd > -1 )
++ close (t->pppox_fd);
++ if (t->udp_fd > -1 )
++ close (t->udp_fd);
+ free (t);
+ free (me);
+ }