x25: use limited socket backlog
[safe/jmp/linux-2.6] / net / x25 / af_x25.c
index 04bec04..9796f3e 100644 (file)
@@ -3,7 +3,7 @@
  *
  *     This is ALPHA test software. This code may break your machine,
  *     randomly fail to work with new releases, misbehave and/or generally
- *     screw up. It might even work. 
+ *     screw up. It might even work.
  *
  *     This code REQUIRES 2.1.15 or higher
  *
  *     X.25 002        Jonathan Naylor Centralised disconnect handling.
  *                                     New timer architecture.
  *     2000-03-11      Henner Eisen    MSG_EOR handling more POSIX compliant.
- *     2000-03-22      Daniela Squassoni Allowed disabling/enabling of 
- *                                       facilities negotiation and increased 
+ *     2000-03-22      Daniela Squassoni Allowed disabling/enabling of
+ *                                       facilities negotiation and increased
  *                                       the throughput upper limit.
  *     2000-08-27      Arnaldo C. Melo s/suser/capable/ + micro cleanups
- *     2000-09-04      Henner Eisen    Set sock->state in x25_accept(). 
+ *     2000-09-04      Henner Eisen    Set sock->state in x25_accept().
  *                                     Fixed x25_output() related skb leakage.
  *     2000-10-02      Henner Eisen    Made x25_kick() single threaded per socket.
  *     2000-10-27      Henner Eisen    MSG_DONTWAIT for fragment allocation.
  *                                     response
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
+#include <linux/capability.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <linux/timer.h>
 #include <linux/string.h>
 #include <linux/net.h>
 #include <linux/if_arp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
-#include <net/tcp.h>
+#include <net/tcp_states.h>
 #include <asm/uaccess.h>
 #include <linux/fcntl.h>
 #include <linux/termios.h>     /* For TIOCINQ/OUTQ */
 #include <linux/notifier.h>
 #include <linux/init.h>
+#include <linux/compat.h>
+#include <linux/ctype.h>
+
 #include <net/x25.h>
+#include <net/compat.h>
 
 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;
 int sysctl_x25_clear_request_timeout   = X25_DEFAULT_T23;
 int sysctl_x25_ack_holdback_timeout    = X25_DEFAULT_T2;
+int sysctl_x25_forward                 = 0;
 
 HLIST_HEAD(x25_list);
 DEFINE_RWLOCK(x25_list_lock);
 
-static struct proto_ops x25_proto_ops;
+static const struct proto_ops x25_proto_ops;
 
 static struct x25_address null_x25_address = {"               "};
 
+#ifdef CONFIG_COMPAT
+struct compat_x25_subscrip_struct {
+       char device[200-sizeof(compat_ulong_t)];
+       compat_ulong_t global_facil_mask;
+       compat_uint_t extended;
+};
+#endif
+
 int x25_addr_ntoa(unsigned char *p, struct x25_address *called_addr,
                  struct x25_address *calling_addr)
 {
-       int called_len, calling_len;
+       unsigned int called_len, calling_len;
        char *called, *calling;
-       int i;
+       unsigned int i;
 
        called_len  = (*p >> 0) & 0x0F;
        calling_len = (*p >> 4) & 0x0F;
@@ -179,6 +193,9 @@ static int x25_device_event(struct notifier_block *this, unsigned long event,
        struct net_device *dev = ptr;
        struct x25_neigh *nb;
 
+       if (!net_eq(dev_net(dev), &init_net))
+               return NOTIFY_DONE;
+
        if (dev->type == ARPHRD_X25
 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
         || dev->type == ARPHRD_ETHER
@@ -244,8 +261,8 @@ static struct sock *x25_find_listener(struct x25_address *addr,
                         * call user data vs this sockets call user data
                         */
                        if(skb->len > 0 && x25_sk(s)->cudmatchlength > 0) {
-                               if((memcmp(x25_sk(s)->calluserdata.cuddata,
-                                       skb->data,
+                               if((memcmp(x25_sk(s)->calluserdata.cuddata,
+                                       skb->data,
                                        x25_sk(s)->cudmatchlength)) == 0) {
                                        sock_hold(s);
                                        goto found;
@@ -317,14 +334,14 @@ static unsigned int x25_new_lci(struct x25_neigh *nb)
 /*
  *     Deferred destroy.
  */
-void x25_destroy_socket(struct sock *);
+static void __x25_destroy_socket(struct sock *);
 
 /*
  *     handler for deferred kills.
  */
 static void x25_destroy_timer(unsigned long data)
 {
-       x25_destroy_socket((struct sock *)data);
+       x25_destroy_socket_from_timer((struct sock *)data);
 }
 
 /*
@@ -334,12 +351,10 @@ static void x25_destroy_timer(unsigned long data)
  *     will touch it and we are (fairly 8-) ) safe.
  *     Not static as it's used by the timer
  */
-void x25_destroy_socket(struct sock *sk)
+static void __x25_destroy_socket(struct sock *sk)
 {
        struct sk_buff *skb;
 
-       sock_hold(sk);
-       lock_sock(sk);
        x25_stop_heartbeat(sk);
        x25_stop_timer(sk);
 
@@ -359,8 +374,7 @@ void x25_destroy_socket(struct sock *sk)
                kfree_skb(skb);
        }
 
-       if (atomic_read(&sk->sk_wmem_alloc) ||
-           atomic_read(&sk->sk_rmem_alloc)) {
+       if (sk_has_allocations(sk)) {
                /* Defer: outstanding buffers */
                sk->sk_timer.expires  = jiffies + 10 * HZ;
                sk->sk_timer.function = x25_destroy_timer;
@@ -370,7 +384,22 @@ void x25_destroy_socket(struct sock *sk)
                /* drop last reference so sock_put will free */
                __sock_put(sk);
        }
+}
 
+void x25_destroy_socket_from_timer(struct sock *sk)
+{
+       sock_hold(sk);
+       bh_lock_sock(sk);
+       __x25_destroy_socket(sk);
+       bh_unlock_sock(sk);
+       sock_put(sk);
+}
+
+static void x25_destroy_socket(struct sock *sk)
+{
+       sock_hold(sk);
+       lock_sock(sk);
+       __x25_destroy_socket(sk);
        release_sock(sk);
        sock_put(sk);
 }
@@ -381,12 +410,13 @@ void x25_destroy_socket(struct sock *sk)
  */
 
 static int x25_setsockopt(struct socket *sock, int level, int optname,
-                         char __user *optval, int optlen)
+                         char __user *optval, unsigned int optlen)
 {
        int opt;
        struct sock *sk = sock->sk;
        int rc = -ENOPROTOOPT;
 
+       lock_kernel();
        if (level != SOL_X25 || optname != X25_QBITINCL)
                goto out;
 
@@ -401,6 +431,7 @@ static int x25_setsockopt(struct socket *sock, int level, int optname,
        x25_sk(sk)->qbitincl = !!opt;
        rc = 0;
 out:
+       unlock_kernel();
        return rc;
 }
 
@@ -409,7 +440,8 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
 {
        struct sock *sk = sock->sk;
        int val, len, rc = -ENOPROTOOPT;
-       
+
+       lock_kernel();
        if (level != SOL_X25 || optname != X25_QBITINCL)
                goto out;
 
@@ -422,7 +454,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
        rc = -EINVAL;
        if (len < 0)
                goto out;
-               
+
        rc = -EFAULT;
        if (put_user(len, optlen))
                goto out;
@@ -430,6 +462,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname,
        val = x25_sk(sk)->qbitincl;
        rc = copy_to_user(optval, &val, len) ? -EFAULT : 0;
 out:
+       unlock_kernel();
        return rc;
 }
 
@@ -438,12 +471,14 @@ static int x25_listen(struct socket *sock, int backlog)
        struct sock *sk = sock->sk;
        int rc = -EOPNOTSUPP;
 
+       lock_kernel();
        if (sk->sk_state != TCP_LISTEN) {
                memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN);
                sk->sk_max_ack_backlog = backlog;
                sk->sk_state           = TCP_LISTEN;
                rc = 0;
        }
+       unlock_kernel();
 
        return rc;
 }
@@ -454,10 +489,10 @@ static struct proto x25_proto = {
        .obj_size = sizeof(struct x25_sock),
 };
 
-static struct sock *x25_alloc_socket(void)
+static struct sock *x25_alloc_socket(struct net *net)
 {
        struct x25_sock *x25;
-       struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, &x25_proto, 1);
+       struct sock *sk = sk_alloc(net, AF_X25, GFP_ATOMIC, &x25_proto);
 
        if (!sk)
                goto out;
@@ -473,19 +508,26 @@ out:
        return sk;
 }
 
-void x25_init_timers(struct sock *sk);
-
-static int x25_create(struct socket *sock, int protocol)
+static int x25_create(struct net *net, struct socket *sock, int protocol,
+                     int kern)
 {
        struct sock *sk;
        struct x25_sock *x25;
-       int rc = -ESOCKTNOSUPPORT;
+       int rc = -EAFNOSUPPORT;
+
+       if (!net_eq(net, &init_net))
+               goto out;
 
-       if (sock->type != SOCK_SEQPACKET || protocol)
+       rc = -ESOCKTNOSUPPORT;
+       if (sock->type != SOCK_SEQPACKET)
                goto out;
 
-       rc = -ENOMEM;
-       if ((sk = x25_alloc_socket()) == NULL)
+       rc = -EINVAL;
+       if (protocol)
+               goto out;
+
+       rc = -ENOBUFS;
+       if ((sk = x25_alloc_socket(net)) == NULL)
                goto out;
 
        x25 = x25_sk(sk);
@@ -513,6 +555,13 @@ static int x25_create(struct socket *sock, int protocol)
        x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE;
        x25->facilities.throughput  = X25_DEFAULT_THROUGHPUT;
        x25->facilities.reverse     = X25_DEFAULT_REVERSE;
+       x25->dte_facilities.calling_len = 0;
+       x25->dte_facilities.called_len = 0;
+       memset(x25->dte_facilities.called_ae, '\0',
+                       sizeof(x25->dte_facilities.called_ae));
+       memset(x25->dte_facilities.calling_ae, '\0',
+                       sizeof(x25->dte_facilities.calling_ae));
+
        rc = 0;
 out:
        return rc;
@@ -526,26 +575,19 @@ static struct sock *x25_make_new(struct sock *osk)
        if (osk->sk_type != SOCK_SEQPACKET)
                goto out;
 
-       if ((sk = x25_alloc_socket()) == NULL)
+       if ((sk = x25_alloc_socket(sock_net(osk))) == NULL)
                goto out;
 
        x25 = x25_sk(sk);
 
        sk->sk_type        = osk->sk_type;
-       sk->sk_socket      = osk->sk_socket;
        sk->sk_priority    = osk->sk_priority;
        sk->sk_protocol    = osk->sk_protocol;
        sk->sk_rcvbuf      = osk->sk_rcvbuf;
        sk->sk_sndbuf      = osk->sk_sndbuf;
        sk->sk_state       = TCP_ESTABLISHED;
-       sk->sk_sleep       = osk->sk_sleep;
        sk->sk_backlog_rcv = osk->sk_backlog_rcv;
-
-       if (sock_flag(osk, SOCK_ZAPPED))
-               sock_set_flag(sk, SOCK_ZAPPED);
-       
-       if (sock_flag(osk, SOCK_DBG))
-               sock_set_flag(sk, SOCK_DBG);
+       sock_copy_flags(sk, osk);
 
        ox25 = x25_sk(osk);
        x25->t21        = ox25->t21;
@@ -554,6 +596,7 @@ static struct sock *x25_make_new(struct sock *osk)
        x25->t2         = ox25->t2;
        x25->facilities = ox25->facilities;
        x25->qbitincl   = ox25->qbitincl;
+       x25->dte_facilities = ox25->dte_facilities;
        x25->cudmatchlength = ox25->cudmatchlength;
        x25->accptapprv = ox25->accptapprv;
 
@@ -567,6 +610,7 @@ static int x25_release(struct socket *sock)
        struct sock *sk = sock->sk;
        struct x25_sock *x25;
 
+       lock_kernel();
        if (!sk)
                goto out;
 
@@ -595,9 +639,9 @@ static int x25_release(struct socket *sock)
                        break;
        }
 
-       sock->sk        = NULL; 
-       sk->sk_socket   = NULL; /* Not used, but we should do this */
+       sock_orphan(sk);
 out:
+       unlock_kernel();
        return 0;
 }
 
@@ -605,24 +649,37 @@ static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
        struct sock *sk = sock->sk;
        struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr;
+       int len, i, rc = 0;
 
+       lock_kernel();
        if (!sock_flag(sk, SOCK_ZAPPED) ||
            addr_len != sizeof(struct sockaddr_x25) ||
-           addr->sx25_family != AF_X25)
-               return -EINVAL;
+           addr->sx25_family != AF_X25) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       len = strlen(addr->sx25_addr.x25_addr);
+       for (i = 0; i < len; i++) {
+               if (!isdigit(addr->sx25_addr.x25_addr[i])) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+       }
 
        x25_sk(sk)->source_addr = addr->sx25_addr;
        x25_insert_socket(sk);
        sock_reset_flag(sk, SOCK_ZAPPED);
        SOCK_DEBUG(sk, "x25_bind: socket is bound\n");
-
-       return 0;
+out:
+       unlock_kernel();
+       return rc;
 }
 
 static int x25_wait_for_connection_establishment(struct sock *sk)
 {
        DECLARE_WAITQUEUE(wait, current);
-        int rc;
+       int rc;
 
        add_wait_queue_exclusive(sk->sk_sleep, &wait);
        for (;;) {
@@ -657,6 +714,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr,
        struct x25_route *rt;
        int rc = 0;
 
+       lock_kernel();
        lock_sock(sk);
        if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
                sock->state = SS_CONNECTED;
@@ -673,7 +731,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr,
        if (sk->sk_state == TCP_ESTABLISHED)
                goto out;
 
-       sk->sk_state   = TCP_CLOSE;     
+       sk->sk_state   = TCP_CLOSE;
        sock->state = SS_UNCONNECTED;
 
        rc = -EINVAL;
@@ -734,10 +792,11 @@ out_put_route:
        x25_route_put(rt);
 out:
        release_sock(sk);
+       unlock_kernel();
        return rc;
 }
 
-static int x25_wait_for_data(struct sock *sk, int timeout)
+static int x25_wait_for_data(struct sock *sk, long timeout)
 {
        DECLARE_WAITQUEUE(wait, current);
        int rc = 0;
@@ -765,7 +824,7 @@ static int x25_wait_for_data(struct sock *sk, int timeout)
        remove_wait_queue(sk->sk_sleep, &wait);
        return rc;
 }
-       
+
 static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
 {
        struct sock *sk = sock->sk;
@@ -773,6 +832,7 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
        struct sk_buff *skb;
        int rc = -EINVAL;
 
+       lock_kernel();
        if (!sk || sk->sk_state != TCP_LISTEN)
                goto out;
 
@@ -789,19 +849,18 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
        if (!skb->sk)
                goto out2;
        newsk            = skb->sk;
-       newsk->sk_socket = newsock;
-       newsk->sk_sleep  = &newsock->wait;
+       sock_graft(newsk, newsock);
 
        /* Now attach up the new socket */
        skb->sk = NULL;
        kfree_skb(skb);
        sk->sk_ack_backlog--;
-       newsock->sk    = newsk;
        newsock->state = SS_CONNECTED;
        rc = 0;
 out2:
        release_sock(sk);
 out:
+       unlock_kernel();
        return rc;
 }
 
@@ -811,10 +870,14 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr,
        struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr;
        struct sock *sk = sock->sk;
        struct x25_sock *x25 = x25_sk(sk);
+       int rc = 0;
 
+       lock_kernel();
        if (peer) {
-               if (sk->sk_state != TCP_ESTABLISHED)
-                       return -ENOTCONN;
+               if (sk->sk_state != TCP_ESTABLISHED) {
+                       rc = -ENOTCONN;
+                       goto out;
+               }
                sx25->sx25_addr = x25->dest_addr;
        } else
                sx25->sx25_addr = x25->source_addr;
@@ -822,9 +885,23 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr,
        sx25->sx25_family = AF_X25;
        *uaddr_len = sizeof(*sx25);
 
-       return 0;
+out:
+       unlock_kernel();
+       return rc;
+}
+
+static unsigned int x25_datagram_poll(struct file *file, struct socket *sock,
+                          poll_table *wait)
+{
+       int rc;
+
+       lock_kernel();
+       rc = datagram_poll(file, sock, wait);
+       unlock_kernel();
+
+       return rc;
 }
+
 int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
                        unsigned int lci)
 {
@@ -833,7 +910,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        struct x25_sock *makex25;
        struct x25_address source_addr, dest_addr;
        struct x25_facilities facilities;
-       int len, rc;
+       struct x25_dte_facilities dte_facilities;
+       int len, addr_len, rc;
 
        /*
         *      Remove the LCI and frame type.
@@ -844,7 +922,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
         *      Extract the X.25 addresses and convert them to ASCII strings,
         *      and remove them.
         */
-       skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
+       addr_len = x25_addr_ntoa(skb->data, &source_addr, &dest_addr);
+       skb_pull(skb, addr_len);
 
        /*
         *      Get the length of the facilities, skip past them for the moment
@@ -860,16 +939,34 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        sk = x25_find_listener(&source_addr,skb);
        skb_push(skb,len);
 
+       if (sk != NULL && sk_acceptq_is_full(sk)) {
+               goto out_sock_put;
+       }
+
        /*
-        *      We can't accept the Call Request.
+        *      We dont have any listeners for this incoming call.
+        *      Try forwarding it.
         */
-       if (sk == NULL || sk_acceptq_is_full(sk))
-               goto out_clear_request;
+       if (sk == NULL) {
+               skb_push(skb, addr_len + X25_STD_MIN_LEN);
+               if (sysctl_x25_forward &&
+                               x25_forward_call(&dest_addr, nb, skb, lci) > 0)
+               {
+                       /* Call was forwarded, dont process it any more */
+                       kfree_skb(skb);
+                       rc = 1;
+                       goto out;
+               } else {
+                       /* No listeners, can't forward, clear the call */
+                       goto out_clear_request;
+               }
+       }
 
        /*
         *      Try to reach a compromise on the requested facilities.
         */
-       if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1)
+       len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities);
+       if (len == -1)
                goto out_sock_put;
 
        /*
@@ -900,9 +997,12 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        makex25->source_addr   = source_addr;
        makex25->neighbour     = nb;
        makex25->facilities    = facilities;
+       makex25->dte_facilities= dte_facilities;
        makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask;
        /* ensure no reverse facil on accept */
        makex25->vc_facil_mask &= ~X25_MASK_REVERSE;
+       /* ensure no calling address extension on accept */
+       makex25->vc_facil_mask &= ~X25_MASK_CALLING_AE;
        makex25->cudmatchlength = x25_sk(sk)->cudmatchlength;
 
        /* Normally all calls are accepted immediatly */
@@ -914,10 +1014,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        /*
         *      Incoming Call User Data.
         */
-       if (skb->len >= 0) {
-               memcpy(makex25->calluserdata.cuddata, skb->data, skb->len);
-               makex25->calluserdata.cudlength = skb->len;
-       }
+       skb_copy_from_linear_data(skb, makex25->calluserdata.cuddata, skb->len);
+       makex25->calluserdata.cudlength = skb->len;
 
        sk->sk_ack_backlog++;
 
@@ -954,6 +1052,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
        size_t size;
        int qbit = 0, rc = -EINVAL;
 
+       lock_kernel();
        if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_OOB|MSG_EOR|MSG_CMSG_COMPAT))
                goto out;
 
