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
@@ -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);
@@ -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);
 }