Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / x25 / af_x25.c
index 614f959..f404f67 100644 (file)
  *     2000-11-14      Henner Eisen    Closing datalink from NETDEV_GOING_DOWN
  *     2002-10-06      Arnaldo C. Melo Get rid of cli/sti, move proc stuff to
  *                                     x25_proc.c, using seq_file
+ *     2005-04-02      Shaun Pereira   Selective sub address matching
+ *                                     with call user data
+ *     2005-04-15      Shaun Pereira   Fast select with no restriction on
+ *                                     response
  */
 
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/capability.h>
 #include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/string.h>
-#include <linux/sockios.h>
 #include <linux/net.h>
-#include <linux/stat.h>
-#include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
-#include <net/tcp.h>
-#include <asm/system.h>
+#include <net/tcp_states.h>
 #include <asm/uaccess.h>
 #include <linux/fcntl.h>
 #include <linux/termios.h>     /* For TIOCINQ/OUTQ */
-#include <linux/mm.h>
-#include <linux/interrupt.h>
 #include <linux/notifier.h>
 #include <linux/init.h>
+#include <linux/compat.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;
@@ -67,12 +66,20 @@ int sysctl_x25_clear_request_timeout   = X25_DEFAULT_T23;
 int sysctl_x25_ack_holdback_timeout    = X25_DEFAULT_T2;
 
 HLIST_HEAD(x25_list);
-rwlock_t x25_list_lock = RW_LOCK_UNLOCKED;
+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)
 {
@@ -223,24 +230,46 @@ static void x25_insert_socket(struct sock *sk)
 
 /*
  *     Find a socket that wants to accept the Call Request we just
- *     received.
+ *     received. Check the full list for an address/cud match.
+ *     If no cuds match return the next_best thing, an address match.
+ *     Note: if a listening socket has cud set it must only get calls
+ *     with matching cud.
  */
-static struct sock *x25_find_listener(struct x25_address *addr)
+static struct sock *x25_find_listener(struct x25_address *addr,
+                                       struct sk_buff *skb)
 {
        struct sock *s;
+       struct sock *next_best;
        struct hlist_node *node;
 
        read_lock_bh(&x25_list_lock);
+       next_best = NULL;
 
        sk_for_each(s, node, &x25_list)
                if ((!strcmp(addr->x25_addr,
-                            x25_sk(s)->source_addr.x25_addr) ||
-                    !strcmp(addr->x25_addr,
-                            null_x25_address.x25_addr)) &&
-                    s->sk_state == TCP_LISTEN) {
-                       sock_hold(s);
-                       goto found;
+                       x25_sk(s)->source_addr.x25_addr) ||
+                               !strcmp(addr->x25_addr,
+                                       null_x25_address.x25_addr)) &&
+                                       s->sk_state == TCP_LISTEN) {
+                       /*
+                        * Found a listening socket, now check the incoming
+                        * 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,
+                                       x25_sk(s)->cudmatchlength)) == 0) {
+                                       sock_hold(s);
+                                       goto found;
+                                }
+                       } else
+                               next_best = s;
                }
+       if (next_best) {
+               s = next_best;
+               sock_hold(s);
+               goto found;
+       }
        s = NULL;
 found:
        read_unlock_bh(&x25_list_lock);
@@ -250,7 +279,7 @@ found:
 /*
  *     Find a connected X.25 socket given my LCI and neighbour.
  */
-struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb)
+static struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb)
 {
        struct sock *s;
        struct hlist_node *node;
@@ -278,7 +307,7 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb)
 /*
  *     Find a unique LCI for a given device.
  */