@@ -1000,6 +1099,12 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
                sx25.sx25_addr   = x25->dest_addr;
        }
 
+       /* Sanity check the packet size */
+       if (len > 65535) {
+               rc = -EMSGSIZE;
+               goto out;
+       }
+
        SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n");
 
        /* Build a packet */
@@ -1022,9 +1127,10 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
         */
        SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n");
 
-       asmptr = skb->h.raw = skb_put(skb, len);
+       skb_reset_transport_header(skb);
+       skb_put(skb, len);
 
-       rc = memcpy_fromiovec(asmptr, msg->msg_iov, len);
+       rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
        if (rc)
                goto out_kfree_skb;
 
@@ -1084,8 +1190,9 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (msg->msg_flags & MSG_OOB)
                skb_queue_tail(&x25->interrupt_out_queue, skb);
        else {
-               len = x25_output(sk, skb);
-               if (len < 0)
+               rc = x25_output(sk, skb);
+               len = rc;
+               if (rc < 0)
                        kfree_skb(skb);
                else if (x25->qbitincl)
                        len++;
@@ -1110,6 +1217,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
        release_sock(sk);
        rc = len;
 out:
+       unlock_kernel();
        return rc;
 out_kfree_skb:
        kfree_skb(skb);
@@ -1130,6 +1238,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
        unsigned char *asmptr;
        int rc = -ENOTCONN;
 
+       lock_kernel();
        /*
         * This works for seqpacket too. The receiver has ordered the queue for
         * us! We do one quick check first though
@@ -1174,8 +1283,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
                }
        }
 
-       skb->h.raw = skb->data;
-
+       skb_reset_transport_header(skb);
        copied = skb->len;
 
        if (copied > size) {
@@ -1183,7 +1291,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
                msg->msg_flags |= MSG_TRUNC;
        }
 
-       /* Currently, each datagram always contains a complete record */ 
+       /* Currently, each datagram always contains a complete record */
        msg->msg_flags |= MSG_EOR;
 
        rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
@@ -1204,6 +1312,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
 out_free_dgram:
        skb_free_datagram(sk, skb);
 out:
+       unlock_kernel();
        return rc;
 }
 
@@ -1215,10 +1324,11 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
        void __user *argp = (void __user *)arg;
        int rc;
 
+       lock_kernel();
        switch (cmd) {
                case TIOCOUTQ: {
-                       int amount = sk->sk_sndbuf -
-                                    atomic_read(&sk->sk_wmem_alloc);
+                       int amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
+
                        if (amount < 0)
                                amount = 0;
                        rc = put_user(amount, (unsigned int __user *)argp);
@@ -1241,8 +1351,14 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCGSTAMP:
                        rc = -EINVAL;
                        if (sk)
-                               rc = sock_get_timestamp(sk, 
-                                               (struct timeval __user *)argp); 
+                               rc = sock_get_timestamp(sk,
+                                               (struct timeval __user *)argp);
+                       break;
+               case SIOCGSTAMPNS:
+                       rc = -EINVAL;
+                       if (sk)
+                               rc = sock_get_timestampns(sk,
+                                               (struct timespec __user *)argp);
                        break;
                case SIOCGIFADDR:
                case SIOCSIFADDR:
@@ -1302,13 +1418,43 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                            facilities.throughput > 0xDD)
                                break;
                        if (facilities.reverse &&
-                               (facilities.reverse | 0x81)!= 0x81)
+                               (facilities.reverse & 0x81) != 0x81)
                                break;
                        x25->facilities = facilities;
                        rc = 0;
                        break;
                }
 
