Extended struct x25_neigh and x25_subscrip_struct to configure following
params through SIOCX25SSUBSCRIP:
  o mode (DTE/DCE)
  o number of channels
  o facilities (packet size, window size)
  o timer T20

Based on this configuration options the follwing changes/extensions
where made:
  o DTE/DCE handling to select the next lc (DCE=from bottom / DTE=from
    top)
  o DTE/DCE handling to set correct clear/reset/restart cause
  o take default facilities from neighbour settings

Signed-off-by: Martin Schiller <m...@dev.tdt.de>
---
 include/net/x25.h        |   7 ++-
 include/uapi/linux/x25.h |  54 ++++++++--------
 net/x25/af_x25.c         | 132 ++++++++++++++++++++++++++++++++-------
 net/x25/x25_facilities.c |   6 +-
 net/x25/x25_link.c       | 104 +++++++++++++++++++++++++-----
 net/x25/x25_subr.c       |  22 ++++++-
 6 files changed, 255 insertions(+), 70 deletions(-)

diff --git a/include/net/x25.h b/include/net/x25.h
index af841c5ede28..6e8600456d39 100644
--- a/include/net/x25.h
+++ b/include/net/x25.h
@@ -140,6 +140,9 @@ struct x25_neigh {
        struct net_device       *dev;
        unsigned int            state;
        unsigned int            extended;
+       unsigned int            dce;
+       unsigned int            lc;
+       struct x25_facilities   facilities;
        struct sk_buff_head     queue;
        unsigned long           t20;
        struct timer_list       t20timer;
@@ -164,6 +167,7 @@ struct x25_sock {
        struct timer_list       timer;
        struct x25_causediag    causediag;
        struct x25_facilities   facilities;
+       unsigned int            socket_defined_facilities;      /* set, if 
facilities changed by SIOCX25SFACILITIES */
        struct x25_dte_facilities dte_facilities;
        struct x25_calluserdata calluserdata;
        unsigned long           vc_facil_mask;  /* inc_call facilities mask */
@@ -215,7 +219,8 @@ int x25_create_facilities(unsigned char *, struct 
x25_facilities *,
                          struct x25_dte_facilities *, unsigned long);
 int x25_negotiate_facilities(struct sk_buff *, struct sock *,
                             struct x25_facilities *,
-                            struct x25_dte_facilities *);
+                               struct x25_dte_facilities *,
+                               struct x25_neigh *);
 void x25_limit_facilities(struct x25_facilities *, struct x25_neigh *);
 
 /* x25_forward.c */
diff --git a/include/uapi/linux/x25.h b/include/uapi/linux/x25.h
index 034b7dc5593a..963848e94880 100644
--- a/include/uapi/linux/x25.h
+++ b/include/uapi/linux/x25.h
@@ -63,31 +63,6 @@ struct sockaddr_x25 {
        struct x25_address sx25_addr;           /* X.121 Address */
 };
 
-/*
- *     DTE/DCE subscription options.
- *
- *      As this is missing lots of options, user should expect major
- *     changes of this structure in 2.5.x which might break compatibilty.
- *      The somewhat ugly dimension 200-sizeof() is needed to maintain
- *     backward compatibility.
- */
-struct x25_subscrip_struct {
-       char device[200-sizeof(unsigned long)];
-       unsigned long   global_facil_mask;      /* 0 to disable negotiation */
-       unsigned int    extended;
-};
-
-/* values for above global_facil_mask */
-
-#define        X25_MASK_REVERSE        0x01    
-#define        X25_MASK_THROUGHPUT     0x02
-#define        X25_MASK_PACKET_SIZE    0x04
-#define        X25_MASK_WINDOW_SIZE    0x08
-
-#define X25_MASK_CALLING_AE 0x10
-#define X25_MASK_CALLED_AE 0x20
-
-
 /*
  *     Routing table control structure.
  */
@@ -127,6 +102,35 @@ struct x25_dte_facilities {
        __u8 called_ae[20];
 };
 
+/*
+ *     DTE/DCE subscription options.
+ *
+ *      As this is missing lots of options, user should expect major
+ *     changes of this structure in 2.5.x which might break compatibilty.
+ *      The somewhat ugly dimension 200-sizeof() is needed to maintain
+ *     backward compatibility.
+ */
+struct x25_subscrip_struct {
+       char device[200-((2 * sizeof(unsigned long)) + sizeof(struct 
x25_facilities) + (2 * sizeof(unsigned int)))];
+       unsigned int            dce;
+       unsigned int            lc;
+       struct x25_facilities   facilities;
+       unsigned long           t20;
+       unsigned long           global_facil_mask;      /* 0 to disable 
negotiation */
+       unsigned int            extended;
+};
+
+/* values for above global_facil_mask */
+
+#define        X25_MASK_REVERSE        0x01
+#define        X25_MASK_THROUGHPUT     0x02
+#define        X25_MASK_PACKET_SIZE    0x04
+#define        X25_MASK_WINDOW_SIZE    0x08
+
+#define X25_MASK_CALLING_AE 0x10
+#define X25_MASK_CALLED_AE 0x20
+
+
 /*
  *     Call User Data structure.
  */
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index d8e5ca251801..439ae65ab7a8 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -72,8 +72,19 @@ static const struct proto_ops x25_proto_ops;
 static const struct x25_address null_x25_address = {"               "};
 
 #ifdef CONFIG_COMPAT
+struct compat_x25_facilities {
+       compat_uint_t   winsize_in, winsize_out;
+       compat_uint_t   pacsize_in, pacsize_out;
+       compat_uint_t   throughput;
+       compat_uint_t   reverse;
+};
+
 struct compat_x25_subscrip_struct {
-       char device[200-sizeof(compat_ulong_t)];
+       char device[200-((2 * sizeof(compat_ulong_t)) + sizeof(struct 
compat_x25_facilities) + (2 * sizeof(compat_uint_t)))];
+       compat_uint_t           dce;
+       compat_uint_t           lc;
+       struct compat_x25_facilities    facilities;
+       compat_ulong_t          t20;
        compat_ulong_t global_facil_mask;
        compat_uint_t extended;
 };
@@ -366,13 +377,26 @@ static unsigned int x25_new_lci(struct x25_neigh *nb)
        unsigned int lci = 1;
        struct sock *sk;
 
-       while ((sk = x25_find_socket(lci, nb)) != NULL) {
-               sock_put(sk);
-               if (++lci == 4096) {
-                       lci = 0;
-                       break;
+       if (nb->dce) {
+               while ((sk = x25_find_socket(lci, nb)) != NULL) {
+                       sock_put(sk);
+                       if (++lci > nb->lc) {
+                               lci = 0;
+                               break;
+                       }
+                       cond_resched();
+               }
+       } else {
+               lci = nb->lc;
+
+               while ((sk = x25_find_socket(lci, nb)) != NULL) {
+                       sock_put(sk);
+                       if (--lci == 0) {
+                               lci = 0;
+                               break;
+                       }
+                       cond_resched();
                }
-               cond_resched();
        }
 
        return lci;
@@ -806,6 +830,10 @@ static int x25_connect(struct socket *sock, struct 
sockaddr *uaddr,
        if (!x25->neighbour)
                goto out_put_route;
 
+       if (!x25->socket_defined_facilities)
+               memcpy(&x25->facilities, &x25->neighbour->facilities,
+                      sizeof(struct x25_facilities));
+
        x25_limit_facilities(&x25->facilities, x25->neighbour);
 
        x25->lci = x25_new_lci(x25->neighbour);
@@ -1039,7 +1067,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct 
x25_neigh *nb,
        /*
         *      Try to reach a compromise on the requested facilities.
         */
-       len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities);
+       len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities, 
nb);
        if (len == -1)
                goto out_sock_put;
 
@@ -1454,10 +1482,15 @@ static int x25_ioctl(struct socket *sock, unsigned int 
cmd, unsigned long arg)
                rc = x25_subscr_ioctl(cmd, argp);
                break;
        case SIOCX25GFACILITIES: {
+               rc = -EINVAL;
                lock_sock(sk);
+               if (sk->sk_state != TCP_ESTABLISHED &&
+                   !x25->socket_defined_facilities)
+                       goto out_gfac_release;
                rc = copy_to_user(argp, &x25->facilities,
                                  sizeof(x25->facilities))
                        ? -EFAULT : 0;
+out_gfac_release:
                release_sock(sk);
                break;
        }
@@ -1471,16 +1504,16 @@ static int x25_ioctl(struct socket *sock, unsigned int 
cmd, unsigned long arg)
                lock_sock(sk);
                if (sk->sk_state != TCP_LISTEN &&
                    sk->sk_state != TCP_CLOSE)
-                       goto out_fac_release;
+                       goto out_sfac_release;
                if (facilities.pacsize_in < X25_PS16 ||
                    facilities.pacsize_in > X25_PS4096)
-                       goto out_fac_release;
+                       goto out_sfac_release;
                if (facilities.pacsize_out < X25_PS16 ||
                    facilities.pacsize_out > X25_PS4096)
-                       goto out_fac_release;
+                       goto out_sfac_release;
                if (facilities.winsize_in < 1 ||
                    facilities.winsize_in > 127)
-                       goto out_fac_release;
+                       goto out_sfac_release;
                if (facilities.throughput) {
                        int out = facilities.throughput & 0xf0;
                        int in  = facilities.throughput & 0x0f;
@@ -1488,19 +1521,20 @@ static int x25_ioctl(struct socket *sock, unsigned int 
cmd, unsigned long arg)
                                facilities.throughput |=
                                        X25_DEFAULT_THROUGHPUT << 4;
                        else if (out < 0x30 || out > 0xD0)
-                               goto out_fac_release;
+                               goto out_sfac_release;
                        if (!in)
                                facilities.throughput |=
                                        X25_DEFAULT_THROUGHPUT;
                        else if (in < 0x03 || in > 0x0D)
-                               goto out_fac_release;
+                               goto out_sfac_release;
                }
                if (facilities.reverse &&
                    (facilities.reverse & 0x81) != 0x81)
-                       goto out_fac_release;
+                       goto out_sfac_release;
                x25->facilities = facilities;
+               x25->socket_defined_facilities = 1;
                rc = 0;
-out_fac_release:
+out_sfac_release:
                release_sock(sk);
                break;
        }
@@ -1652,6 +1686,9 @@ static int compat_x25_subscr_ioctl(unsigned int cmd,
        struct net_device *dev;
        int rc = -EINVAL;
 
+       if (cmd != SIOCX25GSUBSCRIP && cmd != SIOCX25SSUBSCRIP)
+               goto out;
+
        rc = -EFAULT;
        if (copy_from_user(&x25_subscr, x25_subscr32, sizeof(*x25_subscr32)))
                goto out;
@@ -1665,28 +1702,75 @@ static int compat_x25_subscr_ioctl(unsigned int cmd,
        if (nb == NULL)
                goto out_dev_put;
 
-       dev_put(dev);
-
        if (cmd == SIOCX25GSUBSCRIP) {
                read_lock_bh(&x25_neigh_list_lock);
                x25_subscr.extended = nb->extended;
+               x25_subscr.dce               = nb->dce;
+               x25_subscr.lc                = nb->lc;
+               x25_subscr.facilities        = nb->facilities;
+               x25_subscr.t20               = nb->t20;
                x25_subscr.global_facil_mask = nb->global_facil_mask;
                read_unlock_bh(&x25_neigh_list_lock);
                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;
-                       write_lock_bh(&x25_neigh_list_lock);
-                       nb->extended = x25_subscr.extended;
-                       nb->global_facil_mask = x25_subscr.global_facil_mask;
-                       write_unlock_bh(&x25_neigh_list_lock);
+
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (x25_subscr.extended != 0 && x25_subscr.extended != 1)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.dce != 0 && x25_subscr.dce != 1)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.lc < 1 || x25_subscr.lc > 4095)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.pacsize_in < X25_PS16 ||
+                   x25_subscr.facilities.pacsize_in > X25_PS4096)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.pacsize_out < X25_PS16 ||
+                   x25_subscr.facilities.pacsize_out > X25_PS4096)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.winsize_in < 1 ||
+                   x25_subscr.facilities.winsize_in > 127)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.throughput) {
+                       int out = x25_subscr.facilities.throughput & 0xf0;
+                       int in  = x25_subscr.facilities.throughput & 0x0f;
+                       if (!out)
+                               x25_subscr.facilities.throughput |=
+                                       X25_DEFAULT_THROUGHPUT << 4;
+                       else if (out < 0x30 || out > 0xD0)
+                               goto out_dev_and_neigh_put;
+                       if (!in)
+                               x25_subscr.facilities.throughput |=
+                                       X25_DEFAULT_THROUGHPUT;
+                       else if (in < 0x03 || in > 0x0D)
+                               goto out_dev_and_neigh_put;
                }
