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 following 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> --- Change from v1: o fix 'subject_prefix' and 'checkpatch' warnings o fix incompatible assignment of 'struct compat_x25_facilities' --- include/net/x25.h | 8 ++- include/uapi/linux/x25.h | 56 ++++++++------- net/x25/af_x25.c | 145 ++++++++++++++++++++++++++++++++------- net/x25/x25_facilities.c | 6 +- net/x25/x25_link.c | 97 ++++++++++++++++++++++---- net/x25/x25_subr.c | 22 +++++- 6 files changed, 268 insertions(+), 66 deletions(-) diff --git a/include/net/x25.h b/include/net/x25.h index 4c1502e8b2b2..ec00f595fcc6 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,8 @@ struct x25_sock { struct timer_list timer; struct x25_causediag causediag; struct x25_facilities facilities; + /* set, if facilities changed by SIOCX25SFACILITIES */ + unsigned int socket_defined_facilities; struct x25_dte_facilities dte_facilities; struct x25_calluserdata calluserdata; unsigned long vc_facil_mask; /* inc_call facilities mask */ @@ -215,7 +220,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..094dc2cff37b 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,37 @@ 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 compatibility. + * 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 d2a52c254cca..4c2a395fdbdb 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -72,8 +72,21 @@ 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; }; @@ -373,13 +386,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; @@ -813,6 +839,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); @@ -1046,7 +1076,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; @@ -1460,10 +1490,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; } @@ -1477,16 +1512,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; @@ -1494,19 +1529,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; } @@ -1658,6 +1694,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; @@ -1671,28 +1710,86 @@ 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.winsize_in = nb->facilities.winsize_in; + x25_subscr.facilities.winsize_out = nb->facilities.winsize_out; + x25_subscr.facilities.pacsize_in = nb->facilities.pacsize_in; + x25_subscr.facilities.pacsize_out = nb->facilities.pacsize_out; + x25_subscr.facilities.throughput = nb->facilities.throughput; + x25_subscr.facilities.reverse = nb->facilities.reverse; + 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.winsize_in = x25_subscr.facilities.winsize_in; + nb->facilities.winsize_out = x25_subscr.facilities.winsize_out; + nb->facilities.pacsize_in = x25_subscr.facilities.pacsize_in; + nb->facilities.pacsize_out = x25_subscr.facilities.pacsize_out; + nb->facilities.throughput = x25_subscr.facilities.throughput; + nb->facilities.reverse = x25_subscr.facilities.reverse; + 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 92828a8a4ada..2af50d585b4b 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; @@ -261,6 +277,15 @@ void x25_link_device_add(struct net_device *dev) 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; + /* by default don't negotiate throughput */ + nb->facilities.throughput = 0; + nb->facilities.reverse = X25_DEFAULT_REVERSE; /* * Enables negotiation */ @@ -389,28 +414,76 @@ 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..c195d1c89ad7 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