diff mbox series

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

Message ID 20201116073149.23219-2-ms@dev.tdt.de (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [1/6] net/x25: add/remove x25_link_device by NETDEV_REGISTER/UNREGISTER | expand

Checks

Context Check Description
netdev/cover_letter warning Series does not have a cover letter
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Guessed tree name to be net-next
netdev/subject_prefix warning Target tree name not specified in the subject
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 5 this patch: 5
netdev/verify_fixes success Link
netdev/checkpatch warning CHECK: No space is necessary after a cast CHECK: Please don't use multiple blank lines CHECK: spaces preferred around that '-' (ctx:VxV) WARNING: 'compatibilty' may be misspelled - perhaps 'compatibility'? WARNING: 'follwing' may be misspelled - perhaps 'following'? WARNING: Block comments use * on subsequent lines WARNING: Block comments use a trailing */ on a separate line WARNING: Missing a blank line after declarations WARNING: line length of 116 exceeds 80 columns WARNING: line length of 118 exceeds 80 columns WARNING: line length of 125 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns
netdev/build_allmodconfig_warn fail Errors and warnings before: 0 this patch: 6
netdev/header_inline success Link
netdev/stable success Stable not CCed

Commit Message

Martin Schiller Nov. 16, 2020, 7:31 a.m. UTC
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@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(-)

Comments

kernel test robot Nov. 16, 2020, 1:10 p.m. UTC | #1
Hi Martin,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]
[also build test ERROR on net/master linus/master sparc-next/master v5.10-rc4 next-20201116]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Martin-Schiller/net-x25-add-remove-x25_link_device-by-NETDEV_REGISTER-UNREGISTER/20201116-153459
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 0064c5c1b3bf2a695c772c90e8dea38426a870ff
config: x86_64-randconfig-a011-20201116 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project c044709b8fbea2a9a375e4173a6bd735f6866c0c)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/2d44533245f7e8388db93c53c26703ac52650e57
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Martin-Schiller/net-x25-add-remove-x25_link_device-by-NETDEV_REGISTER-UNREGISTER/20201116-153459
        git checkout 2d44533245f7e8388db93c53c26703ac52650e57
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> net/x25/af_x25.c:1709:30: error: assigning to 'struct compat_x25_facilities' from incompatible type 'struct x25_facilities'
                   x25_subscr.facilities        = nb->facilities;
                                                ^ ~~~~~~~~~~~~~~
>> net/x25/af_x25.c:1761:24: error: assigning to 'struct x25_facilities' from incompatible type 'struct compat_x25_facilities'
                   nb->facilities        = x25_subscr.facilities;
                                         ^ ~~~~~~~~~~~~~~~~~~~~~
   2 errors generated.

