diff -urN x25fix.tmp/Documentation/networking/sk_tunnel.txt sktn.tmp/Documentation/networking/sk_tunnel.txt
--- x25fix.tmp/Documentation/networking/sk_tunnel.txt	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/Documentation/networking/sk_tunnel.txt	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,433 @@
+DISCLAIMER: this is experimental code. Be prepared to recover from a
+kernel crashes.
+
+
+
+HOWTO support generic ppp and tunneling on top of your network protocol
+=======================================================================
+
+Copyright 2000 by Henner Eisen. All rights reserved.
+
+This document can be distributed and used under the terms of the GNU
+General Public license.
+
+
+This is a brief instruction for programmers and maintainers of Linux
+network protocol stacks who want to extend a protocol implementation
+in order to tunnel other protocols over a connected socket.
+
+This should work for SOCK_DGRAM and SOCK_SEQPACKET sockets which
+use datagram_poll() and skb_receive_datagram() in a straight forward
+manner. In other cases, it should still be possible, but might require
+more care and additional work.
+
+
+
+User Control
+============
+
+The usage scenario of sk_tunnel is as follows:
+
+1. A user space program creates a socket. After connection is
+   established and maybe some initial data is exchanged, the user program 
+   registers the socket at ppp_generic or at a simple tunnel network
+   interface via ioctl() SIOCSKTNREGISTER.
+
+2. After the above ioctl has returned successfully, the data path of the 
+   socket is connected to some upper layer. Incoming data can no
+   longer be read from the socket but will be passed upstream to a
+   tunnel protocol (either by means of a ppp_channel or by means of a
+   special sktn network interface). Packets written by the upper layer
+   protocol to the sktn or the ppp network interface will be send to
+   the peer.
+
+3. When user calls ioctl() SIOCSKTNUNREGISTER, the socket reverts to
+   normal operation again.
+
+
+Tunnel network devices can be created and deleted dynamically by means
+of the socket ioctl SIOCSKTNNEWIF and SIOCSKTNDELIF. This ioctl takes a
+non-negative integer argument which identifies the sktn network interface.
+The current semantics is that index N refers to the tunnel device
+with name sktnN. e.g.
+
+#include <linux/sk_tunnel.h>
+int err, one=1;
+err=ioctl(s,SIOCTSKTNNEWIF,&one);
+
+will create the linux network interface named `sktn1'.
+
+This interface can be configured by means of the usual utilities (e.g.
+`ifconfig sktn1 up', `route add XXX dev sktn1'). However, as long as no
+socket has registered at that network interface, no data will be
+received from that device and data sent to that device will be discarded.
+
+In order to actually send or receive data via that tunnel network
+interface, the user needs to establish a socket connection
+by means of the connect() or accept() system call, and then do
+
+err=ioctl(s,SIOCTSKTNREGISTER,&one);
+
+SIOCTSKTNREGISTER similarly takes an integer argument. Non-negative
+values identify the sktn network interfaces as created above.
+An index of -1 identifies the ppp_generic layer. Thus, after
+
+int minus_one=-1;
+err=ioctl(s,SIOCTSKTNREGISTER,&minus_one);
+
+the socket connection is registered as a ppp_channel at the ppp_generic
+driver. In contrast to sktn tunnel network interface, the socket
+connection is not yet attached to a particular ppp unit. However, a
+socket registered at ppp also supports some further ioctl()'s, such as
+PPPIOCGCHAN and PPPIOCGUNIT, which will be used by pppd to identify
+and associate the sktn ppp channel to a particular ppp bundle.
+  
+After data path of the socket has been registered successfully, the
+behaviour of system calls trying to send (e.g. sendmsg(), write())
+or receive (e.g. recvmsg(), read()) are undefined. Essentially, the only
+well-defined ioctl() operations on the socket are sktn-specific ioctls
+like PPPIOCGCHAN, PPPIOCGUNIT and SKTNIOCTUNREGISTER. The only other
+system calls with well-defined behaviour are close() and (if supported)
+shutdown().
+
+SKTNIOCTUNREGISTER expects no argument, it unregisters wherever the
+socket is currently registered (be it a particular sktn network
+interface or ppp). It will turn the socket back to normal mode again.
+
+Only one socket connection can be registered at each sktn
+network interface at the same time. But an arbitrary number of
+connections can be registered at ppp_generic at the same time.
+For doing multi-link, you can use techniques like the teql network
+scheduler or multi-link ppp. But this the task of the upper layer
+and totally outside the scope of the sk_tunnel framework.
+
+
+ 
+
+
+Design description -- the kernel side
+=====================================
+
+The sk_tunnel framework is a support library which aims at providing
+the common infrastructure needed by all socket layers that want
+to support tunneling.
+
+First, there is a struct sktn_channel. This objects implements the
+interface between the socket-based network protocol layer and the upper
+layer. Two different upper layers are supported:
+
+- a net_device upper layer, which implements linux net_device
+  interfaces and supports different additional encapsulation methods.
+  (currently, only raw encapsulation is implemented)
+- a ppp upper layer, which implements ppp_channels as needed by the
+  ppp_generic subsystem of the linux kernel.
+
+From the socket's code view, there is no difference in using those
+interfaces, all the upper-layer-specific code is hidden inside the
+sktn core. Thus, once your protocol code has been modified to support
+the sktn interface, it will be possible to do ppp as well as raw
+protocol tunneling on top of your protocol stack.
+
+The socket layer needs to provide and register an
+sk_xmit(struct sock*, struct sk_buff*) callback function. This
+function is called by the sk tunnel layer when a tunnel packet is to be
+sent.
+
+For passing a received packet upstream to the tunnel layer, the
+protocol code calls sktn_rx(struct sktn_channel *, struct sk_buff*).
+
+One essential service provided by the sktn layer is handling
+the tx flow control. Linux network device drivers need to
+flow control the upper layers be means of the kernel functions
+netif_wake_queue() and netif_stop_queue. Linux ppp_channels
+do similarly by calling ppp_output_wakeup() or by return codes
+from the ppp_channel's start_xmit() method.
+
+The sktn tx flow control is based on the standard socket write
+memory accounting. This is usually used to wake up or to send into
+sleep the process writing to the socket. When registered at a tunnel,
+if the socket's wmem account exceeds the high water mark, sktn will
+tell the upper layer to shut up by calling the appropriate
+upper layer flow control request functions above. And it will also
+wake up the upper layer again when the balance of the socket's wmem
+account falls below the low water mark again.
+
+The sktn framework also supports an additional sk_busy(struct sock *)
+callback functions which allows for more sophisticated tx flow
+control. The default flow control should always work, but for low
+bandwidths links, providing an additional protocol specific sk_busy()
+method could improve latency.
+
+Another service provided by the generic sk_tunnel code is doing
+appropriate cloning and passing ownership of the sk_buffs when they
+pass the layer boundaries between the upper and the lower layer
+protocol stacks.
+
+The current implementation avoids patching any network core stuff but
+depends on certain current network core implementation details:
+It expects that the socket up-calls sk->write_space() and
+sk->data_ready() are done with a read lock on sk->callback_lock hold.
+The default socket callbacks do so, and even if your protocol
+implements its own version for the above callbacks, they almost
+certainly also hold these locks when executing. Assuming this, the
+sktn framework can temporarily replace those socket callback functions
+with own methods, which pass frames upstream or flow control the
+upper layers.
+
+
+
+How to use it -- the kernel side
+================================
+
+The public interface definitions for socket tunnels are in <net/sktn.h>.
+The parts of your code dealing with user space communication (e.g.
+SIOCTSKTN ioctl()'s) might also need to include (linux/sk_tunnel.h>.
+As the ppp_channel stuff is tightly coupled with the sktn_channel
+code, it is also necessary to include the relevant ppp header files.
+
+If you want to support socket tunnels from your kernel code, you
+need to provide an sktn_channel object for the duration where
+the ppp_bundle or tunnel device is attached. This is usually done
+by providing a pointer to a struct sktn_channel in your protocol's
+control block area (which can be initialised to NULL
+when your socket code is initialised. (my_cb->sktn in the example).
+(The sktn_null_channel is a dummy channel, serving as a black-hole for
+all data communicated to that channel). (If sktn support were integrated
+in the network core, there could be an sk->sktn field commonly used
+by all protocol stacks which support tunneling).
+
+If the sktn_channel is currently attached to your socket's data path,
+normal socket IO should be bypassed and your protocol routines need
+to do something different than usual.
+
+
+When your protocol is initialised, you need to provide a jump table
+of type const struct {
+	int (* sk_busy) (struct sock *);  /* Return additional tx_busy state
+			information. Insert sktn_sk_default_busy here if you
+			don't provide an own method. */
+
+	int (* sk_xmit) (struct sock *, struct sk_buff *);  /* Send out a
+							       frame. */
+	void (* sk_rx) (struct sock *, struct sk_buff *);  /* given an
+			sk_buff	dequeued from sock's receieve_queue, do
+			remaining protocol processing and pass it
+			upstreams via sktn_rx(). */
+
+	int (* sk_hlen) (struct sock *);  /* Return amount of header space
+			an sk_buff needs before passed down via sk_xmit().*/
+
+	int (* sk_mtu) (struct sock *);  /* Return allowed len of an
+			sk_buff passed down via sk_xmit().*/
+
+} sktn_sk_ops;
+
+
+which needs to be filled with certain functions you need to implement.
+You do not necessarily need to implement an own sk_busy function. Just
+use the public function sktn_sk_default_busy() as the sk_busy method.
+The sk_rx method is optional (it is there for convenience). If you
+cannot use it, you must put a NULL pointer there.
+
+Thus, you will have declaration like
+
+static sktn_sk_ops my_sktn_sk_ops =
+{
+	sk_busy: sktn_sk_default_busy, 
+	sk_xmit: my_sk_xmit,
+	sk_rx: my_sktn_rx,
+	sk_hlen: my_sktn_hlen,
+	sk_mtu: my_sk_mtu
+};
+
+somewhere. What these functions should do is described below.
+You also need to provide storage for a pointer to an sktn_channel
+object -- e.g. in the control block (say my_cb) of your protocol.
+This should be initialised to NULL.
+
+Next, you need to support the channel related PPPIO and SIOCSKTN
+ioctl's. To do so, you'll probably insert something like this
+
+#include <linux/sk_tunnel.h>
+#include <linux/if_ppp.h>
+
+	 :
+
+	case SIOCSKTNNEWIF:
+	case SIOCSKTNDELIF:
+	case SIOCSKTNREGISTER: 
+       	case SIOCSKTNUNREGISTER:
+ 	case PPPIOCGCHAN:
+	case PPPIOCGUNIT:
+	case PPPIOCATTACH:
+       	case PPPIOCDETACH:
+	  /* Dummy TIOC for fooling current pppd */
+	case TIOCEXCL:
+	case TIOCNXCL:
+	case TIOCSETD:
+		return sktn_ioctl( sk, &my_sktn_sk_ops, cmd, arg);
+
+in the switch statement that processes your socket ioctl()s.
+During the time between a successful attachment and detachment of a
+channel, you may not touch the sktn_channel object except by the
+documented support functions.
+
+
+Processing Incoming Packets
+===========================
+
+First, you'll probably write some function, say my_sktn_rx(), which
+takes a frame such as usually present in the socket's receive queue as
+input. That function needs to perform the remaining protocol processing
+(which is usually done on behalf of the socket code's recvmsg() method).
+When all precessing is done (all headers are stripped), the function needs
+to finally call sktn_rx(sktn_channel, skb). After that, the skb is owned
+by the upper layer and may no longer be accessed by you.
+
+But when and how should that function be called? Well, that depends on
+your protocol stack. All Linux stacks are different and might require that
+certain parts of the protocol processing are done from a certain
+context. You know your protocol stack better that I do! Thus, its your
+turn to decide. But I'll try to give you some ideas.
+
+
+If receive protocol processing is done entirely from the socket's input path:
+-----------------------------------------------------------------------------
+
+If your protocol stack used the socket default callback methods,
+chances are good that you don't need to change much. The
+only thing you need to do is to fill the my_sktn_rx() function in
+the sk_rx member field of the sktn_sk_ops type. Anything else is
+taken care of by the sktn framework when you register a channel
+by means of the SIOCSKTNREGISTER ioctl.
+
+If you use non-default socket callbacks, it depends. Maybe it is still
+possible to register the my_sktn_rx() method with the attach ioctl.
+
+If not, maybe process the protocol's input path as usual. If the
+socket is attached to an sktn_channel, do not finally queue the
+sk_buff to the sockets receive queue and do not wake up the socket
+by calling sk->data_ready() (i.e. do not call sock_queue_rcv_skb()).
+Instead, call my_sktn_rx(). The side effects that you have to obey
+really depend on the side effects of your non-default socket callback
+functions, you should now better than I about your non-default
+callbacks.
+
+The current sktn core assumes that the the sock's
+callback_lock is hold while sktn up-calls are executing. Thus, make
+sure that this lock is really hold when you implement your own
+delivery of incoming packets. 
+
+
+If receive protocol processing is done partially from the socket's
+recvmsg (requires a process context) path:
+------------------------------------------------------------------
+
+You need a process receiving data by doing recvmsg in a loop.
+If a state variable indicates that the socket has registered an
+sktn_channel, pass the sk_buff upstream with sktn_rx() instead of
+copying the contents to user space. If you don't receive the data using
+skb_recv_datagram, your data is probably scattered among several
+sk_buffs. In that case -- depending on the framing and fragmentation
+conventions of your protocol -- you might need to copy their contents
+to a single sk_buff and pass the latter one upstream via sktn_rx().
+(like above, do read_lock_bh(&sk->callback_lock) while calling
+sktn_rx()).
+
+
+Processing Outgoing Packets
+===========================
+
+Processing outgoing packets is usually more difficult.
+
+
+If you are lucky, the protocol's output method can be called without
+needing a process context. In that case, you can supply a callback
+function void (*sk_xmit)(struck sock *sk, struct sk_buff * skb).
+This function may pass the skb downstream to the protocol's output
+engine. The callback function is registered to the sk_tunnel system
+when the sktn_ioctl_attach() function is called (my_sk_xmit()
+in the above example). Ensure that the sk_xmit function does not
+cause problems even when the connection changes to any state before
+or during its execution.
+
+Many protocols require the output processing to be done partially from
+a process context. In that case (if your state variable indicates that
+your socket is attached) you need a process waiting for tx skb's in
+a loop. Your (*sk_xmit)(struck sock *sk, struct skbuf * skb)
+callback needs to queue the sk_buff somewhere and wake up the process.
+
+When the process is woken up, dequeue the skb's. If your state variable
+indicates that the socket has not registered an sktn_channel,
+discard the skb's. Otherwise, pass the skb to your protocol's
+output engine as if you had allocated that skb yourself in your sendmsg()
+method and filled in the message data.
+
+One reason requiring a process context for output processing is that
+sockets usually allocate their outgoing data sk_buffs by
+sock_alloc_send_skb() in a non-atomic manner. In order to remedy this,
+the sktn framework changes the sock's sk->allocation and sk->sndtimeo
+value in a manner such that sock_alloc_send_skb() behaves atomically.
+(This is done when registering the sktn_channel, the old values are
+saved and reset when the sktn_channel is unregistered again).
+At least within the PF_X25 stack, it turned out that this change was
+sufficient to allow performing the whole tx processing from 
+soft irq context. Note that you must expect sock_alloc_send_skb() to
+fail occasionally now. However, this is not a problem as it would be
+in sendmsg. If sk_buff allocation failed, you can just discard the
+outgoing frame. Also, allocation of new sk_buffs might not be
+necessary at all. In contrast to sendmsg(), you already get your data
+packaged inside an sk_buff, thus you don't need to allocate one.
+(you still might need to do so if your protocol must fragment the message).
+
+Another problem that makes output more complex than input is queueing
+strategies. Normal network device drivers should keep their
+internal queues as short as possible and rely on the network
+scheduler's dev->qdisc to maintain appropriate queueing strategies
+outside the driver. Driver internal queues should only account for
+the expected latency between the driver's call to netif_wake_queue()
+and the network scheduler's dequeuing call to dev->hard_start_xmit
+(for fast lines, it might also be reasonable to maintain somewhat
+larger internal queues, because that could increase cache hitting rate)
+
+For the same reasons, it is essential that the socket internal
+queues are kept short enough. Otherwise, latency might become
+unnecessarily large, in particular if the bandwidth provided
+by the socket is very small.
+
+On default, tunnel devices will flow control the network scheduler
+(via netif_{stop,wake}_queue()) based on the same socket wmem account
+mechanism that is also usually used to flow control socket related
+write()/sendmsg() system calls. In order to keep socket internal
+queues small enough, the process should set SO_SENDBUF to a
+sufficiently low value.
+
+This should work reasonably well for SOCK_DGRAM sockets. But protocols
+maintaining an acknowledge backlog queue which is accounted to the
+socket's wmem will suffer from decreased throughput if SO_SNDBUF
+is too small. The optimum value for acknowledge queue based flow
+control might differ from the output queue based flow control.
+Thus, determining the optimum value for SO_SNDBUF is not
+necessarily trivial. Even if you have found the best value, flow
+control might still be sub-optimal.
+
+For more sophisticated netif flow control, the tunneling protocol
+needs to provide some assistance such that flow control can be based
+on both, the amount of unacknowledged data (which relates to
+the amount of data queued in the total transmission path, e.g. in the
+wires, queues in the destination host and intermediate routers) and
+the amount of data in the kernel's own output queue. This could be
+done by an additional memory account (maybe implemented via a dummy
+socket?) which measures the pending output path only. Anyway, if you
+come up with a smarter solution for flow controlling the upper layers,
+you need to supply an own version of the sk_busy() method in the
+sktn_sk_ops instance. That function should return 0 if and only
+if the socket is ready to accept more tunnel data for transmission.
+If you just use the sktn_sk_default_busy, the default (solely socket
+wmem account based) flow control will be in effect.
+
+
+1598  Simpson, W.  PPP in X.25.  1994 March; 7 p. (Format: TXT=13835 bytes)
+1356  Malis, A.; Robinson, D.; Ullmann, R.  Multiprotocol
+      Interconnect on X.25 and ISDN in the Packet Mode.  1992 August.
+
diff -urN x25fix.tmp/include/linux/sk_tunnel.h sktn.tmp/include/linux/sk_tunnel.h
--- x25fix.tmp/include/linux/sk_tunnel.h	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/include/linux/sk_tunnel.h	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,30 @@
+#ifndef _SK_TUNNEL_H
+#define _SK_TUNNEL_H
+/*
+ * sktn -- generic SocKet TuNnel interface
+ * 
+ * Support library for tunneling arbitrary protocols over connected sockets.
+ *
+ * Header file containing API definition for user interface
+ * (ioctl definitions for kernel - user land - communication) 
+ *
+ * Copyright 2000 by Henner Eisen. All rights reserved.
+ *
+ * This module 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.
+ */
+
+/*
+ * FIXME: move to <sockios.h> or wherever else is a more appropriate location.
+ * Be aware of possible ioctl number collisions with sockios.h while this
+ * is defined in a separate file.
+ */
+#define SIOCSKTNNEWIF	0x8990	/* Create new socket tunnel net_device */
+#define SIOCSKTNDELIF	0x8991	/* Delete socket tunnel	net_device */
+
+#define SIOCSKTNREGISTER	0x8992	/* Register a socket at sk tunnel */
+#define SIOCSKTNUNREGISTER	0x8993	/* Unregister it */
+
+#endif  _SK_TUNNEL_H
diff -urN x25fix.tmp/include/net/sktn.h sktn.tmp/include/net/sktn.h
--- x25fix.tmp/include/net/sktn.h	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/include/net/sktn.h	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,123 @@
+#ifndef _SKTN_H
+#define _SKTN_H
+/*
+ * sktn -- generic SocKet TuNnel interface
+ * 
+ * Support library for tunneling arbitrary protocols over connected sockets.
+ *
+ * Header file containing API definitions for linux network kernel space
+ * applications.
+ *
+ * Copyright 2000 by Henner Eisen. All rights reserved.
+ *
+ * This module 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 <linux/socket.h>
+
+#include <linux/ppp_channel.h>
+struct sktn_channel;
+/*
+ * Interface to upper layer, initialized by sktn layer. There will be
+ * very few instances of this (one for device, one for ppp upper
+ * layer, and one blackhole) 
+ */ 
+typedef const struct {
+	void (* rx) (struct sktn_channel *, struct sk_buff *);
+			/* For demultiplexing rx methods to dev or ppp
+			   upper layer */
+	void (* write_space) (struct sock * sk);
+			/* Upper layer dependent wakeup method (may be
+			   placed in sk->write_space while tunnel channel
+			   is attached).
+			*/
+	int (* unregister) (struct sktn_channel *);
+} sktn_ops;
+
+/*
+ * Interface callbacks provided by socket layer to sktn_channel layer 
+ *
+ * If the network core provided support for sk tunnel, this could be moved to
+ * the struct sock's proto_ops.
+ */ 
+typedef const struct {
+	int (* sk_busy) (struct sock *);  /* Return additional tx_busy state
+			information. Insert sktn_sk_default_busy here if you
+			don't provide an own method. */
+
+	int (* sk_xmit) (struct sock *, struct sk_buff *);  /* Send out a
+							       frame. */
+	void (* sk_rx) (struct sock *, struct sk_buff *);  /* given an
+			sk_buff	dequeued from sock's receieve_queue, do
+			remaining protocol processing and pass it
+			upstreams via sktn_rx(). */
+
+	int (* sk_hlen) (struct sock *);  /* Return amount of header space
+			an sk_buff needs before passed down via sk_xmit().*/
+
+	int (* sk_mtu) (struct sock *);  /* Return allowed len of an
+			sk_buff passed down via sk_xmit().*/
+
+} sktn_sk_ops;
+
+/*
+ * The sktn_channel object itsself. All fields should be regarded as
+ * private and not be accessed directly. But we need to define this here
+ * in the public header file such that several support functions can
+ * be inlined.
+ */
+struct sktn_channel{
+	struct ppp_channel ppp_chan; /* must be first */
+
+	sktn_ops * up_call;      /* callbacks to upper (ppp/netdev) layer */
+	sktn_sk_ops * sk_call;	/* callback interface to lower (sock) layer
+				 (might be removed in future if sock
+				 proto_ops contained the related callbacks */
+	struct sock *sk;
+	struct net_device *dev; /* non-NULL refers to net_device tunnel
+				   interface */  
+	void (* original_write_space)(struct sock *);
+	void (* original_data_ready)(struct sock *, int);
+	long          original_sndtimeo;
+	unsigned int  original_allocation;
+	int registered;
+	atomic_t refcount;
+	int mru, flags, xmitting;
+};
+
+/*
+ * Default method, should be used by protocol implementations for
+ * sktn_sk_ops.sk_busy() if they don't provide a more sophisticated
+ * method to base tx flow control on.
+ *
+ * With the standard method, flow control solely relies on standard
+ * socket memory accounting and should work with all sockets that do
+ * wmem accounting properly.
+ */
+extern int sktn_sk_default_busy(struct sock *sk);
+
+/*
+ * Deliver tunneled packet to upper layer.
+ */
+extern void __inline__ sktn_rx(struct sktn_channel *ch, struct sk_buff *skb)
+{
+	skb_orphan(skb);
+	skb_pull(skb, skb->h.raw - skb->data);
+	ch->up_call->rx(ch,skb);
+}
+
+/*
+ * ioctl passed from /dev/ppp*
+ */
+extern int sktn_ioctl(struct sock * sk, sktn_sk_ops *,
+		      unsigned int cmd, unsigned long arg);
+
+extern void sktn_socket_unregister(struct sock *sk);
+extern struct sktn_channel * sktn_null_channel;
+#define SKTN_ALLOCATED(CH) ((CH!=sktn_null_channel) && (CH!=NULL))
+#define SK_REGULAR(CH) ((sk!=NULL))
+
+#endif  _SKTN_H
diff -urN x25fix.tmp/include/net/x25.h sktn.tmp/include/net/x25.h
--- x25fix.tmp/include/net/x25.h	Mon Sep 11 18:53:43 2000
+++ sktn.tmp/include/net/x25.h	Mon Sep 11 19:29:22 2000
@@ -123,8 +123,8 @@
 	x25_address		source_addr, dest_addr;
 	struct x25_neigh	*neighbour;
 	unsigned int		lci;
-	unsigned char		state, condition, qbitincl, intflag;
-	unsigned short		vs, vr, va, vl;
+  unsigned /*char*/long volatile		state, condition, qbitincl, intflag;
+  unsigned /*short*/ volatile long		vs, vr, va, vl;
 	unsigned long		t2, t21, t22, t23;
 	unsigned short		fraglen;
 	struct sk_buff_head	ack_queue;
@@ -137,6 +137,11 @@
 	struct x25_facilities	facilities;
 	struct x25_calluserdata	calluserdata;
 	unsigned long 		vc_facil_mask;	/* inc_call facilities mask */
+#define CONFIG_X25_TNL
+#ifdef CONFIG_X25_TNL
+	struct sktn_channel	*sktn;
+#endif
+	atomic_t kick_it;
 } x25_cb;
 
 /* af_x25.c */
diff -urN x25fix.tmp/net/x25/Makefile sktn.tmp/net/x25/Makefile
--- x25fix.tmp/net/x25/Makefile	Fri May 30 06:53:12 1997
+++ sktn.tmp/net/x25/Makefile	Mon Sep 11 18:53:44 2000
@@ -16,6 +16,13 @@
 O_OBJS += sysctl_net_x25.o
 endif
 
+CONFIG_X25_TNL:=y
+ifeq ($(CONFIG_X25_TNL),y)
+sktn_common.o sktn_dev.o sktn_ppp.o: ../../include/linux/sk_tunnel.h sktn_priv.h
+af_x25.o: ../../include/linux/sk_tunnel.h
+O_OBJS += sktn_common.o sktn_dev.o sktn_ppp.o
+endif
+
 include $(TOPDIR)/Rules.make
 
 tar:
diff -urN x25fix.tmp/net/x25/af_x25.c sktn.tmp/net/x25/af_x25.c
--- x25fix.tmp/net/x25/af_x25.c	Mon Sep 11 18:53:43 2000
+++ sktn.tmp/net/x25/af_x25.c	Mon Sep 11 18:53:44 2000
@@ -56,6 +56,13 @@
 #include <linux/init.h>
 #include <net/x25.h>
 
+#ifdef CONFIG_X25_TNL
+#include <linux/sk_tunnel.h>
+#include <net/sktn.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif
+
 int sysctl_x25_restart_request_timeout = X25_DEFAULT_T20;
 int sysctl_x25_call_request_timeout    = X25_DEFAULT_T21;
 int sysctl_x25_reset_request_timeout   = X25_DEFAULT_T22;
@@ -448,6 +455,10 @@
 	skb_queue_head_init(&x25->fragment_queue);
 	skb_queue_head_init(&x25->interrupt_in_queue);
 	skb_queue_head_init(&x25->interrupt_out_queue);
+#ifdef CONFIG_X25_TNL
+
+	x25->sktn = sktn_null_channel;
+#endif
 
 	return sk;
 }
@@ -523,6 +534,10 @@
 
 	init_timer(&x25->timer);
 
+#ifdef CONFIG_X25_TNL
+
+	x25->sktn = NULL;
+#endif
 	return sk;
 }
 
@@ -532,6 +547,9 @@
 
 	if (sk == NULL) return 0;
 
+#ifdef CONFIG_X25_TNL
+	sktn_socket_unregister(sk);
+#endif
 	switch (sk->protinfo.x25->state) {
 
 		case X25_STATE_0:
@@ -986,6 +1004,75 @@
 	return len;
 }
 
+#ifdef CONFIG_X25_TNL
+static int x25_sktn_start_xmit(struct sock *sk, struct sk_buff *skb)
+{
+	unsigned char *asmptr = skb->h.raw;
+
+	/* 
+	 * Build the X.25 header. This is just copied from x25_sendmsg().
+	 * FIXME: as this code is shared with the tunnel send engine now, it
+	 * should be extracted into a separate (possibly inlined) subroutine.
+	 */
+	if (sk->protinfo.x25->neighbour->extended) {
+		/* Build an Extended X.25 header */
+		asmptr    = skb_push(skb, X25_EXT_MIN_LEN);
+		*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ;
+		*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+		*asmptr++ = X25_DATA;
+		*asmptr++ = X25_DATA;
+	} else {
+		/* Build a Standard X.25 header */
+		asmptr    = skb_push(skb, X25_STD_MIN_LEN);
+		*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ;
+		*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+		*asmptr++ = X25_DATA;
+	}
+	
+	if(x25_output(sk, skb)) kfree_skb(skb);
+	x25_kick(sk);
+	return 0;
+}
+
+static void x25_sktn_rx(struct sock * sk, struct sk_buff *skb)
+{
+	skb_pull(skb, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
+	
+	skb->h.raw = skb->data;
+	sktn_rx(sk->protinfo.x25->sktn,skb);
+}
+
+static int x25_sktn_hlen(struct sock *sk)
+{
+	/*
+	 * This could be more sophisticated by investigating the current
+	 * net_device's hard_header_len. This following static value is
+	 * the same as used in x25_sendmsg().
+	 */
+	return X25_MAX_L2_LEN + X25_EXT_MIN_LEN;
+}
+
+static int x25_sktn_mtu(struct sock *sk)
+{
+	/*
+	 * X.25 can fragment packets of arbitrary length and linux-x25 does not
+	 * impose any explicit limits. RFC 1356 requires at least to support
+	 * configuration of MTUs between 576 and 1600 byte.
+	 * Thus, we somewhat arbitrarily choose:
+	 */
+	return 2048;
+}
+
+static sktn_sk_ops x25_sktn_ops =
+{
+	sk_busy: sktn_sk_default_busy, 
+	sk_xmit: x25_sktn_start_xmit,
+	sk_rx: x25_sktn_rx,
+	sk_hlen: x25_sktn_hlen,
+	sk_mtu: x25_sktn_mtu
+};
+
+#endif CONFIG_X25_TNL
 
 static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
 {
@@ -1163,6 +1250,21 @@
 			causediag = sk->protinfo.x25->causediag;
 			return copy_to_user((void *)arg, &causediag, sizeof(causediag)) ? -EFAULT : 0;
 		}
+#ifdef CONFIG_X25_TNL
+		case SIOCSKTNNEWIF:
+		case SIOCSKTNDELIF:
+		case SIOCSKTNREGISTER: 
+        	case SIOCSKTNUNREGISTER:
+	 	case PPPIOCGCHAN:
+		case PPPIOCGUNIT:
+		case PPPIOCATTACH:
+        	case PPPIOCDETACH:
+		  /* Dummy TIOC for fooling current pppd */
+		case TIOCEXCL:
+		case TIOCNXCL:
+		case TIOCSETD:
+			return sktn_ioctl(sk, &x25_sktn_ops, cmd, arg);
+#endif CONFIG_X25_TNL
 
  		default:
 			return dev_ioctl(cmd, (void *)arg);
diff -urN x25fix.tmp/net/x25/sktn_common.c sktn.tmp/net/x25/sktn_common.c
--- x25fix.tmp/net/x25/sktn_common.c	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/net/x25/sktn_common.c	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,450 @@
+/*
+ * sktn -- generic SocKet TuNnel interface
+ *
+ * Support library for tunneling arbitrary protocols over connected sockets.
+ *
+ * This file contains common stuff (not related to special upper layers).
+ *
+ * Copyright 2000 by Henner Eisen. All rights reserved.
+ *
+ * This module 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 <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <net/sock.h>
+#include <linux/spinlock.h>
+
+#include <linux/sk_tunnel.h>
+#include <net/sktn.h>
+#include "sktn_priv.h"
+
+/*
+ * Default sktn callbacks for inactive sktn_channels
+ */
+
+static void sktn_rx_blackhole(struct sktn_channel *ch, struct sk_buff *skb)
+{
+	printk(KERN_DEBUG "sktn_blackhole(): data discarded\n");
+	kfree_skb(skb);
+}
+
+static void sktn_no_write_space(struct sock *sk)
+{
+	printk(KERN_DEBUG "sktn_no_write_space()\n");
+}
+
+static int sktn_no_unregister(struct sktn_channel *ch)
+{
+	printk(KERN_DEBUG "sktn_no_unregister()\n");
+	return 0;
+}
+
+static sktn_ops sktn_inactive_ops = {
+	sktn_rx_blackhole,
+	sktn_no_write_space,
+	sktn_no_unregister
+};
+
+static int no_sk_xmit(struct sock *sk, struct sk_buff *skb)
+{
+	printk(KERN_DEBUG "sk tunnel no_output(): data discarded\n");
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static int always_busy(struct sock *sk)
+{
+	printk(KERN_DEBUG "BUG: sk tunnel always_busy() called!\n");
+	return 1;
+}
+
+static sktn_sk_ops sktn_inactive_sk_ops = {
+	sk_busy: always_busy,
+	sk_xmit: no_sk_xmit,
+	sk_rx: /* XXX what's that? */ NULL
+};
+
+/*
+ * dummy (blackhole) instance of an sktn_channel
+ */
+static struct sktn_channel null_channel_instance =
+{
+	up_call: &sktn_inactive_ops,
+	sk_call: &sktn_inactive_sk_ops
+};
+
+struct sktn_channel * sktn_null_channel = &null_channel_instance;
+
+/*
+ * Default method, can be used by protocol implementations for
+ * sktn_sk_ops.sk_busy(). With this method, the socket is never
+ * marked explictly to be busy for tx. As a result, flow control
+ * solely relies on standard socket wmem memory accounting.
+ * Should work with all sockets that do wmem accounting properly.
+ *
+ * Individual network stack implementations can also provide
+ * more sophisticated methods when they register a tunnel device or
+ * a ppp channel in order to keep internal output queues shorter.
+ */
+int sktn_sk_default_busy(struct sock *sk)
+{
+	return 0;
+}
+
+static void sktn_init_data(struct sktn_channel *ch)
+{
+	memset(ch,0,sizeof(*ch));
+
+	ch->up_call = &sktn_inactive_ops;
+	ch->sk_call = &sktn_inactive_sk_ops;
+	atomic_set(&ch->refcount,1);
+}
+
+void sktn_hold(struct sktn_channel *ch)
+{
+	atomic_inc(&ch->refcount);
+}
+
+void sktn_put(struct sktn_channel *ch)
+{
+	if(atomic_dec_and_test(&ch->refcount)){
+		if( SKTN_ALLOCATED(ch) ){ 
+			kfree(ch);
+			/* MOD_DEC_USE_COUNT; */
+		}
+	}
+}
+
+/*
+ * Default method to be placed in sk->data_ready for sockets which
+ * don't need a process context for performing the remaining protocol
+ * processing after the sk_buff has been queued to sk_receive_queue.
+ *
+ * The user needs to register an rx() method which performs the remaining
+ * protocol processing (e.g. pull of remaining headers) and finally calls
+ * sktn_rx(sktn,skb).
+ */
+static void sktn_def_readable(struct sock *sk, int len)
+{
+	struct sk_buff * skb;
+
+        read_lock(&sk->callback_lock);
+
+        if(!sk->dead) {
+		struct sktn_channel * sktn = *sktn_by_sk(sk);
+		while( (skb=skb_dequeue(&sk->receive_queue)) ){
+			sktn->sk_call->sk_rx(sk,skb);
+		}
+        }
+
+        read_unlock(&sk->callback_lock);
+}
+
+#if 0 /* does not work when compiled as a module (symbols are not exported) */
+/*
+ * Hack: addresses might be used to identify default socket methods. This is
+ * only used for generating debug/warning messages and not used directly.
+ */
+extern void sock_def_write_space(struct sock*);
+extern void sock_def_readable(struct sock*,int);
+#endif
+
+/**
+ *	sktn_attach_sock: attach a sock to an sktn_channel.
+ *	@ch: sktn_channel to attach to 
+ *	@sk: sock which shall be attached
+ *	@sk_ops: callbacks from channel to socket.
+ *
+ *      should be called with sktn_lock and sk->callback_lock hold writeable.
+ */
+void sktn_attach_sock(struct sktn_channel *ch, struct sock *sk, sktn_sk_ops * sk_ops)
+{
+	struct sktn_channel **chsk;
+
+	if(ch->sk == sk) return;
+	sock_hold(sk);
+	sktn_hold(ch);
+
+	ch->sk = sk;
+	ch->original_write_space = sk->write_space;
+	ch->original_data_ready  = sk->data_ready;
+
+#if 0
+	if(sk->write_space != sock_def_write_space)
+		printk(KERN_DEBUG "sktn: replacing non-default sk->write_space\n");
+#endif
+	sk->write_space = ch->up_call->write_space;
+
+	/*
+	 * Sockets which require to read the receive queue from a
+	 * process context or handle passing frames upstreams totally
+	 * on their own will have this set to NULL.
+	 */
+ 	if( sk_ops->sk_rx ){
+#if 0
+		if(sk->data_ready != sock_def_readable)
+			printk(KERN_DEBUG "sktn: replacing non-default sk->data_ready\n");
+#endif
+		sk->data_ready = sktn_def_readable;
+	}
+
+	/*
+	 * This dirty trick allows sock_alloc_send_skb() to be
+	 * called from NET_TX_SOFTIRQ.
+	 *
+	 * FIXME: Even if we allow this, we need to make this conditional
+	 * such that which absolutly need doing xmit from process context
+	 * can keep the original allocation and sndtimeo state.
+	 */
+	ch->original_allocation = sk->allocation;
+	ch->original_sndtimeo   = sk->sndtimeo;
+	sk->allocation = GFP_ATOMIC;
+	sk->sndtimeo = 0;
+
+	ch->sk_call = sk_ops;
+	
+	chsk = sktn_by_sk(sk);
+	*chsk = ch;
+}
+
+/**
+ * sktn_release_sock: de-associate a socket from an sktn_channel.
+ * @ch: channel whose attached socket should be detached 
+ *
+ *	should be called with sktn_lock and sk->callback_lock writeable hold;
+ */
+void sktn_release_sock(struct sktn_channel *ch)
+{
+	struct sock *sk;
+	struct sktn_channel **chsk;
+
+	sk = ch->sk; 
+
+	ch->sk_call = &sktn_inactive_sk_ops;
+	ch->sk = NULL;
+
+	sk->write_space = ch->original_write_space;
+	sk->data_ready = ch->original_data_ready;
+
+	chsk = sktn_by_sk(sk);
+	if( *chsk != ch ) printk(KERN_WARNING "sktn_release_sock(): "
+				 "channel pointers inconsistent\n");
+	*chsk = sktn_null_channel;
+
+	sktn_put(ch);
+	sock_put(sk);
+}
+
+/**
+ *	sktn_unregister - unregister sktn_channel from upper (tunnel) layer
+ *	@ch: sktn_channel to unregister
+ */
+static int sktn_unregister(struct sktn_channel **ch)
+{
+	struct sktn_channel *tmp = *ch;
+	int err;
+	if(!SKTN_ALLOCATED(tmp)){
+		printk(KERN_WARNING "sktn_release(): no channel attached\n");
+		return -ENXIO;
+	}
+
+	err = tmp->up_call->unregister(tmp);
+	if(!err) sktn_put(tmp);
+	return err;
+}
+
+static int sktn_ioctl_register(struct sock * sk, sktn_sk_ops * sk_ops, unsigned int cmd, long arg)
+{
+	int err=-EINVAL, id;
+	struct sktn_channel *ch;
+
+	if (!capable(CAP_NET_ADMIN)) return -EACCES;
+	/*
+	 * Race: socket might become disconnected while we are executing.
+	 * But the channel code needs to be save even when socket becomes
+	 * disconnected, such that this check is only to detect common
+	 * application errors. It also protects us reliably from attaching
+	 * sockets in the listen state.
+	 *
+	 * FIXME: we need appropriate locking even during the check.
+	 * XXX invetigate whether lock_sock() is appropriate for this
+	 * (and can also be used instead of global sktn semaphore).
+	 */
+	if(sk->socket && (!sk->dead) && sk->socket->state != SS_CONNECTED)
+		return -ENOTCONN;
+
+	if( SKTN_ALLOCATED( *(sktn_by_sk(sk)) ) ) return -EALREADY;
+
+	ch = sktn_new();
+	if(!SKTN_ALLOCATED(ch)) return -ENOMEM;
+
+	if (get_user(id, (int *)arg)){
+		err = -EFAULT;
+	} else {
+		err = -EINVAL;
+		if( id >= 0 ) {
+			err = sktn_dev_register(ch,sk,sk_ops,id);
+		} else if(id == -1) {
+			err = sktn_ppp_register(ch,sk,sk_ops);
+		}
+	}
+	if( err ){
+		sktn_put(ch);
+	} else {
+		/* MOD_INC_USE_COUNT; */
+	}
+	return err;
+}
+
+/*
+ * Some ioctl()s might sleep and some (as well as sktn_socket_unregister())
+ * might manipulate the socket's sktn reference pointer. We currently
+ * serialize them by means of just one global semaphore.
+ *
+ * XXX check whether lock_sock()/release_sock() can be used instead.
+ */
+static DECLARE_MUTEX(sktn_sem_instance);
+static struct semaphore * sktn_sem = &sktn_sem_instance;
+
+/*
+ *
+ */
+static spinlock_t sktn_spinlock = SPIN_LOCK_UNLOCKED;
+
+void sktn_lock(struct sktn_channel *ch)
+{
+	spin_lock(&sktn_spinlock);
+}
+
+void sktn_unlock()
+{
+	spin_unlock(&sktn_spinlock);
+}
+
+int sktn_ioctl(struct sock * sk, sktn_sk_ops * sk_ops,
+	       unsigned int cmd, unsigned long arg)
+{
+	struct sktn_channel **ch;
+	int err;
+
+	if(!capable(CAP_NET_ADMIN)) return -EACCES;
+
+	err = down_interruptible(sktn_sem);
+        if( err ) return err;
+	ch = sktn_by_sk(sk);
+
+	switch( cmd ){
+	case PPPIOCATTACH:
+	case PPPIOCDETACH:
+	case PPPIOCGCHAN:
+	case PPPIOCGUNIT:
+		if(!SKTN_ALLOCATED(*ch)){
+			err = -ENXIO;
+			break;
+		}
+		if( ((*ch)->registered) != SKTN_REGISTERED_PPP ){
+			err = -ENXIO;
+			break;
+		}
+		err = sktn_ppp_sock_ioctl( &( (*ch)->ppp_chan ), cmd, arg);
+		break;
+#define CASE_IOC_DUMMY(IOC) case IOC:\
+		err=0;\
+		printk(KERN_DEBUG "dummy ppp_sock ioctl " #IOC " called\n");\
+		break;
+	CASE_IOC_DUMMY(TIOCEXCL)
+	CASE_IOC_DUMMY(TIOCNXCL)
+	CASE_IOC_DUMMY(TIOCSETD)
+
+	case SIOCSKTNNEWIF:
+	case SIOCSKTNDELIF:
+		err = sktn_dev_sock_ioctl( *ch, cmd, arg);
+		break;
+	case SIOCSKTNREGISTER: 
+		err = sktn_ioctl_register(sk, sk_ops, cmd, arg);
+		break;
+	case SIOCSKTNUNREGISTER:
+		err = sktn_unregister(ch);
+		break;
+	default:
+		printk(KERN_WARNING "unexpected sktn_sock_ioctl(%x)\n",cmd);
+	}
+
+	up(sktn_sem);
+	return err;
+}		
+
+void sktn_socket_unregister(struct sock * sk)
+{
+	struct sktn_channel **ch;
+	int err;
+	down(sktn_sem);
+
+	ch = sktn_by_sk(sk);
+	if(! *ch) {
+		printk(KERN_DEBUG "sktn_socket_unreg: sk->sktn=NULL (was not initialized)\n"); 
+		goto error;;
+	}
+	if( SKTN_ALLOCATED(*ch) ) err = sktn_unregister(ch);
+ error:
+	up(sktn_sem);
+}		
+
+struct sktn_channel * sktn_new(void)
+{
+	struct sktn_channel * ch = kmalloc(sizeof(*ch), GFP_KERNEL);
+	if(! ch) return sktn_null_channel;
+	
+	sktn_init_data(ch);
+
+	ch->ppp_chan.private = ch;
+
+	/* FIXME: insert somewhat more appropriate values here */
+
+	ch->ppp_chan.mtu    = 0;	/* max transmit packet size */
+	ch->ppp_chan.hdrlen = 0;	/* amount of headroom channel needs */
+	ch->ppp_chan.speed  = 0;	/* transfer rate (bytes/second) */
+	ch->ppp_chan.latency= 0;	/* overhead time in milliseconds */
+
+	return ch;
+}
+
+
+/* temporarily removed inlined implementation for ease of bug-tracking */
+int /*HACK __inline__*/  sktn_finish_xmit(struct sktn_channel *ch,
+				       struct sock *sk,
+				       struct sk_buff *skb)
+{
+	struct sk_buff * nskb;
+
+	nskb = skb_clone(skb,GFP_ATOMIC);
+	if(!nskb) return 1;
+
+	if(sk) {
+		skb_set_owner_w(nskb,sk);
+	} else {
+		printk(KERN_DEBUG "set_owner_w %p supressed\n",sk);
+	}
+	nskb->h.raw = nskb->nh.raw = nskb->data;
+
+	/* printk(KERN_DEBUG "sk_xmit\n"); */
+	if(ch->sk_call->sk_xmit(sk,nskb)){
+		dev_kfree_skb(nskb);
+		return 1;
+	}
+	/*
+	 * In order to maintain upper layer's sock wmem based flow control,
+	 * it might make sense do delay the dev_kfree_skb(skb) until the
+	 * cloned nskb is freed. But this requires further tricks, such as
+	 * hooking in the nskb's destructor, which is left for a future
+	 * hacking session.
+	 */
+	dev_kfree_skb(skb);
+	return 0;
+}
diff -urN x25fix.tmp/net/x25/sktn_dev.c sktn.tmp/net/x25/sktn_dev.c
--- x25fix.tmp/net/x25/sktn_dev.c	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/net/x25/sktn_dev.c	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,476 @@
+/*
+ * sktn -- generic SocKet TuNnel interface
+ *
+ * Support library for tunneling arbitrary protocols over connected sockets.
+ *
+ * This file contains (raw) net device tunnel specific stuff.
+ *
+ * Copyright 2000 by Henner Eisen. All rights reserved.
+ *
+ * This module 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 <linux/netdevice.h>
+#include <net/sock.h>
+#include <linux/if_arp.h>
+/* preliminary hack until ARPHRD_SKTN is defined in above header file */
+#ifndef ARPHRD_SKTN
+#define ARPHRD_SKTN (ARPHRD_VOID-1)
+#else
+#error remove this
+#endif
+#include <linux/rtnetlink.h> /* solely for rntl_[un]lock() */
+
+#include <linux/sk_tunnel.h>
+#include <net/sktn.h>
+#include "sktn_priv.h"
+
+/*
+ ********************************************************************
+ * sktn tunnel net_device methods.
+ *
+ ********************************************************************
+ */
+
+#define SKTN_DEFAULT_TX_TIMEOUT  (2*HZ)
+#define SKTN_DEFAULT_HHLEN (24)
+#define SKTN_DEV_NAME_LEN (9)
+
+struct sktn_dev_priv{
+	struct sktn_channel * ch;
+	struct net_device_stats stats;
+	unsigned short raw_proto;
+	unsigned short encap_hlen;
+};
+
+
+static int sktn_dev_open(struct net_device *dev)
+{
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static int sktn_dev_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+
+	return 0;
+}
+
+static struct net_device_stats * sktn_dev_get_stats(struct net_device *dev)
+{
+	struct sktn_dev_priv *sp = dev->priv;
+
+	return &sp->stats;
+}
+
+static struct net_device * sktn_dev_get_by_id(int id)
+{
+	char name[SKTN_DEV_NAME_LEN];
+	struct net_device * dev;
+
+	sprintf(name,"sktn%.4d",id);
+	dev = dev_get_by_name(name);
+
+	return dev;
+}
+
+static void sktn_dev_tx_timeout(struct net_device *dev)
+{
+	struct sktn_dev_priv *sp = dev->priv;
+
+	printk(KERN_WARNING "%s: tx_timeout\n", dev->name);
+	/*
+	 * Maybe also reset lower layer protocol, but that requires
+	 * further support from the sk_tunnel framework 
+	 *
+	 * For now, only the absolute minimum necessary for testing.
+	 */
+	sp-> stats.tx_errors++;
+	/* 
+	 * Trick: see comment before sktn_dev_attach().
+	 * Hopefully, softnet does not hold this regularily when
+	 * watchdog timer is called.
+	 */ 
+	//spin_lock_bh(&dev->xmit_lock); 
+	if(sp->ch && !sktn_tx_busy(sp->ch)){
+		netif_wake_queue(dev);
+	}
+	//spin_unlock_bh(&dev->xmit_lock); 
+}
+
+static int sktn_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	switch(cmd){
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int sktn_dev_xmit(struct sk_buff *skb, struct net_device * dev)
+{
+	struct sktn_dev_priv *sp = dev->priv;
+	struct sktn_channel *ch;
+	struct sock *sk; 
+	int ret;
+
+	ch = sp->ch;
+	if( !SKTN_ALLOCATED(ch) ){
+		dev_kfree_skb(skb);
+		sp->stats.tx_dropped++;
+		return 0;
+	}
+	sk = ch->sk;
+	
+	dev->trans_start = jiffies;
+	if(skb->protocol != sp->raw_proto){
+		printk(KERN_DEBUG "%s: wrong proto %hd passed for xmit\n",
+		       dev->name, ntohs(skb->protocol)); 
+		dev_kfree_skb(skb);
+		return 0;
+	}
+		
+	ret = sktn_finish_xmit(ch,sk,skb);
+	if(!ret){
+		sp->stats.tx_bytes += skb->len;
+		sp->stats.tx_packets++; /* maybe move this to
+					  sktn_dev_write_space() */
+	}
+
+	/*
+	 * This is the only place where state can be changed to 'busy'.
+	 * The default socket callbacks grab the read lock, thus
+	 * we serialize against them by holding the write_lock.
+	 * Clearing from sktn_tx_timeout() will not occur concurrently
+	 * because the latter also grabs the dev->xmit_lock;
+	 *
+	 * Using the right lock needs further work (we need to deal
+	 * with the sock beeing detached from channel during work).
+	 */
+	if(!sk) {
+		printk(KERN_DEBUG "HACK: sktn_dev_xmit write_lock_bh suppressed\n");
+		return ret;
+	}
+	write_lock_bh(&sk->callback_lock);
+	if(sktn_tx_busy(ch)){
+		netif_stop_queue(dev);
+	}
+	write_unlock_bh(&sk->callback_lock);
+
+	return ret;
+}
+
+static int sktn_dev_set_encap(struct net_device * dev, int encap)
+{
+	/*
+	 * Changing encapsulation is only allowed when interface is down.
+	 */
+	if( netif_running(dev) ) return -EBUSY;
+
+	/*
+	 * XXX for supporting different encapsulations, we need
+	 * to check the encap paramater. For now, we don't (we always
+	 * assume raw encapsulation).
+	 */
+	dev->set_multicast_list = NULL;
+	dev->hard_header        = NULL;
+	dev->hard_start_xmit    = sktn_dev_xmit;
+	dev->flags = IFF_NOARP|IFF_POINTOPOINT;
+	dev->type = ARPHRD_SKTN;
+	dev->addr_len = 0;
+
+	return 0;
+}
+
+static int sktn_dev_init(struct net_device * dev)
+{
+	dev->hard_header_len = SKTN_DEFAULT_HHLEN;
+        dev->watchdog_timeo  = SKTN_DEFAULT_TX_TIMEOUT; 
+	dev->open	     = sktn_dev_open;
+	dev->stop	     = sktn_dev_close;
+	dev->get_stats	     = sktn_dev_get_stats;
+        dev->tx_timeout	     = sktn_dev_tx_timeout;
+	dev->do_ioctl        = sktn_dev_ioctl;
+
+	dev->mtu	  = 1500;
+	dev->tx_queue_len = 5;
+
+	sktn_dev_set_encap(dev,0);
+
+	return 0;
+}
+
+static void sktn_dev_destruct(struct net_device * dev)
+{
+	struct sktn_dev_priv *sp = dev->priv;
+	kfree(sp);
+	/* MOD_DEC_USE_COUNT; */
+}
+
+/*
+ ********************************************************************
+ * sktn tunnel device management methods
+ *
+ * These are called on behalf of the protocol stack (e.g. sktn
+ * specific socket ioctl's, sock_release methods)
+ ********************************************************************
+ */
+
+/**
+ *	sktn_device_new	- create new network device for socket tunnel
+ *	@id: id identifying the device
+ */
+static int sktn_dev_new(int id)
+{
+	int err;
+	struct sktn_dev_priv * sp;
+	char name[SKTN_DEV_NAME_LEN];
+	struct net_device * dev;
+
+	sprintf(name,"sktn%.4d",id);
+	dev = dev_alloc(name,&err);
+
+	if(err<0) return err;
+
+	sp = kmalloc(sizeof(*sp),GFP_KERNEL);
+	if(!sp){
+		kfree(dev);
+		return -ENOMEM;
+	}
+	memset(sp,0,sizeof(*sp));
+	sp->raw_proto = ntohs(ETH_P_IP);
+	sp->ch = sktn_null_channel;
+
+	dev->new_style  = 1;
+	dev->destructor = sktn_dev_destruct;
+	dev->priv       = sp;
+        dev->init       = sktn_dev_init;
+
+        rtnl_lock();
+       	err = register_netdevice(dev);
+        rtnl_unlock();
+
+	if(err){
+		kfree(dev);
+		kfree(sp);
+		return err;
+	}
+
+	/* MOD_INC_USE_COUNT; */
+
+	return 0;
+}
+
+/**
+ *	sktn_device_release - delete socket tunnel network device
+ *	@idx: id identifying the device.
+ */
+static int sktn_dev_release(int id)
+{
+	struct sktn_dev_priv *sp;
+	int err;
+	struct net_device * dev;
+
+	dev = sktn_dev_get_by_id(id);
+
+	if(!dev) return -ENODEV;
+
+	err = -EINVAL;
+	if( dev->type != ARPHRD_SKTN) goto error;
+
+	sp = dev->priv;
+	err = -EBUSY;
+	if(SKTN_ALLOCATED(sp->ch)) goto error;
+	
+	rtnl_lock();
+	unregister_netdevice(dev);
+	rtnl_unlock();
+	err = 0;
+
+ error:
+	dev_put(dev);
+
+	return err;
+}
+
+/**
+ *	sktn_dev_write_space - sk->write_space() method for sktn netdevice
+ *	@sk: socket which reports free space in its output queue
+ *
+ *      This will replace the struct sock's write_space() method
+ *      while the socket is used by the tunneling device. It maps socket
+ *      write flow control to netif queue flow control primitives. 
+ */
+static void sktn_dev_write_space(struct sock *sk)
+{
+	SKTN_WRITE_SPACE_WRAPPER( netif_wake_queue(ch->dev) );
+}
+
+static void sktn_dev_rx(struct sktn_channel * ch, struct sk_buff * skb)
+{
+	struct net_device * dev = ch->dev;
+	struct sktn_dev_priv * sp = dev->priv;
+
+	skb->dev = dev;
+	skb->pkt_type = PACKET_HOST;
+	/* skb->mac.raw = skb->data; */
+	skb->protocol = sp->raw_proto;
+
+	sp->stats.rx_bytes += skb->len;
+	sp->stats.rx_packets++;
+
+	netif_rx(skb);
+}
+
+
+/**
+ * sktn_dev_unregister - detach and unregister socket from tunnel net_device
+ * @ch: sktn channel that is currently attaching socket and net_device
+ */ 
+static int sktn_dev_unregister(struct sktn_channel *ch)
+{
+	int err;
+	struct net_device * dev;
+	struct sktn_dev_priv * sp;
+	struct sock * sk = ch->sk;
+
+	sktn_lock(ch);
+
+	err = -ENODEV;
+	if(ch->registered != SKTN_REGISTERED_DEV){
+		printk(KERN_WARNING "sktn_dev_unregister() called while "
+		       "registered at %d\n",ch->registered);
+		return err;
+	}
+
+
+
+	dev = ch->dev;
+	err = -ENODEV;
+	if( ! dev ) goto error;
+	
+	spin_lock_bh(&dev->xmit_lock);
+	write_lock_bh(&sk->callback_lock);
+
+	sp = dev->priv;
+	sp->ch = sktn_null_channel;
+	sktn_put(ch);
+	ch->up_call = &sktn_inactive_ops;
+	ch->dev = NULL;
+	dev_put(dev);
+	
+	sktn_release_sock(ch);
+
+	ch->registered = 0;
+	err = 0;
+
+	write_unlock_bh(&sk->callback_lock);
+	spin_unlock_bh(&dev->xmit_lock);
+ error:
+	sktn_unlock();
+	return err;
+}
+
+
+static sktn_ops sktn_dev_ops = {
+	rx: sktn_dev_rx,
+	write_space: sktn_dev_write_space,
+        unregister: sktn_dev_unregister
+};
+
+/**
+ * sktn_dev_register: register and attach a socket to a tunnel net_device
+ * @ch: sktn channel serving as the bridge (must be freshly initialized)
+ * @sk: sock which should be attached to tunnel net_netdev
+ * @id: interface id identifying the device to attach to
+ */ 
+int sktn_dev_register(struct sktn_channel *ch, struct sock *sk, sktn_sk_ops * sk_ops, int id)
+{
+	int err;
+	struct net_device * dev;
+	struct sktn_dev_priv * sp;
+	/*
+	 * We need to make sure that sktn channel is not manipulated while
+	 * net_device uses it.
+	 *
+	 * There are two ways an sktn_channel can be used by a tunnel net_device.
+	 * One are the upcalls from the socket, which can be supressed by holding
+	 * the sk->callback_lock writeable. The other is by net_device downcalls,
+	 * which can be supressed by holding dev->xmit_lock.
+	 *
+	 * In order to avoid doing our additional own locking (which would slow
+	 * down fast path and be an additional source of dead lock bugs), we use
+	 * those locks and grab them both while detaching.
+	 */
+	sktn_lock(ch);
+	write_lock_bh(&sk->callback_lock);
+
+	dev = sktn_dev_get_by_id(id);
+	err = -ENODEV;
+	if( ! dev ) goto error;
+
+	spin_lock_bh(&dev->xmit_lock);
+
+	err = -EINVAL;
+	if( dev->type != ARPHRD_SKTN) goto error_unlock_xmit;
+
+	sp = dev->priv;
+	err = -EBUSY;
+	if( SKTN_ALLOCATED(sp->ch) )  goto error_unlock_xmit;
+
+	/*
+	 * Check whether the socket's header len and mtu size are sufficient
+	 * for the currently set net_device's mtu and hard header len.
+	 *
+	 * Decreasing the dev->hard_header_len and dev->mtu values here is
+	 * dangerous because there could already be sk_buffs (with
+	 * insufficient size or header space) queued for the net_device.
+	 * Better generally return an error and let user space deal with it. 
+	 *
+	 * There seems to be no dedicated error code that perfectly matches
+	 * such condition. IMHO the following code is the most appropriate:
+	 */
+	err = -ENOSPC;
+	if( (dev->mtu + sp->encap_hlen) > sk_ops->sk_mtu(sk))
+		goto error_unlock_xmit;
+	if( dev->hard_header_len < (sp->encap_hlen + sk_ops->sk_hlen(sk)))
+		goto error_unlock_xmit;
+
+	dev_hold(dev);
+	ch->dev = dev;
+	sktn_hold(ch);
+	sp->ch = ch;
+
+	ch->registered = SKTN_REGISTERED_DEV;
+	ch->up_call = &sktn_dev_ops;
+	
+	sktn_attach_sock(ch, sk, sk_ops);
+	err = 0;
+
+ error_unlock_xmit:
+	spin_unlock_bh(&dev->xmit_lock);
+	dev_put(dev);
+ error:
+	write_unlock_bh(&sk->callback_lock);
+	sktn_unlock();
+	return err;
+}
+
+int sktn_dev_sock_ioctl(struct sktn_channel *ch, unsigned int cmd, unsigned long arg)
+{
+	int id;
+	
+	if (get_user(id, (int *)arg)) return -EFAULT;
+
+	switch(cmd){
+	case SIOCSKTNNEWIF:
+		return sktn_dev_new(id);
+	case SIOCSKTNDELIF:
+		return sktn_dev_release(id);
+	}
+	return -EINVAL;
+}
diff -urN x25fix.tmp/net/x25/sktn_ppp.c sktn.tmp/net/x25/sktn_ppp.c
--- x25fix.tmp/net/x25/sktn_ppp.c	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/net/x25/sktn_ppp.c	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,315 @@
+/*
+ * sktn -- generic SocKet TuNnel interface
+ *
+ * Support library for tunneling arbitrary protocols over connected sockets.
+ *
+ * This file contains ppp tunnel specific stuff.
+ *
+ * Copyright 2000 by Henner Eisen. All rights reserved.
+ *
+ * This module 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 <linux/netdevice.h>
+#include <net/sock.h>
+
+#include <linux/sk_tunnel.h>
+#include <linux/ppp_channel.h>
+#include <net/sktn.h>
+#include "sktn_priv.h"
+
+/*
+ * struct ppp_channel_ops::start_xmit() interface callback
+ * from ppp_generic layer
+ */
+static int sktn_ppp_start_xmit(struct ppp_channel * p, struct sk_buff *skb)
+{
+	/* struct sktn_channel *ch = (struct sktn_channel *) p; */
+	struct sktn_channel *ch = p->private;
+	struct sock *sk = ch->sk;
+	int ret;
+	
+	/*
+	 * the return logic of ppp_start_xmit() is currently the opposite
+	 * of dev->hard_start_xmit(). 0 means not successful.
+	 */
+	if(sktn_tx_busy(ch)) return 0;
+
+	/*
+	 * Work around to protect us from beeing called re-entrant for
+	 * same channel. See comments in sktn_ppp_write_space() below
+	 * rationale.
+	 */
+	if(ch->xmitting){
+		printk(KERN_WARNING "re-entrant call to sktn_ppp_start_xmit()\n");
+		return 0;
+	}
+	ch->xmitting = 1;
+	ret = sktn_finish_xmit(ch,sk,skb);
+	ch->xmitting = 0;
+
+	return !ret;
+}
+
+/*
+ * ppp_channel related ioctls passed via /dev/ppp* from ppp_generic layer
+ */
+int sktn_pppch_ioctl(struct ppp_channel *pc,
+		     unsigned int cmd, unsigned long arg)
+{
+	int err, val;
+	struct sktn_channel * ch = pc->private;
+
+	/* should never happen, just a bug trap */
+	if(! pc){
+		printk(KERN_DEBUG "NULL channel\n");
+		return -ENXIO;
+	}
+
+	/*
+	 * protect ppp_channel from beeing removed while ioctl is sleeping
+	 *
+	 * In order to be bullet proof, the ppp_generic layer hopefully
+	 * does protect this against concurrent PPPIOCDETACH ioctl.
+	 */
+	err = down_interruptible(sktn_sem);
+	if(err) return err;
+	sktn_hold(pc->private);
+	err = -EFAULT;
+
+	switch (cmd) {
+	/*
+	 * Dummy calls until pppd (or this function ;=) is updated.
+	 * pppd should not call them
+	 * Some of them might still need to be implemented here
+	 */
+#define IOC_LOG(IOC,VAL) printk(KERN_WARNING "pppch ioctl(" #IOC ",%d)called\n",VAL)
+	case PPPIOCGMRU:
+		IOC_LOG(PPPIOCGMRU,ch->mru);
+		if (put_user(ch->mru, (int *) arg))
+			break;
+		err = 0;
+		break;
+	case PPPIOCSMRU:
+		if (get_user(val, (int *) arg))
+			break;
+		IOC_LOG(PPPIOCSMRU,val);
+		/* for know, we assume that max mtu is also allowed as mru */
+		if (val > ch->ppp_chan.mtu) val = ch->ppp_chan.mtu;
+		if (val < PPP_MRU ) val = PPP_MRU; 
+		ch->mru = val;
+		err = 0;
+		break;
+		/* async maps are not supported. Allways return 0 here ... */
+	case PPPIOCGASYNCMAP:
+		IOC_LOG(PPPIOCGASYNCMAP,0);
+		if (put_user(0, (int *) arg))
+			break;
+		err = 0;
+		break;
+	case PPPIOCGRASYNCMAP:
+		IOC_LOG(PPPIOCGRASYNCMAP,0);
+		if (put_user(0, (int *) arg))
+			break;
+		err = 0;
+		break;
+		/* ... and report error when non-null async map is requested 
+		 ... but pppd is buggy and heavily tries to set non-0 maps,
+		 * even in sync mode */
+	case PPPIOCSASYNCMAP:
+		if (get_user(val, (int *) arg))
+			break;
+		IOC_LOG(PPPIOCSASYNCMAP,val);
+		err = 0;
+		/* if(val) err = -EINVAL; */
+		break;
+	case PPPIOCSRASYNCMAP:
+		if (get_user(val, (int *) arg))
+			break;
+		IOC_LOG(PPPIOCSRASYNCMAP,val);
+		err = 0;
+		/* if(val) err = -EINVAL; */
+		break;
+
+#define CASE_IOC_DUMMY(IOC) case IOC:\
+		err=0;\
+		printk(KERN_WARNING " dummy pppch ioctl " #IOC " called\n");\
+		break;
+		/* most flags are for ppp layer. Only those flags that
+		 * affect us make sense here */
+	CASE_IOC_DUMMY(PPPIOCGFLAGS)
+	CASE_IOC_DUMMY(PPPIOCSFLAGS)
+		/* The whole async map stuff makes no sense here.
+		 * (maybe some day, we support SOCK_STREAM ...;)
+		 */
+	CASE_IOC_DUMMY(PPPIOCSXASYNCMAP)
+	CASE_IOC_DUMMY(PPPIOCGXASYNCMAP)
+
+	default:
+		printk(KERN_WARNING " unexpected ppp_channel ioctl(%x)\n", cmd );
+		err = -ENOIOCTLCMD;
+		goto error;
+	}
+ error:
+	sktn_put(pc->private);
+	up(sktn_sem);
+
+	return err;
+}
+
+static struct ppp_channel_ops sktn_ppp_channel_ops = {
+	start_xmit: sktn_ppp_start_xmit,
+	ioctl: sktn_pppch_ioctl
+};
+
+/*
+ * ppp channel related ioctl()'s on socket file descriptors
+ */
+int sktn_ppp_sock_ioctl(struct ppp_channel *pc,
+		     unsigned int cmd, unsigned long arg)
+{
+	int err;
+
+	sktn_hold(pc->private); 
+
+	switch (cmd) {
+        case PPPIOCGCHAN:
+                err = -EFAULT;
+                if (put_user(ppp_channel_index(pc), (int *) arg))
+                        break;
+                err = 0;
+                break;
+        case PPPIOCGUNIT:
+                err = -EFAULT;
+                if (put_user(ppp_unit_number(pc), (int *) arg))
+                        break;
+                err = 0;
+                break;
+	case PPPIOCATTACH:
+		err = ppp_channel_ioctl(pc, cmd, arg);
+		/* if(!err) sktn_hold(pc->private); */
+		break;
+	case PPPIOCDETACH:
+		err = ppp_channel_ioctl(pc, cmd, arg);
+		/* if(!err) sktn_put(pc->private); */
+		break;
+	default:
+		err = -ENOIOCTLCMD;
+	}
+
+	sktn_put(pc->private);
+	return err;
+}
+
+/*
+ * sk->write_space() method which wakes up the ppp channel's xmit path
+ */
+static void sktn_ppp_write_space(struct sock *sk)
+{
+	/*
+	 * ppp_generic seems to call ppp_channel_ops.start_xmit() when
+	 * ppp_output_wakup() is called even during execution of
+	 * ppp_channel_ops.start_xmit(). This can cause
+	 * ppp_channel_ops.start_xmit() beeing called re-entrant which
+	 * it is not designed for. To work around this, we use our own
+	 * locking in order to detect re-entrant calls and abort.
+	 *
+	 * ppp_generic tries to seriaize start_xmit()'s for same ppp_channel
+	 * by means of a spin_lock_bh(). This should reliably protect
+	 * start_xmit() beeing called from two concurrent threads (e.g.
+	 * ppp_start_xmit() and tx-done irq). But it does not protect
+	 * beeing called recursivly from a single thread.
+	 *
+	 * The following check only safely detects/avoids reentrant calls
+	 * from same start_xmit thread. It does not reliably protect
+	 * us when sk->write_space() is called from irq. The latter should
+	 * already be impossible due to ppp_generic xmit locking.
+ 	 */
+
+	SKTN_WRITE_SPACE_WRAPPER( if(!ch->xmitting) ppp_output_wakeup(&ch->ppp_chan) );
+}
+
+static int sktn_ppp_unregister(struct sktn_channel *ch)
+{
+	int err;
+	struct sock * sk = ch->sk;
+
+	sktn_hold(ch);
+	sktn_lock(ch);
+
+	write_lock_bh(&sk->callback_lock);
+
+	err = -ENODEV;
+	if(ch->registered != SKTN_REGISTERED_PPP){
+		printk(KERN_DEBUG "sktn_ppp_unregister() called while "
+		       "registered at %d\n",ch->registered);
+		goto error;
+	}
+
+	ppp_unregister_channel(&ch->ppp_chan);
+	err = 0;
+	ch->up_call = &sktn_inactive_ops;
+	ch->registered = 0;
+
+	sktn_release_sock(ch);
+
+	sktn_put(ch);
+	err = 0;
+ error:
+	write_unlock_bh(&sk->callback_lock);
+	sktn_unlock();
+	sktn_put(ch);
+	return 0;
+}
+
+static sktn_ops sktn_ppp_ops = {
+/* 
+ * The first typecast trick works with ppp links that don't require
+ * an additional link encapsulation frame header. For other links,
+ * we need to provide an own callback function which removes
+ * encapsulation headers and then calls ppp_input().
+ */
+	rx: (void (*) (struct sktn_channel *, struct sk_buff *)) ppp_input,
+	write_space: sktn_ppp_write_space,
+        unregister: sktn_ppp_unregister
+};
+
+
+
+int sktn_ppp_register(struct sktn_channel *ch, struct sock *sk,
+		      sktn_sk_ops * sk_ops){
+	int err;
+
+	sktn_hold(ch);
+	sktn_lock(ch);
+	write_lock_bh(&sk->callback_lock);
+
+	ch->ppp_chan.ops = & sktn_ppp_channel_ops;
+	ch->ppp_chan.hdrlen = sk_ops->sk_hlen(sk);
+	ch->ppp_chan.mtu = sk_ops->sk_mtu(sk);
+	ch->mru = ch->ppp_chan.mtu;
+
+	ch->up_call = &sktn_ppp_ops;
+	sktn_attach_sock(ch, sk, sk_ops);
+
+	err = ppp_register_channel(&ch->ppp_chan);
+	if (err){
+		sktn_release_sock(ch);
+		ch->up_call = &sktn_inactive_ops;
+		goto error;
+	}
+
+	sktn_hold(ch);
+	ch->registered = SKTN_REGISTERED_PPP;
+
+	err=0;
+ error:	
+	write_unlock_bh(&sk->callback_lock);
+	sktn_unlock();
+	sktn_put(ch);
+
+	return err;
+}
diff -urN x25fix.tmp/net/x25/sktn_priv.h sktn.tmp/net/x25/sktn_priv.h
--- x25fix.tmp/net/x25/sktn_priv.h	Thu Jan  1 01:00:00 1970
+++ sktn.tmp/net/x25/sktn_priv.h	Mon Sep 11 18:53:44 2000
@@ -0,0 +1,159 @@
+#ifndef _SKTN_PRIV_H
+#define _SKTN_PRIV_H
+
+/*
+ * sktn -- generic SocKet TuNnel interface
+ * 
+ * Support library for tunneling arbitrary protocols over connected sockets.
+ *
+ * Header file containing internal API definitions.
+ *
+ * Copyright 2000 by Henner Eisen. All rights reserved.
+ *
+ * This module 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 <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+
+#define SKTN_REGISTERED_DEV 1
+#define SKTN_REGISTERED_PPP 2
+
+extern int sktn_dev_register(struct sktn_channel *, struct sock *, sktn_sk_ops*, int);
+
+extern int sktn_ppp_register(struct sktn_channel *, struct sock *, sktn_sk_ops*);
+
+extern int sktn_wmem_busy(struct sock *);
+
+extern void sktn_attach_sock(struct sktn_channel *, struct sock *, sktn_sk_ops *);
+extern void sktn_release_sock(struct sktn_channel *);
+
+
+extern void sktn_hold(struct sktn_channel *);
+extern void sktn_put(struct sktn_channel *);
+
+/*
+ * pass tx skb to lower (the tunneling protocol's) layer.
+ */
+extern int /*HACK __inline__*/  sktn_finish_xmit(struct sktn_channel *ch,
+				       struct sock *sk,
+				       struct sk_buff *skb)
+#if 0
+     /* HACK moved to extern in sktn_dev.c */
+{
+	struct sk_buff * nskb;
+
+	nskb = skb_clone(skb,GFP_ATOMIC);
+	if(!nskb) return 1;
+
+	skb_set_owner_w(nskb,sk);
+	nskb->h.raw = nskb->nh.raw = nskb->data;
+
+	if(ch->sk_call->sk_xmit(sk,nskb)){
+		dev_kfree_skb(nskb);
+		return 1;
+	}
+	/*
+	 * In order to maintain upper layer's sock wmem based flow control,
+	 * it might make sense do delay the dev_kfree_skb(skb) until the
+	 * cloned nskb is freed. But this requires further tricks, such as
+	 * hooking in the nskb's destructor, which is left for a future
+	 * hacking session.
+	 */
+	dev_kfree_skb(skb);
+	return 0;
+}
+#else
+;
+#endif
+
+/*
+ * Check whether the socket is ready to accept more tunnel data for
+ * transmission.
+ */
+extern int __inline__ sktn_tx_busy(struct sktn_channel *ch)
+{
+	return ((sock_wspace(ch->sk) == 0) || ch->sk_call->sk_busy(ch->sk));
+}
+
+/* 
+ * As there is no sock->sktn (yet?), we need this in order to
+ * protocol-depedently locate an sktn control block from the sk callback's.
+ *
+ * Hopefully, the extra work and memory references induced by this
+ * can be removed by an optimzing compiler when this will be inlined.
+ *
+ * The only alternative would be to code the sk callback functions
+ * individually for each protocol family.
+ */
+
+extern __inline__ struct sktn_channel ** sktn_by_sk(struct sock *sk)
+{
+	switch(sk->family){
+#ifdef CONFIG_X25_TNL
+	case AF_X25:
+		return &(sk->protinfo.x25->sktn);
+#endif
+#if 0
+	/* 
+	 * Other protocol familes need to insert their protocol specific
+	 * sktn location methods here.
+	 */
+#endif
+	};
+	printk(KERN_WARNING "sktn(): PFamily %d: no sktn pointer\n", sk->family);
+	return NULL;
+};
+
+extern sktn_sk_ops sktn_inactive_sk_ops;
+extern sktn_ops sktn_inactive_ops;
+
+/*
+ * Generate Replacements for the sk callback functions sk->write_space.
+ * If the protocol family code uses the network core/sock.c default callback
+ * sock_def_write_space(), chances are good that replacing the sk
+ * callback by these already does most of the work necessary for
+ * interfacing the socket code to the tunnel.
+ *
+ * sk->write_space method is replaced by an upper layer specific
+ * function, which is implemented in sktn_ppp.c and sktn_dev.c.
+ * To generate such a replacement use the following macro like
+ *
+ * static void sktn_my_write_space(struct sock *sk)
+ *{
+ * 	SKTN_WRITE_SPACE_WRAPPER( <supply write wakeup hook here> )
+ *}
+ * Using a macro for this is somewhat ugly, but saves an additional
+ * virtualization layer. (We only need two instaces of this).
+ */
+
+#define SKTN_WRITE_SPACE_WRAPPER( WAKEUP_STATEMENT )\
+\
+        read_lock(&sk->callback_lock);\
+\
+        if(!sk->dead){\
+		struct sktn_channel * ch = * sktn_by_sk(sk);\
+		if( !sktn_tx_busy(ch) ){\
+			WAKEUP_STATEMENT;\
+		}\
+        }\
+\
+        read_unlock(&sk->callback_lock);
+
+extern struct sktn_channel * sktn_new(void);
+
+extern int sktn_ppp_sock_ioctl(struct ppp_channel *, 
+			      unsigned int, unsigned long);
+
+extern int sktn_dev_sock_ioctl(struct sktn_channel *, 
+			      unsigned int, unsigned long);
+
+extern struct semaphore * sktn_sem;
+extern void sktn_lock(struct sktn_channel*);
+extern void sktn_unlock(void);
+
+#endif  _SKTN_PRIV_H
diff -urN x25fix.tmp/net/x25/x25_out.c sktn.tmp/net/x25/x25_out.c
--- x25fix.tmp/net/x25/x25_out.c	Mon Sep 11 19:07:34 2000
+++ sktn.tmp/net/x25/x25_out.c	Mon Sep 11 19:26:44 2000
@@ -16,6 +16,7 @@
  *	X.25 001	Jonathan Naylor	Started coding.
  *	X.25 002	Jonathan Naylor	New timer architecture.
  *      2000-09-04	Henner Eisen    Prevented x25_output() skb leakage.
+ *      2000-09-04	Henner Eisen Made x25_kick() single threaded per socket.
  */
 
 #include <linux/config.h>
@@ -129,7 +130,7 @@
 	x25_transmit_link(skb, sk->protinfo.x25->neighbour);	
 }
 
-void x25_kick(struct sock *sk)
+static void __inline__ __x25_kick(struct sock *sk)
 {
 	struct sk_buff *skb, *skbn;
 	unsigned short start, end;
@@ -196,6 +197,16 @@
 	sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
 
 	x25_stop_timer(sk);
+}
+
+void x25_kick(struct sock *sk)
+{
+	atomic_inc(&sk->protinfo.x25->kick_it);
+ 	if((atomic_read(&sk->protinfo.x25->kick_it)) != 1) return;
+ 
+ 	do { 
+ 		__x25_kick(sk); 
+ 	} while (!atomic_dec_and_test(&sk->protinfo.x25->kick_it));
 }
 
 /*