+               if (x25_subscr.facilities.reverse &&
+                   (x25_subscr.facilities.reverse & 0x81) != 0x81)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.t20 < 1 * HZ || x25_subscr.t20 > 300 * HZ)
+                       goto out_dev_and_neigh_put;
+
+               rc = 0;
+               write_lock_bh(&x25_neigh_list_lock);
+               nb->extended          = x25_subscr.extended;
+               nb->dce               = x25_subscr.dce;
+               nb->lc                = x25_subscr.lc;
+               nb->facilities        = x25_subscr.facilities;
+               nb->t20               = x25_subscr.t20;
+               nb->global_facil_mask = x25_subscr.global_facil_mask;
+               write_unlock_bh(&x25_neigh_list_lock);
        }
+       dev_put(dev);
+
        x25_neigh_put(nb);
 out:
        return rc;
+out_dev_and_neigh_put:
+       x25_neigh_put(nb);
 out_dev_put:
        dev_put(dev);
        goto out;
diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c
index 8e1a49b0c0dc..e6c9f9376206 100644
--- a/net/x25/x25_facilities.c
+++ b/net/x25/x25_facilities.c
@@ -263,13 +263,17 @@ int x25_create_facilities(unsigned char *buffer,
  *     The only real problem is with reverse charging.
  */
 int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk,
-               struct x25_facilities *new, struct x25_dte_facilities *dte)
+               struct x25_facilities *new, struct x25_dte_facilities *dte,
+               struct x25_neigh *nb)
 {
        struct x25_sock *x25 = x25_sk(sk);
        struct x25_facilities *ours = &x25->facilities;
        struct x25_facilities theirs;
        int len;
 
+       if (!x25->socket_defined_facilities)
+               ours = &nb->facilities;
+
        memset(&theirs, 0, sizeof(theirs));
        memcpy(new, ours, sizeof(*new));
        memset(dte, 0, sizeof(*dte));
diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c
index 22055ee40056..fabac6331a59 100644
--- a/net/x25/x25_link.c
+++ b/net/x25/x25_link.c
@@ -125,8 +125,16 @@ static void x25_transmit_restart_request(struct x25_neigh 
*nb)
        *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
        *dptr++ = 0x00;
        *dptr++ = X25_RESTART_REQUEST;
-       *dptr++ = 0x00;
-       *dptr++ = 0;
+
+       *dptr = 0x00;   /* cause */
+
+       /* set bit 8, if DTE and cause != 0x00 */
+       if (!nb->dce && *dptr != 0x00)
+               *dptr |= (unsigned char) 0x80;
+
+       dptr++;
+
+       *dptr++ = 0x00; /* diagnostic */
 
        skb->sk = NULL;
 
@@ -181,8 +189,16 @@ void x25_transmit_clear_request(struct x25_neigh *nb, 
unsigned int lci,
                                         X25_GFI_STDSEQ);
        *dptr++ = (lci >> 0) & 0xFF;
        *dptr++ = X25_CLEAR_REQUEST;
-       *dptr++ = cause;
-       *dptr++ = 0x00;
+
+       *dptr = cause;  /* cause */
+
+       /* set bit 8, if DTE and cause != 0x00 */
+       if (!nb->dce && *dptr != 0x00)
+               *dptr |= (unsigned char) 0x80;
+
+       dptr++;
+
+       *dptr++ = 0x00; /* diagnostic */
 
        skb->sk = NULL;
 
@@ -260,9 +276,19 @@ void x25_link_device_add(struct net_device *dev)
        timer_setup(&nb->t20timer, x25_t20timer_expiry, 0);
 
        dev_hold(dev);
-       nb->dev      = dev;
-       nb->state    = X25_LINK_STATE_0;
-       nb->extended = 0;
+       nb->dev                    = dev;
+       nb->state                  = X25_LINK_STATE_0;
+       nb->extended               = 0;
+       nb->dce                    = 0;
+       nb->lc                     = 10;
+       nb->facilities.winsize_in  = X25_DEFAULT_WINDOW_SIZE;
+       nb->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE;
+       nb->facilities.pacsize_in  = X25_DEFAULT_PACKET_SIZE;
+       nb->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE;
+       nb->facilities.throughput  = 0; /* by default don't negotiate
+                                                  throughput */
+       nb->facilities.reverse     = X25_DEFAULT_REVERSE;
+       nb->t20                    = sysctl_x25_restart_request_timeout;
        /*
         * Enables negotiation
         */
@@ -270,7 +296,6 @@ void x25_link_device_add(struct net_device *dev)
                                       X25_MASK_THROUGHPUT |
                                       X25_MASK_PACKET_SIZE |
                                       X25_MASK_WINDOW_SIZE;
-       nb->t20      = sysctl_x25_restart_request_timeout;
        refcount_set(&nb->refcnt, 1);
 
        write_lock_bh(&x25_neigh_list_lock);
@@ -395,28 +420,75 @@ int x25_subscr_ioctl(unsigned int cmd, void __user *arg)
        if ((nb = x25_get_neigh(dev)) == NULL)
                goto out_dev_put;
 
-       dev_put(dev);
-
        if (cmd == SIOCX25GSUBSCRIP) {
                read_lock_bh(&x25_neigh_list_lock);
                x25_subscr.extended          = nb->extended;
+               x25_subscr.dce               = nb->dce;
+               x25_subscr.lc                = nb->lc;
+               x25_subscr.facilities        = nb->facilities;
+               x25_subscr.t20               = nb->t20;
                x25_subscr.global_facil_mask = nb->global_facil_mask;
                read_unlock_bh(&x25_neigh_list_lock);
                rc = copy_to_user(arg, &x25_subscr,
                                  sizeof(x25_subscr)) ? -EFAULT : 0;
        } else {
                rc = -EINVAL;
-               if (!(x25_subscr.extended && x25_subscr.extended != 1)) {
-                       rc = 0;
-                       write_lock_bh(&x25_neigh_list_lock);
-                       nb->extended         = x25_subscr.extended;
-                       nb->global_facil_mask = x25_subscr.global_facil_mask;
-                       write_unlock_bh(&x25_neigh_list_lock);
+
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (x25_subscr.extended != 0 && x25_subscr.extended != 1)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.dce != 0 && x25_subscr.dce != 1)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.lc < 1 || x25_subscr.lc > 4095)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.pacsize_in < X25_PS16 ||
+                   x25_subscr.facilities.pacsize_in > X25_PS4096)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.pacsize_out < X25_PS16 ||
+                   x25_subscr.facilities.pacsize_out > X25_PS4096)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.winsize_in < 1 ||
+                   x25_subscr.facilities.winsize_in > 127)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.facilities.throughput) {
+                       int out = x25_subscr.facilities.throughput & 0xf0;
+                       int in  = x25_subscr.facilities.throughput & 0x0f;
+                       if (!out)
+                               x25_subscr.facilities.throughput |=
+                                       X25_DEFAULT_THROUGHPUT << 4;
+                       else if (out < 0x30 || out > 0xD0)
+                               goto out_dev_and_neigh_put;
+                       if (!in)
+                               x25_subscr.facilities.throughput |=
+                                       X25_DEFAULT_THROUGHPUT;
+                       else if (in < 0x03 || in > 0x0D)
+                               goto out_dev_and_neigh_put;
                }