vim +1709 net/x25/af_x25.c

  1678	
  1679	#ifdef CONFIG_COMPAT
  1680	static int compat_x25_subscr_ioctl(unsigned int cmd,
  1681			struct compat_x25_subscrip_struct __user *x25_subscr32)
  1682	{
  1683		struct compat_x25_subscrip_struct x25_subscr;
  1684		struct x25_neigh *nb;
  1685		struct net_device *dev;
  1686		int rc = -EINVAL;
  1687	
  1688		if (cmd != SIOCX25GSUBSCRIP && cmd != SIOCX25SSUBSCRIP)
  1689			goto out;
  1690	
  1691		rc = -EFAULT;
  1692		if (copy_from_user(&x25_subscr, x25_subscr32, sizeof(*x25_subscr32)))
  1693			goto out;
  1694	
  1695		rc = -EINVAL;
  1696		dev = x25_dev_get(x25_subscr.device);
  1697		if (dev == NULL)
  1698			goto out;
  1699	
  1700		nb = x25_get_neigh(dev);
  1701		if (nb == NULL)
  1702			goto out_dev_put;
  1703	
  1704		if (cmd == SIOCX25GSUBSCRIP) {
  1705			read_lock_bh(&x25_neigh_list_lock);
  1706			x25_subscr.extended = nb->extended;
  1707			x25_subscr.dce		     = nb->dce;
  1708			x25_subscr.lc		     = nb->lc;
> 1709			x25_subscr.facilities	     = nb->facilities;
  1710			x25_subscr.t20		     = nb->t20;
  1711			x25_subscr.global_facil_mask = nb->global_facil_mask;
  1712			read_unlock_bh(&x25_neigh_list_lock);
  1713			rc = copy_to_user(x25_subscr32, &x25_subscr,
  1714					sizeof(*x25_subscr32)) ? -EFAULT : 0;
  1715		} else {
  1716			rc = -EINVAL;
  1717	
  1718			if (dev->flags & IFF_UP)
  1719				return -EBUSY;
  1720	
  1721			if (x25_subscr.extended != 0 && x25_subscr.extended != 1)
  1722				goto out_dev_and_neigh_put;
  1723			if (x25_subscr.dce != 0 && x25_subscr.dce != 1)
  1724				goto out_dev_and_neigh_put;
  1725			if (x25_subscr.lc < 1 || x25_subscr.lc > 4095)
  1726				goto out_dev_and_neigh_put;
  1727			if (x25_subscr.facilities.pacsize_in < X25_PS16 ||
  1728			    x25_subscr.facilities.pacsize_in > X25_PS4096)
  1729				goto out_dev_and_neigh_put;
  1730			if (x25_subscr.facilities.pacsize_out < X25_PS16 ||
  1731			    x25_subscr.facilities.pacsize_out > X25_PS4096)
  1732				goto out_dev_and_neigh_put;
  1733			if (x25_subscr.facilities.winsize_in < 1 ||
  1734			    x25_subscr.facilities.winsize_in > 127)
  1735				goto out_dev_and_neigh_put;
  1736			if (x25_subscr.facilities.throughput) {
  1737				int out = x25_subscr.facilities.throughput & 0xf0;
  1738				int in  = x25_subscr.facilities.throughput & 0x0f;
  1739				if (!out)
  1740					x25_subscr.facilities.throughput |=
  1741						X25_DEFAULT_THROUGHPUT << 4;
  1742				else if (out < 0x30 || out > 0xD0)
  1743					goto out_dev_and_neigh_put;
  1744				if (!in)
  1745					x25_subscr.facilities.throughput |=
  1746						X25_DEFAULT_THROUGHPUT;
  1747				else if (in < 0x03 || in > 0x0D)
  1748					goto out_dev_and_neigh_put;
  1749			}
  1750			if (x25_subscr.facilities.reverse &&
  1751			    (x25_subscr.facilities.reverse & 0x81) != 0x81)
  1752				goto out_dev_and_neigh_put;
  1753			if (x25_subscr.t20 < 1 * HZ || x25_subscr.t20 > 300 * HZ)
  1754				goto out_dev_and_neigh_put;
  1755	
  1756			rc = 0;
  1757			write_lock_bh(&x25_neigh_list_lock);
  1758			nb->extended	      = x25_subscr.extended;
  1759			nb->dce		      = x25_subscr.dce;
  1760			nb->lc		      = x25_subscr.lc;
> 1761			nb->facilities	      = x25_subscr.facilities;
  1762			nb->t20		      = x25_subscr.t20;
  1763			nb->global_facil_mask = x25_subscr.global_facil_mask;
  1764			write_unlock_bh(&x25_neigh_list_lock);
  1765		}
  1766		dev_put(dev);
  1767	
  1768		x25_neigh_put(nb);
  1769	out:
  1770		return rc;
  1771	out_dev_and_neigh_put:
  1772		x25_neigh_put(nb);
  1773	out_dev_put:
  1774		dev_put(dev);
  1775		goto out;
  1776	}
  1777	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

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: