From patchwork Mon Nov 16 07:31:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Schiller X-Patchwork-Id: 1400673 Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=dev.tdt.de Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4CZLPk1FR8z9sSn for ; Mon, 16 Nov 2020 18:32:26 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727739AbgKPHcU (ORCPT ); Mon, 16 Nov 2020 02:32:20 -0500 Received: from mxout70.expurgate.net ([91.198.224.70]:16796 "EHLO mxout70.expurgate.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726247AbgKPHcU (ORCPT ); Mon, 16 Nov 2020 02:32:20 -0500 Received: from [127.0.0.1] (helo=localhost) by relay.expurgate.net with smtp (Exim 4.92) (envelope-from ) id 1keYzm-000If8-Mh; Mon, 16 Nov 2020 08:32:14 +0100 Received: from [195.243.126.94] (helo=securemail.tdt.de) by relay.expurgate.net with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1keYzl-000T7q-Vh; Mon, 16 Nov 2020 08:32:14 +0100 Received: from securemail.tdt.de (localhost [127.0.0.1]) by securemail.tdt.de (Postfix) with ESMTP id 9B8F9240049; Mon, 16 Nov 2020 08:32:13 +0100 (CET) Received: from mail.dev.tdt.de (unknown [10.2.4.42]) by securemail.tdt.de (Postfix) with ESMTP id 1D623240047; Mon, 16 Nov 2020 08:32:13 +0100 (CET) Received: from mschiller01.dev.tdt.de (unknown [10.2.3.20]) by mail.dev.tdt.de (Postfix) with ESMTPSA id 6D07020115; Mon, 16 Nov 2020 08:32:12 +0100 (CET) From: Martin Schiller To: andrew.hendry@gmail.com, davem@davemloft.net, kuba@kernel.org, xie.he.0141@gmail.com Cc: linux-x25@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Martin Schiller Subject: [PATCH 2/6] net/x25: make neighbour params configurable Date: Mon, 16 Nov 2020 08:31:45 +0100 Message-ID: <20201116073149.23219-2-ms@dev.tdt.de> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201116073149.23219-1-ms@dev.tdt.de> References: <20201116073149.23219-1-ms@dev.tdt.de> MIME-Version: 1.0 X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.dev.tdt.de X-purgate: clean X-purgate-ID: 151534::1605511934-0001632B-B9FFA228/0/0 X-purgate-type: clean Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 Reported-by: kernel test robot --- 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: