[PATCH 2/6] net/x25: make neighbour params configurable

From: Martin Schiller
Date: Mon Nov 16 2020 - 02:33:04 EST


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 <ms@xxxxxxxxxx>
---
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