-unsigned int x25_new_lci(struct x25_neigh *nb)
+static unsigned int x25_new_lci(struct x25_neigh *nb)
 {
        unsigned int lci = 1;
        struct sock *sk;
@@ -347,6 +376,7 @@ void x25_destroy_socket(struct sock *sk)
                /* Defer: outstanding buffers */
                sk->sk_timer.expires  = jiffies + 10 * HZ;
                sk->sk_timer.function = x25_destroy_timer;
+               sk->sk_timer.data = (unsigned long)sk;
                add_timer(&sk->sk_timer);
        } else {
                /* drop last reference so sock_put will free */
@@ -363,7 +393,7 @@ void x25_destroy_socket(struct sock *sk)
  */
 
 static int x25_setsockopt(struct socket *sock, int level, int optname,
-                         char *optval, int optlen)
+                         char __user *optval, int optlen)
 {
        int opt;
        struct sock *sk = sock->sk;
@@ -377,7 +407,7 @@ static int x25_setsockopt(struct socket *sock, int level, int optname,
                goto out;
 
        rc = -EFAULT;
-       if (get_user(opt, (int *)optval))
+       if (get_user(opt, (int __user *)optval))
                goto out;
 
        x25_sk(sk)->qbitincl = !!opt;
@@ -387,7 +417,7 @@ out:
 }
 
 static int x25_getsockopt(struct socket *sock, int level, int optname,
-                         char *optval, int *optlen)
+                         char __user *optval, int __user *optlen)
 {
        struct sock *sk = sock->sk;
        int val, len, rc = -ENOPROTOOPT;
@@ -430,35 +460,29 @@ static int x25_listen(struct socket *sock, int backlog)
        return rc;
 }
 
+static struct proto x25_proto = {
+       .name     = "X25",
+       .owner    = THIS_MODULE,
+       .obj_size = sizeof(struct x25_sock),
+};
+
 static struct sock *x25_alloc_socket(void)
 {
-       struct x25_opt *x25;
-       struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, 1, NULL);
+       struct x25_sock *x25;
+       struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, &x25_proto, 1);
 
        if (!sk)
                goto out;
 
-       x25 = sk->sk_protinfo = kmalloc(sizeof(*x25), GFP_ATOMIC);
-       if (!x25)
-               goto frees;
-
-       memset(x25, 0, sizeof(*x25));
-
-       x25->sk = sk;
-
        sock_init_data(NULL, sk);
-       sk_set_owner(sk, THIS_MODULE);
 
+       x25 = x25_sk(sk);
        skb_queue_head_init(&x25->ack_queue);
        skb_queue_head_init(&x25->fragment_queue);
        skb_queue_head_init(&x25->interrupt_in_queue);
        skb_queue_head_init(&x25->interrupt_out_queue);
 out:
        return sk;
-frees:
-       sk_free(sk);
-       sk = NULL;
-       goto out;
 }
 
 void x25_init_timers(struct sock *sk);
@@ -466,7 +490,7 @@ void x25_init_timers(struct sock *sk);
 static int x25_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
-       struct x25_opt *x25;
+       struct x25_sock *x25;
        int rc = -ESOCKTNOSUPPORT;
 
        if (sock->type != SOCK_SEQPACKET || protocol)
@@ -478,8 +502,10 @@ static int x25_create(struct socket *sock, int protocol)
 
        x25 = x25_sk(sk);
 
-       sock_init_data(sock, sk);
-       sk_set_owner(sk, THIS_MODULE);
+       sk->sk_socket = sock;
+       sk->sk_type = sock->type;
+       sk->sk_sleep = &sock->wait;
+       sock->sk = sk;
 
        x25_init_timers(sk);
 
@@ -492,6 +518,9 @@ static int x25_create(struct socket *sock, int protocol)
        x25->t23   = sysctl_x25_clear_request_timeout;
        x25->t2    = sysctl_x25_ack_holdback_timeout;
        x25->state = X25_STATE_0;
+       x25->cudmatchlength = 0;
+       x25->accptapprv = X25_DENY_ACCPT_APPRV;         /* normally no cud  */
+                                                       /* on call accept   */
 
        x25->facilities.winsize_in  = X25_DEFAULT_WINDOW_SIZE;
        x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE;
@@ -499,6 +528,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;
@@ -507,7 +543,7 @@ out:
 static struct sock *x25_make_new(struct sock *osk)
 {
        struct sock *sk = NULL;
-       struct x25_opt *x25, *ox25;
+       struct x25_sock *x25, *ox25;
 
        if (osk->sk_type != SOCK_SEQPACKET)
                goto out;
@@ -523,11 +559,10 @@ static struct sock *x25_make_new(struct sock *osk)
        sk->sk_protocol    = osk->sk_protocol;
        sk->sk_rcvbuf      = osk->sk_rcvbuf;
        sk->sk_sndbuf      = osk->sk_sndbuf;
-       sk->sk_debug       = osk->sk_debug;
        sk->sk_state       = TCP_ESTABLISHED;
        sk->sk_sleep       = osk->sk_sleep;
-       sk->sk_zapped      = osk->sk_zapped;
        sk->sk_backlog_rcv = osk->sk_backlog_rcv;
+       sock_copy_flags(sk, osk);
 
        ox25 = x25_sk(osk);
        x25->t21        = ox25->t21;
@@ -536,6 +571,9 @@ 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;
 
        x25_init_timers(sk);
 out:
@@ -545,7 +583,7 @@ out:
 static int x25_release(struct socket *sock)
 {
        struct sock *sk = sock->sk;
-       struct x25_opt *x25;
+       struct x25_sock *x25;
 
        if (!sk)
                goto out;
@@ -586,14 +624,14 @@ 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;
 
-       if (!sk->sk_zapped ||
+       if (!sock_flag(sk, SOCK_ZAPPED) ||
            addr_len != sizeof(struct sockaddr_x25) ||
            addr->sx25_family != AF_X25)
                return -EINVAL;
 
        x25_sk(sk)->source_addr = addr->sx25_addr;
        x25_insert_socket(sk);
-       sk->sk_zapped = 0;
+       sock_reset_flag(sk, SOCK_ZAPPED);
        SOCK_DEBUG(sk, "x25_bind: socket is bound\n");
 
        return 0;
@@ -632,7 +670,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr,
                       int addr_len, int flags)
 {
        struct sock *sk = sock->sk;
-       struct x25_opt *x25 = x25_sk(sk);
+       struct x25_sock *x25 = x25_sk(sk);
        struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr;
        struct x25_route *rt;
        int rc = 0;
@@ -677,7 +715,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr,
                goto out_put_neigh;
 
        rc = -EINVAL;
-       if (sk->sk_zapped) /* Must bind first - autobinding does not work */
+       if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */
                goto out_put_neigh;
 
        if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr))
@@ -717,7 +755,7 @@ out:
        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;
@@ -769,7 +807,6 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
        if (!skb->sk)
                goto out2;
        newsk            = skb->sk;
-       newsk->sk_pair   = NULL;
        newsk->sk_socket = newsock;
        newsk->sk_sleep  = &newsock->wait;
 
@@ -791,7 +828,7 @@ 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_opt *x25 = x25_sk(sk);
+       struct x25_sock *x25 = x25_sk(sk);
 
        if (peer) {
                if (sk->sk_state != TCP_ESTABLISHED)
@@ -811,9 +848,10 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
 {
        struct sock *sk;
        struct sock *make;
-       struct x25_opt *makex25;
+       struct x25_sock *makex25;
        struct x25_address source_addr, dest_addr;
        struct x25_facilities facilities;
+       struct x25_dte_facilities dte_facilities;
        int len, rc;
 
        /*
@@ -828,20 +866,30 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
        skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
 
        /*
-        *      Find a listener for the particular address.
+        *      Get the length of the facilities, skip past them for the moment
+        *      get the call user data because this is needed to determine
+        *      the correct listener
+        */
+       len = skb->data[0] + 1;
+       skb_pull(skb,len);
+
+       /*
+        *      Find a listener for the particular address/cud pair.
         */
-       sk = x25_find_listener(&source_addr);
+       sk = x25_find_listener(&source_addr,skb);
+       skb_push(skb,len);
 
        /*
         *      We can't accept the Call Request.
         */
-       if (!sk || sk->sk_ack_backlog == sk->sk_max_ack_backlog)
+       if (sk == NULL || sk_acceptq_is_full(sk))
                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;
 
        /*
@@ -859,7 +907,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
                goto out_sock_put;
 
        /*
-        *      Remove the facilities, leaving any Call User Data.
+        *      Remove the facilities
         */
        skb_pull(skb, len);
 
@@ -872,9 +920,19 @@ 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;
-
-       x25_write_internal(make, X25_CALL_ACCEPTED);
+       /* 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 */
+       if(makex25->accptapprv & X25_DENY_ACCPT_APPRV) {
+               x25_write_internal(make, X25_CALL_ACCEPTED);
+               makex25->state = X25_STATE_3;
+       }
 
        /*
         *      Incoming Call User Data.
@@ -884,10 +942,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
                makex25->calluserdata.cudlength = skb->len;
        }
 
-       makex25->state = X25_STATE_3;
-
        sk->sk_ack_backlog++;
-       make->sk_pair = sk;
 
        x25_insert_socket(make);
 
@@ -913,7 +968,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
                       struct msghdr *msg, size_t len)
 {
        struct sock *sk = sock->sk;
-       struct x25_opt *x25 = x25_sk(sk);
+       struct x25_sock *x25 = x25_sk(sk);
        struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name;
        struct sockaddr_x25 sx25;
        struct sk_buff *skb;
@@ -922,7 +977,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
        size_t size;
        int qbit = 0, rc = -EINVAL;
 
-       if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_OOB | MSG_EOR))
+       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_OOB|MSG_EOR|MSG_CMSG_COMPAT))
                goto out;
 
        /* we currently don't support segmented records at the user interface */
@@ -930,7 +985,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
                goto out;
 
        rc = -EADDRNOTAVAIL;
-       if (sk->sk_zapped)
+       if (sock_flag(sk, SOCK_ZAPPED))
                goto out;
 
        rc = -EPIPE;
@@ -1090,7 +1145,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
                       int flags)
 {
        struct sock *sk = sock->sk;
-       struct x25_opt *x25 = x25_sk(sk);
+       struct x25_sock *x25 = x25_sk(sk);
        struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name;
        size_t copied;
        int qbit;
@@ -1179,7 +1234,8 @@ out:
 static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
        struct sock *sk = sock->sk;
-       struct x25_opt *x25 = x25_sk(sk);
+       struct x25_sock *x25 = x25_sk(sk);
+       void __user *argp = (void __user *)arg;
        int rc;
 
        switch (cmd) {
@@ -1188,7 +1244,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                                     atomic_read(&sk->sk_wmem_alloc);
                        if (amount < 0)
                                amount = 0;
-                       rc = put_user(amount, (unsigned int *)arg);
+                       rc = put_user(amount, (unsigned int __user *)argp);
                        break;
                }
 
@@ -1201,7 +1257,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                         */
                        if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
                                amount = skb->len;
-                       rc = put_user(amount, (unsigned int *)arg);
+                       rc = put_user(amount, (unsigned int __user *)argp);
                        break;
                }
 
@@ -1209,7 +1265,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        rc = -EINVAL;
                        if (sk)
                                rc = sock_get_timestamp(sk, 
-                                               (struct timeval *)arg); 
+                                               (struct timeval __user *)argp); 
                        break;
                case SIOCGIFADDR:
                case SIOCSIFADDR:
@@ -1228,20 +1284,20 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        rc = -EPERM;
                        if (!capable(CAP_NET_ADMIN))
                                break;
-                       rc = x25_route_ioctl(cmd, (void *)arg);
+                       rc = x25_route_ioctl(cmd, argp);
                        break;
                case SIOCX25GSUBSCRIP:
-                       rc = x25_subscr_ioctl(cmd, (void *)arg);
+                       rc = x25_subscr_ioctl(cmd, argp);
                        break;
                case SIOCX25SSUBSCRIP:
                        rc = -EPERM;
                        if (!capable(CAP_NET_ADMIN))
                                break;
-                       rc = x25_subscr_ioctl(cmd, (void *)arg);
+                       rc = x25_subscr_ioctl(cmd, argp);
                        break;
                case SIOCX25GFACILITIES: {
                        struct x25_facilities fac = x25->facilities;
-                       rc = copy_to_user((void *)arg, &fac,
+                       rc = copy_to_user(argp, &fac,
                                          sizeof(fac)) ? -EFAULT : 0;
                        break;
                }
@@ -1249,7 +1305,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCX25SFACILITIES: {
                        struct x25_facilities facilities;
                        rc = -EFAULT;
-                       if (copy_from_user(&facilities, (void *)arg,
+                       if (copy_from_user(&facilities, argp,
                                           sizeof(facilities)))
                                break;
                        rc = -EINVAL;
@@ -1268,16 +1324,47 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        if (facilities.throughput < 0x03 ||
                            facilities.throughput > 0xDD)
                                break;
-                       if (facilities.reverse && facilities.reverse != 1)
+                       if (facilities.reverse &&
+                               (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((void *)arg, &cud,
+                       rc = copy_to_user(argp, &cud,
                                          sizeof(cud)) ? -EFAULT : 0;
                        break;
                }
@@ -1286,7 +1373,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        struct x25_calluserdata calluserdata;
 
                        rc = -EFAULT;
-                       if (copy_from_user(&calluserdata, (void *)arg,
+                       if (copy_from_user(&calluserdata, argp,
                                           sizeof(calluserdata)))
                                break;
                        rc = -EINVAL;
@@ -1300,26 +1387,176 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCX25GCAUSEDIAG: {
                        struct x25_causediag causediag;
                        causediag = x25->causediag;
-                       rc = copy_to_user((void *)arg, &causediag,
+                       rc = copy_to_user(argp, &causediag,
                                          sizeof(causediag)) ? -EFAULT : 0;
                        break;
                }
 
+               case SIOCX25SCUDMATCHLEN: {
+                       struct x25_subaddr sub_addr;
+                       rc = -EINVAL;
+                       if(sk->sk_state != TCP_CLOSE)
+                               break;
+                       rc = -EFAULT;
+                       if (copy_from_user(&sub_addr, argp,
+                                       sizeof(sub_addr)))
+                               break;
+                       rc = -EINVAL;
+                       if(sub_addr.cudmatchlength > X25_MAX_CUD_LEN)
+                               break;
+                       x25->cudmatchlength = sub_addr.cudmatchlength;
+                       rc = 0;
+                       break;
+               }
+
+               case SIOCX25CALLACCPTAPPRV: {
+                       rc = -EINVAL;
+                       if (sk->sk_state != TCP_CLOSE)
+                               break;
+                       x25->accptapprv = X25_ALLOW_ACCPT_APPRV;
+                       rc = 0;
+                       break;
+               }
+
+               case SIOCX25SENDCALLACCPT:  {
+                       rc = -EINVAL;
+                       if (sk->sk_state != TCP_ESTABLISHED)
+                               break;
+                       if (x25->accptapprv)    /* must call accptapprv above */
+                               break;
+                       x25_write_internal(sk, X25_CALL_ACCEPTED);
+                       x25->state = X25_STATE_3;
+                       rc = 0;
+                       break;
+               }
+
                default:
-                       rc = dev_ioctl(cmd, (void *)arg);
+                       rc = -ENOIOCTLCMD;
                        break;
        }
 
        return rc;
 }
 
-struct net_proto_family x25_family_ops = {
+static 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;
+               if (sk)
+                       rc = compat_sock_get_timestamp(sk,
+                                       (struct timeval __user*)argp);
+               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;
+               rc = x25_route_ioctl(cmd, argp);
+               break;
+       case SIOCX25GSUBSCRIP:
+               rc = compat_x25_subscr_ioctl(cmd, argp);
+               break;
+       case SIOCX25SSUBSCRIP:
+               rc = -EPERM;
+               if (!capable(CAP_NET_ADMIN))
+                       break;
+               rc = compat_x25_subscr_ioctl(cmd, argp);
+               break;
+       case SIOCX25GFACILITIES:
+       case SIOCX25SFACILITIES:
+       case SIOCX25GDTEFACILITIES:
+       case SIOCX25SDTEFACILITIES:
+       case SIOCX25GCALLUSERDATA:
+       case SIOCX25SCALLUSERDATA:
+       case SIOCX25GCAUSEDIAG:
+       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 SOCKOPS_WRAPPED(x25_proto_ops) = {
        .family =       AF_X25,
        .owner =        THIS_MODULE,
        .release =      x25_release,
@@ -1330,6 +1567,9 @@ static struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
        .getname =      x25_getname,
        .poll =         datagram_poll,
        .ioctl =        x25_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = compat_x25_ioctl,
+#endif
        .listen =       x25_listen,
        .shutdown =     sock_no_shutdown,
        .setsockopt =   x25_setsockopt,
@@ -1348,7 +1588,7 @@ static struct packet_type x25_packet_type = {
        .func = x25_lapb_receive_frame,
 };
 
-struct notifier_block x25_dev_notifier = {
+static struct notifier_block x25_dev_notifier = {
        .notifier_call = x25_device_event,
 };
 
@@ -1368,6 +1608,11 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
 
 static int __init x25_init(void)
 {
+       int rc = proto_register(&x25_proto, 0);
+
+       if (rc != 0)
+               goto out;
+
        sock_register(&x25_family_ops);
 
        dev_add_pack(&x25_packet_type);
@@ -1380,7 +1625,8 @@ static int __init x25_init(void)
        x25_register_sysctl();
 #endif
        x25_proc_init();
-       return 0;
+out:
+       return rc;
 }
 module_init(x25_init);
 
@@ -1399,6 +1645,7 @@ static void __exit x25_exit(void)
        dev_remove_pack(&x25_packet_type);
 
        sock_unregister(AF_X25);
+       proto_unregister(&x25_proto);
 }
 module_exit(x25_exit);