+               case SIOCX25GDTEFACILITIES: {
+                       rc = copy_to_user(argp, &x25->dte_facilities,
+                                               sizeof(x25->dte_facilities));
+                       if (rc)
+                               rc = -EFAULT;
+                       break;
+               }
+
+               case SIOCX25SDTEFACILITIES: {
+                       struct x25_dte_facilities dtefacs;
+                       rc = -EFAULT;
+                       if (copy_from_user(&dtefacs, argp, sizeof(dtefacs)))
+                               break;
+                       rc = -EINVAL;
+                       if (sk->sk_state != TCP_LISTEN &&
+                                       sk->sk_state != TCP_CLOSE)
+                               break;
+                       if (dtefacs.calling_len > X25_MAX_AE_LEN)
+                               break;
+                       if (dtefacs.calling_ae == NULL)
+                               break;
+                       if (dtefacs.called_len > X25_MAX_AE_LEN)
+                               break;
+                       if (dtefacs.called_ae == NULL)
+                               break;
+                       x25->dte_facilities = dtefacs;
+                       rc = 0;
+                       break;
+               }
+
                case SIOCX25GCALLUSERDATA: {
                        struct x25_calluserdata cud = x25->calluserdata;
                        rc = copy_to_user(argp, &cud,
@@ -1339,6 +1485,17 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        break;
                }
 
+               case SIOCX25SCAUSEDIAG: {
+                       struct x25_causediag causediag;
+                       rc = -EFAULT;
+                       if (copy_from_user(&causediag, argp, sizeof(causediag)))
+                               break;
+                       x25->causediag = causediag;
+                       rc = 0;
+                       break;
+
+               }
+
                case SIOCX25SCUDMATCHLEN: {
                        struct x25_subaddr sub_addr;
                        rc = -EINVAL;
@@ -1348,7 +1505,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        if (copy_from_user(&sub_addr, argp,
                                        sizeof(sub_addr)))
                                break;
-                       rc = -EINVAL;
+                       rc = -EINVAL;
                        if(sub_addr.cudmatchlength > X25_MAX_CUD_LEN)
                                break;
                        x25->cudmatchlength = sub_addr.cudmatchlength;
@@ -1377,21 +1534,151 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               default:
-                       rc = dev_ioctl(cmd, argp);
+               default:
+                       rc = -ENOIOCTLCMD;
                        break;
        }
+       unlock_kernel();
 
        return rc;
 }
 