+               if (x25_subscr.facilities.reverse &&
+                   (x25_subscr.facilities.reverse & 0x81) != 0x81)
+                       goto out_dev_and_neigh_put;
+               if (x25_subscr.t20 < 1 * HZ || x25_subscr.t20 > 300 * HZ)
+                       goto out_dev_and_neigh_put;
+
+               rc = 0;
+               write_lock_bh(&x25_neigh_list_lock);
+               nb->extended          = x25_subscr.extended;
+               nb->dce               = x25_subscr.dce;
+               nb->lc                = x25_subscr.lc;
+               nb->facilities        = x25_subscr.facilities;
+               nb->t20               = x25_subscr.t20;
+               nb->global_facil_mask = x25_subscr.global_facil_mask;
+               write_unlock_bh(&x25_neigh_list_lock);
        }
+       dev_put(dev);
+
        x25_neigh_put(nb);
 out:
        return rc;
+out_dev_and_neigh_put:
+       x25_neigh_put(nb);
 out_dev_put:
        dev_put(dev);
        goto out;
diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c
index 0285aaa1e93c..b1bbabfbe26f 100644
--- a/net/x25/x25_subr.c
+++ b/net/x25/x25_subr.c
@@ -218,15 +218,31 @@ void x25_write_internal(struct sock *sk, int frametype)
                case X25_CLEAR_REQUEST:
                        dptr    = skb_put(skb, 3);
                        *dptr++ = frametype;
-                       *dptr++ = x25->causediag.cause;
+
+                       *dptr = x25->causediag.cause;
+
+                       /* set bit 8, if DTE and cause != 0x00 */
+                       if (!x25->neighbour->dce && *dptr != 0x00)
+                               *dptr |= (unsigned char) 0x80;
+
+                       dptr++;
+
                        *dptr++ = x25->causediag.diagnostic;
                        break;
 
                case X25_RESET_REQUEST:
                        dptr    = skb_put(skb, 3);
                        *dptr++ = frametype;
-                       *dptr++ = 0x00;         /* XXX */
-                       *dptr++ = 0x00;         /* XXX */
+
+                       *dptr = 0x00;   /* cause */
+
+                       /* set bit 8, if DTE and cause != 0x00 */
+                       if (!x25->neighbour->dce && *dptr != 0x00)
+                               *dptr |= (unsigned char) 0x80;
+
+                       dptr++;
+
+                       *dptr++ = 0x00; /* diagnostic */
                        break;
 
                case X25_RR:
-- 
2.20.1

Reply via email to