-static struct net_proto_family x25_family_ops = {
+static const struct net_proto_family x25_family_ops = {
        .family =       AF_X25,
        .create =       x25_create,
        .owner  =       THIS_MODULE,
 };
 
-static struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
+#ifdef CONFIG_COMPAT
+static int compat_x25_subscr_ioctl(unsigned int cmd,
+               struct compat_x25_subscrip_struct __user *x25_subscr32)
+{
+       struct compat_x25_subscrip_struct x25_subscr;
+       struct x25_neigh *nb;
+       struct net_device *dev;
+       int rc = -EINVAL;
+
+       rc = -EFAULT;
+       if (copy_from_user(&x25_subscr, x25_subscr32, sizeof(*x25_subscr32)))
+               goto out;
+
+       rc = -EINVAL;
+       dev = x25_dev_get(x25_subscr.device);
+       if (dev == NULL)
+               goto out;
+
+       nb = x25_get_neigh(dev);
+       if (nb == NULL)
+               goto out_dev_put;
+
+       dev_put(dev);
+
+       if (cmd == SIOCX25GSUBSCRIP) {
+               x25_subscr.extended = nb->extended;
+               x25_subscr.global_facil_mask = nb->global_facil_mask;
+               rc = copy_to_user(x25_subscr32, &x25_subscr,
+                               sizeof(*x25_subscr32)) ? -EFAULT : 0;
+       } else {
+               rc = -EINVAL;
+               if (x25_subscr.extended == 0 || x25_subscr.extended == 1) {
+                       rc = 0;
+                       nb->extended = x25_subscr.extended;
+                       nb->global_facil_mask = x25_subscr.global_facil_mask;
+               }
+       }
+       x25_neigh_put(nb);
+out:
+       return rc;
+out_dev_put:
+       dev_put(dev);
+       goto out;
+}
+
+static int compat_x25_ioctl(struct socket *sock, unsigned int cmd,
+                               unsigned long arg)
+{
+       void __user *argp = compat_ptr(arg);
+       struct sock *sk = sock->sk;
+
+       int rc = -ENOIOCTLCMD;
+
+       switch(cmd) {
+       case TIOCOUTQ:
+       case TIOCINQ:
+               rc = x25_ioctl(sock, cmd, (unsigned long)argp);
+               break;
+       case SIOCGSTAMP:
+               rc = -EINVAL;
+               lock_kernel();
+               if (sk)
+                       rc = compat_sock_get_timestamp(sk,
+                                       (struct timeval __user*)argp);
+               unlock_kernel();
+               break;
+       case SIOCGSTAMPNS:
+               rc = -EINVAL;
+               lock_kernel();
+               if (sk)
+                       rc = compat_sock_get_timestampns(sk,
+                                       (struct timespec __user*)argp);
+               unlock_kernel();
+               break;
+       case SIOCGIFADDR:
+       case SIOCSIFADDR:
+       case SIOCGIFDSTADDR:
+       case SIOCSIFDSTADDR:
+       case SIOCGIFBRDADDR:
+       case SIOCSIFBRDADDR:
+       case SIOCGIFNETMASK:
+       case SIOCSIFNETMASK:
+       case SIOCGIFMETRIC:
+       case SIOCSIFMETRIC:
+               rc = -EINVAL;
+               break;
+       case SIOCADDRT:
+       case SIOCDELRT:
+               rc = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       break;
+               lock_kernel();
+               rc = x25_route_ioctl(cmd, argp);
+               unlock_kernel();
+               break;
+       case SIOCX25GSUBSCRIP:
+               lock_kernel();
+               rc = compat_x25_subscr_ioctl(cmd, argp);
+               unlock_kernel();
+               break;
+       case SIOCX25SSUBSCRIP:
+               rc = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       break;
+               lock_kernel();
+               rc = compat_x25_subscr_ioctl(cmd, argp);
+               unlock_kernel();
+               break;
+       case SIOCX25GFACILITIES:
+       case SIOCX25SFACILITIES:
+       case SIOCX25GDTEFACILITIES:
+       case SIOCX25SDTEFACILITIES:
+       case SIOCX25GCALLUSERDATA:
+       case SIOCX25SCALLUSERDATA:
+       case SIOCX25GCAUSEDIAG:
+       case SIOCX25SCAUSEDIAG:
+       case SIOCX25SCUDMATCHLEN:
+       case SIOCX25CALLACCPTAPPRV:
+       case SIOCX25SENDCALLACCPT:
+               rc = x25_ioctl(sock, cmd, (unsigned long)argp);
+               break;
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+       return rc;
+}
+#endif
+
+static const struct proto_ops x25_proto_ops = {
        .family =       AF_X25,
        .owner =        THIS_MODULE,
        .release =      x25_release,
@@ -1400,8 +1687,11 @@ static struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
        .socketpair =   sock_no_socketpair,
        .accept =       x25_accept,
        .getname =      x25_getname,
-       .poll =         datagram_poll,
+       .poll =         x25_datagram_poll,
        .ioctl =        x25_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = compat_x25_ioctl,
+#endif
        .listen =       x25_listen,
        .shutdown =     sock_no_shutdown,
        .setsockopt =   x25_setsockopt,
@@ -1412,11 +1702,8 @@ static struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
        .sendpage =     sock_no_sendpage,
 };
 
-#include <linux/smp_lock.h>
-SOCKOPS_WRAP(x25_proto, AF_X25);
-
-static struct packet_type x25_packet_type = {
-       .type = __constant_htons(ETH_P_X25),
+static struct packet_type x25_packet_type __read_mostly = {
+       .type = cpu_to_be16(ETH_P_X25),
        .func = x25_lapb_receive_frame,
 };
 
@@ -1436,6 +1723,9 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
                        x25_disconnect(s, ENETUNREACH, 0, 0);
 
        write_unlock_bh(&x25_list_lock);
+
+       /* Remove any related forwards */
+       x25_clear_forward_by_dev(nb->dev);
 }
 
 static int __init x25_init(void)
@@ -1445,20 +1735,31 @@ static int __init x25_init(void)
        if (rc != 0)
                goto out;
 
-       sock_register(&x25_family_ops);
+       rc = sock_register(&x25_family_ops);
+       if (rc != 0)
+               goto out_proto;
 
        dev_add_pack(&x25_packet_type);
 
-       register_netdevice_notifier(&x25_dev_notifier);
+       rc = register_netdevice_notifier(&x25_dev_notifier);
+       if (rc != 0)
+               goto out_sock;
 
-       printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n");
+       printk(KERN_INFO "X.25 for Linux Version 0.2\n");
 
-#ifdef CONFIG_SYSCTL
        x25_register_sysctl();
-#endif
-       x25_proc_init();
+       rc = x25_proc_init();
+       if (rc != 0)
+               goto out_dev;
 out:
        return rc;
+out_dev:
+       unregister_netdevice_notifier(&x25_dev_notifier);
+out_sock:
+       sock_unregister(AF_X25);
+out_proto:
+       proto_unregister(&x25_proto);
+       goto out;
 }
 module_init(x25_init);
 
@@ -1468,9 +1769,7 @@ static void __exit x25_exit(void)
        x25_link_free();
        x25_route_free();
 
-#ifdef CONFIG_SYSCTL
        x25_unregister_sysctl();
-#endif
 
        unregister_netdevice_notifier(&x25_dev_notifier);